6주차 Unit 7.1 — JDBC만 쓸 때의 반복 코드

Psj·2026년 6월 1일

F-lab

목록 보기
208/240

Unit 7.1 — JDBC만 쓸 때의 반복 코드

F-LAB JAVA · 6주차 · Phase 7 · JdbcTemplate (반복 제거)
🛠️ Phase 7 시작 — 5주차 패턴의 실현


📌 학습 목표

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

  • JDBC 코드의 반복 은?
  • try/catch/finally 의 부담 은?
  • 본 로직 < 자원 관리 코드 의 의미는?
  • close 누락 → Connection 누수 위험은?
  • 변하는 부분 vs 변하지 않는 부분 은?
  • 5주차의 어떤 패턴 적용?
  • try-with-resources 로 줄여도 한계 는?
  • 예외 처리의 부담 은?
  • 다음 — JdbcTemplate 의 동기는?

🎯 핵심 한 문장

JDBC 만 쓰면 매 쿼리마다 Connection·PreparedStatement·ResultSet 의 try/catch/finally 자원 관리 코드가 본 로직보다 더 길어지고 close 누락 시 Connection 누수 위험까지 있는데, 이는 5주차의 "변하지 않는 흐름 + 변하는 부분" 패턴 (템플릿 메소드 / 전략 패턴) 으로 해결할 수 있는 문제로 JdbcTemplate (다음 Unit) 이 그 답이다.
JDBC 만 쓰면 매 쿼리마다 똑같은 try/catch/finally 패턴이 반복된다 — Connection 획득, PreparedStatement 생성, 파라미터 바인딩, ResultSet 처리, 그리고 역순으로 자원 해제.
문제는 — 본 로직 (SQL 실행/결과 처리) 보다 자원 관리 코드 (try/catch/finally, null 체크, 중첩 try) 가 더 길고 복잡 하다는 것이다.
실수로 close() 를 누락하면 Connection 누수 가 발생해 풀의 모든 Connection 이 소진되어 서비스가 멈출 수도 있다.
5주차에서 배운 "변하지 않는 흐름 + 변하는 부분" 패턴 — 흐름 (연결/해제) 은 변하지 않고 SQL·파라미터·매핑만 변하므로 템플릿 메소드 + 전략 패턴 으로 분리할 수 있으며 (try-with-resources 로 일부만 해결), 이것이 다음 Unit 의 JdbcTemplate 의 정확한 동기다.

비유 — 매번 똑같은 사전 준비 + 정리

JDBC 반복 = 실험실 매번 같은 절차:

매 실험 (매 쿼리):
  1. 가운 입기 (Connection 획득)
  2. 비커 가져오기 (PreparedStatement)
  3. 시약 준비 (파라미터)
  4. 실험 (SQL 실행) ← 핵심 단 한 줄
  5. 결과 측정 (ResultSet)
  6. 비커 씻기 (ResultSet close)
  7. 시약 정리 (Statement close)
  8. 가운 벗기 (Connection close)
  
  → 6, 7, 8 도 실패 시 처리 (try/catch)

본 작업 < 준비/정리:
  - 실험은 30초
  - 준비+정리는 5분
  - 매번 똑같음

누수 위험:
  - 가운 안 벗고 가면? (close 누락)
  - 다음 사람 가운 없음
  - 실험실 마비

5주차 패턴:
  - 흐름 (입기/벗기) = 변하지 않음
  - 실험 (SQL) = 변함
  - → 분리하면? (다음 Unit JdbcTemplate)

자동화 가능:
  - 로봇 (템플릿) 이 입기/벗기 담당
  - 사람은 실험 (SQL) 만
  - 효율 + 안전

→ JDBC 반복 = 자원 관리 보일러플레이트, 본 로직 < 인프라 코드, 다음 — JdbcTemplate.


🧭 9개 섹션 로드맵

1. JDBC 코드의 반복
2. try/catch/finally 부담
3. 본 로직 < 자원 관리
4. close 누락 누수
5. 변하는 / 변하지 않는 부분
6. 5주차 패턴 적용
7. try-with-resources 의 한계
8. 예외 처리의 부담
9. 다음 — JdbcTemplate

1️⃣ JDBC 코드의 반복

1.1 전형적 JDBC 코드

// 전형적 JDBC 조회
public Shipment get(Long id) throws Exception {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        conn = dataSource.getConnection();
        stmt = conn.prepareStatement(
            "select * from shipments where id = ?");
        stmt.setLong(1, id);
        rs = stmt.executeQuery();
        if (rs.next()) {
            Shipment s = new Shipment();
            s.setId(rs.getLong("id"));
            s.setBlNo(rs.getString("bl_no"));
            return s;
        }
        return null;
    } catch (SQLException e) {
        throw e;
    } finally {
        if (rs != null) try { rs.close(); } catch (SQLException e) { }
        if (stmt != null) try { stmt.close(); } catch (SQLException e) { }
        if (conn != null) try { conn.close(); } catch (SQLException e) { }
    }
}

1.2 반복 부분

반복 부분:

  - Connection/Statement/ResultSet 변수 선언
  - try/catch/finally 구조
  - finally 의 자원 해제 (역순)
  - null 체크
  - 자원 해제 시 SQLException 처리 (중첩)

→ 모든 메서드에 같은 패턴

1.3 매 메서드마다

매 메서드마다:

  add() 메서드: 같은 패턴
  update() 메서드: 같은 패턴
  get() 메서드: 같은 패턴
  delete() 메서드: 같은 패턴
  findAll() 메서드: 같은 패턴
  ...

1.4 ILIC 의 맥락

// 반복 (ILIC, 102 테이블 × 여러 메서드)
public class ShipmentDao {
    private final DataSource dataSource;
    
    public Shipment get(Long id) throws Exception {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try { /* SQL: select */ } catch (SQLException e) { throw e; }
        finally { /* close 들 */ }
        return null;
    }
    
    public void add(Shipment s) throws Exception {
        Connection conn = null;
        PreparedStatement stmt = null;
        try { /* SQL: insert */ } catch (SQLException e) { throw e; }
        finally { /* close 들 */ }
    }
    
    public void update(Shipment s) throws Exception {
        // 또 같은 패턴
    }
    
    public void delete(Long id) throws Exception {
        // 또 같은 패턴
    }
    // 431 API 마다 이걸?
}
class Shipment {
    void setId(Long id) {}
    void setBlNo(String s) {}
}

1.5 자기 점검 답변

JDBC 코드의 반복은?

:
1. 반복:

  • try/catch/finally
  1. 부분:

    • 자원 선언/해제
  2. 매 메서드:

    • 같은 패턴
  3. 누적:

    • 부담 ↑

2️⃣ try/catch/finally 부담

2.1 구조

try/catch/finally 구조:

  try {
    // 본 로직
  } catch (SQLException e) {
    // 예외 처리
  } finally {
    // 자원 해제 (역순)
    if (rs != null) try { rs.close(); } catch (...) { }
    if (stmt != null) try { stmt.close(); } catch (...) { }
    if (conn != null) try { conn.close(); } catch (...) { }
  }

2.2 중첩 try/catch

중첩 try/catch:

  finally 안의 close():
    - SQLException 던질 수
    - 처리 필요 (또 try/catch)
    - 중첩

→ 가독성 ↓

2.3 자원 해제 순서

자원 해제 순서:

  생성 역순:
    1. ResultSet (마지막 생성, 먼저 close)
    2. Statement
    3. Connection (첫 생성, 마지막 close)

  → 역순 (LIFO)

2.4 null 체크

null 체크:

  자원이 만들어졌는지 확인:
    - 만들기 전 예외 시 null
    - close() 시 NullPointerException 방지

→ 추가 코드

2.5 ILIC 의 맥락

// try/catch/finally 부담 (ILIC)
public Shipment get(Long id) throws Exception {
    Connection conn = null;       // 선언
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        // 본 로직 (간단)
        conn = dataSource.getConnection();
        stmt = conn.prepareStatement("select * from shipments where id = ?");
        stmt.setLong(1, id);
        rs = stmt.executeQuery();
        if (rs.next()) {
            // 매핑
            return new Shipment();
        }
        return null;
    } catch (SQLException e) {
        // 예외 처리
        throw e;
    } finally {
        // 자원 해제 (역순, 중첩 try)
        if (rs != null) {
            try { rs.close(); } 
            catch (SQLException e) { /* 무시? 로깅? */ }
        }
        if (stmt != null) {
            try { stmt.close(); } 
            catch (SQLException e) { /* ... */ }
        }
        if (conn != null) {
            try { conn.close(); } 
            catch (SQLException e) { /* ... */ }
        }
    }
}
// 본 로직 5줄, 자원 관리 20줄
class Shipment {}
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }

2.6 자기 점검 답변

try/catch/finally 의 부담은?

:
1. 구조:

  • try/catch/finally
  1. 중첩:

    • finally 안 try/catch
  2. 순서:

    • 역순 (LIFO)
  3. null 체크:

    • 추가 코드

3️⃣ 본 로직 < 자원 관리

3.1 코드 비율

코드 비율:

  본 로직:
    - SQL 실행: 1 줄
    - 결과 처리: 2-3 줄
    - 매핑: 5-10 줄

  자원 관리:
    - 변수 선언: 3 줄
    - try/catch: 2 줄
    - finally: 9-15 줄 (자원 해제)
    - 중첩 try/catch

→ 자원 관리가 더 김

3.2 본질 vs 인프라

본질 vs 인프라:

  본질 (비즈니스):
    - SQL
    - 파라미터
    - 매핑

  인프라 (기술):
    - 자원 관리
    - 예외 처리
    - 트랜잭션

→ 인프라가 본질 가림

3.3 가독성

가독성:

  본 로직 묻힘:
    - 자원 관리 코드 사이
    - 핵심 파악 어려움
    - 유지보수 ↓

3.4 ILIC 의 맥락

// 본 로직 vs 자원 관리 비율 (ILIC)
public Shipment get(Long id) throws Exception {
    Connection conn = null;                                    // 인프라
    PreparedStatement stmt = null;                              // 인프라
    ResultSet rs = null;                                        // 인프라
    try {                                                        // 인프라
        conn = dataSource.getConnection();                       // 인프라
        stmt = conn.prepareStatement(                            // 본질 (SQL)
            "select * from shipments where id = ?");
        stmt.setLong(1, id);                                     // 본질 (파라미터)
        rs = stmt.executeQuery();                                // 본질 (실행)
        if (rs.next()) {                                         // 본질
            Shipment s = new Shipment();                         // 본질 (매핑)
            s.setId(rs.getLong("id"));                           // 본질
            s.setBlNo(rs.getString("bl_no"));                    // 본질
            return s;                                            // 본질
        }
        return null;                                             // 본질
    } catch (SQLException e) {                                   // 인프라
        throw e;                                                 // 인프라
    } finally {                                                  // 인프라
        if (rs != null) try { rs.close(); } catch (SQLException e) { } // 인프라
        if (stmt != null) try { stmt.close(); } catch (SQLException e) { } // 인프라
        if (conn != null) try { conn.close(); } catch (SQLException e) { } // 인프라
    }
}
// 본질: ~8 줄 / 인프라: ~12 줄
// → 인프라가 더 많음
class Shipment { void setId(Long id) {} void setBlNo(String s) {} }
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }

3.5 자기 점검 답변

본 로직 < 자원 관리 코드의 의미는?

:
1. 비율:

  • 자원 관리 ≥ 본 로직
  1. 본질 vs 인프라:

    • 본질 가림
  2. 가독성:

    • 핵심 묻힘
  3. 유지보수:


4️⃣ close 누락 누수

4.1 close 누락

close 누락:

  실수로 finally 누락:
    - try 블록만
    - 예외 시 자원 해제 X

  또는:
    - finally 있지만 null 체크 빠뜨려 NPE
    - close 가 예외 던져 다음 close X

4.2 Connection 누수

Connection 누수:

  close 안 됨:
    - 풀에 안 돌아옴
    - 사용 중 표시 유지
    - 새 요청에 못 줌

→ 풀 소진

4.3 누적 효과

누적 효과:

  요청 1000회 처리:
    - 매번 누수 1개
    - 풀 크기 10
    - 10번 만에 풀 소진

  → 11번째 요청 대기/실패
  → 서비스 멈춤

4.4 발견의 어려움

발견의 어려움:

  - 정상 처리는 보임
  - 누수는 누적되어야 보임
  - 시간 지나 서비스 다운
  - 추적 어려움

→ 사고 가능

4.5 ILIC 의 맥락

// 누수 위험 (ILIC)

// ❌ finally 빠뜨림
public Shipment getBad(Long id) throws Exception {
    Connection conn = dataSource.getConnection();
    PreparedStatement stmt = conn.prepareStatement("...");
    stmt.setLong(1, id);
    ResultSet rs = stmt.executeQuery();
    // ↑ 만약 위에서 예외 발생하면:
    // → conn.close() 호출 안 됨 → 누수
    
    if (rs.next()) return new Shipment();
    
    rs.close();    // 정상 시만 호출
    stmt.close();
    conn.close();
    return null;
}

// 누적:
// - 매 요청 누수 1
// - 풀 크기 10
// - 10번 만에 풀 소진
// - 새 요청 connection-timeout

// → 서비스 장애
class Shipment {}
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }

4.6 자기 점검 답변

close 누락 → Connection 누수 위험은?

:
1. 누락:

  • finally/null 체크
  1. 누수:

    • 풀로 안 돌아옴
  2. 누적:

    • 풀 소진
  3. 발견:

    • 어려움

5️⃣ 변하는 / 변하지 않는 부분

5.1 변하지 않는 부분

변하지 않는 부분 (모든 메서드 공통):

  - Connection 획득
  - Statement 생성
  - 자원 해제 (역순)
  - 예외 처리

→ 흐름이 동일

5.2 변하는 부분

변하는 부분 (메서드마다 다름):

  - SQL 문자열
  - 파라미터 바인딩
  - 결과 매핑 (ResultSet → 객체)

→ 비즈니스 다름

5.3 분리 가능

분리 가능:

  변하지 않는 = 공통 (재사용)
    - 한 곳에 두면
    - 모두가 같이 씀

  변하는 = 메서드별
    - 인자로 받으면
    - 다양화

→ 5주차 패턴!

5.4 패턴의 신호

패턴의 신호:

  "변하지 않는 + 변하는":
    - 템플릿 메소드 패턴
    - 또는 전략 패턴
    - 5주차 단골

→ 분리 + 추상화

5.5 ILIC 의 맥락

// 변하는 / 변하지 않는 (ILIC)

// 변하지 않는 (흐름)
// - Connection 획득
// - PreparedStatement 생성/실행
// - ResultSet 처리
// - 자원 해제

// 변하는 (메서드별)
public Shipment get(Long id) throws Exception {
    Connection conn = null; /* ... */ try { /* ... */
    
    // 변하는 SQL
    stmt = conn.prepareStatement("select * from shipments where id = ?");
    stmt.setLong(1, id);   // 변하는 파라미터
    rs = stmt.executeQuery();
    
    if (rs.next()) {
        // 변하는 매핑
        Shipment s = new Shipment();
        s.setId(rs.getLong("id"));
        return s;
    }
    /* ... */ } finally { /* ... */ }
    return null;
}

public Booking getBooking(Long id) throws Exception {
    Connection conn = null; /* ... */ try { /* ... */
    
    // 변하는 SQL (다름)
    stmt = conn.prepareStatement("select * from bookings where id = ?");
    stmt.setLong(1, id);
    rs = stmt.executeQuery();
    
    if (rs.next()) {
        // 변하는 매핑 (다름)
        Booking b = new Booking();
        b.setId(rs.getLong("id"));
        return b;
    }
    /* ... */ } finally { /* ... */ }
    return null;
}

// → 흐름 같음, SQL/매핑만 다름
// → 5주차 패턴으로 해결!
class Shipment { void setId(Long id) {} }
class Booking { void setId(Long id) {} }
PreparedStatement stmt;
ResultSet rs;
DataSource dataSource;
interface DataSource {}

5.6 자기 점검 답변

변하는 부분 vs 변하지 않는 부분은?

:
1. 변하지 않는:

  • Connection/자원 관리
  1. 변하는:

    • SQL/파라미터/매핑
  2. 분리 가능:

    • 5주차 패턴
  3. 신호:

    • 템플릿/전략

6️⃣ 5주차 패턴 적용

6.1 템플릿 메소드 패턴

템플릿 메소드 패턴:

  부모 클래스:
    - 변하지 않는 흐름 (템플릿)
    - 변하는 부분은 추상 메서드

  자식 클래스:
    - 변하는 부분 구현 (오버라이드)

→ 5주차에서 본 패턴

6.2 전략 패턴

전략 패턴:

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

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

→ 더 유연

6.3 적용

JDBC 에 적용:

  변하지 않는 흐름:
    - Connection 획득/해제
    - PreparedStatement 생성/해제
    - 예외 처리
    → 템플릿 안

  변하는 부분:
    - SQL (전략)
    - 파라미터 설정 (전략)
    - 결과 매핑 (전략, RowMapper)
    → 외부 주입

→ JdbcTemplate 이 그것

6.4 OCP

OCP 달성:

  새 쿼리 추가:
    - 새 전략 (Lambda/메서드)
    - 템플릿 그대로 (변경 X)

→ 확장 ✅ 변경 닫힘 ✅

6.5 ILIC 의 맥락

// 5주차 패턴 적용 (개념)

// 변하지 않는 흐름 (템플릿)
public abstract class JdbcOperationTemplate<T> {
    public T execute(String sql) throws Exception {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            conn = dataSource.getConnection();
            stmt = conn.prepareStatement(sql);
            // 변하는 부분 (자식이 구현)
            setParameters(stmt);
            rs = stmt.executeQuery();
            return mapResult(rs);   // 변하는 부분
        } finally {
            // 자원 해제 (한 곳)
            if (rs != null) try { rs.close(); } catch (SQLException e) { }
            if (stmt != null) try { stmt.close(); } catch (SQLException e) { }
            if (conn != null) try { conn.close(); } catch (SQLException e) { }
        }
    }
    
    protected abstract void setParameters(PreparedStatement stmt) throws SQLException;
    protected abstract T mapResult(ResultSet rs) throws SQLException;
    DataSource dataSource;
}

// 자식 (메서드별)
class ShipmentGetTemplate extends JdbcOperationTemplate<Shipment> {
    private Long id;
    public ShipmentGetTemplate(Long id) { this.id = id; }
    
    @Override
    protected void setParameters(PreparedStatement stmt) throws SQLException {
        stmt.setLong(1, id);
    }
    
    @Override
    protected Shipment mapResult(ResultSet rs) throws SQLException {
        if (rs.next()) {
            Shipment s = new Shipment();
            s.setId(rs.getLong("id"));
            return s;
        }
        return null;
    }
}
// → 5주차 템플릿 메소드 적용
// → Spring 의 JdbcTemplate 은 더 정교 (다음 Unit)
class Shipment { void setId(Long id) {} }
interface DataSource { Connection getConnection() throws SQLException; }

6.6 자기 점검 답변

5주차의 어떤 패턴 적용?

:
1. 템플릿 메소드:

  • 흐름 + 추상 메서드
  1. 전략 패턴:

    • 전략 주입
  2. 적용:

    • JdbcTemplate
  3. OCP:

    • 달성

7️⃣ try-with-resources 의 한계

7.1 try-with-resources

// try-with-resources (Java 7+)
try (Connection conn = dataSource.getConnection();
     PreparedStatement stmt = conn.prepareStatement("...");
     ResultSet rs = stmt.executeQuery()) {
    // 본 로직
    if (rs.next()) {
        return new Shipment();
    }
}   // 자동 close (역순)
class Shipment {}

7.2 개선 효과

개선 효과:

  - finally 불필요 (자동 close)
  - null 체크 X
  - 역순 자동
  - 가독성 ↑

→ Java 7+ 의 큰 개선

7.3 하지만 한계

한계 (try-with-resources):

  여전히 남는 부담:
    - 매 메서드마다 작성 (반복)
    - 예외 변환 X (SQLException 그대로)
    - 트랜잭션 통합 X
    - DataSource 통합 X
    - 풀 + 트랜잭션 + 매핑 등은 직접

→ 충분치 않음

7.4 비교

비교:

전통 JDBC:
  - try/catch/finally
  - 자원 해제 코드 많음
  - 누수 위험

try-with-resources:
  - 자원 자동 해제
  - 코드 짧음
  - 누수 X

JdbcTemplate:
  - 위 + 예외 변환
  - + 매핑 자동
  - + 트랜잭션 통합
  - + 더 짧음

→ try-with-resources < JdbcTemplate

7.5 ILIC 의 맥락

// try-with-resources (ILIC)
public Shipment getWithTry(Long id) throws SQLException {
    try (Connection conn = dataSource.getConnection();
         PreparedStatement stmt = conn.prepareStatement(
             "select * from shipments where id = ?")) {
        stmt.setLong(1, id);
        try (ResultSet rs = stmt.executeQuery()) {
            if (rs.next()) {
                Shipment s = new Shipment();
                s.setId(rs.getLong("id"));
                return s;
            }
            return null;
        }
    }
}
// 더 짧긴 하지만:
// - 매번 작성
// - SQLException 그대로 던짐 (Spring 은 DataAccessException 으로 변환)
// - 트랜잭션 (autoCommit 처리) 별도
// - 매핑 직접

// → JdbcTemplate 이 한 단계 더 (다음 Unit)
class Shipment { void setId(Long id) {} }
DataSource dataSource;
interface DataSource { Connection getConnection() throws SQLException; }

7.6 자기 점검 답변

try-with-resources 로 줄여도 한계는?

:
1. try-with-resources:

  • 자동 close
  1. 개선:

    • finally 불필요
  2. 한계:

    • 매번 작성
    • 예외 변환 X
  3. JdbcTemplate:

    • 더 진화

8️⃣ 예외 처리의 부담

8.1 SQLException

SQLException:

  Checked Exception:
    - 반드시 처리 (try/catch 또는 throws)
    - 매 메서드에 throws SQLException
    - 또는 catch

→ 메서드 시그니처 오염

8.2 의미 없는 catch

// 의미 없는 catch
try {
    // ...
} catch (SQLException e) {
    e.printStackTrace();   // 실용성 ↓
    throw new RuntimeException(e);
}
// 의미 있는 처리 어려움

8.3 DB 별 예외 다름

DB 별 예외 다름:

  같은 의미라도:
    - MySQL: SQLException (errorCode)
    - Oracle: SQLException (다른 errorCode)
    - PostgreSQL: PSQLException

  → 일관 처리 어려움

8.4 Spring 의 해결

Spring 의 해결 (JdbcTemplate):

  SQLException → DataAccessException:
    - Runtime Exception (Unchecked)
    - DB 무관 일관
    - 의미 있는 분류

  - EmptyResultDataAccessException
  - DuplicateKeyException
  - DataIntegrityViolationException

→ Phase 7 후속

8.5 ILIC 의 맥락

// 예외 처리 부담 (ILIC)

// JDBC 직접 (Checked, throws 폭증)
public class ShipmentDao {
    public Shipment get(Long id) throws SQLException {
        // throws 필요
    }
    public void add(Shipment s) throws SQLException {
        // throws
    }
}

// Service 도 영향
public class ShipmentService {
    public void process(Shipment s) throws SQLException {
        // throws 전파
    }
}

// Controller 도?
@RestController
public class ShipmentController {
    // throws SQLException 도 처리 (?)
    public Shipment get(Long id) throws SQLException {
        // 어색
    }
}

// Spring JdbcTemplate:
// - DataAccessException (RuntimeException)
// - throws 불필요
// - 전역 예외 핸들러로 처리
class Shipment {}

8.6 자기 점검 답변

예외 처리의 부담은?

:
1. SQLException:

  • Checked
  1. 부담:

    • throws 폭증
  2. DB 별:

    • 예외 다름
  3. Spring 해결:

    • DataAccessException

9️⃣ 다음 — JdbcTemplate

9.1 JdbcTemplate 예고

JdbcTemplate (Unit 7.2):

  Spring 의 해결:
    - 반복 코드 제거
    - 자동 자원 관리
    - 예외 변환
    - 람다와 잘 맞음

→ 5주차 패턴의 결정체

9.2 구조 미리보기

// JdbcTemplate (미리보기, 다음 Unit)
@Repository
public class ShipmentDao {
    private final JdbcTemplate jdbcTemplate;
    
    public ShipmentDao(JdbcTemplate jt) {
        this.jdbcTemplate = jt;
    }
    
    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
        );
    }
    // 본 로직만 (SQL/매핑/파라미터)
    // 자원 관리/예외 처리 X
    // 5줄!
}
class Shipment { void setId(Long id) {} }
class JdbcTemplate {
    <T> T queryForObject(String sql, RowMapper<T> m, Object... args) {
        return null;
    }
    interface RowMapper<T> { T mapRow(ResultSet rs, int n) throws SQLException; }
}

9.3 5주차 정신

5주차 정신:

  JdbcTemplate:
    - 템플릿 메소드 + 전략
    - DI (DataSource 받음)
    - OCP (새 쿼리 = 새 람다)
    - 관심사 분리

→ 5주차의 결정판

9.4 면접 단골 질문 매핑

Q핵심 답변
JDBC 반복?try/catch/finally
부담?자원 관리
본 로직 비율?적음
누수?close 누락
변하는/변하지 않는?SQL/흐름
5주차 패턴?템플릿/전략
try-with-resources?일부 해결
SQLException?Checked 부담
다음?JdbcTemplate
5주차 정신?관심사 분리

9.5 자기 점검 체크리스트

반복

  • try/catch/finally

부담

  • 자원 관리

본 로직 비율

  • 적음

누수

  • 위험

변하는/안 변하는

  • 구분

5주차 패턴

  • 템플릿/전략

try-with-resources

  • 한계

예외

  • Checked

9.6 추가 심화 질문

Q1: try-with-resources 의 close 순서?

답:

  • 선언 역순 (LIFO)
  • 마지막 선언 자원이 먼저 close
  • AutoCloseable 인터페이스
  • Java 7+

Q2: AutoCloseable?

답:

  • close() 메서드
  • try-with-resources 와 사용
  • Connection/Statement/ResultSet 구현
  • 자동 close

Q3: Connection 누수 감지?

답:

  • HikariCP leak-detection-threshold
  • 일정 시간 안 반환
  • 경고 로그
  • 누수 추적

Q4: SQLException 의 errorCode/sqlState?

답:

  • DB 별 errorCode
  • 표준 sqlState
  • 둘 다 활용
  • Spring 이 분류

Q5: 트랜잭션 매니저?

답:

  • Spring TransactionManager
  • @Transactional 처리
  • Connection 의 autoCommit 관리
  • JDBC 직접 시 부담

🎯 핵심 요약 — 3줄 정리

1. JDBC 의 반복

  • 매 메서드마다 try/catch/finally + 자원 해제 (Connection/Statement/ResultSet)
  • 본 로직보다 자원 관리 코드가 더 김, close 누락 시 Connection 누수 위험

2. 변하는 / 변하지 않는 부분

  • 변하지 않는: 흐름 (연결/해제/예외 처리)
  • 변하는: SQL / 파라미터 / 매핑
  • 5주차 템플릿 메소드 + 전략 패턴의 신호

3. try-with-resources 의 한계와 다음

  • try-with-resources 는 자원 해제 자동화 (일부 해결)
  • 하지만 예외 변환·트랜잭션·매핑 등은 여전히 직접
  • 다음 — JdbcTemplate (5주차 패턴의 결정체)

📚 다음으로...

Unit 7.2 — JdbcTemplate 의 등장 ★깊이

이번 Unit에서 JDBC 반복 문제를 봤다면, 다음은 JdbcTemplate (★ 깊이 파기).

  • 5주차 템플릿 메소드 + 전략 패턴
  • 자원 관리/예외 변환 자동
  • Connection/PreparedStatement/ResultSet 숨김
  • 람다 잘 맞음

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 구조적 의미

6주차 누적 진행

🧪 Part A (9 Unit) ✅
💾 Part B — DB 접근의 진화
  ✅ Phase 3 — JDBC (3)
  ✅ Phase 4 — Connection Pool (4)
  ✅ Phase 5 — DataSource (4)
  ✅ Phase 6 — ACID (6)
  🛠️ Phase 7 — JdbcTemplate (1/5 진행)

총: 27/28 Unit (96%!)

🛠️ Phase 7 시작 — JdbcTemplate

profile
Software Developer

0개의 댓글