패스잇

백엔드 개발

JPA / ORM 면접 질문

영속성 컨텍스트, 지연 로딩과 N+1, 더티 체킹, 1차/2차 캐시 — JPA 면접은 ORM이 만들어 내는 추상화 뒤의 SQL을 이해하는지를 묻습니다. 흔히 놓치는 함정을 모범답안과 함께 정리했습니다.

총 21문제 · 기초 7 · 중급 7 · 심화 7 · 모범답안 포함

JPA / ORM 면접 질문 — 기초

Q1 기초

JPA에서 @Embedded와 @Embeddable의 용도와 사용법에 대해 설명해주세요.

힌트 · 값 타입 컬렉션과 엔티티의 차이점을 생각해보세요.

JPA에서 @Embedded와 @Embeddable은 값 타입을 정의하고 사용하는 데 사용됩니다.

전체 모범답안 펼치기

JPA에서 @Embedded@Embeddable은 값 타입을 정의하고 사용하는 데 사용됩니다.

@Embeddable은 값 타입 클래스 자체에 붙여서, 이 클래스가 다른 엔티티에 포함될 수 있는 값 타입임을 명시합니다. 예를 들어 주소 정보를 담는 Address 클래스에 @Embeddable을 붙일 수 있습니다.

@Embedded는 엔티티 클래스 내에서 @Embeddable로 정의된 값 타입을 필드로 선언할 때 사용합니다. 이를 통해 엔티티는 값 타입의 속성들을 자신의 속성처럼 사용할 수 있게 됩니다. 객체 합성을 통해 코드 재사용성을 높이고, 엔티티를 더 의미있게 구성할 수 있습니다.

값 타입은 엔티티와 달리 식별자를 가지지 않고, 속성 값으로만 구별됩니다. 값 타입의 속성이 변경되면, 해당 값 타입 전체를 새로운 값으로 교체하는 방식으로 동작합니다.

#값 타입#재사용성#객체 합성#@Embeddable#@Embedded

이 질문 단독 페이지 →

Q2 기초

Spring Data JPA의 Query Method 네이밍 규칙과 사용법에 대해 설명해주세요.

힌트 · findBy, countBy, existsBy 등의 키워드와 조합을 설명해보세요.

Spring Data JPA의 Query Method는 메서드 이름만으로 쿼리를 자동으로 생성해주는 편리한 기능입니다.

전체 모범답안 펼치기

Spring Data JPA의 Query Method는 메서드 이름만으로 쿼리를 자동으로 생성해주는 편리한 기능입니다.

가장 기본적인 규칙은 findBy 키워드로 시작하는 것입니다. 예를 들어 findByEmail(String email) 이라는 메서드를 정의하면, email 컬럼과 일치하는 엔티티를 찾는 쿼리가 자동으로 생성됩니다.

And, Or 키워드를 사용해서 조건을 조합할 수도 있습니다. findByFirstNameAndLastName(String firstName, String lastName)처럼 사용하면 firstName과 lastName이 모두 일치하는 엔티티를 찾습니다.

countBy, existsBy 같은 키워드를 사용해서 개수를 세거나 존재 여부를 확인할 수도 있습니다.

만약 복잡한 쿼리가 필요하다면 @Query 어노테이션을 사용해서 직접 JPQL이나 Native SQL 쿼리를 정의할 수도 있습니다. 하지만 간단한 조회 조건이라면 Query Method 네이밍 규칙을 활용하는 것이 코드 가독성을 높이고 개발 생산성을 향상시키는 데 도움이 됩니다.

#메서드_이름#쿼리_자동_생성#findBy#And/Or#Custom_Query

이 질문 단독 페이지 →

Q3 기초

JPA에서 엔티티 설계 시 @Setter 사용을 지양하는 이유와 대안은 무엇인가요?

힌트 · 객체의 일관성 유지와 비즈니스 메서드 사용을 생각해보세요.

JPA 엔티티 설계 시 @Setter를 지양하는 이유는 객체의 일관성을 해칠 수 있기 때문입니다. @Setter를 무분별하게 사용하면 객체의 상태를 예측하기 어려워지고, SideEffect가 발생할 가능성이 높아집니…

전체 모범답안 펼치기

JPA 엔티티 설계 시 @Setter를 지양하는 이유는 객체의 일관성을 해칠 수 있기 때문입니다. @Setter를 무분별하게 사용하면 객체의 상태를 예측하기 어려워지고, Side-Effect가 발생할 가능성이 높아집니다.

대안으로는 다음과 같은 방법들이 있습니다.

  1. 생성자를 통한 초기화: 객체 생성 시 필요한 값을 받아 불변성을 확보합니다.
  2. 비즈니스 로직 메서드 활용: 엔티티 내에서 상태를 변경하는 메서드를 직접 정의하여, 객체의 상태 변화를 제어하고 도메인 로직을 캡슐화합니다. 예를 들어, order.cancel() 메서드처럼 객체의 상태 변화를 명확하게 표현하는 것이죠.
  3. 필요한 경우 제한적인 Setter 사용: 정말 필요한 경우에만, 그리고 그 이유를 명확히 설명할 수 있을 때만 @Setter를 사용합니다.

이러한 방법을 통해 객체지향적인 설계를 유지하고, 데이터의 일관성을 확보할 수 있습니다.

#불변성#객체지향#Side-Effect#도메인_로직#생성자

이 질문 단독 페이지 →

Q4 기초

JPA Auditing(@CreatedDate, @LastModifiedDate)의 설정 방법과 동작 원리를 설명해주세요.

힌트 · @EnableJpaAuditing과 @EntityListeners를 생각해보세요.

JPA Auditing은 엔티티의 생성 시간이나 수정 시간을 자동으로 기록해주는 기능입니다. 설정 방법은 크게 두 가지 단계로 나눌 수 있습니다.

전체 모범답안 펼치기

JPA Auditing은 엔티티의 생성 시간이나 수정 시간을 자동으로 기록해주는 기능입니다. 설정 방법은 크게 두 가지 단계로 나눌 수 있습니다.

먼저, Spring Boot 애플리케이션의 설정 클래스에 @EnableJpaAuditing 어노테이션을 추가하여 Auditing 기능을 활성화합니다.

다음으로, Auditing 정보를 관리할 BaseEntity 클래스를 만들고, @MappedSuperclass 어노테이션을 붙여 상속을 통해 엔티티들이 해당 필드를 갖도록 합니다. BaseEntity에는 @CreatedDate, @LastModifiedDate 등의 어노테이션을 사용하여 생성일과 수정일 필드를 정의합니다. 필요에 따라 @CreatedBy, @LastModifiedBy를 사용하여 생성자, 수정자를 기록할 수도 있습니다. 이때, 누가 생성하고 수정했는지 기록하려면 AuditorAware 인터페이스를 구현하여 현재 사용자의 정보를 제공해야 합니다.

동작 원리는 JPA가 엔티티의 상태 변화를 감지하고, Persistence Context에 반영하기 전에 엔티티 리스너를 통해 Auditing 관련 어노테이션이 붙은 필드에 값을 자동으로 설정하는 방식입니다.

#@EnableJpaAuditing#BaseEntity#AuditorAware#Persistence Context#엔티티 리스너

이 질문 단독 페이지 →

Q5 기초

JPA에서 @GeneratedValue 전략(IDENTITY, SEQUENCE, TABLE, AUTO)의 차이점은?

힌트 · 각 전략의 동작 방식과 배치 INSERT 영향을 비교해보세요.

JPA에서 @GeneratedValue는 엔티티의 기본 키 값을 자동으로 생성하는 전략을 지정합니다. 네 가지 주요 전략이 있습니다.

전체 모범답안 펼치기

JPA에서 @GeneratedValue는 엔티티의 기본 키 값을 자동으로 생성하는 전략을 지정합니다. 네 가지 주요 전략이 있습니다.

IDENTITY 전략은 데이터베이스의 자동 증가 컬럼 기능을 사용합니다. 엔티티를 저장할 때 데이터베이스가 즉시 ID를 할당하므로, 배치 INSERT 시 각 INSERT 문이 개별적으로 실행되어 성능 저하가 발생할 수 있습니다.

SEQUENCE 전략은 데이터베이스 시퀀스를 사용하여 ID를 생성합니다. 엔티티를 저장하기 전에 시퀀스에서 다음 값을 가져오므로, 배치 INSERT 시에도 시퀀스 값을 미리 가져와 사용할 수 있어 성능에 유리합니다.

TABLE 전략은 별도의 테이블을 생성하여 ID를 관리합니다. 이 테이블에 ID 값을 저장하고 관리하는 방식으로, 데이터베이스 종류에 상관없이 일관된 ID 생성이 가능하지만, 추가적인 테이블 접근으로 인해 성능 오버헤드가 발생할 수 있습니다.

AUTO 전략은 JPA 구현체(예: Hibernate)가 데이터베이스 종류와 설정을 기반으로 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택합니다. 가장 유연하지만, 어떤 전략이 선택될지 예측하기 어려울 수 있습니다.

요약하자면, IDENTITY는 DB 의존적이고 배치 INSERT에 불리하며, SEQUENCE는 DB 시퀀스를 활용해 배치 INSERT에 유리합니다. TABLE은 범용적이지만 성능 오버헤드가 있고, AUTO는 자동 선택 전략입니다.

#IDENTITY#SEQUENCE#TABLE#AUTO#데이터베이스

이 질문 단독 페이지 →

Q6 기초

JPA에서 엔티티 상태(비영속, 영속, 준영속, 삭제)에 대해 설명해주세요.

힌트 · 각 상태에서 영속성 컨텍스트와의 관계를 설명해보세요.

JPA에서 엔티티 상태는 크게 4가지로 나눌 수 있습니다.

전체 모범답안 펼치기

JPA에서 엔티티 상태는 크게 4가지로 나눌 수 있습니다.

첫째, 비영속(New/Transient) 상태는 엔티티 객체가 생성되었지만 아직 영속성 컨텍스트와 관련이 없는 상태입니다. 단순히 new 키워드로 생성된 객체라고 생각하시면 됩니다.

둘째, 영속(Managed) 상태는 엔티티 매니저를 통해 영속성 컨텍스트에 저장되어 관리되는 상태입니다. EntityManager.persist() 메서드를 통해 영속 상태로 만들 수 있고, 트랜잭션이 커밋될 때 데이터베이스에 반영됩니다. 영속성 컨텍스트에 의해 변경사항이 추적됩니다.

셋째, 준영속(Detached) 상태는 영속성 컨텍스트에 의해 관리되다가 분리된 상태입니다. EntityManager.detach() 메서드나 영속성 컨텍스트가 종료될 때 발생합니다. 더 이상 영속성 컨텍스트의 관리를 받지 않지만, 데이터베이스에는 여전히 존재합니다.

마지막으로, 삭제(Removed) 상태는 엔티티가 영속성 컨텍스트에서 삭제된 상태입니다. EntityManager.remove() 메서드를 통해 삭제 상태로 만들 수 있으며, 트랜잭션 커밋 시 데이터베이스에서도 삭제됩니다.

각 상태는 영속성 컨텍스트와의 관계에 따라 구분되며, 엔티티 매니저를 통해 상태를 변경하고 관리할 수 있습니다.

#영속성 컨텍스트#EntityManager#트랜잭션#객체 그래프#flush

이 질문 단독 페이지 →

Q7 기초

JPA(Java Persistence API)란 무엇이며, 자바 애플리케이션에서 어떤 역할을 하는 기술 표준인가요?

힌트 · JPA는 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스 집합이자 표준 명세입니다. 객체-관계 매핑(ORM)을 통해 객체 지향적인 방식으로 데이터베이스를 조작할 수 있도록 돕습니다.

JPA는 Java Persistence API의 약자로, 자바 애플리케이션에서 관계형 데이터베이스를 다루기 위한 표준 명세입니다. 쉽게 말해, 데이터베이스와 자바 객체 사이의 '다리' 역할을 한다고 생각하시면 됩니다…

전체 모범답안 펼치기

JPA는 Java Persistence API의 약자로, 자바 애플리케이션에서 관계형 데이터베이스를 다루기 위한 표준 명세입니다. 쉽게 말해, 데이터베이스와 자바 객체 사이의 '다리' 역할을 한다고 생각하시면 됩니다.

JPA의 핵심은 ORM(Object-Relational Mapping) 기술을 제공한다는 점입니다. ORM을 통해 개발자는 객체 지향적인 코드로 데이터베이스를 조작할 수 있습니다. 예를 들어, 데이터베이스 테이블을 직접 쿼리하는 대신, 자바 객체를 생성하고 수정하는 방식으로 데이터를 관리할 수 있습니다.

JPA를 사용하면 데이터베이스 벤더에 종속되지 않고, 표준화된 방식으로 데이터베이스 작업을 수행할 수 있다는 장점이 있습니다. Hibernate나 EclipseLink 같은 JPA 구현체를 사용하면 JPA 명세에 따라 데이터베이스 연동을 쉽게 처리할 수 있습니다.

#ORM#영속성#엔티티#데이터베이스#자바

이 질문 단독 페이지 →

읽기만으론 부족합니다 — 직접 말해보세요

패스잇 앱에서 JPA / ORM 질문에 직접 답하면 AI가 1:1로 답변을 코칭합니다.

JPA / ORM 면접 질문 — 중급

Q8 중급

JPA의 N+1 문제가 무엇이며, 어떻게 해결할 수 있나요?

힌트 · Fetch Join, @EntityGraph, Batch Size 등을 생각해보세요.

JPA의 N+1 문제는 연관 관계가 있는 엔티티를 조회할 때 발생하는 성능 문제입니다. 예를 들어, 한 명의 사용자가 여러 개의 게시글을 가지고 있을 때, 사용자를 조회하는 쿼리 한 번에, 각 사용자의 게시글을 조회…

전체 모범답안 펼치기

JPA의 N+1 문제는 연관 관계가 있는 엔티티를 조회할 때 발생하는 성능 문제입니다. 예를 들어, 한 명의 사용자가 여러 개의 게시글을 가지고 있을 때, 사용자를 조회하는 쿼리 한 번에, 각 사용자의 게시글을 조회하는 쿼리가 사용자 수만큼 추가적으로 발생하는 현상입니다.

해결 방법으로는 크게 세 가지가 있습니다. 첫째, Fetch Join을 사용해서 한 번의 쿼리로 필요한 연관 엔티티를 모두 가져올 수 있습니다. 둘째, @EntityGraph 어노테이션을 사용하여 필요한 연관 관계를 명시적으로 지정하여 로딩할 수 있습니다. 셋째, Batch Size를 설정하여 지정된 개수만큼의 연관 엔티티를 한 번에 로딩하도록 최적화할 수 있습니다. LAZY 로딩 전략을 사용하는 경우, 필요할 때만 연관 엔티티를 로딩하도록 설정하여 초기 로딩 성능을 개선할 수 있지만, 잘못 사용하면 N+1 문제를 야기할 수 있으므로 주의해야 합니다.

#N+1 문제#fetch join#EntityGraph#Batch Size#LAZY 로딩

이 질문 단독 페이지 →

Q9 중급

JPA의 영속성 컨텍스트(Persistence Context)란 무엇이며, 1차 캐시의 역할은 무엇인가요?

힌트 · 엔티티의 생명주기와 영속성 컨텍스트의 관계를 설명해보세요.

JPA의 영속성 컨텍스트는 엔티티를 영구 저장하기 전에 잠시 보관하는 논리적인 영역입니다. 엔티티 매니저를 통해 접근하며, 엔티티의 생명 주기를 관리하는 역할을 합니다.

전체 모범답안 펼치기

JPA의 영속성 컨텍스트는 엔티티를 영구 저장하기 전에 잠시 보관하는 논리적인 영역입니다. 엔티티 매니저를 통해 접근하며, 엔티티의 생명 주기를 관리하는 역할을 합니다.

1차 캐시는 영속성 컨텍스트 내부에 존재하는 캐시입니다. 엔티티를 조회할 때 데이터베이스에 직접 접근하지 않고 1차 캐시에서 먼저 찾아 성능을 향상시킵니다. 또한, 영속성 컨텍스트 내에서 엔티티의 동일성을 보장하는 중요한 역할을 합니다. 같은 엔티티를 여러 번 조회해도 1차 캐시 덕분에 항상 동일한 객체를 반환받을 수 있습니다.

예를 들어, em.find(Member.class, 1L)을 여러 번 호출해도 1차 캐시에 있는 동일한 Member 객체를 반환합니다. 쓰기 지연 기능도 영속성 컨텍스트를 통해 구현됩니다. 변경된 엔티티는 바로 데이터베이스에 반영되지 않고 영속성 컨텍스트에 보관되다가 트랜잭션 커밋 시점에 일괄적으로 반영됩니다.

#영속성 컨텍스트#엔티티 매니저#1차 캐시#동일성 보장#쓰기 지연

이 질문 단독 페이지 →

Q10 중급

JPA에서 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)의 차이점과 사용 시 주의사항은?

힌트 · 성능과 프록시 객체의 관점에서 설명해보세요.

JPA에서 지연 로딩과 즉시 로딩은 연관된 엔티티를 언제 데이터베이스에서 가져올지 결정하는 방식입니다.

전체 모범답안 펼치기

JPA에서 지연 로딩과 즉시 로딩은 연관된 엔티티를 언제 데이터베이스에서 가져올지 결정하는 방식입니다.

지연 로딩(Lazy Loading)은 연관된 엔티티가 실제로 사용될 때까지 로딩을 미룹니다. 예를 들어, Member 엔티티를 조회할 때 Team 엔티티는 프록시 객체로만 존재하고, team.getName()처럼 실제 Team 데이터에 접근할 때 데이터베이스에서 가져옵니다. 장점은 초기 조회 성능을 향상시킬 수 있다는 것이지만, 필요할 때마다 쿼리가 실행되어 N+1 문제를 야기할 수 있습니다.

즉시 로딩(Eager Loading)은 엔티티를 조회할 때 연관된 엔티티까지 한 번에 가져옵니다. Member 엔티티를 조회할 때 Team 엔티티도 함께 로딩되는 것이죠. 장점은 한 번에 필요한 데이터를 모두 가져오므로 N+1 문제를 방지할 수 있지만, 사용하지 않는 연관 엔티티까지 로딩하여 초기 조회 성능을 저하시킬 수 있습니다.

사용 시 주의사항은 다음과 같습니다. 지연 로딩은 N+1 문제를 주의해야 하며, 필요한 경우 Fetch Join을 사용하여 해결할 수 있습니다. 즉시 로딩은 예상치 못한 연관 엔티티까지 로딩될 수 있으므로, 정말 필요한 경우에만 사용하는 것이 좋습니다. 상황에 따라 적절한 로딩 전략을 선택하고, 성능 테스트를 통해 최적의 방법을 찾는 것이 중요합니다.

#지연 로딩#즉시 로딩#N+1 문제#성능#FetchType

이 질문 단독 페이지 →

Q11 중급

JPA의 더티 체킹(Dirty Checking)이란 무엇이며, 어떻게 동작하나요?

힌트 · 스냅샷 비교와 트랜잭션 커밋 시점의 동작을 설명해보세요.

JPA의 더티 체킹은 영속성 컨텍스트가 관리하는 엔티티의 변경 사항을 자동으로 감지하고 데이터베이스에 반영하는 메커니즘입니다.

전체 모범답안 펼치기

JPA의 더티 체킹은 영속성 컨텍스트가 관리하는 엔티티의 변경 사항을 자동으로 감지하고 데이터베이스에 반영하는 메커니즘입니다.

동작 방식은 이렇습니다. 엔티티를 영속성 컨텍스트에 처음 로드할 때, JPA는 해당 엔티티의 스냅샷을 찍어 보관합니다. 트랜잭션이 커밋되는 시점에, 현재 엔티티의 상태와 스냅샷을 비교하여 변경된 부분이 있는지 확인합니다.

만약 변경된 부분이 있다면, JPA는 자동으로 UPDATE 쿼리를 생성하여 데이터베이스에 변경 사항을 반영합니다. 개발자가 직접 EntityManager.persist() 같은 메서드를 호출하지 않아도 변경 사항이 자동으로 업데이트되는 것이죠. 이 덕분에 개발자는 엔티티의 변경 사항을 일일이 추적하고 업데이트하는 번거로움을 덜 수 있습니다.

#영속성 컨텍스트#변경 감지#스냅샷#트랜잭션 커밋#자동 업데이트

이 질문 단독 페이지 →

Q12 중급

JPA의 @ManyToOne, @OneToMany 관계에서 양방향 매핑 시 주의사항은 무엇인가요?

힌트 · 연관관계의 주인, 편의 메서드, 무한 루프 방지를 생각해보세요.

JPA에서 @ManyToOne, @OneToMany 양방향 매핑 시 주의할 점은 연관관계의 주인 설정입니다. 한쪽은 주인이 되어야 하고, 주인만이 데이터베이스에 반영됩니다. 주인이 아닌 쪽은 mappedBy 속성을…

전체 모범답안 펼치기

JPA에서 @ManyToOne, @OneToMany 양방향 매핑 시 주의할 점은 연관관계의 주인 설정입니다. 한쪽은 주인이 되어야 하고, 주인만이 데이터베이스에 반영됩니다. 주인이 아닌 쪽은 mappedBy 속성을 사용해 주인을 지정해야 합니다.

또 다른 주의사항은 순환 참조 문제입니다. A가 B를 참조하고, B가 다시 A를 참조하면 toString(), equals(), hashCode() 구현 시 StackOverflowError가 발생하거나 JSON 직렬화 시 무한 루프에 빠질 수 있습니다. 이를 방지하기 위해 한쪽에서만 참조하거나, @JsonIgnore 등을 사용해야 합니다.

마지막으로, 객체 그래프의 일관성을 유지하기 위해 편의 메서드를 사용하는 것이 좋습니다. 예를 들어, Order에 Product를 추가하는 메서드를 만들어서 양쪽 연관관계를 한 번에 관리하는 것이죠. 영속성 컨텍스트와 데이터베이스의 불일치를 막을 수 있습니다.

#순환참조#StackOverflowError#JSON 직렬화#영속성 컨텍스트#cascade

이 질문 단독 페이지 →

Q13 중급

JPQL과 QueryDSL의 차이점과 각각의 장단점에 대해 설명해주세요.

힌트 · 타입 안전성, 동적 쿼리, 학습 곡선을 비교해보세요.

JPQL과 QueryDSL은 JPA를 사용할 때 쿼리를 작성하는 방식입니다. JPQL은 SQL과 유사한 문자열 기반의 쿼리 언어이고, QueryDSL은 자바 코드로 쿼리를 작성할 수 있게 해주는 프레임워크입니다.

전체 모범답안 펼치기

JPQL과 QueryDSL은 JPA를 사용할 때 쿼리를 작성하는 방식입니다. JPQL은 SQL과 유사한 문자열 기반의 쿼리 언어이고, QueryDSL은 자바 코드로 쿼리를 작성할 수 있게 해주는 프레임워크입니다.

가장 큰 차이점은 타입 안전성입니다. JPQL은 컴파일 시점에 오류를 잡기 어렵지만, QueryDSL은 컴파일 시점에 타입 오류를 발견할 수 있습니다.

동적 쿼리 측면에서는 QueryDSL이 훨씬 강력합니다. 조건에 따라 쿼리를 유연하게 변경하기 쉽습니다. JPQL은 문자열을 조합해야 해서 복잡해질 수 있습니다.

학습 곡선은 JPQL이 더 낮습니다. SQL에 익숙하다면 쉽게 배울 수 있습니다. QueryDSL은 별도의 학습이 필요하지만, 익숙해지면 생산성이 높아집니다.

간단히 요약하면, JPQL은 배우기 쉽지만 타입 안전성이 떨어지고 동적 쿼리에 약합니다. QueryDSL은 학습 곡선이 있지만 타입 안전성이 높고 동적 쿼리에 강력합니다. 프로젝트의 복잡도와 요구사항에 따라 적절한 기술을 선택하는 것이 중요합니다.

#JPQL#QueryDSL#ORM#타입 안정성#동적 쿼리

이 질문 단독 페이지 →

Q14 중급

@Transactional의 readOnly 속성은 어떤 역할을 하며, 성능에 어떤 영향을 주나요?

힌트 · 더티 체킹 스킵, 플러시 모드, DB 최적화를 생각해보세요.

@Transactional의 readOnly 속성은 트랜잭션을 읽기 전용으로 설정하는 역할을 합니다. 이 속성을 true로 설정하면 JPA는 몇 가지 최적화를 수행합니다.

전체 모범답안 펼치기

@Transactional의 readOnly 속성은 트랜잭션을 읽기 전용으로 설정하는 역할을 합니다. 이 속성을 true로 설정하면 JPA는 몇 가지 최적화를 수행합니다.

가장 중요한 것은 더티 체킹을 스킵한다는 점입니다. 데이터베이스에서 읽어온 엔티티의 변경 사항을 감지하고 자동으로 업데이트하는 과정을 생략하여 연산 비용을 줄입니다. 또한, Flush Mode를 MANUAL로 설정하여 불필요한 flush를 방지합니다.

readOnly 속성을 사용하면 데이터베이스 커넥션에 대한 자원을 효율적으로 사용할 수 있고, 트랜잭션 격리 수준을 높여 Dirty Read를 방지하는 데에도 도움을 줄 수 있습니다. 결과적으로 읽기 전용 트랜잭션의 성능을 향상시키는 효과가 있습니다.

#트랜잭션 격리 수준#Dirty Read 방지#Flush Mode#최적화#리소스 절약

이 질문 단독 페이지 →

JPA / ORM 면접 질문 — 심화

Q15 심화

JPA에서 상속 관계 매핑 전략(SINGLE_TABLE, JOINED, TABLE_PER_CLASS)의 차이점과 선택 기준은?

힌트 · 쿼리 성능, 정규화 수준, NULL 허용 여부를 비교해보세요.

JPA 상속 관계 매핑 전략은 크게 세 가지가 있습니다.

전체 모범답안 펼치기

JPA 상속 관계 매핑 전략은 크게 세 가지가 있습니다.

  • SINGLE_TABLE 전략: 하나의 테이블에 모든 엔티티를 저장합니다. 구현이 간단하지만, 자식 엔티티에만 존재하는 컬럼은 NULL 값을 허용해야 하고, 조회 성능은 대체로 빠릅니다.

  • JOINED 전략: 부모 엔티티와 자식 엔티티 각각 테이블을 만들고, 조인을 통해 데이터를 가져옵니다. 정규화 수준이 높지만, 조인 연산 때문에 성능이 저하될 수 있습니다.

  • TABLE_PER_CLASS 전략: 각 자식 엔티티마다 테이블을 생성합니다. NULL 컬럼 문제는 없지만, 부모 엔티티를 조회할 때 모든 자식 테이블을 UNION 해야 하므로 성능이 매우 떨어질 수 있습니다.

선택 기준은 다음과 같습니다. 데이터가 자주 변경되고 조회 성능이 중요하다면 SINGLE_TABLE, 정규화가 중요하고 데이터가 자주 변경되지 않는다면 JOINED, 특별한 경우가 아니라면 TABLE_PER_CLASS는 지양하는 것이 좋습니다. 상황에 따라 적절한 전략을 선택해야 합니다.

#단일 테이블 전략#조인 전략#구현 클래스마다 테이블 전략#정규화#성능

이 질문 단독 페이지 →

Q16 심화

JPA 2차 캐시(Second Level Cache)란 무엇이며, 1차 캐시와의 차이점은 무엇인가요?

힌트 · 캐시 범위, 동시성, 설정 방법을 비교해보세요.

JPA 2차 캐시는 EntityManagerFactory 레벨에서 관리되는 공유 캐시입니다. 1차 캐시는 영속성 컨텍스트, 즉 EntityManager 내부에 존재하며 트랜잭션 범위 내에서만 유효합니다.

전체 모범답안 펼치기

JPA 2차 캐시는 EntityManagerFactory 레벨에서 관리되는 공유 캐시입니다. 1차 캐시는 영속성 컨텍스트, 즉 EntityManager 내부에 존재하며 트랜잭션 범위 내에서만 유효합니다.

가장 큰 차이점은 캐시 범위입니다. 1차 캐시는 EntityManager마다 독립적으로 존재하지만, 2차 캐시는 여러 EntityManagerFactory 인스턴스 간에 공유될 수 있습니다. 따라서 2차 캐시는 애플리케이션 전체에서 자주 사용되는 데이터를 캐싱하여 성능을 향상시키는 데 유용합니다.

동시성 측면에서 1차 캐시는 단일 트랜잭션 내에서만 사용되므로 동시성 문제가 발생하지 않지만, 2차 캐시는 여러 트랜잭션에서 동시에 접근할 수 있으므로 적절한 캐시 전략(예: 읽기-쓰기 캐시, 트랜잭션 캐시)을 선택해야 합니다.

2차 캐시를 사용하려면 persistence.xml 또는 application.properties/yml 파일에서 캐시 프로바이더를 설정하고, 캐싱할 엔티티에 @Cacheable 어노테이션을 추가해야 합니다. 클러스터링 환경에서는 캐시 서버를 구성하여 캐시 데이터를 공유할 수도 있습니다.

#영속성 컨텍스트#공유 캐시#EntityManagerFactory#클러스터링#캐시 전략

이 질문 단독 페이지 →

Q17 심화

OSIV(Open Session In View)란 무엇이며, 장단점은 무엇인가요?

힌트 · 영속성 컨텍스트 범위와 지연 로딩, 커넥션 점유 문제를 생각해보세요.

OSIV는 Open Session In View의 약자로, 영속성 컨텍스트를 뷰 템플릿까지 열어두는 방식입니다. 보통 트랜잭션은 서비스 계층에서 끝나지만, OSIV를 사용하면 뷰에서도 지연 로딩을 사용할 수 있다는…

전체 모범답안 펼치기

OSIV는 Open Session In View의 약자로, 영속성 컨텍스트를 뷰 템플릿까지 열어두는 방식입니다. 보통 트랜잭션은 서비스 계층에서 끝나지만, OSIV를 사용하면 뷰에서도 지연 로딩을 사용할 수 있다는 장점이 있습니다.

하지만 OSIV는 커넥션 점유 시간을 늘려 성능 문제를 야기할 수 있고, 뷰에서 데이터를 수정할 가능성이 생겨 예측 못한 side effect가 발생할 수 있습니다. 따라서 읽기 전용으로 사용하거나, 트랜잭션 범위를 신중하게 설정하는 것이 중요합니다. 최근에는 API 기반 개발이 많아지면서 OSIV보다는 DTO를 적극적으로 활용하는 추세입니다.

#영속성 컨텍스트#트랜잭션#지연 로딩#OSIV#성능

이 질문 단독 페이지 →

Q18 심화

JPA 성능 최적화를 위한 전략들에 대해 종합적으로 설명해주세요.

힌트 · N+1 해결, 페치 조인, 배치 사이즈, 읽기 전용 쿼리 등을 종합해보세요.

JPA 성능 최적화는 애플리케이션 성능에 큰 영향을 미치기 때문에 중요하다고 생각합니다. 크게 N+1 문제 해결, 쿼리 최적화, 영속성 컨텍스트 활용, 그리고 기타 전략으로 나눠서 설명드릴 수 있습니다.

전체 모범답안 펼치기

JPA 성능 최적화는 애플리케이션 성능에 큰 영향을 미치기 때문에 중요하다고 생각합니다. 크게 N+1 문제 해결, 쿼리 최적화, 영속성 컨텍스트 활용, 그리고 기타 전략으로 나눠서 설명드릴 수 있습니다.

먼저 N+1 문제 해결을 위해 fetch join을 적극적으로 활용합니다. 연관된 엔티티를 한 번에 가져와 불필요한 쿼리를 줄일 수 있습니다. 또한 @BatchSize 어노테이션을 사용하여 IN 쿼리를 통해 한 번에 연관 엔티티들을 로딩하는 방법도 있습니다.

쿼리 최적화 측면에서는 인덱스 활용이 중요합니다. 자주 사용되는 컬럼에 인덱스를 생성하여 검색 속도를 향상시킬 수 있습니다. 또한, 읽기 전용 쿼리의 경우 @QueryHints를 사용하여 하이버네이트에게 힌트를 주어 성능을 개선할 수 있습니다.

영속성 컨텍스트를 효율적으로 관리하는 것도 중요합니다. 불필요한 엔티티 변경 추적을 막기 위해 읽기 전용 엔티티로 조회하거나, EntityManagerclear() 메서드를 사용하여 영속성 컨텍스트를 주기적으로 비워주는 것이 좋습니다.

마지막으로, 필요에 따라 네이티브 쿼리나 JDBC API를 직접 사용하여 JPA의 오버헤드를 줄일 수도 있습니다.

#N+1 문제#fetch join#인덱스#쿼리 힌트#영속성 컨텍스트

이 질문 단독 페이지 →

Q19 심화

JPA에서 @OneToOne 관계의 지연 로딩이 동작하지 않는 경우와 해결 방법은?

힌트 · 프록시 생성 조건과 외래 키 위치를 생각해보세요.

JPA에서 @OneToOne 관계의 지연 로딩이 기대대로 동작하지 않는 경우가 종종 있습니다. 주로 두 가지 이유 때문인데요.

전체 모범답안 펼치기

JPA에서 @OneToOne 관계의 지연 로딩이 기대대로 동작하지 않는 경우가 종종 있습니다. 주로 두 가지 이유 때문인데요.

첫째, JPA 구현체(Hibernate 등)가 프록시 객체를 생성할 수 없는 상황일 수 있습니다. 예를 들어, 연관 관계를 가진 엔티티가 이미 영속성 컨텍스트에 로딩되어 있다면, 프록시 대신 실제 객체를 바로 반환하게 됩니다.

둘째, 외래 키의 위치 문제입니다. @OneToOne 관계에서 외래 키가 주인 엔티티에 있는 경우, JPA는 연관된 엔티티를 로딩하기 위해 JOIN 쿼리를 사용할 수밖에 없습니다. 이 경우 FetchType.LAZY로 설정해도 즉시 로딩이 발생합니다.

해결 방법은 다음과 같습니다.

  • @OneToOne 관계에서 외래 키를 소유하지 않은 엔티티가 주인이 되도록 설정합니다.
  • @JoinColumn을 사용하여 외래 키 매핑을 명시적으로 지정하고, 필요하다면 @Fetch(FetchMode.SELECT)를 사용하여 N+1 문제를 방지합니다.
  • 정말 필요한 경우에만 FetchType.EAGER를 사용하고, 그렇지 않다면 FetchType.LAZY를 유지하는 것이 좋습니다.
#FetchType.EAGER#프록시 객체#JPA 구현체#영속성 컨텍스트#JOIN

이 질문 단독 페이지 →

Q20 심화

프로젝트에서 JPA를 사용하면서 겪었던 성능 이슈와 해결 경험을 공유해주세요.

힌트 · 실제 문제 상황, 원인 분석, 해결 과정을 구체적으로 설명해보세요.

네, JPA를 사용하면서 성능 이슈를 겪었던 경험을 말씀드리겠습니다.

전체 모범답안 펼치기

네, JPA를 사용하면서 성능 이슈를 겪었던 경험을 말씀드리겠습니다.

한 번은 사용자 정보를 조회하는 기능에서 N+1 문제가 발생했습니다. 사용자 엔티티와 연관된 프로필 정보를 함께 조회해야 했는데, JPA가 사용자 정보를 먼저 조회한 후, 각 사용자의 프로필 정보를 Lazy Loading 방식으로 추가적으로 조회하면서 쿼리가 너무 많이 발생했습니다.

이 문제를 해결하기 위해 Fetch Join을 사용했습니다. JPQL 쿼리에서 SELECT u FROM User u JOIN FETCH u.profile 과 같이 작성하여 사용자 정보를 조회할 때 프로필 정보까지 한 번에 가져오도록 했습니다.

또한, 조회 조건에 사용되는 컬럼에 인덱스를 추가하여 쿼리 성능을 개선했습니다. OSIV (Open Session In View) 설정도 확인하여 불필요한 DB 연결을 줄이도록 했습니다. 이러한 튜닝을 통해 응답 시간을 크게 단축할 수 있었습니다.

#N+1 문제#fetch join#인덱스#쿼리 튜닝#OSIV

이 질문 단독 페이지 →

Q21 심화

대규모 트래픽 환경에서 JPA의 N+1 문제를 근본적으로 해결하기 위한 다양한 전략들을 설명하고, 각 전략의 장단점 및 적절한 사용 시나리오를 비교하여 설명해 주십시오. 단순히 FetchType 변경을 넘어선 심층적인 접근 방식들을 포함해야 합니다.

힌트 · BatchSize, @Fetch(FetchMode.SUBSELECT), Join Fetch, DTO Projection, QueryDSL 등을 활용한 최적화 방안을 비교 설명합니다. 각 방법의 메모리 사용량, 쿼리 수, 복잡도 등을 고려해야 합니다.

JPA N+1 문제는 연관된 엔티티를 조회할 때 발생하는 추가적인 쿼리 문제죠. 단순히 FetchType.LAZY를 EAGER로 바꾸는 건 해결책이 될 수 없고, 오히려 성능을 더 악화시킬 수 있습니다.

전체 모범답안 펼치기

JPA N+1 문제는 연관된 엔티티를 조회할 때 발생하는 추가적인 쿼리 문제죠. 단순히 FetchType.LAZYEAGER로 바꾸는 건 해결책이 될 수 없고, 오히려 성능을 더 악화시킬 수 있습니다.

근본적인 해결책으로는 여러 가지가 있습니다. 첫째, JOIN FETCH를 사용하면 한 번의 쿼리로 연관된 엔티티를 함께 가져올 수 있습니다. 하지만 연관 관계가 복잡해지면 쿼리가 너무 커질 수 있다는 단점이 있습니다. 둘째, @BatchSize를 사용하면 지정된 크기만큼 IN 쿼리를 사용하여 N+1 문제를 완화할 수 있습니다. 메모리 사용량은 줄지만, 쿼리 수가 완전히 줄어들지는 않습니다. 셋째, QueryDSL을 사용해서 DTO Projection을 하면 필요한 데이터만 선택적으로 가져올 수 있어서 네트워크 트래픽을 줄일 수 있습니다. 복잡도는 올라가지만 성능 개선 효과가 큽니다. 마지막으로, 페이징 시에는 COUNT 쿼리를 최적화하는 것도 중요합니다.

각 전략은 장단점이 있기 때문에, 트래픽 패턴, 데이터 모델 복잡성, 그리고 성능 요구 사항을 고려해서 적절한 방법을 선택해야 합니다. 예를 들어, 간단한 연관 관계에서는 JOIN FETCH가 좋고, 복잡한 경우에는 DTO Projection이 더 효과적일 수 있습니다.

#JOIN FETCH#Entity Graph#Batch Size#QueryDSL#페이징 최적화

이 질문 단독 페이지 →

함께 보면 좋은 백엔드 개발 면접 질문

← 전체 면접 질문 카테고리 보기

보유한 JPA / ORM 질문은 이게 전부가 아닙니다

패스잇 앱에는 직무별 면접 질문 수천 개와 모범답안이 담겨 있습니다. AI 모의면접으로 직접 답하고, 약점을 분석받아 보세요.