6주차 Unit 7.2 — JdbcTemplate 의 등장

Psj·2026년 6월 1일

F-lab

목록 보기
209/239

Unit 7.2 — JdbcTemplate 의 등장

F-LAB JAVA · 6주차 · Phase 7 · JdbcTemplate (반복 제거)
★ 깊이 파기 — 5주차 디자인 패턴의 결정체


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • JdbcTemplate 의 정의는?
  • 5주차 템플릿 메소드 + 전략 패턴 의 구현인 이유는?
  • JdbcTemplate 이 숨기는 것 (Connection/Statement/ResultSet/예외) 은?
  • 개발자는 변하는 부분만 의 의미는?
  • "Template" 이름 의 의미는?
  • JdbcTemplate 의 기본 사용 은?
  • 예외 변환 (SQLException → DataAccessException) 은?
  • DataSource 주입 은?
  • JDBC 직접 vs JdbcTemplate 비교는?

🎯 핵심 한 문장

JdbcTemplate 은 5주차 템플릿 메소드 + 전략 패턴의 실제 구현으로, 변하지 않는 흐름 (Connection 획득/반환·PreparedStatement 생성/해제·ResultSet 처리·예외 변환) 을 자기 안에 숨기고 개발자는 변하는 부분 (SQL·파라미터·RowMapper) 만 람다로 전달하면 되며, 이름 그대로 "Template" 이 핵심 정신이다.
JdbcTemplate 은 Spring JDBC 의 핵심 클래스로, JDBC 반복 코드를 제거하는 5주차의 템플릿 메소드 + 전략 패턴 의 실제 구현이다.
변하지 않는 부분 — Connection 획득/반환 (DataSource 자동), PreparedStatement 생성/해제, ResultSet 처리, SQLException → DataAccessException 변환 — 을 JdbcTemplate 안에 숨긴다.
개발자는 변하는 부분만 전달한다 — SQL 문자열, 파라미터, 결과 매핑 (RowMapper, 람다로 가능).
이름의 "Template" 이 그 정신을 정확히 표현한다 — 5주차의 템플릿 메소드 패턴 자체이며, JDBC 직접 코드 (try/catch/finally + 매핑 10-20줄) 가 JdbcTemplate 으로 단 3-5줄이 된다.

비유 — 자판기 (Template) vs 직접 요리

JdbcTemplate = 자판기:

JDBC 직접 = 매번 요리:
  - 가스 켜기 (Connection)
  - 팬 꺼내기 (PreparedStatement)
  - 재료 (파라미터)
  - 요리 (실행) ← 핵심
  - 접시 (ResultSet)
  - 설거지 (close 들)
  - 가스 끄기 (Connection close)

JdbcTemplate = 자판기 (Template):
  - 가스/팬/접시/설거지 → 자판기 안
  - 사용자: 메뉴 (SQL), 토핑 (파라미터), 그릇 (RowMapper) 만
  - 결과 받음

숨겨진 것:
  - Connection 획득/반환
  - PreparedStatement 생성/해제
  - ResultSet 처리
  - 예외 변환 (SQLException → DataAccessException)

개발자 = 메뉴 선택:
  - SQL: "select * from shipments where id = ?"
  - 파라미터: id
  - 매핑: rs → Shipment

5주차 패턴:
  - 자판기 = 템플릿 (변하지 않는 흐름)
  - 메뉴/토핑 = 전략 (변하는 부분)
  - 람다로 전달 가능

코드 차이:
  - 직접: 20+ 줄
  - JdbcTemplate: 3-5 줄
  - 차이 4배 이상

→ JdbcTemplate = 흐름 숨김 + 변하는 부분만, 5주차 템플릿/전략의 결정체.


🧭 9개 섹션 로드맵

1. JdbcTemplate 정의
2. 5주차 패턴의 구현
3. 숨기는 것
4. 변하는 부분만
5. "Template" 이름의 의미
6. 기본 사용
7. 예외 변환
8. DataSource 주입
9. JDBC 직접 vs JdbcTemplate

1️⃣ JdbcTemplate 정의

1.1 정의

JdbcTemplate:

  Spring JDBC 의 핵심 클래스:
    - org.springframework.jdbc.core.JdbcTemplate
    - JDBC 반복 제거
    - 5주차 패턴 구현

1.2 목적

목적:

  - 반복 코드 제거 (try/catch/finally)
  - 자원 관리 자동
  - 예외 변환
  - 매핑 도구 (RowMapper)

1.3 위치

Spring 모듈:

  spring-jdbc:
    - JdbcTemplate
    - DataSourceTransactionManager
    - DriverManagerDataSource
    - 등

→ Spring JDBC 핵심

1.4 Spring Boot 자동

Spring Boot 자동:

  spring-boot-starter-jdbc:
    - JdbcTemplate 빈 자동 등록
    - DataSource 자동 주입
    - 별도 설정 X

→ @Autowired 만

1.5 ILIC 의 맥락

// JdbcTemplate (ILIC)
@Repository
public class ShipmentStatisticsDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentStatisticsDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        // Spring Boot 가 자동 주입 (HikariDataSource 기반)
    }
    
    public int countByStatus(String status) {
        return jdbcTemplate.queryForObject(
            "select count(*) from shipments where status = ?",
            Integer.class, status);
        // 한 줄로 끝
    }
    
    // ILIC: JPA 가 주력, 복잡 통계는 JdbcTemplate 보완
}
class JdbcTemplate {
    <T> T queryForObject(String sql, Class<T> requiredType, Object... args) {
        return null;
    }
}

1.6 자기 점검 답변

JdbcTemplate 의 정의는?

:
1. JdbcTemplate:

  • Spring JDBC 핵심
  1. 목적:

    • 반복 제거
  2. 위치:

    • spring-jdbc
  3. 자동:

    • Spring Boot

2️⃣ 5주차 패턴의 구현

2.1 5주차 패턴 복습

5주차 템플릿 메소드 패턴:

  부모 (템플릿):
    - 변하지 않는 흐름

  자식 (구체):
    - 변하는 부분 (오버라이드)

→ 흐름 + 가변 부분 분리

2.2 5주차 전략 패턴

5주차 전략 패턴:

  컨텍스트:
    - 변하지 않는 흐름

  전략 (인터페이스):
    - 변하는 부분
    - 외부 주입

→ 더 유연 (자식 안 필요)

2.3 JdbcTemplate 의 구조

JdbcTemplate 구조:

  JdbcTemplate (템플릿/컨텍스트):
    - 흐름 (Connection/Statement/처리/해제)
    - 모든 메서드 공통

  전략 (인터페이스, 주입):
    - PreparedStatementCreator
    - PreparedStatementSetter
    - RowMapper
    - ResultSetExtractor
    - 등

→ 5주차 두 패턴의 결합

2.4 query() 내부

// JdbcTemplate.query() 내부 (개념)
public <T> List<T> query(String sql, 
                         PreparedStatementSetter pss,
                         RowMapper<T> rowMapper) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement(sql);
        pss.setValues(ps);                  // ← 전략 (변하는 부분)
        rs = ps.executeQuery();
        List<T> result = new ArrayList<>();
        int rowNum = 0;
        while (rs.next()) {
            result.add(rowMapper.mapRow(rs, rowNum++));  // ← 전략
        }
        return result;
    } catch (SQLException e) {
        throw new DataAccessException(e);   // 변환
    } finally {
        // 자원 해제
        // ...
    }
}
DataSource dataSource;
interface PreparedStatementSetter { void setValues(PreparedStatement ps) throws SQLException; }
interface RowMapper<T> { T mapRow(ResultSet rs, int n) throws SQLException; }
class DataAccessException extends RuntimeException { DataAccessException(Throwable t){} }
interface DataSource { Connection getConnection() throws SQLException; }

2.5 ILIC 의 맥락

// JdbcTemplate = 5주차 패턴 (ILIC)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentDao(JdbcTemplate jt) {
        this.jdbcTemplate = jt;
    }
    
    public List<Shipment> findByStatus(String status) {
        return jdbcTemplate.query(
            "select * from shipments where status = ?",
            // 전략 1: 파라미터 설정 (PreparedStatementSetter, 람다)
            ps -> ps.setString(1, status),
            // 전략 2: 매핑 (RowMapper, 람다)
            (rs, rowNum) -> {
                Shipment s = new Shipment();
                s.setId(rs.getLong("id"));
                s.setStatus(rs.getString("status"));
                return s;
            }
        );
        // JdbcTemplate (템플릿): 흐름 처리
        // 람다 (전략): 변하는 부분
    }
}
class Shipment {
    void setId(Long id) {}
    void setStatus(String s) {}
}

2.6 자기 점검 답변

5주차 템플릿 메소드 + 전략 패턴의 구현인 이유는?

:
1. 5주차 패턴:

  • 템플릿 + 전략
  1. 구조:

    • JdbcTemplate (흐름) + 전략
  2. 결합:

    • 두 패턴
  3. 결정체:

    • 실제 구현

3️⃣ 숨기는 것

3.1 숨겨진 것들

JdbcTemplate 이 숨기는 것:

1. Connection 획득 (DataSource 자동)
2. Connection 반환 (close)
3. PreparedStatement 생성
4. PreparedStatement 해제
5. ResultSet 처리
6. ResultSet 해제
7. SQLException → DataAccessException 변환
8. 자원 해제 순서 (역순)
9. null 체크

3.2 개발자는 모름

개발자는 모름:

  - 어디서 Connection 얻나? (DataSource)
  - 어떻게 PreparedStatement 만드나?
  - 결과 자원 어떻게 해제?
  - 예외 변환은 누가?

→ 자판기 안의 일

3.3 자동화의 가치

자동화의 가치:

  - 실수 X (close 누락)
  - 가독성 ↑ (본 로직만)
  - 유지보수 ↑
  - 일관 (모든 곳 같은 처리)

3.4 비교

// JDBC 직접 (20+ 줄)
public Shipment getDirect(Long id) throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement("...");
        ps.setLong(1, id);
        rs = ps.executeQuery();
        // 매핑
        // ...
    } catch (SQLException e) {
        throw e;
    } finally {
        // close 들...
    }
    return null;
}

// JdbcTemplate (3-5 줄)
public Shipment get(Long id) {
    return jdbcTemplate.queryForObject(
        "select * from shipments where id = ?",
        (rs, n) -> new Shipment(),
        id);
}
class Shipment {}
JdbcTemplate jdbcTemplate;
DataSource dataSource;
class JdbcTemplate {
    <T> T queryForObject(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n); }
}
interface DataSource { Connection getConnection() throws SQLException; }

3.5 ILIC 의 맥락

// 숨기는 것의 효과 (ILIC)

// JdbcTemplate 이 숨겨주는 것:
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    public ShipmentDao(JdbcTemplate jt) { this.jdbcTemplate = jt; }
    
    public void add(Shipment s) {
        jdbcTemplate.update(
            "insert into shipments (bl_no, weight) values (?, ?)",
            s.getBlNo(), s.getWeight());
        // 숨겨진 것:
        // - Connection 자동 획득 (HikariCP 풀에서)
        // - PreparedStatement 자동 생성
        // - 파라미터 바인딩
        // - executeUpdate
        // - 모든 자원 자동 해제
        // - SQLException → DataAccessException
        // - 트랜잭션 동기화 (Spring 의 TransactionManager 와)
    }
    
    public Shipment get(Long id) {
        return jdbcTemplate.queryForObject(
            "select * from shipments where id = ?",
            (rs, n) -> {
                Shipment s = new Shipment();
                s.setId(rs.getLong("id"));
                return s;
            }, id);
        // 위와 같은 처리 + ResultSet 처리
    }
}
// → 개발자는 본 로직만
class Shipment {
    void setId(Long id) {}
    String getBlNo() { return null; }
    java.math.BigDecimal getWeight() { return null; }
}
class JdbcTemplate {
    void update(String s, Object... a) {}
    <T> T queryForObject(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n); }
}

3.6 자기 점검 답변

JdbcTemplate 이 숨기는 것은?

:
1. 숨기는 것:

  • Connection/PreparedStatement/ResultSet
  1. + 예외 변환:

    • DataAccessException
  2. 자동:

    • 자원 관리/순서
  3. 효과:

    • 본 로직만

4️⃣ 변하는 부분만

4.1 변하는 3가지

개발자가 전달하는 변하는 부분:

1. SQL 문자열
2. 파라미터 (PreparedStatementSetter)
3. 결과 매핑 (RowMapper)

4.2 SQL

// SQL (변하는 부분)
jdbcTemplate.queryForObject(
    "select * from shipments where id = ?",   // ← SQL
    ...
);

4.3 파라미터

// 파라미터 (가변 인자 또는 PreparedStatementSetter)

// 방법 1: 가변 인자
jdbcTemplate.queryForObject("... where id = ?", mapper, id);

// 방법 2: PreparedStatementSetter (람다)
jdbcTemplate.query("... where status = ?", 
    ps -> ps.setString(1, "BOOKED"),   // ← Setter
    mapper);

4.4 RowMapper

// RowMapper (매핑)
RowMapper<Shipment> mapper = (rs, rowNum) -> {
    Shipment s = new Shipment();
    s.setId(rs.getLong("id"));
    s.setBlNo(rs.getString("bl_no"));
    return s;
};

jdbcTemplate.queryForObject("...", mapper, id);

4.5 람다와 잘 맞음

람다와 잘 맞음:

  RowMapper:
    - 함수형 인터페이스
    - 람다 표현 가능

  PreparedStatementSetter:
    - 함수형 인터페이스
    - 람다 표현 가능

→ 3주차 람다 활용

4.6 ILIC 의 맥락

// 변하는 부분만 (ILIC)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    public ShipmentDao(JdbcTemplate jt) { this.jdbcTemplate = jt; }
    
    public List<Shipment> findByStatus(String status) {
        return jdbcTemplate.query(
            // 1. SQL (변하는 부분)
            "select id, bl_no, status, weight from shipments where status = ?",
            // 2. RowMapper (변하는 부분, 람다)
            (rs, rowNum) -> {
                Shipment s = new Shipment();
                s.setId(rs.getLong("id"));
                s.setBlNo(rs.getString("bl_no"));
                s.setStatus(rs.getString("status"));
                s.setWeight(rs.getBigDecimal("weight"));
                return s;
            },
            // 3. 파라미터 (변하는 부분)
            status
        );
    }
    
    public List<Shipment> findHeavy(BigDecimal minWeight) {
        return jdbcTemplate.query(
            "select * from shipments where weight > ?",
            (rs, n) -> new Shipment(),
            minWeight
        );
        // 같은 메서드, 다른 SQL/파라미터/매핑
    }
}
// → 변하는 부분 (3가지) 만 전달
// → 흐름은 JdbcTemplate (템플릿)
class Shipment {
    void setId(Long id) {}
    void setBlNo(String s) {}
    void setStatus(String s) {}
    void setWeight(java.math.BigDecimal w) {}
}
class JdbcTemplate {
    <T> java.util.List<T> query(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n); }
}

4.7 자기 점검 답변

개발자는 변하는 부분만의 의미는?

:
1. 변하는 3가지:

  • SQL/파라미터/매핑
  1. SQL:

    • 문자열
  2. 파라미터:

    • 가변 인자/Setter
  3. RowMapper:

    • 람다 (함수형)

5️⃣ "Template" 이름의 의미

5.1 Template = 템플릿

"Template" 이름:

  = 템플릿 (틀, 본보기):
    - 정해진 형태
    - 변하지 않는 부분
    - 5주차 템플릿 메소드 패턴

→ 의도 명확

5.2 5주차 정신 명시

5주차 정신 명시:

  Spring 개발자가:
    - 의도적으로 "Template" 이름
    - 디자인 패턴 분명히
    - "이 클래스 = 템플릿"

→ 좋은 명명

5.3 다른 Template 클래스

다른 Template:

  Spring 의 Template 패턴:
    - RestTemplate (HTTP)
    - JmsTemplate (JMS)
    - RedisTemplate (Redis)
    - 등

  → 같은 패턴, 다른 도메인
  → 일관된 명명

5.4 명명 = 의도

명명 = 의도:

  좋은 이름:
    - 디자인 의도
    - 사용법 암시
    - 5주차 패턴 적용 신호

→ 코드 가독성

5.5 ILIC 의 맥락

// Template 패턴들 (ILIC 에서 사용 가능)

// JdbcTemplate (DB)
@Autowired JdbcTemplate jdbcTemplate;
jdbcTemplate.query(...);

// RestTemplate (HTTP)
@Autowired RestTemplate restTemplate;
restTemplate.getForObject(...);

// RedisTemplate (Redis)
@Autowired RedisTemplate<String, Object> redisTemplate;
redisTemplate.opsForValue().set(...);

// → 모두 같은 5주차 템플릿 패턴
// → 흐름 숨기고 변하는 부분만
// → "Template" 일관

// ILIC 도:
// - JdbcTemplate 일부 사용 (복잡 쿼리)
// - 외부 API 호출 시 RestTemplate (또는 WebClient)
// - 캐시 시 RedisTemplate
class JdbcTemplate { void query(String s) {} }
class RestTemplate { <T> T getForObject(String s) { return null; } }
class RedisTemplate<K, V> { ValueOperations<K, V> opsForValue() { return null; } }
interface ValueOperations<K, V> { void set(K k, V v); }

5.6 자기 점검 답변

"Template" 이름의 의미는?

:
1. Template:

  • 5주차 템플릿 메소드
  1. 명시:

    • 의도 분명
  2. 다른 Template:

    • RestTemplate 등
  3. 일관:

    • 같은 패턴

6️⃣ 기본 사용

6.1 빈 등록

// Spring Boot 자동 (별도 설정 X)
// 또는 명시
@Configuration
class JdbcConfig {
    @Bean
    JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

6.2 주입

// 생성자 주입 (권장)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

6.3 핵심 메서드

JdbcTemplate 핵심 메서드:

  update(sql, params)
    - INSERT/UPDATE/DELETE
    - 영향받은 행 수

  queryForObject(sql, mapper, params)
    - 단일 행 조회
    - 객체 1개

  query(sql, mapper, params)
    - 여러 행 조회
    - List<객체>

  execute(sql)
    - 임의 SQL (DDL)

6.4 ILIC 의 맥락

// 기본 사용 (ILIC)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    // INSERT
    public void add(Shipment s) {
        jdbcTemplate.update(
            "insert into shipments (bl_no, weight, status) values (?, ?, ?)",
            s.getBlNo(), s.getWeight(), s.getStatus());
    }
    
    // SELECT (단일)
    public Shipment get(Long id) {
        return jdbcTemplate.queryForObject(
            "select * from shipments where id = ?",
            (rs, n) -> mapRow(rs),
            id);
    }
    
    // SELECT (다수)
    public List<Shipment> findByStatus(String status) {
        return jdbcTemplate.query(
            "select * from shipments where status = ?",
            (rs, n) -> mapRow(rs),
            status);
    }
    
    // UPDATE
    public int updateStatus(Long id, String status) {
        return jdbcTemplate.update(
            "update shipments set status = ? where id = ?",
            status, id);
    }
    
    // DELETE
    public int delete(Long id) {
        return jdbcTemplate.update(
            "delete from shipments where id = ?",
            id);
    }
    
    private Shipment mapRow(ResultSet rs) throws SQLException {
        Shipment s = new Shipment();
        s.setId(rs.getLong("id"));
        s.setBlNo(rs.getString("bl_no"));
        return s;
    }
}
class Shipment {
    void setId(Long id) {}
    void setBlNo(String s) {}
    String getBlNo() { return null; }
    java.math.BigDecimal getWeight() { return null; }
    String getStatus() { return null; }
}
class JdbcTemplate {
    void update(String s, Object... a) {}
    <T> T queryForObject(String s, RowMapper<T> r, Object... a) { return null; }
    <T> java.util.List<T> query(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n) throws SQLException; }
}

6.5 자기 점검 답변

JdbcTemplate 의 기본 사용은?

:
1. 빈 등록:

  • Spring Boot 자동
  1. 주입:

    • 생성자
  2. 핵심 메서드:

    • update/queryForObject/query
  3. 사용:

    • 간결

7️⃣ 예외 변환

7.1 SQLException → DataAccessException

JdbcTemplate 의 예외 변환:

  SQLException (Checked):
    - DB 별 다른 errorCode
    - DB 종속

  → 변환

  DataAccessException (Unchecked):
    - Spring 표준
    - DB 무관
    - 의미별 분류

7.2 계층

DataAccessException 계층:

  DataAccessException (Runtime)
  ├── DataIntegrityViolationException (제약 위반)
  ├── DuplicateKeyException (중복)
  ├── EmptyResultDataAccessException (결과 없음)
  ├── IncorrectResultSizeDataAccessException (결과 수 이상)
  ├── DeadlockLoserDataAccessException (데드락)
  ├── CannotAcquireLockException (락 못 얻음)
  └── ...

7.3 DB 무관

DB 무관:

  MySQL 의 "중복 키" 예외:
    - SQLException + errorCode 1062

  Oracle 의 "중복 키" 예외:
    - SQLException + errorCode ORA-00001

  → 둘 다 DuplicateKeyException 으로
  → 같은 처리

7.4 RuntimeException

RuntimeException (Unchecked):

  - throws 불필요
  - 코드 깔끔
  - 전역 처리 가능 (@ExceptionHandler)

→ 모던 Spring 스타일

7.5 ILIC 의 맥락

// 예외 변환 활용 (ILIC)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    public ShipmentDao(JdbcTemplate jt) { this.jdbcTemplate = jt; }
    
    public void add(Shipment s) {
        try {
            jdbcTemplate.update(
                "insert into shipments (bl_no) values (?)",
                s.getBlNo());
        } catch (DuplicateKeyException e) {
            // BL 번호 중복 (DB 무관)
            throw new BusinessException("BL 번호 중복: " + s.getBlNo());
        }
        // SQLException 안 던짐 (DataAccessException)
        // throws 불필요
    }
    
    public Shipment get(Long id) {
        try {
            return jdbcTemplate.queryForObject(
                "select * from shipments where id = ?",
                (rs, n) -> new Shipment(), id);
        } catch (EmptyResultDataAccessException e) {
            return null;   // 결과 없음 (DB 무관)
        }
    }
}
// 전역 예외 핸들러
@RestControllerAdvice
class GlobalExceptionHandler {
    @ExceptionHandler(DataIntegrityViolationException.class)
    ResponseEntity<?> handleIntegrity(DataIntegrityViolationException e) {
        return null;
    }
}
class Shipment { String getBlNo() { return null; } }
class JdbcTemplate {
    void update(String s, Object... a) {}
    <T> T queryForObject(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n); }
}
class DuplicateKeyException extends RuntimeException {}
class EmptyResultDataAccessException extends RuntimeException {}
class DataIntegrityViolationException extends RuntimeException {}
class BusinessException extends RuntimeException { BusinessException(String s){} }
class ResponseEntity<T> {}

7.6 자기 점검 답변

예외 변환 (SQLException → DataAccessException) 은?

:
1. 변환:

  • Checked → Unchecked
  1. 계층:

    • 의미별 분류
  2. DB 무관:

    • 일관 처리
  3. 장점:

    • throws X, 깔끔

8️⃣ DataSource 주입

8.1 DataSource 의존

JdbcTemplate 의 의존:

  JdbcTemplate:
    - DataSource 받음 (5주차 DI)
    - getConnection() 호출
    - 인터페이스에만 의존

8.2 생성

// JdbcTemplate 생성
DataSource dataSource = ...;   // HikariDataSource 등
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 또는 setDataSource()

8.3 Spring Boot 자동

// Spring Boot 자동 (별도 코드 X)
@Repository
public class ShipmentDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    // Spring Boot 가:
    // 1. HikariDataSource 빈 (auto-config)
    // 2. JdbcTemplate 빈 (auto-config, DataSource 주입)
    // 3. ShipmentDao 에 JdbcTemplate 주입
}

8.4 5주차 DI 의 실제

5주차 DI 의 실제:

JdbcTemplate:
  - DataSource 외부 주입
  - 자기가 만들지 X
  - 5주차 DI 원칙

→ 좋은 사례

8.5 ILIC 의 맥락

// DataSource 주입 (ILIC)

// Spring Boot 자동 흐름:
// 1. application.yml 에 spring.datasource.* 설정
// 2. Spring Boot 가 HikariDataSource 빈 생성
// 3. JdbcTemplate 빈 생성 (DataSource 자동 주입)
// 4. Repository 에 JdbcTemplate 주입

@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    // JdbcTemplate 가 내부적으로:
    // - dataSource.getConnection() 호출 (HikariCP 풀)
    // - SQL 실행
    // - close() (풀로 반환)
    
    // 모두 자동
}

// 명시적 설정 (필요 시)
@Configuration
class JdbcConfig {
    @Bean
    JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
        // DataSource 는 또 다른 빈 (HikariDataSource)
    }
}
class JdbcTemplate { JdbcTemplate(DataSource d) {} }
interface DataSource {}

8.6 자기 점검 답변

DataSource 주입은?

:
1. 의존:

  • DataSource
  1. 생성:

    • new JdbcTemplate(dataSource)
  2. Spring Boot:

    • 자동
  3. 5주차 DI:

    • 실제 사례

9️⃣ JDBC 직접 vs JdbcTemplate

9.1 비교 (조회)

// JDBC 직접 (Unit 7.1)
public Shipment getDirect(Long id) throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement(
            "select * from shipments where id = ?");
        ps.setLong(1, id);
        rs = ps.executeQuery();
        if (rs.next()) {
            Shipment s = new Shipment();
            s.setId(rs.getLong("id"));
            return s;
        }
        return null;
    } finally {
        if (rs != null) try { rs.close(); } catch (SQLException e) { }
        if (ps != null) try { ps.close(); } catch (SQLException e) { }
        if (conn != null) try { conn.close(); } catch (SQLException e) { }
    }
}

// JdbcTemplate
public Shipment get(Long id) {
    return jdbcTemplate.queryForObject(
        "select * from shipments where id = ?",
        (rs, n) -> {
            Shipment s = new Shipment();
            s.setId(rs.getLong("id"));
            return s;
        }, id);
}
class Shipment { void setId(Long id) {} }
DataSource dataSource;
JdbcTemplate jdbcTemplate;
interface DataSource { Connection getConnection() throws SQLException; }
class JdbcTemplate {
    <T> T queryForObject(String s, RowMapper<T> r, Object... a) { return null; }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n) throws SQLException; }
}

9.2 코드 줄 수

코드 줄 수:

  JDBC 직접: 20+ 줄
  JdbcTemplate: 5-7 줄

  → 1/4 ~ 1/3

9.3 비교 표

항목JDBC 직접JdbcTemplate
자원 관리수동 (try/finally)자동
예외SQLException (Checked)DataAccessException (Unchecked)
코드 줄길음짧음
누수 위험있음없음
가독성
람다제한적적극

9.4 어떻게 작동

JdbcTemplate 의 마법:

  사용자가 보는 것:
    - 짧은 코드

  실제 일어나는 것:
    - 내부에서 try/catch/finally
    - 자원 관리
    - 예외 변환
    - 5주차 패턴

→ 5주차 패턴이 작은 인터페이스로

9.5 ILIC 의 맥락

// 전후 비교 (ILIC)

// Before (JDBC 직접, 만약)
public void addShipmentDirect(Shipment s) throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement(
            "insert into shipments (bl_no, weight, status) values (?, ?, ?)");
        ps.setString(1, s.getBlNo());
        ps.setBigDecimal(2, s.getWeight());
        ps.setString(3, s.getStatus());
        ps.executeUpdate();
    } finally {
        if (ps != null) try { ps.close(); } catch (SQLException e) { }
        if (conn != null) try { conn.close(); } catch (SQLException e) { }
    }
}
// 15+ 줄

// After (JdbcTemplate)
public void addShipment(Shipment s) {
    jdbcTemplate.update(
        "insert into shipments (bl_no, weight, status) values (?, ?, ?)",
        s.getBlNo(), s.getWeight(), s.getStatus());
}
// 4 줄

// → 1/4 줄, 가독성 ↑, 누수 X, 예외 깔끔
class Shipment {
    String getBlNo() { return null; }
    java.math.BigDecimal getWeight() { return null; }
    String getStatus() { return null; }
}
DataSource dataSource;
JdbcTemplate jdbcTemplate;
interface DataSource { Connection getConnection() throws SQLException; }
class JdbcTemplate { void update(String s, Object... a) {} }

9.6 면접 단골 질문 매핑

Q핵심 답변
JdbcTemplate?Spring JDBC 핵심
5주차 패턴?템플릿 + 전략
숨기는 것?Connection/Statement/예외
변하는 부분?SQL/파라미터/매핑
"Template" 의미?5주차 템플릿
예외 변환?DataAccessException
DataSource?외부 주입
줄 수?1/4
람다?RowMapper
Spring Boot?자동

9.7 자기 점검 체크리스트

정의

  • Spring JDBC

5주차 패턴

  • 템플릿/전략

숨기는 것

  • 자원/예외

변하는 부분

  • 3가지

"Template"

  • 의미

기본 사용

  • 메서드

예외 변환

  • DataAccessException

DataSource

  • DI

9.8 추가 심화 질문

Q1: NamedParameterJdbcTemplate?

답:

  • 이름 있는 파라미터 (:name)
  • 가독성 ↑ (? 보다)
  • JdbcTemplate 의 확장
  • MapSqlParameterSource

Q2: SimpleJdbcInsert?

답:

  • INSERT 전용 도구
  • 자동 키 생성 받기
  • 컬럼 자동 추론
  • 편의 도구

Q3: JdbcTemplate vs JPA?

답:

  • JdbcTemplate: SQL 직접
  • JPA: 객체 중심
  • 혼용 가능
  • 복잡 쿼리는 JdbcTemplate

Q4: RowCallbackHandler?

답:

  • 결과를 List 로 반환 X
  • 각 행 처리 콜백
  • 대량 데이터
  • 스트리밍

Q5: 배치 작업?

답:

  • jdbcTemplate.batchUpdate
  • 여러 SQL 묶음
  • 성능 ↑ (네트워크 왕복 ↓)
  • 대량 INSERT/UPDATE

🎯 핵심 요약 — 3줄 정리

1. JdbcTemplate

  • Spring JDBC 의 핵심 클래스, 5주차 템플릿 메소드 + 전략 패턴의 실제 구현
  • 변하지 않는 흐름 (자원/예외 처리) 숨기고, 변하는 부분 (SQL/파라미터/RowMapper) 만 받음

2. 숨기는 것 + 변하는 것

  • 숨김: Connection/PreparedStatement/ResultSet/SQLException → DataAccessException
  • 받음: SQL 문자열 + 파라미터 (가변 인자) + RowMapper (람다 가능)

3. 효과

  • JDBC 직접 20+ 줄 → JdbcTemplate 5-7 줄 (1/4)
  • 누수 위험 X, 예외 깔끔 (Unchecked), 5주차 정신의 결정체

📚 다음으로...

Unit 7.3 — update / queryForObject / query

이번 Unit에서 JdbcTemplate 을 봤다면, 다음은 핵심 메서드 사용법.

  • update(): INSERT/UPDATE/DELETE
  • queryForObject(): 단일 행
  • query(): 여러 행
  • 예외 (EmptyResult / IncorrectResultSize)

Phase 7 진행 상황

🛠️ Phase 7 — JdbcTemplate
  ✅ Unit 7.1 JDBC 만 쓸 때 반복 코드
  ✅ Unit 7.2 JdbcTemplate 등장 ★깊이 ← 여기
  ⏭ Unit 7.3 update/queryForObject/query
  ⏭ Unit 7.4 RowMapper
  ⏭ Unit 7.5 JdbcTemplate 구조적 의미

★ 깊이 파기 — JdbcTemplate 의 등장 완료

profile
Software Developer

0개의 댓글