반응형

Java 125

[Spring JPA 공식 문서 기반] 15. N+1 문제는 왜 구조적으로 발생할까

14편에서는 Lazy Loading이“항상 터지는 기능처럼 보이는 이유”가 실제로는 트랜잭션과 엔티티 상태를 잘못 다룬 결과라는 점을 살펴봤습니다.이번 글에서는 Lazy Loading과 항상 함께 언급되는,N+1 문제가왜 실수로 생기는 버그가 아니라 구조적으로 발생할 수밖에 없는 문제인지를 Spring JPA 공식 문서 관점에서 정리해보겠습니다.1. N+1 문제를 단순한 “실수”로 보면 안 되는 이유N+1 문제는 보통 이렇게 설명됩니다.“쿼리를 잘못 써서 SELECT가 N번 더 나간다”이 설명은절반만 맞습니다.왜냐하면 N+1 문제는JPA의 동작 방식과객체 그래프 탐색 모델이결합되면서의도하지 않아도 자연스럽게 발생하기 때문입니다.2. N+1의 기본 구조다음과 같은 상황을 생각해봅시다.List orders ..

[Spring JPA 공식 문서 기반] 14. Lazy Loading은 왜 항상 터질까

13편에서는 즉시 로딩(EAGER)이“항상 함께 로딩한다”는 전제 때문에 실무에서 구조적인 문제를 만들 수밖에 없다는 점을 살펴봤습니다.그 다음으로 자연스럽게 나오는 말이 있습니다.“그럼 Lazy는 안전한가요?”하지만 실무에서는 오히려“Lazy는 항상 터진다”는 인식이 더 강합니다.이번 글에서는 이 인식이 왜 생겼는지, 그리고 그 오해의 정체가 무엇인지 공식 문서 관점에서 정리해보겠습니다.1. Lazy Loading은 원래 문제가 아니다먼저 분명히 해야 할 사실이 있습니다.Lazy Loading 자체는 문제 있는 기능이 아니다오히려 JPA 공식 문서 기준으로 보면, Lazy Loading은연관관계를 필요할 때만 로딩하고불필요한 JOIN을 피하며조회 성능을 제어하기 위한 기본 전략입니다.즉,Lazy는 “위..

[Spring JPA 공식 문서 기반] 13. 즉시 로딩(EAGER)은 왜 항상 문제를 만들까

12편에서는 연관관계의 주인이 왜 중요한지, 그리고 그 선택이UPDATE SQL 증가와 성능 문제로 어떻게 이어지는지를 살펴봤습니다.이번 글에서는 그 연관관계 설계에서가장 위험한 선택으로 꼽히는즉시 로딩(EAGER)이 왜 실무에서 거의 항상 문제를 만드는지를 공식 문서 기준으로 정리해보겠습니다.1. EAGER는 왜 존재할까먼저 오해부터 정리해야 합니다.EAGER는잘못된 옵션 ❌비권장 기능 ❌이 아닙니다.JPA 공식 문서 기준으로, EAGER는“해당 연관 엔티티가 항상 함께 필요하다는 강한 가정”을 표현하기 위해 존재합니다.즉,“이 연관관계는 분리해서 쓰일 일이 없다”라는 설계 의도를 코드로 드러내는 옵션입니다.2. 문제는 ‘항상’이라는 전제다EAGER의 가장 큰 문제는 옵션 자체가 아니라,“항상 함께 필..

[Spring JPA 공식 문서 기반] 12. 연관관계의 주인은 왜 중요할까

11편에서는 OSIV(Open Session In View)가왜 기본값이었고, 왜 운영 환경에서 문제가 되었는지를 살펴봤습니다.이제 시리즈는 JPA에서 가장 많은 실무 버그가 발생하는 영역,연관관계로 들어갑니다.그 출발 질문은 이것입니다.연관관계의 주인은 왜 이렇게 중요할까?이 질문에 답하지 못하면,값을 넣었는데 저장이 안 되거나UPDATE SQL이 두 번 나가거나객체와 DB 상태가 어긋나는문제를 반복해서 겪게 됩니다.1. 연관관계의 주인은 “객체 개념”이 아니다많은 개발자가 연관관계의 주인을 이렇게 오해합니다.“누가 더 중요하니까 주인이지?”하지만 Spring JPA 공식 문서 기준으로, 연관관계의 주인은객체 모델의 개념이 아니라 DB 외래 키(FK)를 누가 관리하느냐의 문제입니다.즉,객체 그래프의 주..

[Spring JPA 공식 문서 기반] 11. OSIV는 왜 기본값이었고, 왜 문제였을까

10편에서는 읽기 전용 트랜잭션이“쓰기 방지 옵션”이 아니라 “조회용 Persistence Context를 올바르게 유지하기 위한 선언”이라는 점을 살펴봤습니다.이번 글에서는 그 흐름의 마지막 퍼즐 조각,OSIV(Open Session In View)가왜 기본값이었고, 왜 문제가 되었는지를 공식 문서 관점에서 정리해보겠습니다.1. OSIV는 어떤 문제를 해결하려고 등장했을까OSIV가 등장한 배경은 아주 현실적인 문제였습니다.“컨트롤러나 뷰에서 Lazy Loading이 자꾸 터진다”당시의 전형적인 구조는 이랬습니다.Service 계층에서 트랜잭션 종료Controller / View에서 엔티티 접근LazyInitializationException 발생OSIV는 이 문제를 아주 단순한 방식으로 해결합니다.“요..

[Spring JPA 공식 문서 기반] 10. 읽기 전용 트랜잭션의 진짜 의미

9편에서는 트랜잭션이 없을 때 JPA의 핵심 기능이“왜 반쪽짜리로 무너지는지”를 Persistence Context와 엔티티 상태 흐름을 통해 정리했습니다.이번 글에서는 그 다음으로 자연스럽게 따라오는 질문,“그렇다면 읽기만 하는 경우에도 왜 트랜잭션을 써야 할까?”에 답해보겠습니다.1. 읽기 전용 트랜잭션은 왜 오해를 부를까@Transactional(readOnly = true)를 보면 많은 개발자가 이렇게 이해합니다.“이 메서드는 DB에 쓰기가 안 나간다”하지만 이 이해는정확하지 않습니다.읽기 전용 트랜잭션은쓰기 자체를 금지하는 기능 ❌JPA의 동작 방식을 힌트로 알려주는 설정 ⭕입니다.2. 읽기 전용 트랜잭션의 진짜 목적Spring JPA 공식 문서 기준으로, 읽기 전용 트랜잭션의 목적은 다음 두 ..

[Spring JPA 공식 문서 기반] 9. 트랜잭션이 없으면 JPA는 왜 반쪽짜리일까

8편에서는 EntityManager가 왜 Thread-safe 하지 않은지, 그리고 그 이유가“EntityManager는 트랜잭션에 바인딩된 상태 관리자”라는 점에 있음을 정리했습니다.이번 글에서는 이 논의를 한 단계 더 밀어보겠습니다.트랜잭션이 없으면 JPA는 실제로 어떻게 동작할까?결론부터 말하면,트랜잭션이 없는 JPA는 핵심 기능의 절반이 동작하지 않습니다.그래서 우리는“JPA는 트랜잭션 기반 기술”이라고 말합니다.1. 가장 흔한 착각부터 짚자다음과 같은 코드를 보면 많은 개발자가 이렇게 기대합니다.public void updateMember() { Member m = em.find(Member.class, 1L); m.setName("newName");}겉보기에는:엔티티를 조회했고상태를..

[Spring JPA 공식 문서 기반] 8. EntityManager는 왜 Thread-safe 하지 않을까

7편에서는 영속성 컨텍스트와의 연결을 끊는 행위,detach / clear / merge가 왜 위험하고, 언제 필요한지를 살펴봤습니다.이번 글에서는 그 모든 동작의 중심에 있는 객체,EntityManager가왜 Thread-safe 하지 않은지를 Spring JPA 공식 문서 기준으로 트랜잭션 · 스레드 · 영속성 컨텍스트 관점에서 정리해보겠습니다.1. 많은 개발자가 처음 던지는 질문Spring 환경에서 우리는 보통 다음과 같은 코드를 작성합니다.@Repositorypublic class MemberRepository { @PersistenceContext private EntityManager em;}이 코드를 보면 자연스럽게 이런 의문이 듭니다.EntityManager는 싱글톤 Bean인데..

[Spring JPA 공식 문서 기반] 6. Flush는 왜 이렇게 예측하기 어려울까

5편에서는 JPA의 변경 감지(Dirty Checking)가“객체의 상태 변화를 SQL로 번역하는 규칙”이라는 점을 살펴봤습니다.이번 글에서는 그 다음 단계에서 가장 많은 혼란을 만드는 개념,Flush를 “쓰기 SQL과의 관계”를 중심으로 공식 문서 관점에서 정리해보겠습니다.Flush를 이해하지 못하면,왜 읽기 쿼리 전에 UPDATE가 나가는지왜 SQL은 나갔는데 롤백되는지왜 실행 시점을 코드로 예측할 수 없는지를 끝까지 설명할 수 없습니다.1. Flush를 Commit으로 이해하는 순간 문제가 시작된다Flush를 처음 접하면 대부분 이렇게 이해합니다.“Flush는 Commit 직전에 실행되는 과정이다”이 설명은절반만 맞고, 절반은 틀립니다.공식 문서 기준으로 두 개념은 명확히 구분됩니다.Flush – ..

[Spring JPA 공식 문서 기반] 5. 변경 감지(Dirty Checking)는 왜 이렇게 구현됐을까

4편에서는 Persistence Context가 단순한 캐시가 아니라,엔티티의 상태와 동일성을 지키기 위한 규칙 집합이라는 점을 살펴봤습니다.이번 글에서는 그 Persistence Context가 가장 강하게 모습을 드러내는 지점,변경 감지(Dirty Checking)를 공식 문서 관점에서 구조적으로 풀어보겠습니다.1. JPA에서 UPDATE는 왜 직접 호출하지 않을까JPA를 처음 접하면 이 점이 가장 낯설게 느껴집니다.member.setName("newName");// update 쿼리는 어디 있지?SQL을 직접 쓰던 시절이라면, 이 코드는아무 일도 하지 않은 코드처럼 보입니다.하지만 JPA에서는 이 한 줄이UPDATE SQL의 의도를 표현합니다.이게 가능한 이유가 바로 변경 감지입니다.2. 변경 감지..

반응형