[LG U+ 유레카 3기] Spring Data JPA find() 메서드 실습
2025. 11. 26. 17:54ㆍJava/JPA
Spring Data JPA find() 메서드 실습 - Student 검색 패턴 정리
❶ 상황 설명
이번 오전 실습에서는 Spring Data JPA의 메서드 이름 규칙 기반 조회(find)를 집중적으로 연습했다.
특히 Student 엔티티를 대상으로 다양한 검색 패턴을 만들어 보는 것이 목표였다.
- 단일 조건 조회:
findByName() - 복합 조건 조회:
findByEmailAndPhone(),findByEmailOrPhone() - 문자열 패턴 조회:
StartingWith,EndingWith,Containing,Like - 정렬:
findAllByOrderByNameDesc() - 범위 검색:
findByIdBetween()
핵심은 “쿼리를 직접 안 써도, 메서드 이름만으로 조건과 정렬을 표현할 수 있다”를 몸으로 체득하는 것.
❷ Student 엔티티 – 조회 대상 도메인
실습에서 조회의 대상이 되는 엔티티는 Student 테이블과 매핑되는 클래스이다.
package com.mycom.myapp.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Column;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Entity
@Table(name = "student")
@Getter
@Setter
@ToString
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 200, nullable = false)
private String email;
@Column(length = 13, nullable = false)
private String phone;
@Column(length = 255, nullable = false)
private String name;
}
@Entity,@Table: JPA가 관리하는 엔티티 + 테이블 이름 지정@Id+@GeneratedValue(IDENTITY): PK + AUTO_INCREMENT 전략@Column: 길이와 null 여부 설정
이제 이 Student 엔티티를 기준으로, Spring Data JPA의 findBy... 메서드들을 다양하게 만들어본 것이 이번 실습의 내용이다.
❸ Repository – 메서드 이름만으로 쿼리 만들기
핵심은 Spring Data JPA 메서드 이름 규칙이다.StudentRepository에서 메서드 이름만 보고도 어떤 SQL이 나갈지 유추할 수 있게 되는 것이 목표였다.
package com.mycom.myapp.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.mycom.myapp.entity.Student;
public interface StudentRepository extends JpaRepository<Student, Integer> {
// 1) 단일 조건
List<Student> findByName(String name);
// 2) 복합 조건 (AND / OR)
List<Student> findByEmailAndPhone(String email, String phone);
List<Student> findByEmailOrPhone(String email, String phone);
// 3) 문자열 패턴
List<Student> findByNameStartingWith(String name);
List<Student> findByEmailEndingWith(String email);
List<Student> findByPhoneContaining(String phone);
List<Student> findByNameLike(String name);
// 4) 정렬
List<Student> findAllByOrderByNameDesc();
// 5) 범위 검색
List<Student> findByIdBetween(int from, int to);
}
📌 메서드 이름 → SQL 매핑 요약
| 메서드 이름 | 의미 | 대략적인 JPQL / SQL |
|---|---|---|
findByName |
이름이 정확히 일치 | where name = ? |
findByEmailAndPhone |
이메일과 전화번호 둘 다 일치 | where email = ? and phone = ? |
findByEmailOrPhone |
이메일 또는 전화번호 중 하나라도 일치 | where email = ? or phone = ? |
findByNameStartingWith |
이름이 특정 문자열로 시작 | where name like '값%' |
findByEmailEndingWith |
이메일이 특정 문자열로 끝남 | where email like '%값' |
findByPhoneContaining |
전화번호에 특정 문자열이 포함 | where phone like '%값%' |
findByNameLike |
직접 패턴을 넣어서 like 검색 | where name like ? (와일드카드는 개발자가 붙임) |
findAllByOrderByNameDesc |
전체를 name 내림차순으로 정렬 | order by name desc |
findByIdBetween |
id가 구간 안에 포함되는 데이터 | where id between ? and ? (양끝 포함) |
❹ Service & Controller – 레이어드 아키텍처 적용
① Service 인터페이스
package com.mycom.myapp.service;
import java.util.List;
import com.mycom.myapp.entity.Student;
public interface StudentServiceFind {
List<Student> findByName(String name);
List<Student> findByEmailAndPhone(String email, String phone);
List<Student> findByEmailOrPhone(String email, String phone);
List<Student> findByNameStartingWith(String name);
List<Student> findByEmailEndingWith(String email);
List<Student> findByPhoneContaining(String phone);
List<Student> findByNameLike(String name);
List<Student> findAllByOrderByNameDesc();
List<Student> findByIdBetween(int from, int to);
}
② Service 구현체
package com.mycom.myapp.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.mycom.myapp.entity.Student;
import com.mycom.myapp.repository.StudentRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class StudentServiceFindImpl implements StudentServiceFind {
private final StudentRepository studentRepository;
@Override
public List<Student> findByName(String name) {
return studentRepository.findByName(name);
}
@Override
public List<Student> findByEmailAndPhone(String email, String phone) {
return studentRepository.findByEmailAndPhone(email, phone);
}
@Override
public List<Student> findByEmailOrPhone(String email, String phone) {
return studentRepository.findByEmailOrPhone(email, phone);
}
@Override
public List<Student> findByEmailEndingWith(String email) {
return studentRepository.findByEmailEndingWith(email);
}
@Override
public List<Student> findByPhoneContaining(String phone) {
return studentRepository.findByPhoneContaining(phone);
}
@Override
public List<Student> findByNameStartingWith(String name) {
return studentRepository.findByNameStartingWith(name);
}
@Override
public List<Student> findByNameLike(String name) {
// Like 패턴은 직접 만들어서 넘겨준다.
return studentRepository.findByNameLike("%" + name + "%");
}
@Override
public List<Student> findAllByOrderByNameDesc() {
return studentRepository.findAllByOrderByNameDesc();
}
@Override
public List<Student> findByIdBetween(int from, int to) {
return studentRepository.findByIdBetween(from, to);
}
}
③ Controller – REST API 엔드포인트
package com.mycom.myapp.controller;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mycom.myapp.entity.Student;
import com.mycom.myapp.service.StudentServiceFind;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/students")
@RequiredArgsConstructor
public class StudentControllerFind {
private final StudentServiceFind studentServiceFind;
@GetMapping("/find/name")
public List<Student> findByName(@RequestParam("name") String name) {
return studentServiceFind.findByName(name);
}
@GetMapping("/find/emailandphone")
public List<Student> findByEmailAndPhone(@RequestParam("email") String email,
@RequestParam("phone") String phone) {
return studentServiceFind.findByEmailAndPhone(email, phone);
}
@GetMapping("/find/emailorphone")
public List<Student> findByEmailOrPhone(@RequestParam("email") String email,
@RequestParam("phone") String phone) {
return studentServiceFind.findByEmailOrPhone(email, phone);
}
@GetMapping("/find/namestartingwith")
public List<Student> findByNameStartingWith(@RequestParam("name") String name) {
return studentServiceFind.findByNameStartingWith(name);
}
@GetMapping("/find/emailendingwith")
public List<Student> findByEmailEndingWith(@RequestParam("email") String email) {
return studentServiceFind.findByEmailEndingWith(email);
}
@GetMapping("/find/phonecontaining")
public List<Student> findByPhoneContaining(@RequestParam("phone") String phone) {
return studentServiceFind.findByPhoneContaining(phone);
}
@GetMapping("/find/namelike")
public List<Student> findByNameLike(@RequestParam("name") String name) {
return studentServiceFind.findByNameLike(name);
}
@GetMapping("/find/orderbynamedesc")
public List<Student> findAllByOrderByNameDesc() {
return studentServiceFind.findAllByOrderByNameDesc();
}
@GetMapping("/find/idbetween")
public List<Student> findByIdBetween(@RequestParam("from") int from,
@RequestParam("to") int to) {
return studentServiceFind.findByIdBetween(from, to);
}
}
이렇게 해서 Controller → Service → Repository → Entity → DB 흐름을 깔끔하게 나눈 레이어드 아키텍처로 작성했다.
❺ Postman으로 테스트한 주요 예시
1) 이름 정확 일치 검색
GET http://localhost:8080/students/find/name?name=홍길동1

2) 이메일 + 전화번호 AND 검색
GET http://localhost:8080/students/find/emailandphone?email=1@gildong.com&phone=010-0000-0001
3) 이메일 OR 전화번호 검색
GET http://localhost:8080/students/find/emailorphone?email=1@gildong.com&phone=010-0000-0003
4) 이름 시작 문자열 검색 (StartingWith)
GET http://localhost:8080/students/find/namestartingwith?name=홍길동

5) 이메일 끝나는 문자열 검색 (EndingWith)
GET http://localhost:8080/students/find/emailendingwith?email=@gmail.com
6) 전화번호 포함 문자열 검색 (Containing)
GET http://localhost:8080/students/find/phonecontaining?phone=0000
7) Like 검색 (직접 패턴 구성)
GET http://localhost:8080/students/find/namelike?name=홍길동
8) 이름 내림차순 정렬
GET http://localhost:8080/students/find/orderbynamedesc

9) ID 범위 검색 (Between)
GET http://localhost:8080/students/find/idbetween?from=31&to=40

각 요청에 대해 Hibernate가 출력하는 SQL 로그를 보면서, “메서드 이름 → 실제 쿼리”가 머릿속에서 연결되도록 확인했다.
❻ 개념 정리 – 꼭 기억해둘 포인트
- Spring Data JPA 메서드 규칙을 이해하면, 간단한 조회는 JPQL/SQL을 직접 쓰지 않고도 구현 가능하다.
findByXxx: 기본 equal 검색And,Or: 복합 조건 결합StartingWith / EndingWith / Containing: 내부적으로like를 사용Like: 패턴("%값%")은 개발자가 직접 만들어서 넣는 방식OrderBy필드명Desc/Asc: 정렬 조건을 메서드 이름에 포함Between: 범위 검색이며, 양 끝값도 포함된다.- 레이어드 아키텍처:
Controller → Service → Repository → Entity → DB구조를 유지하면, 나중에 기능이 복잡해져도 유지보수가 훨씬 쉽다.
❼ 오늘 실습에서 얻은 교훈 / 느낀 점
- 간단한 조회 기능 수준에서는, “메서드 이름을 잘 짓는 것 = 쿼리를 잘 짜는 것”과 비슷하다는 감각을 얻었다.
findByNameLike("%" + name + "%")처럼, 결국 뒤에서 어떤 SQL이 나갈지를 상상할 수 있어야 진짜로 JPA를 이해한 것이다.- Controller, Service, Repository를 분리해 둔 덕분에, 중간에 검색 조건을 추가하거나 바꿀 때 구조 전체를 바꿀 필요가 없다.
- 이번 실습은 이후에 배울 페이징(Pageable), 정렬(Sort), 동적 쿼리(JPQL/QueryDSL)을 이해하는 기초가 된다.
오전 실습 덕분에, 앞으로 프로젝트에서 학생/회원/게시글 등의 검색 기능을 만들 때 Spring Data JPA의 메서드 이름 기반 조회를 자신 있게 활용할 수 있는 기반이 만들어졌다.
'Java > JPA' 카테고리의 다른 글
| [LG U+ 유레카 3기] Spring Data JPA CRUD + Lombok + 패턴 실습 정리 (0) | 2025.11.25 |
|---|---|
| [LG U+ 유레카 3기] JPA N+1 · Fetch Join · JPQL Join (0) | 2025.11.20 |
| [LG U+ 유레카 3기] 순수 JPA 복합키 실습(@IdClass & @EmbeddedId) (1) | 2025.11.17 |
| [LG U+ 유레카 3기]순수JPA 실습 — JPQL (Java Persistence Query Language) (0) | 2025.10.20 |
| [LG U+ 유레카 3기]순수 JPA 실습 엔티티 생명주기 (persist / find / merge / detach / remove) (0) | 2025.10.20 |