spring boot 환경에서 Spring Data JPA를 쓰면서 팀프로젝트를 행복하게 개발하고 있었다.
모든게 순조로웠지만, 어느 기능을 개발하다가 에러가 났다..
현재 개발환경에서 json 로그를 남기기 위해 콘솔창이 엉망이라 일단 챗지피티한테 로그들고 헐레벌떡 달려가봤는데...
음....
아......
도저히 왜 Null인지 알 수가 없었다.
Null로 가져오는 데이터는 분명 프록시 객체로 설정해둔 친구이기에 읽어와야하는데 도저히 읽어오지를 않았다.
그래서 디버거로 살펴봤는데... 프록시 객체가 아니었다.
정확히는 프록시 객체로 설정해둔건 맞는데, 현재 영속 상태인 객체였기에 생긴 문제였다.
어...? 근데 영속 상태가 뭐지..? 영속성 컨텍스트는?
"영속성 컨텍스트(Persistence Context)"는 데이터베이스와 애플리케이션 사이의 상호작용을 관리하는 개념으로, 주로 객체지향 프로그래밍과 관계형 데이터베이스를 사용하는 웹 애플리케이션과 관련하여 사용됩니다. 영속성 컨텍스트는 데이터베이스와 객체 간의 매핑을 처리하며, 데이터베이스로부터 데이터를 검색하고 변경 사항을 반영하는 중요한 기능을 제공합니다.
- 2023.07.21 Chat GPT 3.5
지피티야 고맙다!
JPA 또한 ORM 프레임워크로 데이터베이스와 객체간 매핑을 위해 영속성 컨텍스트를 가지게 된다.
내가 경험했던 버그라기보단 무지로 인한 실수도 영속성 컨텍스트의 특징에 의해 생긴 것이기 때문에 한번 알아보자.
아까 프록시 객체라고 했던 것이 지연 로딩과 연관되어 있다.
지연 로딩이란 필요할 때까지 객체를 로드하지 않는 기능인데, 아직 로드되지 않는 객체를 프록시 객체라고 한다.
예전에 JPA를 처음 경험하며 지연로딩이 있는걸 몰랐을 때,
"어? A객체에 B객체 리스트를 가지고 있으면, A객체를 여러개 로드하면 너무 메모리를 많이 잡아먹지 않을까?"
라는 생각을 했었지만, 이를 위해 지연 로딩이라는 개념이 있었고, 아무리 A객체를 많이 불러내어도 B객체 리스트는 프록시 객체이기 때문에 메모리에 불러와지지 않는다.
이걸 몰랐어서 버그가 생기고 말았다
영속성 컨텍스트는 DB에 저장될, 저장된 객체를 캐싱해둔다. 이를 통해서 매번 해당 객체에 접근할 때, DB까지 다녀올 수고를 줄일 수 있는데....
아까 생겼던 버그를 좀 더 자세히 설명하자면,
A, B, C 순서대로 객체를 생성했는데, B는 A를 가지고, A는 C를 가진다.
복잡해서 죄송합니다 이 구조가 맞는지부터 의심하고 싶긴합니다 죄송합니다
이후 B객체를 DTO 객체로 변환하는 과정에서 A에 있는 C객체가 Null인 상태였다.
1차 캐시라는 개념을 알기 전까지는 지연 로딩 개념만 머리에 있었어서, 당연히 B객체가 프록시 객체이기 때문에 나중에 호출할 때, C를 불러올 것이라고 생각했지만, 불러오지 않았다.
왜냐하면 B객체를 생성하면서 이미 1차 캐시에 잡혀버렸기 때문이다.
아무래도 프록시 객체가 캐싱이 되어있으면, DB에 있는 값을 읽어오지 않기 때문에 C객체를 못불러왔던 것 같다.
따라서 C객체를 생성한 후에 B객체에 바로 C객체의 정보를 넘겨줌으로써 버그를 해결했다.
변경 감지라고하는 더티 체킹은 트랜잭션 범위 내에 있는 영속 객체가 변경 사항이 있어도 별도의 저장 작업 수행 없이 DB에 반영할 수 있다.
이번에 팀 프로젝트를 진행하면서 처음 spring boot를 다루게 됐는데, 신기한 기능들이 정말 많은 것 같다. 앞으로도 종종 문외한이기에 생긴 버그를 포스트로 적지 않을까 싶다. 매핑 관련해서도 조만간 적어봐야지...