⚙️ Spring Data JPA에서 JPQL 적용하기
🧠 왜 JPQL인가?
@Query 로 JPQL 직접 작성, @Param 으로 파라미터 바인딩@Query("select new ...Dto(s.name, s.age) from Student s " +
"where s.name like %:keyword% and s.age between :minAge and :maxAge")
List<StudentDto> findByNameContainingAndAgeBetween(
@Param("keyword") String keyword,
@Param("minAge") int minAge,
@Param("maxAge") int maxAge);
🧾 설정 파일 (properties vs yml)
application.properties
application.yml
💡 둘 다 함께 둘 수 있지만 properties 우선순위가 더 높음
보통 하나로 통일해서 사용
🧱 도메인 & DTO
🧩 엔티티
Tutor (1) ↔ (N) Student@OneToMany(mappedBy="tutor") List<Student> students@ManyToOne(fetch=LAZY) @JoinColumn(name="tutor_id") Tutor tutor📦 DTO
TutorStudentCountDto(name, count) : 튜터별 학생 수 집계 결과StudentDto(name, age) : 검색 결과 슬림 뷰StudentTutorDto(name, age, tutorName) : 학생+튜터명 혼합 응답🧰 Repository JPQL 패턴
1. 🧮 집계 + 그룹바이 → DTO 프로젝션
@Query("select new ...TutorStudentCountDto(t.name, count(s)) " +
"from Tutor t join t.students s group by t.name")
List<TutorStudentCountDto> findTutorStudentCounts();
2. 🔍 조건 검색(이름 like, 나이 범위) → DTO 프로젝션
@Query("select new ...StudentDto(s.name, s.age) " +
"from Student s where s.name like %:keyword% " +
"and s.age >= :minAge and s.age <= :maxAge")
List<StudentDto> findByNameContainingAndAgeBetween(...);
3. ⚡ N+1 완화 F fetch join
@Query("select s from Student s join fetch s.tutor")
List<Student> findStudentsWithTutor();
🧭 Service & Controller 흐름
Service: Repository 호출만 담당(비즈니스 규칙·매핑)
getTutorStudentCounts() → 집계 DTO 반환searchStudents() → 조건 검색 DTO 반환getStudentsWithTutors() → 엔티티 조회 후 StudentTutorDto로 매핑Controller: REST 엔드포인트
GET /tutors/student-counts → 튜터별 학생 수GET /students/search?keyword=&minAge=&maxAge= → 학생 검색GET /students/with-tutors → 학생 + 튜터 이름(페치 조인 기반)🧪 실행 결과 요약
group by + DTO 프로젝션 → 집계 SQL 실행 확인like + age 범위 → DTO로 바로 매핑join fetch s.tutor → N+1 없이 한 방 조회📸 Postman & 콘솔 SQL 캡처로 JPQL → SQL 변환이 잘 반영되는 것을 확인
🚀
@PostConstruct활용
application.ymlprofiles:
active: dev
@Component @Profile("dev") + @PostConstruct✅ 개발 환경 데이터 자동화로 API 시연/테스트가 한결 편해짐
🧠 요약 정리
| 구분 | 핵심 내용 | 포인트 |
|---|---|---|
| JPQL 필요성 | Query Method 한계, N+1 발생 | @Query + @Param 활용 |
| 프로젝션 | DTO 직접 생성자 프로젝션 | 트래픽 절감 · 표현 최소화 |
| 집계 | group by + count | TutorStudentCountDto |
| 조건 검색 | like + 범위(>=, <=) | StudentDto |
| N+1 완화 | join fetch | findStudentsWithTutor() |
| 설정 | properties vs yml | yml 가독성↑, properties 우선순위↑ |
| 부트스트랩 | @PostConstruct + @Profile("dev") | 개발 프로필에서만 시드 데이터 |