Skip to content

"영속성 컨텍스트 알아보기"

Intro

JPA 를 배우며 영속성 컨텍스트(persistence context)라는 단어를 처음 들었다. 처음 듣고는 아리송했다. 영속, 맥락(context) 두 단어의 조합도 안 어울렸고 단어만 보고는 쉽게 이해가 가지 않았다. JPA를 이해하는 가장 중요한 용어라고 하여 이 글을 쓰며 영속성 컨텍스트를 더 이해해보려고 한다.

영속성 컨텍스트와 엔티티의 역할

책에 의하면 영속성 컨텍스트를 ‘엔티티를 영구 저장하는 환경’ (1) 이라 해석하고 있다. 내가 아는 엔티티는 엔티티 클래스를 통해 생성된 객체이다. 엔티티 클래스는 @Entity 어노테이션을 이용해 클래스를 데이터베이스(DB) 테이블과 매핑시킨다. 클래스에 @Table, @Column 등의 어노테이션을 사용해서 테이블과 칼럼의 이름 등을 정하고 @Id 를 통해 테이블 식별자를 지정한다. 이 밖에도 여러 어노테이션으로 테이블 명세를 작성한다.

스크린샷 2022-01-10 오전 12 39 09 <작성한 엔티티 클래스>.

스크린샷 2022-01-10 오전 12 42 30 <매핑된 테이블>.

다시 해석해보면, 영속성 컨텍스트는 ‘엔티티 클래스를 통해 생성된 객체를 영구 저장하는 환경’ 이라 풀이할 수 있다. 그렇다면 이제 엔티티 클래스를 통해 생성된 엔티티 객체가 하는 역할과 이를 저장하는 환경에 대해 더 알아보자. 엔티티 객체는 최종적으로 테이블의 인스턴스를 구성하는 역할을 한다. 그렇다고 생성된 모든 엔티티가 테이블 인스턴스(데이터)가 되는 것은 아니다. 엔티티 객체를 저장하는 환경에 보관하고 그 뒤 DB에 온전히 저장될 때, 테이블의 인스턴스가 된다. 여기서 저장하는 환경이 영속성 컨텍스트다. 즉, 엔티티가 최종적인 역할을 하기위해서는 영속성 컨텍스트에 보관되는 상태가 필요하다.

엔티티의 생명주기

엔티티 객체는 영속성 컨텍스트와의 관계에 따라 4가지의 상태를 가진다.

  1. 비영속 (new/transient)

    Blog blog = new Blog();
    blog.setId(1L);
    blog.setTitle("첫번째 글");
    blog.setText("내용 미정");
    

    엔티티 객체를 생성했다고 영속상태에 놓이는 것은 아니다. 아직은 그저 하나의 객체이고 영속성 컨텍스트와 관련이 없다. 이런 상태를 비영속 상태라고 한다. 다시 말하면 남은 3가지 상태는 영속성 컨텍스트와 직접적인 관련이 있다.

  2. 영속 (managed)

    entityManager.persist(blog);
    

    엔티티 객체 blog 는 엔티티 매니저에 의해 영속 상태에 놓인다. 드디어 영속성 컨텍스트 안에 들어온 상태이다.

  3. 준영속 (detached)

    영속성 컨텍스트에 들어온 상태가 있다면, 나가는 상태도 존재한다. 엔티티가 준영속 상태에 놓이게 되면, 밑에 설명할 영속 상태에서 지원받는 기능들은 이제 동작하지 않게 된다. 다만 영속 상태를 경험했기에, 식별자 값은 가지게 된다.

    entityManager.detach(blog);
    
  4. 삭제 (removed)

    entityManager.remove(blog);
    

    엔티티가 영속성 컨텍스트와 매핑된 DB 테이블에서 삭제된다.

영속 상태의 장점

영속성(Persistence)은 데이터의 지속성을 의미한다. 그렇기에 애플리케이션을 종료하고 다시 실행하더라도 이전에 저장한 데이터를 다시 불러올 수 있는 기술(2) 이다. 여기서 데이터의 지속성은 DB에 데이터를 보존하는 것을 목적으로 하는 것을 말한다(3). 다시 말하면, DB에 저장하고 불러오는 모든 데이터들이 영속성 컨텍스트를 거치게 된다.

그렇다면 어떤 장점이 있기에, JPA 는 DB 에 바로 저장하지 않고 영속성 컨텍스트(Persistence Context)를 두는 걸까?

기본적으로 영속 상태에 들어온 엔티티들은 식별자 값(@Id로 매핑한 값)으로 구분된다. 구분된 엔티티들은 영속성 컨텍스트 안에 존재하는 내부 캐시(1차 캐시라고 부름)에 저장되는데 식별자 값을 키로, 엔티티 객체를 값으로 저장한다. 이 식별자 값은 DB 테이블의 pk 와 매핑되어 있다. 캐시가 존재하기에 값을 찾을 때, 캐시를 먼저 조회한다. 그리고 캐시 안에 데이터가 있다면 DB 를 조회하지 않는다. 만약 캐시에 데이터가 없으면 DB 를 조회한 후, 엔티티를 생성해 캐시에 저장한다. DB 를 직접 조회하지 않아도 되기에 성능은 좋아진다. 이 과정에서 동일성도 보장하고 있다. 같은 식별자 값을 가지면 참조하는 주소도 같다.

더티 체킹이라 부르는 변경 감지 기능도 제공한다. 영속 상태 안에 있는 엔티티 객체를 대상으로 호출 하는 시점과 트랜잭션이 끝나는 시점을 비교한다. 변경이 있다면, 이를 감지하고 update 쿼리를 DB 에 자동으로 보내준다. 또한 영속성 컨텍스트 안에 쓰기 지연 저장소를 두어 트랜잭션 범위 내의 쿼리를 한 번에 DB 에 보낸다. 지연 로딩기능에도 영속성 컨텍스트가 이용된다. 지연 로딩 설정을 걸어둔 객체가 실제 사용될 때, 영속성 컨텍스트가 DB를 조회해 필요한 엔티티 객체를 생성한다. 영속 상태에 있지 않은 객체는 지연 로딩을 사용할 수 없다.

마치며..

영속성 컨텍스트는 애플리케이션과 실제 DB 사이에서 가상의 DB 역할을 한다. 영속성 컨텍스트를 사용하면, 위에 말한 영속성을 충분히 만족시키면서도 성능상의 큰 이점을 챙길 수 있다. 다음 글에서는 영속 상태의 엔티티와 트랜잭션의 관계를 더 알아봐야겠다.

참고

(1) 김영한 <자바 ORM 표준 JPA 프로그래밍> p.92
(2) 엄진영 <자바 웹 개발 워크북> p.559
(3) 위키백과 - 지속성 (https://ko.wikipedia.org/wiki/지속성)


작성자: 김민철

작성일: 2022-01-10