코드잇 스프린트/TIL

[TIL] 반복되는 JDBC, JdbcTemplate으로 줄이기

solytory 2026. 9. 3. 22:12

스프링은 JDBC를 어떻게 추상화했는가. JdbcTemplate 정의

  • JDBC API 사용 시 ① 접속 → ② 쿼리 → ③ 결과 처리라는 반복되는 패턴이 존재한다. 스프링은 이 반복 작업(커넥션 획득/해제, 예외 처리 등)을 대신 처리해 주는 JdbcTemplate 클래스를 제공한다.
  • 왜 스프링에서 이런 기능을 제공할까? 위에서 서술한 반복 패턴은 애플리케이션이 DB와 소통할 때마다 매번 발생하는 보일러플레이트다. 스프링은 개발자가 이 반복 작업에서 벗어나 SQL과 비즈니스 로직 작성에만 집중할 수 있도록 JdbcTemplate을 제공한다.

 

아래와 같이  전과 후 코드를 비교해 보자

// Before

    public User findById(int userId) throws SQLException {
        Connection connection = null; // 1.
        PreparedStatement statement = null;   // 2.
        ResultSet resultSet = null;   // 3.

        try {
            connection = dataSource.getConnection(); // 1. Hikari 커넥션 풀에서 Connection 객체 하나를 꺼내옴
            statement = connection.prepareStatement("SELECT * FROM \"user\" WHERE id = ?"); // 2. SQL을 실행할 Statement 객체 생성
            statement.setInt(1, userId); // 3. SQL 실행
            resultSet = statement.executeQuery();// 3. 결과를 ResultSet에 저장


            if (resultSet.next()) {
                return new User(
                        resultSet.getInt("id"),
                        resultSet.getString("name"),
                        resultSet.getInt("age"),
                        resultSet.getString("job"),
                        resultSet.getString("specialty"),
                        resultSet.getTimestamp("created_at")
                                .toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDateTime()
                );
            }

            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "유저 정보가 존재하지 않습니다 - id : " + userId);

        } catch (SQLException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "자원에 대한 접근에 문제가 있습니다.");
        } finally {
            // 자원반납
            if (resultSet != null) resultSet.close();   // 1.
            if (statement != null) statement.close();   // 2.
            if (connection != null) connection.close(); // 3.
        }
    }
    
    
   
// After
    private final JdbcTemplate jdbcTemplate;
    
    public User findById(int userId) {
        String getUserQuery = "SELECT * FROM \"user\" WHERE id = ?";
        int getUserParams = userId;

        return this.jdbcTemplate.queryForObject(
                getUserQuery,
                (resultSet, rowNum) -> new User(
                        resultSet.getInt("id"),
                        resultSet.getString("name"),
                        resultSet.getInt("age"),
                        resultSet.getString("job"),
                        resultSet.getString("specialty"),
                        resultSet.getTimestamp("created_at")
                                .toInstant()
                                .atZone(ZoneId.systemDefault())
                                .toLocalDateTime()
                ),
                getUserParams
        );
    }

 

  • Before/After 이점 정리
    • try-catch-finally 제거 → Connection/Statement/ResultSet 자원 반납 코드가 사라짐
    • SQLException을 스프링의 DataAccessException 계열(런타임 예외)로 자동 변환 → checked exception 처리 부담 감소
    • SQL과 RowMapper(결과→엔티티 변환 로직)만 작성하면 됨 → 개발자는 "무엇을 조회할지"에만 집중
  • DataSource 주입 방식
    • @Configuration 클래스에서 @Bean으로 DataSource(HikariCP) 등록
    • Spring Boot의 auto-configuration이 이 DataSource를 기반으로 JdbcTemplate 빈을 자동 생성
    • Repository에서는 @RequiredArgsConstructor 생성자 주입으로 JdbcTemplate을 그대로 받아서 사용 (직접 new JdbcTemplate(dataSource) 할 필요 없음) 

 

JdbcTemplate 주요 메서드

  • queryForObject : 결과가 정확히 1건 일 때 (0건 / 2건 이상이면 예외)
  • query : 결과가 여러 건일 때 (List<T> 반환, 없으면 빈 리스트)
  • queryForList : 엔티티 매핑 없이 단순 값 리스트 조회 (List <String>)
  • queryForStream : query와 비슷하지만 Stream<T>로 반환, 대용량 데이터 처리에 적합
  • update : INSERT/UPDATE/DELETE, 영향받은 row 수(int) 반환한다.
💡 checked exception 처리 부담 감소가 무엇일까?

Java의 예외는 크게 두 가지로 나뉜다.

하나는 checked exception인데, 컴파일러가 "이거 처리 안 하면 컴파일 안 해줌"이라고 강제하는 예외다. SQLException이 대표적인 예. 그래서 JDBC 코드에서 SQL 관련 메서드를 호출하면, try-catch로 잡아주거나 메서드 시그니처에 throws SQLException을 붙여줘야만 컴파일이 된다. 즉 컴파일러 차원에서 "예외 상황 대비했어?"를 검사당하는 셈이다.

반대로 unchecked exception(RuntimeException 계열)은 처리를 안 해도 컴파일이 그냥 통과된다. catch를 하든 안 하든 자유고, 강제되지 않는다.

JdbcTemplate은 SQLException을 잡아서 RuntimeException 계열인 DataAccessException으로 바꿔서 던져주기 때문에, 매번 SQL 호출할 때마다 강제로 try-catch를 쓸 필요가 없어진다. 이게 "checked exception 처리 부담이 줄어든다"는 말의 의미다.
반응형