Spring/Spring Data JPA

[자바 ORM 표준 JPA 프로그래밍] 13. 웹 애플리케이션과 영속성 관리

noahkim_ 2025. 4. 26. 05:03

김영한 님의 "자바 ORM 표준 JPA 프로그래밍" 책을 정리한 포스팅 입니다.


1. 트랜잭션 범위의 영속성 컨텍스트

기본 전략

구분 내용
생명 주기
트랜잭션과 영속성 컨텍스트는 같은 생존 범위를 가짐
- 트랜잭션이 시작될 때 영속성 컨텍스트 생성 (초기화 상태)
- 트랜잭션이 종료될 때 영속성 컨텍스트 종료
공유
- 같은 트랜잭션 간 같은 영속성 컨텍스트를 공유함
- 다른 트랜잭션 간 다른 영속성 컨텍스트를 가짐

 

 

예시) 공유

더보기

같은 트랜잭션

@Test
@Transactional
void same_transaction() {
    User user1 = entityManager.find(User.class, 1);
    user1.setName("test");

    searchName();
}

private void searchName() {
    User user = entityManager.find(User.class, 1);
    assertEquals(user.getName(), "test");
}

 

다른 트랜잭션

@Transactional
public void methodA() {
    Member member1 = em.find(Member.class, 1L);
    member1.setName("노아");
}

@Transactional
public void methodB() {
    Member member2 = em.find(Member.class, 1L); // DB에서 다시 조회
    System.out.println(member2.getName());     // 출력: 원래 DB에 저장된 이름
}
  • methodA의 변경은 커밋되지 않았다면 methodB에서는 반영되지 않음.

 

예시) 생명 주기

더보기
→ [Filter / Interceptor] 
  → [Controller] 
     → ┌──────────────────────────────────────────────────┐
       │    트랜잭션 범위 (영속성 컨텍스트 생존 범위)               │
       │                                                  │
       │    [Service (@Transactional 시작)] → [Repository] │
       └──────────────────────────────────────────────────┘
          → [View (Model 반환)]
  • Filter / Interceptor → Controller → View : 준영속 상태
  • Service → Repository : 영속 상태

 

2. 준영속 상태와 지연 로딩

  • 준영속 상태는 영속성 컨텍스트의 관리에서 벗어난 엔티티 상태. (ex. detach)
  • 서비스 계층 이외는 준영속 상태이므로 다른 계층(뷰)에서 엔티티 접근 시 문제 발생
    • 지연 로딩 실패 (LazyInitializationException)
    • 변경 감지 실패 (Dirty Checking)

해결 방법

해결 방법 설명 장점 단점
JPQL Fetch Join join fetch로 필요한 연관 엔티티 미리 로딩
→ 지연 로딩 실패 해결
준영속 상태 발생 ❌
성능 문제 (N+1 발생)
뷰 의존
FACADE 계층 도입 프록시 초기화를 담당하는 계층을 도입
(Presentation Layer ↔️ Service Layer)

FACADE 계층에서 트랜잭션 시작
(프록시 초기화를 위해 영속성 컨텍스트 필요)
프록시 초기화
트랜잭션 관리
구조 복잡도 ↑
OSIV
(Open Session In View)
트랜잭션 범위를 뷰 렌더링까지 확장
자동으로 지연로딩
트랜잭션이 길어짐
 성능 저하
→ 리소스 점유

 

예제) JPQL Fetch Join

더보기
SELECT o FROM Order o JOIN FETCH o.customer WHERE o.status = 'SHIPPED'

 

예제) FACADE

더보기
[Presentation Layer]
        │
        ▼
[Facade Layer] ← 프록시 초기화, 트랜잭션 시작
        │
        ▼
[Service Layer] ← 비즈니스 로직 처리
        │
        ▼
[Repository Layer] ← DB 접근
@Service
public class TeamService {

    @Autowired
    private TeamRepository teamRepository;

    @Transactional(readOnly = true) // 트랜잭션 관리
    public List<User> getUsersByTeamId(Integer teamId) {
        Team team = teamRepository.findById(teamId).orElseThrow();
        return team.getMembers(); // LAZY 로딩된 팀 멤버들
    }
}
@Facade
public class TeamFacade {

    @Autowired
    private TeamService teamService;

    @Transactional // Facade에서 트랜잭션 관리
    public List<User> getUsersWithTeamName(Integer teamId) {
        List<User> users = teamService.getUsersByTeamId(teamId);

        // 프록시 초기화: LAZY 로딩된 Team 정보 초기화
        users.forEach(user -> {
            System.out.println("User: " + user);
            user.getTeam().getName();  // team 필드를 강제로 초기화
        });

        return users;
    }
}
@RestController
@RequestMapping("/teams")
public class TeamController {

    @Autowired
    private TeamFacade teamFacade;

    @GetMapping("/{teamId}/users")
    public List<User> getUsers(@PathVariable Integer teamId) {
        return teamFacade.getUsersWithTeamName(teamId);
    }
}

 

3. OSIV

  • 영속성 컨텍스트(Persistence Context)를 뷰(View)까지 열어두는 방식

 

버전

항목 과거 OSIV (요청 당 트랜잭션)
Spring OSIV (비즈니스 계층 트랜잭션)
OSIV 방식 - 영속성 컨텍스트 생성: 서블릿 필터/인터셉터
- 트랜잭션 시작: 서블릿 필터/인터셉터
- 영속성 컨텍스트 생성: 서블릿 필터/인터셉터
- 트랜잭션 시작: 서비스 계층
영속성 컨텍스트 상태 - 시작: 클라이언트 요청 시작
- 종료: 클라이언트 요청 종료
- 시작: 클라이언트 요청 시작 (트랜잭션 내 사용됨)
- 종료: 클라이언트 요청 종료
트랜잭션 관리 프레젠테이션 계층에서 시작 서비스 계층에서 시작
영속성 컨텍스트 플러시 클라이언트 요청 종료 시
트랜잭션 종료
영속성 컨텍스트 종료 시점 클라이언트 요청 종료 시 클라이언트 요청 종료 시
트랜잭션 없이 읽기 ✅ (쿼리가 발생하지 않을 경우만 가능)
주의 사항 - 프레젠테이션 계층에서 엔티티 변경
- 수정을 막기 위한 방법 필요 (엔티티 래핑 등)
- 프레젠테이션 계층에서 엔티티 직접 수정
- 서비스 계층에서 수정해야 엔티티 변경이 DB에 반영됨

 

 

셋팅) Spring OSIV

더보기
spring.jpa.open-in-view=true
  • 클라이언트 요청 시작 ~ 응답 완료까지 영속성 컨텍스트가 열려 있음

 

4. 너무 엄격한 계층

  • Spring OSIV는 뷰 계층에서 영속성 컨텍스트를 참조할 수 있는 방법을 제공
  • OSIV를 사용하면 유연하고 실용적 관점으로 접근하는 좋은 방법임