[JPA] 영속성 컨텍스트(Persistence Context)란?
Spring

[JPA] 영속성 컨텍스트(Persistence Context)란?

728x90

처음에 Young속성? 어린 속성인가? 라고 생각했던 제 자신이 부끄러워지는 순간입니다... 하하

난 물속성인데 넌 영속성이니? ^^ (죄송합니다)

자 JPA 라는 개념을 접하면 반드시 마주하는 단어가 바로 이 영속성 컨텍스트인데요! 오늘은 살짝 복잡하지만 저희의 삶을 편하게 해주는 JPA의 영속성 컨텍스트에 대해 알아보겠습니다!


영속성 컨텍스트(Persistence Context)의 개념

"영속성"은 사전적인 의미로 "오래도록 또는 영원히 계속되는 성질이나 능력"입니다. 프로그래밍 상에서 영원이 계속된다는 건 무슨 뜻일까요? 바로 프로그램이 아무리 종료되고 재시작되어도 남아있는 것이죠. 예를 들면 프로그램이 실행된 후 부터 생명주기를 가지는 객체보다는, DB에 저장된 데이터가 영속성을 가졌다고 볼 수 있겠죠.

 

이러한 뜻에서 영속성 컨텍스트란 엔티티를 영구 저장하는 환경이라는 뜻으로, 애플리케이션과 DB사이에서 영구적으로 저장될 객체를 보관하는 가상의 DB역할을 합니다. (쉽게 Entity를 보관하는 가상의 DB라고 생각하시면 됩니다)

 

엔티티(Entity)란?

검지 - 인형 왼팔, 엄지 - 인형 왼다리, 중지 - 인형 오른다리, 약지 - 인형 머리, 소지 - 인형 오른팔 이렇게 맵핑 돼있다

JPA에서 엔티티란 DB 테이블에 대응하는 하나의 클래스를 의미합니다. 자바에서는 일반 객체를 생성해서 쓸수도 있지만, 해당 객체에 @Entity 어노테이션을 붙이게되면, 특정 DB 테이블과 연동이되어 테이블의 row 하나가 객체와 맵핑이 되고, 객체내부 변수들이 해당 row 에 있는 column과 연결이 되도록 관리됩니다.

 

오잉?? 애플리케이션도 아니고 DB도 아닌곳에 또 객체를 저장한다고요? 왠 낭비야

단편적으로 보면 메모리 낭비같아보일 수 있지만, 사실 이 때문에 애플리케이션과 DB는 훨씬 빠른속도로 읽기/쓰기가 이루어질 수 있습니다.

 

그럼 영속성 컨텍스트는 DB처럼 따로 저장해주고 그래야돼요?

[Spring] JPA, Hibernate, Spring Data JPA 는 다르다! 편에서 EntityManagerFactory, EntityManager, EntityTransaction이 JPA의 핵심이라고 했었죠! 바로 이들이 영속성 컨텍스트(엔티티가 저장되는 가상DB)에 접근하고, 엔티티를 저장/조회 및 관리하는 역할을 해주기 때문입니다. 하나씩 알아볼까요?


엔티티 매니저(EntityManager)

말그대로 위에서 설명한 엔티티를 관리하는 역할을 합니다. 객체를 생성하는 것처럼 엔티티 매니저를 생성하면 생성한 엔티티 매니저를 통해 영속성 컨텍스트에 엔티티를 저장하거나, 조회하거나, 삭제하거나 하는 동작을 할 수 있습니다. 마치 DB처럼요! 

EntityManager em = new EntityManager();
em.persist(DannyObject);

예를 들어 위 코드로 영속성 컨텍스트에 엔티티를 저장할 수 있습니다 참 쉽죠?

 

그런데 EntityManager는 JPA에서 제공하는 interface로 이미 Bean으로 등록되어 있기 때문에 Autowired로 사용해서 스프링 컨테이너가 알아서 관리해주도록 할 수 있습니다. 아래처럼요 (혹시 이 말이 잘 이해가 안된다면 여기를 보고오세요!)

@Autowired
private EntityManager em;

em.persist(DannyObject);

 

어 근데 저는 JPA를 쓰면서 EntityManager를 만든적이 없는데요?

네! Spring Data JPA에서 사용하는 Repository 인터페이스는 EntiryManager를 직접 개발자가 코드를 쓰지 않아도 되도록 한번더 감싸져있습니다. Spring Data JPA에서 제공하지 않는 기능을 사용하거나 특별한 문제가 있어서 별도로 customizing을 해야한다면 EntityManager를 직접 만들어서 처리할 수 있습니다.

 

따라서 EntityManager에 대해 정리하면 특징은 다음과 같습니다

  • 영속성 컨텍스트에서 Entity를 관리(저장, 조회, 삭제 등등)한다.
  • JPA에서 제공하는 interface로 spring bean으로 등록되어 있어 Autowired로 사용할 수 있다.

엔티티(Entity)의 생명주기

엔티티는 EntityManager를 통해 영속성 컨텍스트에 저장이되긴 하지만, 실제 DB가 아니기게 엔티티는 생명주기를 갖습니다. 그리고 상태별로 이름이 존재합니다.

비영속(new/transient) 상태

DannyEntity dannyEntity = new DannyEntity();

객체만 생성하고 아직 persist() 메소드를 통해 영속성 컨텍스트에 저장되지 않은 상태입니다.

위 코드처럼 단순히 엔티티 객체만 생성된 상태라면 비영속 상태라고 부릅니다.

 

영속(managed) 상태

@Autowired
private EntityManager em;

em.persist(dannyEntity);

생성된 엔티티가 영속성 컨텍스트에 저장된 상태입니다

위 코드처럼 생성된 엔티티가 persist() 메소드를 통해 저장됐다면 영속 상태라고 부릅니다.

 

준영속(detached) 상태

// dannyEntity를 영속성 컨텍스트에서 분리하면 준영속 상태가 된다.
em.detach(dannyEntity);
// 영속성 콘텍스트를 비우면 영속된 엔티티들은 모두 준영속 상태가 된다. (대기 상태에 있는 변경 데이터들도 삭제)
em.clear();
// 영속성 콘텍스트를 종료해도 영속된 엔티티들은 모두 준영속 상태가 된다.
em.close();
// detach로 준영속 상태가 된 엔티티를 merge를 하면 다시 영속 상태가 된다.
em.merge(dannyEntity);

위 코드처럼 영속성 컨텍스트에 저장되었다가 분리된 상태를 준영속 상태라고 부릅니다.

merge() 메소드로 다시 준영속 엔티티를 영속상태로 만들 수 있습니다.

 

삭제(removed) 상태

 em.remove(dannyEntity);

위 코드처럼 엔티티가 remove 메소드를 통해 영속성 컨텍스트와 DB 두곳에서 삭제가 된 상태를 삭제 상태라고 부릅니다.

 

왜이렇게 상태도 많고 복잡하고 하.. 저 그냥 바로 DB랑 연결할래요 ㅠㅠ

잠깐만요!! 이제부터 왜 이렇게 복잡한 영속성 컨텍스트라는 가상의 DB공간이 필요한지 말해줄게요!! 여기엔 엄청난 장점이 있습니다 ㅎㅎㅎㅎ


영속성 컨텍스트의 장점

1차 캐시

스프링의 역사편에서도 언급했지만, DB에 연결해서 DB에서 직접 정보를 가져오는 행동은 엄청 비싼편입니다 (다른 로직에 비해 오래 걸린다는 뜻이죠). 그래서 이 DB에 직접 연결해서 조회하는 것을 줄일수록 처리속도가 엄청나게 향상되겠죠! 그래서 영속성 컨텍스트는 DB에 있는 정보를 들고있습니다

 

영속성 컨텍스트를 이용한 DB 데이터 조회 흐름을 한번 따라가볼까요?

  1. 1차 캐시에서 엔티티를 찾는다
  2. 있으면 메모리에 있는 1차 캐시에서 엔티티를 조회하고 반납한다.
  3. 없으면 DB에서 조회한다.
  4. 조회한 데이터로 엔티티를 생성해 1차 캐시에 저장한다. (엔티티를 영속상태로 만든다)
  5. 조회한 엔티티를 반환한다.

한번 영속성 컨텍스트에 저장된 엔티티는 entityManager가 없어질때까지(같은 Transaction에 포함된다면) 계속 영속성 컨텍스트에 존재하기 때문에 이렇게 한번 조회한 엔티티는 다음에는 엄청나게 빠른 속도로 조회가 가능합니다. 예를 들어 JPA를 이용해서 같은 정보를 100번 조회하는 코드를 짰다면, 실제 DB조회는 1번만하고 나머지 99번은 1차 캐시(영속성 컨텍스트)에서 가져옵니다.

 

** 영속 엔티티는 동일성을 보장합니다! (동일성과 동등성의 차이를 알고싶다면 여기를 봐주세요!) 동일성을 보장한다는건 같은 엔티티를 여러번 불러와도 같은 주소에 있는 객체를 가져온다는 것이고 곧 완전히 동일한 객체를 가져온다는 뜻이기도 합니다. 

[잠깐]
1차 캐시는 쓰레드끼리 공유되지 않고, 트랜잭션의 범위 안에서만 사용하는 굉장히 짧은 캐시 레이어입니다. 따라서 100명 한테 요청 100개 오면, 엔티티 매니저가 100개 생기고 1차캐시도 100개가 생깁니다. 스레드가 종료되면, 그때 다 사라지게 됩니다. 전체에서 쓰는 글로벌 캐시는 2차 캐시라고 부릅니다

 

쓰기 지연(transactional write-behind)

(거의 다 왔어요 조금만 힘냅시다 흑흑 ㅠㅠ)

잠시 한숨 돌리고 근본적인 ORM으로 돌아가봅시다. ORM 기술이란 객체와 DB를 연결해주는 것이었죠? 하지만 DB에서 조회를 하거나 저장이 되려면 결국 SQL문이 만들어져야 합니다.

 

근데 예를들어서 로직상 DB에서 엔티티 A 업데이트 -> 엔티티 B 지우기 -> 엔티티 C 저장을 한번에 진행하는 로직이 있다고 가정해봅시다. 만약 엔티티 C를 저장하는 부분에서 알수없는 오류가 발생해서 애플리케이션이 실패했다면 데이터의 신뢰성을 위해 지워진 엔티티 B를 다시 만들고, 엔티티 A를 다시 원상복구 시켜야겠죠! 이걸 직접 전부다 SQL문으로 수행한다면 안그래도 비싼 DB 연결이 더 비싸질겁니다.

 

따라서 JPA는 트랜잭션(Transaction)을 커밋할때까지 영속성 컨텍스트의 내부 쿼리 저장소에 실행돼야하는 SQL문이 전부 저장됩니다. 그리고 커밋이 되면 모아둔 쿼리를 한번에 DB에 보냅니다. 이렇게 다 모아서 나중에 실행하기 때문에 이것을 "쓰기 지연" 이라고 부릅니다.

 

플러시(flush)

flush() 메소드를 사용하면 영속성 컨텍스트에 있는 엔티티의 내용을 데이터베이스에 반영하게됩니다! (드디어..) 하지만 DB반영을 한다는 것이지 영속성 컨텍스트에서 사라지는 것은 아닙니다. 이렇게 flush() 메소드를 호출하거나 위에서 나온대로 트랜잭션을 커밋하게 되면 영속성 컨텍스트의 내부 쿼리저장소에 쌓인 모든 SQL문이 DB에 호출되고, DB에 최종적으로 변화가 일어납니다.

 

변경 감지(Dirty Checking)

더러움 체킹... 어떤 더러움을 체크한다는 걸까요? 바로 영속성 컨텍스트에 저장된 엔티티와 실제 DB에 저장된 데이터와 차이가 있는지 체크하는 것인데요. 동기화가 안돼있으니 더럽(?)다고 말하는 것이라고...이라고 생각합니다. (왜 dirty인지는 모르겠네요 ㅠ) 

 

예를 들어서 우리가 dannyEntity를 만들어서 영속성 컨텍스트에 저장하고 flush()를 통해 DB에도 저장을 했습니다. 그럼 영속성 컨텍스트에 엔티티가 존재하게 되겠죠? 근데 사실 이때 스냅샷 필드도 따로 저장하여 "저장하던 그 순간의 엔티티"에 관한 정보도 보유하고 있습니다.

 

그럼 이상태에서 제가 dannyEntity를 변경하고 persist() 메소드를 통해 영속성 컨텍스트에 있는 엔티티를 변경했습니다. 그럼 스냅샷에  저장된 dannyEntity와 새로 저장된 dannyEntity와 값이 다르겠죠? 이상태에서 flush()를 하면 영속성 컨텍스트는 "어? 야 지금 영속성 컨텍스트에 있는거 DB에 저장된거랑 다른데? 너 실수한듯 ㅎ 내가 업데이트 SQL 쿼리 내부저장소에 넣어줄게!^^" 라고 하면서 수정쿼리를 내부 쿼리 저장소에 넣고 flush()합니다. 이렇게 엔티티 변경사항을 감지해서 DB와 동기화 시켜주는 것을 바로 Dity 
Checking 이라고합니다!


EntityManagerFactory

살짝 타이밍이 늦었는데 여기까지 알았으면 이 정의는 간단합니다.

EntityManagerFactory는 말그대로 공장처럼 위에서 명시된 EntityManager를 생성해주는 역할을 합니다. 일반적으로 DB당 하나씩 존재합니다.

 

위에서도 잠깐 명시했지만 여러 EntityManager는 하나의 영속성 컨텍스트를 바라볼 수 있지만, EntityManager 자체를 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 쓰레드 간에 절대 공유하면 안 됩니다. 따라서 Transaction마다 계속해서 만들어줘야하고, 이렇게 만들어주는 역할을 EntityManagerFactory가 해줍니다.


오늘은 JPA의 진가를 알려주는 영속성 컨텍스트에 대해서 알아보았습니다. 어쩌다보니 JPA의 개념보다 훨씬 길고 어려운 글이 되어버렸네요 ㅠ 그런데 항상 느끼는거지만, 개념이 복잡할수록 우리가 사용할때는 훨씬 쉬워지는 것 같습니다.  읽어주셔서 감사합니다!

 

출처:
- (JPA) Entity와 EntityManager와 EntityManagerFactory
- [Spring JPA] 영속성 컨텍스트(Persistence Context)
- JPA 영속성 컨텍스트란?
- (JPA) Entity와 EntityManager와 EntityManagerFactory

 

728x90