JPA·ORM 엔티티 Repository 쿼리 메서드 정리 📝 문제풀이
◀ 이전 📋 목차 CH 09 ▶
🗃️ CHAPTER 08 · 중급

SQL을 직접 안 짜고 DB를 다룬다고?

SQL 과목에서 배운 테이블·CRUD를 떠올려 보세요. 스프링에서는 객체를 다루듯 DB를 다뤄요. 이번 장은 JPA와 Spring Data JPA로 반복적인 SQL을 어떻게 없애는지 배웁니다.

🎯 이 장을 끝내면
🔗
JPA·ORM
ORM과 JPA가 뭔가요?
SQL을 직접 쓰지 않고 객체로 DB를 다루는 기술이에요.

ORM(Object-Relational Mapping)은 자바 객체와 DB 테이블을 매핑해, SQL을 직접 쓰지 않고 객체를 다루듯 DB를 다루는 기술이에요. 예를 들어 userRepository.save(user) 한 줄이면, ORM이 알아서 INSERT SQL을 만들어 실행해줘요.

🗣️
ORM을 통역사에 비유하면? 자바는 객체라는 말을 쓰고, 데이터베이스는 표(테이블)라는 말을 써요. 서로 언어가 다르죠. ORM은 그 사이에서 "이 객체는 이 행(row)이에요" 하고 자동으로 번역해주는 통역사예요. 덕분에 우리는 낯선 SQL 대신 익숙한 자바 객체만 다루면 됩니다.
📊 테이블 ↔ 객체, 어떻게 대응되나 SQL 과목에서 다룬 행(row)이 자바의 객체 하나가 되고, 컬럼이 객체의 필드가 돼요. ORM은 이 둘 사이를 자동으로 변환해줘요. JPA(Java Persistence API)는 자바의 ORM 표준(명세)이에요. 표준일 뿐이라 실제로 동작시키는 구현체가 필요한데, 보통 Hibernate를 써요.
🔤
매핑(Mapping) — 서로 다른 두 가지를 짝지어 연결해 두는 것이에요. 여기서는 자바 객체 ↔ DB 테이블, 필드 ↔ 컬럼을 하나하나 짝지어 두는 걸 말해요. 짝만 정해두면 한쪽을 바꿀 때 다른 쪽도 자동으로 따라와요.
🔤
영속성(Persistence) — JPA(Java Persistence API)의 그 'P'예요. 객체를 DB에 저장해, 프로그램을 껐다 켜도 데이터가 계속 남아 있게 하는 것을 뜻해요. 메모리 속 객체는 프로그램이 끝나면 사라지지만, DB에 영속화해 두면 오래 보관되죠. 머릿속으로만 기억하는 대신 공책에 적어 두는 것과 같아요.
🧩
자바 객체
User user = ...
🔗
JPA / ORM
객체 ↔ 테이블 자동 변환
🗄️
DB 테이블
users 행(row)
ORM JPA Hibernate @Entity @Id JpaRepository 쿼리 메서드 CRUD
🔤
CRUD — 데이터를 다루는 기본 네 가지 작업, 생성(Create)·조회(Read)·수정(Update)·삭제(Delete)의 앞글자를 딴 말이에요.
🧭
연결 포인트. SQL 과목에서 SELECT·INSERT를 직접 짰다면, JPA는 그걸 객체 조작으로 대신해줘요. SQL을 몰라도 되는 게 아니라, 반복적인 CRUD SQL을 안 짜도 되는 거예요.
🧩
엔티티
엔티티(@Entity): 테이블과 매핑되는 클래스
클래스 = 테이블, 필드 = 컬럼이라고 생각하면 쉬워요.

엔티티는 DB 테이블과 매핑되는 자바 클래스예요. 클래스에 @Entity를 붙이면 "이 클래스는 테이블과 연결돼요"라는 표시가 돼요.

🔤
어노테이션(Annotation) — 클래스나 필드 위에 붙이는 @로 시작하는 표시(이름표)예요. @Entity는 "이건 테이블과 연결되는 클래스야", @Id는 "이 필드가 기본 키야"처럼, JPA에게 살짝 알려주는 쪽지 역할을 해요.
어노테이션역할
@Entity이 클래스가 테이블과 매핑되는 엔티티임을 표시.
@Id이 필드가 기본 키(Primary Key)임을 표시.
@GeneratedValue기본 키 값을 자동으로 증가·생성(SQL의 AUTO_INCREMENT 같은 역할).
🧩 엔티티 클래스 예시 아래처럼 클래스에 어노테이션만 붙이면, JPA가 User 테이블과 자동으로 연결해요. 각 필드가 컬럼이 돼요.
@Entity
class User {
    @Id
    @GeneratedValue        // id 값 자동 증가
    Long id;               // 기본 키 컬럼

    String name;           // name 컬럼
}
💡
@Id 는 필수. 엔티티에는 반드시 기본 키가 있어야 해요. @GeneratedValue를 함께 쓰면 우리가 id를 직접 넣지 않아도 DB가 알아서 채워줘요.
🖼️ 그림으로 보기 — Entity ↔ 테이블 매핑
자바 객체 · @Entity class User { Long id; String name; String email; } 매핑 (ORM) 🗄️ DB 테이블 · users id name email 1 태호 t@ex.com 행(row) 하나 = 객체 하나
@Entity 클래스의 필드가 테이블의 열과 대응돼요. 객체를 저장하면 JPA가 자동으로 INSERT SQL로 바꿔 실행해요(ORM).
📦
Repository
Spring Data JPA: Repository만 만들면 CRUD 자동
구현 코드 없이 인터페이스만! 이게 이번 장의 핵심이에요.

JpaRepository<엔티티, ID타입>를 상속한 인터페이스만 만들면, save·findById·findAll·delete 같은 기본 CRUD가 자동 제공돼요. 구현 코드를 한 줄도 안 써도 스프링이 알아서 만들어줘요.

📦 Repository 인터페이스 예시 User 엔티티를 다루고, 기본 키 타입이 Long이니 JpaRepository<User, Long>을 상속해요. 본문은 비어 있어도 CRUD가 다 생겨요.
interface UserRepository extends JpaRepository<User, Long> {
    // 본문이 비어 있어도
    // save, findById, findAll, delete 등 자동 제공!
}
자동 제공 메서드하는 일
save(entity)저장(INSERT) 또는 수정(UPDATE).
findById(id)기본 키로 한 건 조회(SELECT).
findAll()전체 조회.
delete(entity)삭제(DELETE).
🔤
JDBC — 자바에서 SQL로 DB에 직접 접근하는 표준 방식이에요. JPA가 대신해 주기 전에는 이걸로 SQL과 연결 코드를 일일이 손으로 짰어요.
이게 왜 대단할까. 순수 JDBC로는 INSERT/SELECT SQL과 커넥션 코드를 매번 짜야 했어요. Spring Data JPA는 JpaRepository<User, Long> 한 줄로 그 반복을 전부 없애줘요.
🪄 그림으로 보기 — 인터페이스만 선언하면 벌어지는 일
📝
① 인터페이스만 선언
JpaRepository를 상속한 빈 인터페이스 한 개. 구현 코드는 안 써요.
🪄
② 스프링이 구현 자동 생성
실행 시점에 스프링이 구현체를 대신 만들어 끼워줘요.
③ CRUD 바로 사용
save·findById·findAll·delete를 그냥 호출하면 돼요.
🔍
쿼리 메서드
쿼리 메서드: 이름만으로 쿼리가 생긴다
SQL을 안 쓰고, 메서드 이름 규칙으로 조회를 만들어요.

기본 CRUD 말고 조건 조회가 필요할 땐, 메서드 이름을 규칙대로 짓기만 하면 스프링이 쿼리를 자동 생성해줘요. 이걸 쿼리 메서드라고 해요.

🔍 쿼리 메서드 예시 findBy 뒤에 필드 이름을 붙이면 그 조건으로 검색돼요. GreaterThan 같은 키워드도 이름에 넣을 수 있어요.
interface UserRepository extends JpaRepository<User, Long> {
    // name = ? 로 조회하는 SELECT 자동 생성
    List<User> findByName(String name);

    // age > ? 로 조회
    List<User> findByAgeGreaterThan(int age);
}
메서드 이름자동 생성되는 조건
findByName(name)name 컬럼이 일치하는 행 조회.
findByAgeGreaterThan(age)age > 값 인 행 조회.
findByNameAndAge(name, age)name 일치 AND age 일치.
🧠
이름 = 쿼리. findBy + 필드 + 조건 키워드(GreaterThan, Like, Between 등)로 이름을 지으면, SQL을 한 줄도 안 쓰고 조회가 완성돼요.
🧩 그림으로 보기 — 메서드 이름이 곧 쿼리 조건
시작 키워드
findBy
"조회할게요"라는 신호
필드 이름
Age
age 컬럼을 조건으로
조건 키워드
GreaterThan
"~보다 큰"(>) 조건
자동 생성 결과
WHERE age > ?
SQL을 한 줄도 안 썼어요
findByAgeGreaterThan이라는 이름 하나가 위 네 조각으로 분해돼 쿼리로 번역돼요.
🎁
정리
그래서 무엇이 좋아졌나
반복적인 CRUD SQL이 사라지고, 생산성이 올라가요.
📉 순수 JDBC vs 🍃 Spring Data JPA 순수 JDBC: 테이블마다 INSERT/SELECT/UPDATE/DELETE SQL과 매핑 코드를 직접 반복.
Spring Data JPA: JpaRepository<User, Long> 상속 한 줄로 기본 CRUD 자동, 조건 조회는 쿼리 메서드로. 반복적인 CRUD SQL을 안 짜도 되니 생산성이 크게 올라가요. 단, 복잡한 쿼리가 필요하면 JPQL이나 @Query로 직접 작성할 수도 있어요.
⚖️
만능은 아니에요. 통계·다중 조인처럼 복잡한 쿼리는 이름 규칙만으로 어려워요. 그럴 땐 @QueryJPQL(또는 네이티브 SQL)을 써서 직접 정의해요. SQL 실력이 여전히 필요한 이유예요.
🔤
JPQL · 네이티브 SQL — JPQL(Java Persistence Query Language)은 테이블이 아니라 엔티티(자바 객체)를 대상으로 쓰는 SQL 비슷한 조회 언어예요. 문법은 SQL과 닮았지만 FROM User처럼 테이블 이름 대신 엔티티 이름을 적어, 어떤 DB를 쓰든 똑같이 동작해요. 반면 네이티브 SQL은 우리가 아는 진짜 SQL을 그대로 쓰는 것(특정 DB 문법까지)이에요. 쉽게 말해 JPQL은 "객체 버전 SQL", 네이티브 SQL은 "원래 SQL"이라고 보면 돼요.
🧠 이 장 핵심 요약
📝
문제풀이 · 점검
배운 걸 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 네 가지 유형(객관식 · O/X · 빈칸 · 개념)을 섞어 두었어요.
CH 09 🛡️ 예외 처리와 검증 — 견고한 API를 위한 에러·유효성 다루기