
4. 스프링 JdbcTemplate
1. JdbcTemplate이란?
- JDBC
자바가 데이터베이스에 쿼리(SQL)를 보내고 결과를 받는 표준 API예요.
하지만 매번 접속 열고(Connection), 쿼리 문장 만들고(PreparedStatement), 결과 읽고(ResultSet), 예외 처리하는 반복 코드가 많아요. - JdbcTemplate
스프링이 만든 도우미(helper) 클래스예요.
“연결부터 정리까지” 복잡한 반복 코드를 대부분 대신 해 주고,
우리가 할 일은 순수한 SQL(문장) 작성과
“결과를 자바 객체로 mapping”하는 부분뿐이에요.
2. 코드 한 줄씩 보기
2-1. 생성자에서 DataSource 주입받기
public class JdbcTemplateMemberRepository implements MemberRepository {
private final JdbcTemplate jdbcTemplate;
public JdbcTemplateMemberRepository(DataSource dataSource) {
// 스프링이 만들어 놓은 DataSource(커넥션 풀)를 받아서
// JdbcTemplate 객체를 직접 생성해 둡니다.
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
…
}
- DataSource:
DB 접속 정보(URL, 아이디, 비밀번호 등)를 담고 있는 커넥션 풀(pool)이에요. - JdbcTemplate(dataSource):
이걸 통해 “커넥션 가져오기 → 쿼리 실행 → 커넥션 반납” 과정을 모두 처리해 줍니다.
2-2. 회원 저장하기 (save)
@Override
public Member save(Member member) {
// 간단한 insert 작업을 도와주는 유틸
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("member") // 테이블 이름
.usingGeneratedKeyColumns("id"); // 자동 생성된 PK 컬럼
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName()); // 넣을 값(name 컬럼)
// 실행하고 자동 생성된 id 값을 받아옴
Number key = jdbcInsert.executeAndReturnKey(
new MapSqlParameterSource(parameters)
);
member.setId(key.longValue()); // 엔티티에 id 세팅
return member;
}
- SimpleJdbcInsert:
매번 INSERT INTO member(name) VALUES(?) 쓰지 않고, 더 편하게 사용 가능해요. - executeAndReturnKey:
DB가 만들어 준 자동 증가(id)를 돌려받아서, 우리 객체에도 반영합니다.
2-3. 조회하기 (findById, findAll, findByName)
@Override
public Optional<Member> findById(Long id) {
List<Member> result = jdbcTemplate.query(
"select * from member where id = ?",
memberRowMapper(), // 결과 매핑 규칙
id // ? 에 바인딩할 값
);
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return jdbcTemplate.query(
"select * from member",
memberRowMapper()
);
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = jdbcTemplate.query(
"select * from member where name = ?",
memberRowMapper(),
name
);
return result.stream().findAny();
}
- query(sql, rowMapper, 파라미터…)
- SQL 실행
- ResultSet의 각 행(row)을 memberRowMapper()로 Member 객체로 바꿔줌
- List<Member> 반환
2-4. 결과 매핑 규칙 (RowMapper)
private RowMapper<Member> memberRowMapper() {
// 람다식 사용: ResultSet → Member 객체로 변환
return (rs, rowNum) -> {
Member member = new Member();
member.setId(rs.getLong("id")); // id 컬럼 읽어서 세팅
member.setName(rs.getString("name"));// name 컬럼 읽어서 세팅
return member;
};
}
- RowMapper:
DB가 돌려준 한 줄(row) 의 데이터를
우리가 쓰는 Member 객체로 변환(mapping) 해 주는 역할입니다.
3. Spring 설정에서 JdbcTemplate 사용하기
3-1. SpringConfig에 빈 등록
@Configuration
public class SpringConfig {
private final DataSource dataSource; // 스프링이 자동으로 만들어 줌
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
// 서비스는 레포지토리를 생성자 주입으로 받습니다.
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// 원하는 구현체로 바꿔 끼워쓸 수 있어요!
//return new MemoryMemberRepository(); // 메모리 저장소
//return new JdbcMemberRepository(dataSource); // 순수 JDBC
return new JdbcTemplateMemberRepository(dataSource); // JdbcTemplate 사용
}
}
- @Bean:
이 메서드의 리턴 값을 스프링이 관리하는 “빈(Component)”으로 만들어 줍니다. - 구현체 선택:
MemoryMemberRepository, JdbcMemberRepository, JdbcTemplateMemberRepository 중
하나만 주석 해제해서 사용하면, 나머지 코드는 전혀 변경 없이 동작합니다.
4. 요약: 왜 이렇게 쓰는 걸까?
- 반복 코드 제거
- JDBC 고유의 복잡한 연결/정리 코드를 JdbcTemplate이 대신 처리해 줘요.
- 직접 SQL 제어
- MyBatis, JPA와 달리 SQL을 “내가 원하는 대로” 직접 쓸 수 있어요.
- 복잡한 쿼리나 성능 튜닝이 필요할 때 유리합니다.
- 스프링과의 결합(Integration)
- DataSource·Transaction 같은 스프링 기능을 그대로 이용할 수 있어요.
- 설정만 바꾸면 H2, MySQL, Oracle 등 다른 DB로 손쉽게 전환 가능!
🔥 꼭 기억해야 할 포인트
- JdbcTemplate 은 “JDBC API 위에 얹힌 편리한 래퍼(wrapper)”다!
- SimpleJdbcInsert 로는 INSERT가, query() 로는 SELECT가 간단해진다.
- RowMapper 는 “한 줄 → 객체” 변환기.
- @Bean 설정 하나만 바꿔서 메모리 저장소 ↔ 순수 JDBC ↔ JdbcTemplate 저장소를 자유롭게 바꿀 수 있다!

5. JPA
1. JPA란 무엇인가요?
- 원래 JDBC를 쓰면
- Connection 열고,
- PreparedStatement 만들고(SQL 써야 하고),
- ResultSet 돌면서 컬럼 하나하나 꺼내서 객체에 담고,
- 예외 처리하고,
- 커넥션 닫고…
매번 이런 반복 코드가 너무 많았어요.
- JPA는
- 이 반복 코드를 거의 땡겨 줍니다.
- 우리가 작성하는 건 **“내가 저장하고 싶은 자바 객체”**와
- “어떤 객체를 저장할 건지 선언”(애너테이션)뿐이에요.
- SQL을 직접 쓰지 않아도 JPA가 자동으로 만들어서 실행해 줍니다.
- 장점!
- 객체 중심 설계로 패러다임 전환
- 개발 생산성↑ (반복 코드↓)
- 복잡한 쿼리는 JPQL(객체지향 SQL)로, 간단한 건 자동 생성!
2. 필요한 라이브러리(Gradle에 추가)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2' // 테스트·개발용 가벼운 DB
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
- spring-boot-starter-data-jpa 안에 JDBC도 다 포함돼 있어요.
- H2는 메모리 DB라 가볍게 실험할 때 좋아요.
3. 스프링 부트에 JPA 설정하기
src/main/resources/application.yml (또는 .properties) 에 이렇게 쓰면 돼요:
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
show-sql: true # 콘솔에 SQL이 찍히게
hibernate:
ddl-auto: create-drop # 애플리케이션 실행할 때 테이블 자동 생성·삭제
- ddl-auto 옵션
- create-drop → 시작할 때 테이블 만들고, 종료할 때 지워요.
- update → 변경된 엔티티에 맞춰 테이블을 “수정”
- none → 자동 생성·수정을 끄기
4. 엔티티(Entity) 만들기
@Entity // 이 클래스가 DB 테이블과 1:1 매핑된다는 표시
public class Member {
@Id // 이 필드가 PK(기본키)란 뜻
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // DB가 자동으로 만들어 주는 번호
private String name; // 회원 이름
// ↓ 여기에 getter/setter만 있으면 끝!
}
- **@Entity**를 붙이면
→ JPA가 이 클래스를 “DB 테이블처럼” 관리해 줘요. - @Id + @GeneratedValue
→ id 컬럼을 자동 증가(1, 2, 3…)로 만들어 줍니다.
5. JPA Repository 직접 구현하기 - JpaMemberRepository
package hello.hello_spring.repository;
import hello.hello_spring.domain.Member;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import java.util.List;
import java.util.Optional;
@Transactional
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
public Member save(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
}
- EntityManager는 JPA를 쓰려면 Entity Manager가 필요하다.
→ JPA가 제공하는 “DB에 실제로 저장·조회”해 주는 객체예요.
-> JPA entity manager로 도근 것이 동작한다.- 만들어진 EntityManager을 인잭션 받으면 된다: 프로퍼티랑 데이터 베이스를 이용해서 만들어 진다. 떄문에 데이터 소스를 다 내부적으로 들고 있어서 DB 통신을 내부적으로 할 수 있다.
- JPQL
→ "select m from Member m" 이렇게 객체 기준으로 조회합니다.
6. 서비스에 트랜잭션(@Transactional) 적용
@Service
@Transactional // 이 클래스의 public 메서드를 실행할 때 트랜잭션을 자동으로 처리
public class MemberService {
// …
}
- 트랜잭션 안에서만 JPA의 모든 저장·수정·삭제(데이터 변경)가 제대로 동작해요.
- 메서드가 정상 종료되면 commit,
- 런타임 예외 터지면 rollback!
7. 스프링 설정(SpringConfig)에서 JPA 사용하기
package hello.hello_spring;
import hello.hello_spring.repository.JpaMemberRepository;
import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.service.MemberService;
import jakarta.persistence.EntityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
private final EntityManager em;
public SpringConfig(DataSource dataSource, EntityManager em) {
this.dataSource = dataSource;
this.em = em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository(); //인터페이스에는 new 안됨. 구현체를 리턴.
//return new MemoryMemberRepository();
//return new JdbcMemberRepository(dataSource);//인터페이스에는 new 안됨. 구현체를 리턴.
return new JpaMemberRepository(em);
}
}
- new JpaMemberRepository(em) 한 줄로
→ JPA가 대신 SQL 짜고, 테이블 만들고, 저장·조회까지 다 해 줍니다. - Hibernate: 오픈 소스 구현체이다.

8. 한 눈에 보는 흐름
- 의존성(라이브러리) 추가 → JPA, H2
- 설정(application.yml) → DB 접속 정보 + ddl-auto
- 엔티티(@Entity) 만들기
- Repository 구현 → EntityManager 사용
- Service에 @Transactional → 트랜잭션 관리
- SpringConfig에 Bean 등록 → JPA 구현체 선택
이제 “객체 중심”으로 코드를 짜고, 반복되는 JDBC 코드는 JPA가 모두 처리해 주는 마법 같은 세계에 오신 걸 환영해요! 😄
6. 스프링 데이터 JPA
1. 스프링 데이터 JPA란?
- JPA 위에 올라탄, “반복되는 데이터베이스 CRUD 코드를 자동으로 만들어 주는 프레임워크”예요.
- 인터페이스 선언만으로(구현 클래스 없이)
- 저장, 조회, 수정, 삭제 같은 기본 기능이 모두 제공되고,
- 메서드 이름만 보고도 원하는 조회(SQL)를 자동으로 만들어 줍니다.
- 덕분에 개발자는 오직 비즈니스 로직(예: “회원 가입 처리”나 “이메일 중복 확인”)에만 집중할 수 있어요.
2. 기본 설정 — JPA까지는 그대로
스프링 데이터 JPA를 쓰려면, 앞에서 썼던 **JPA 설정(application.yml, 엔티티, spring-boot-starter-data-jpa)**은 전부 그대로 사용하면 됩니다.
(추가로 라이브러리 더 넣을 필요 없이, 이미 JPA 스타터 안에 다 포함돼 있어요.)
3. 핵심 코드: 인터페이스만 선언하기 - SpringDataJpaMemberRepository
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
// 1) JpaRepository가 제공하는 모든 기본 CRUD를
// MemberRepository 인터페이스에 “자동 상속” 시켜 줍니다.
// <Member, Long> 은 <엔티티 클래스, PK 타입> 지정
//
// 2) MemberRepository라는 나만의 인터페이스도 함께 상속하여
// 기존 서비스 코드(Controller/Service)에서 MemberRepository 타입을
// 그대로 사용할 수 있게 해 줍니다.
public interface SpringDataJpaMemberRepository
extends JpaRepository<Member, Long>, MemberRepository {
// 이름으로 조회하는 메서드 시그니처만 선언해 주면,
// “select m from Member m where m.name = ?” 쿼리가 자동으로 생성됩니다.
Optional<Member> findByName(String name);
}
- 스프링 데이터 JPA가 JPA 리포지토리를 받고 있으면 구현제를 자동으로 만들어줘서 스프링Bean를 자동으로 등록한다.
- extends JpaRepository<Member, Long>
→ save(), findAll(), findById(), delete() 등 모든 기본 메서드가 바로 사용 가능 - , MemberRepository
→ 기존에 만든 MemberRepository(메서드 정의된 인터페이스)를 함께 상속해서,
→ 서비스 코드는 아무 수정 없이 새 리포지토리를 바로 주입받아 쓸 수 있어요.
4. 스프링 설정 — 빈(Bean) 등록이 필요 없다! - SpringConfig
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 스프링 데이터 JPA 인터페이스만 있으면,
// 스프링 부트가 애플리케이션 구동 시에
// SpringDataJpaMemberRepository 구현체를 **자동 생성**해서
// 스프링 빈으로 **등록**해 줍니다.
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
// 스프링 빈(MemberRepository 구현체)이 자동 주입됨
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
// 기존과 똑같이 MemberService에 주입해 주면 끝!
return new MemberService(memberRepository);
}
}
- MemberRepository memberRepository 파라미터로 받기만 하면,
- 스프링 데이터 JPA가 만든 실제 구현체(SpringDataJpaMemberRepository)를 주입해 줍니다.
- 따라서 @Bean memberRepository() 같은 코드도 아예 필요 없어져요.
5. 자동 제공 기능
기능 설명
| 기본 CRUD | save(), findById(), findAll(), delete(), count() 등을 별도 구현 없이 사용 가능 |
| 메서드 이름만으로 조회 | findByName(), findByEmail(), findByAgeGreaterThan() 등 메서드 이름만 선언하면 쿼리 자동 생성 |
| 페이징 & 정렬 | findAll(Pageable pageable) 같은 메서드로 페이지별 조회와 정렬을 자동 지원 |
6. 복잡한 쿼리가 필요하다면?
- Querydsl: 자바 코드로 “타입 안전한 동적 쿼리”를 작성
- 네이티브 쿼리: @Query(value = "SELECT ...", nativeQuery = true)
- JdbcTemplate: 아주 복잡하거나 튜닝이 필요한 SQL은 직접 작성
하지만 일반적인 CRUD와 간단한 조회는 모두 스프링 데이터 JPA만으로 거의 커버할 수 있어서,
개발자는 중요한 로직에만 집중할 수 있습니다.
출처 : 김영한, 『스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술』, 인프런 강의.
'DEVELOPMENT > Spring' 카테고리의 다른 글
| [스프링부트 완전정복] 2. 스프링 부트 개발 환경 설정 및 예제 맛보기 (0) | 2025.10.01 |
|---|---|
| [스프링부트 완전정복] 1. 스프링 부트 소개 (1) | 2025.09.30 |
| 6. 스프링 DB 접근 기술1 (0) | 2025.05.25 |
| 5. 회원 관리 예쩨 - 웹 MVC 개발 (0) | 2025.05.12 |
| 4. 스프링 빈과 의존관계 (0) | 2025.05.12 |