JPA에서 엔티티의 연관 관계를 로딩하는 두 가지 방식
JPA
ORM
Hibernate
엔티티가 처음 로드될 때 연관된 엔티티를 즉시 로딩하지 않고, 해당 엔티티에 실제로 접근할 때 연관된 엔티티를 로딩하는 방식입니다.
필요한 데이터만 로드해야 하는 상황이라면 Lazy Loading을 선택합니다.
기본적으로 연관된 엔티티를 프록시 객체로 설정하고, 필요한 시점에 실제 데이터를 가져옵니다.
프록시 객체란?
실제 엔티티 대신에 대리 역할을 하는 객체
즉, 데이터베이스에서 실제 데이터를 가져오기 전까지 엔티티의 "대리자"로서 존재하는 객체
Post 엔티티가 조회될 때, Comment는 로딩되지 않고 프록시 객체로 설정됩니다.
이후, Post에서 Comment를 조회하는 메서드를 호출하면 프록시 객체가 실제 데이터베이스에서 Comment 데이터를 가져옵니다.
장점
단점
사용예시
항상 함께 사용하는 데이터라면 Eager Loading을 선택해 초기 로딩 시 한 번에 데이터를 가져오는 것이 유리합니다.
초기 쿼리에서 연관된 모든 엔티티를 함께 로딩하므로, 이후 데이터베이스 접근을 줄일 수 있습니다.
장점
단점
사용 예시
JOIN FETCH를 사용해서 연관된 엔티티를 함께 한번에 조회할 수 있습니다.@BatchSize 어노테이션 사용@BatchSize(size = 10)으로 설정하면 최대 10개의 연관 엔티티를 한 번에 가져와 성능을 향상시킬 수 있습니다.(주로 @OneToMany에 붙여줌)코드의 작은 단위(주로 메서드나 클래스)를 개별적으로 테스트하여 원하는 동작을 하는지 확인하는 것
장점
단점
애플리케이션의 여러 모듈이 실제로 통합된 상태에서 상호작용이 올바르게 이루어지는지를 테스트하는 것입니다.
@SpringBootTest : 전체 애플리케이션을 띄워서 실행
장점
단점
서비스단 로직은 주로 단위테스트로
컨트롤러나 레포지토리는 통합테스트로
@WebMvcTest , mockMvc 주입받아서 테스트테스트 코드 작성 도구와 프레임워크
@Before, @After등 / assertEqualsQueryDSL이란?
EntityManager를 주입받아 JPA와 상호작용할 수 있도록 하고, JPAQueryFactory를 빈으로 등록하여 QueryDSL을 사용할 수 있도록 설정합니다.QueryRepository 인터페이스를 만들어 동적 쿼리 메서드를 정의하고, 이를 구현하는 QueryRepositoryImpl 클래스를 작성해 실제 쿼리 로직을 구현합니다. QueryDSL을 이렇게 분리하여 작성하는 이유
트랜잭션이란?
트랜잭션 격리 수준은 동시에 실행되는 트랜잭션 간의 데이터 일관성을 어떻게 유지할지를 결정합니다 격리 수준에 따라 읽기 작업의 일관성이나 동시성 제어가 달라집니다:
READ UNCOMMITTED: 다른 트랜잭션에서 커밋되지 않은 데이터까지 읽을 수 있습니다(Dirty Read 허용).
READ COMMITTED: 다른 트랜잭션에서 커밋한 데이터만 읽을 수 있습니다(Dirty Read 방지).
REPEATABLE READ: 트랜잭션 동안 읽은 데이터는 변경되지 않도록 보장합니다(Phantom Read 방지).
SERIALIZABLE: 가장 높은 격리 수준으로, 모든 트랜잭션이 순차적으로 실행되도록 합니다.
@ Transactional이란?
Aop
Aop가 프록시 방식을 사용하는 이유
1. 런타임 시점의 삽입 가능:
Spring AOP는 컴파일 시점이 아니라 런타임 시점에 프록시 객체를 생성하여 부가적인 기능을 주업무 코드에 삽입한다. 이는 AOP를 적용하는 대상 클래스의 바이트코드를 수정하지 않고, 부가적인 기능을 주입할 수 있게 한다. 따라서 애플리케이션을 실행 중에 동적으로 관심사를 적용할 수 있다.
2. 코드 재사용과 모듈화:
여러 클래스나 메서드에서 공통적으로 필요한 기능(예: 로깅, 보안 체크, 트랜잭션 관리 등)을 하나의 공통 모듈로 분리하여 관리할 수 있다. 이를 AOP로 구현한 프록시 객체를 여러 클래스에서 재사용할 수 있으며, 이로 인해 코드 중복을 줄이고, 유지보수성을 향상시킨다.
3. 관심사의 분리:
AOP는 관심사를 주업무 코드와 분리하여 코드의 가독성을 높이고 유지보수를 용이하게 한다. 주업무 코드는 핵심 비즈니스 로직에만 집중할 수 있고, 부가적인 기능은 프록시 객체가 담당하게 된다. 이로 인해 코드의 응집도가 증가하고 결합도가 감소하여 코드의 이해와 관리가 쉬워진다.
트랜잭션의 시작과 종료, 전파 방식에 따라 트랜잭션이 어떻게 관리되는지를 결정하는 개념입니다.
이를 통해 트랜잭션의 범위와 실행 흐름을 제어할 수 있습니다.
전파 범위는 트랜잭션이 이미 진행 중일 때, 새로 호출된 메서드가 기존 트랜잭션에 참여할지 아니면 새로 트랜잭션을 시작할지를 결정합니다.
REQUIRED (기본값):
REQUIRES_NEW:
SUPPORTS:
MANDATORY:
NOT_SUPPORTED:
NEVER:
NESTED:
트랜잭션이 시작되고 종료될 때까지의 과정을 말합니다.
트랜잭션 시작(Begin)
@Transactional이 선언된 메서드가 호출될 때 트랜잭션 매니저가 트랜잭션을 시작합니다.트랜잭션 진행(Execution)
트랜잭션 커밋(Commit)
트랜잭션 롤백(Rollback)
예외 발생 시 트랜잭션 롤백을 어떻게 제어할 수 있나요?
@Transactional은 기본적으로 RuntimeException이나 Error가 발생할 때 트랜잭션을 롤백합니다. rollbackFor 속성을 사용하여 특정 예외에 대해 롤백을 설정할 수 있고, noRollbackFor 속성으로 특정 예외가 발생해도 롤백하지 않도록 제어할 수 있습니다. 이를 통해 예외 상황에 따른 트랜잭션 제어가 가능합니다." @Transactional을 클래스와 메서드에 동시에 적용하면 어떻게 동작하나요?
@Transactional을 동시에 적용하면, 메서드 레벨의 설정이 우선적으로 적용됩니다. 클래스 레벨에 선언된 @Transactional은 기본 설정으로 사용되고, 메서드에 별도로 지정된 옵션이 있으면 메서드의 옵션이 클래스의 설정을 덮어씁니다."트랜잭션 매니저는 어떤 역할을 하나요?
JpaTransactionManager는 JPA와 함께 사용하고, DataSourceTransactionManager는 JDBC 기반의 트랜잭션을 관리합니다. 적절한 트랜잭션 매니저를 선택하여 데이터베이스와의 트랜잭션을 일관되게 관리할 수 있습니다."