Spring Data JPA 실습 — Repository · N+1 · DTO 변환
Spring Data JPA 실습 — Repository · N+1 · DTO 변환
🎯 이 lesson 을 읽고 나면
이 lesson 을 다 읽고 나면 아래 3가지를 자신 있게 할 수 있습니다.
- ▸✅ JpaRepository 만 상속해서 0줄 코드로 CRUD 구현
- ▸✅ N+1 문제 → @EntityGraph 또는 JOIN FETCH 로 해결
- ▸✅ Lazy vs Eager + 실무에서 무조건 Lazy 인 이유
학습 목표를 체크리스트로 두고 다 답할 수 있게 되면 lesson 을 닫으세요.
JpaRepository — 메서드를 *이름만 짓고 끝*
가장 큰 마법
인터페이스 한 줄 만 선언하면 — Spring 이 런타임에 구현체를 자동 생성 합니다. 이미 사용 가능한 메서드:
- ▸
save(entity)— INSERT 또는 UPDATE - ▸
findById(id)— Optional 반환 - ▸
findAll()— 전체 조회 - ▸
count()— 개수 - ▸
deleteById(id)— 삭제 - ▸
existsById(id)— 존재 여부
메서드 이름으로 쿼리 만들기
메서드 이름 규칙 만 지키면 Spring 이 자동으로 SQL 생성. 가독성·속도 모두 압승.
키워드: findBy, countBy, existsBy, deleteBy + 필드명 + (Containing, GreaterThan, In, Between, OrderBy...Asc/Desc ...)
직접 JPQL 쓰기 — @Query
복잡한 쿼리는 JPQL (Java Persistence Query Language) 로 작성:
JPQL 은 SQL 과 비슷해 보이지만 "엔티티 이름" 으로 작성 — 테이블이 아닙니다.
페이징 — Pageable
무한 스크롤·페이지 네비게이션 모두 이걸로 끝.
N+1 문제 — 실무에서 가장 자주 만나는 함정
문제 상황
사용자 100명 → 쿼리 101번 발생. 데이터 늘수록 지수적으로 느려짐. 이게 N+1 문제.
해결 1 — @EntityGraph (가장 간단)
한 번의 JOIN 쿼리로 끝. SQL 로는:
해결 2 — JOIN FETCH (JPQL 직접)
DISTINCT 가 중요 — 안 쓰면 중복 행 이 늘어납니다.
해결 3 — Batch Size (@BatchSize)
완전 해결은 아니지만 N+1 → N/배치사이즈 +1 로 줄임. 다중 연관 관계 + 페이징 조합에서 유용.
Lazy vs Eager — 언제 뭘 쓰나
실무 규칙: 무조건 LAZY. EAGER 는 예측 불가능한 쿼리 폭발 의 원인. 필요할 땐 그때그때 @EntityGraph 또는 JOIN FETCH.
N+1 디버깅 — show-sql 켜놓기
개발 중 콘솔에 실제 발생 SQL 이 보입니다. 같은 쿼리가 수십 번 반복 되면 N+1 의심 신호.
Entity → DTO 변환 — *왜 분리해야 하나*
Entity 를 직접 응답하면 안 되는 이유
4가지 문제:
1. 순환 참조: User ↔ Order 양방향 매핑이면 무한 JSON 직렬화 → StackOverflow.
2. 민감 정보 노출: password·internalNote 같은 필드가 자동 노출.
3. API 스키마와 DB 스키마 결합: DB 컬럼명 바꾸면 API 도 같이 깨짐.
4. 불필요한 쿼리: Lazy 필드를 Jackson 이 직렬화하려다 추가 쿼리 발생.
DTO 패턴
Static factory 메서드 (from) 가 변환 책임 — Entity 와 DTO 가 서로 모르는 관계 유지.
입력용 DTO 도 분리
@Valid 가 Bean Validation (@Email·@NotBlank·@Size) 을 자동 실행. 검증 실패 시 400 Bad Request 자동 반환.
MapStruct — 변환 코드 자동 생성
DTO 가 많아지면 from() 메서드도 많아집니다. MapStruct 라이브러리가 컴파일 타임에 변환 코드 생성 — 보일러플레이트 제거.
중·대규모 프로젝트의 표준 입니다. 처음엔 수동으로 짜다가 익숙해지면 도입하세요.
🤖 AI 에게 이렇게 요청해보세요
이 lesson 의 개념을 알면 AI 에게 구체적으로 지시할 수 있습니다. 막연한 "고쳐줘" 가 아니라 어휘를 가진 요청 — 그게 토큰 절약의 출발점입니다.
- ▸"이 findAll + 반복문이 N+1 인데 @EntityGraph 로 해결해줘"
- ▸"이 Entity 를 UserResponse DTO 로 변환하는 static factory 메서드 추가해줘"
- ▸"JpaRepository 에 findByEmailAndActive 메서드 시그니처 추가해줘"
왜 이게 토큰을 줄이나
개념을 모를 땐 AI 답변을 받고도 "그게 뭐예요?" 를 다시 물어야 합니다. 그 "다시 물음" 이 토큰을 잡아먹습니다. 개념 한 번 익혀두면 대화가 한 번에 끝납니다.