본문 바로가기
Lecture/토비의 스프링 부트 - 이해와 원리

토비의 스프링 부트 - Spring JDBC 자동 구성 개발

by Soono991 2023. 2. 22.
💡이 포스팅은 토비님의 인프런 강의인 토비의 스프링 부트 - 이해와 원리를 수강하고 학습한 내용을 정리한 포스팅입니다.

 

토비님의 강의를 수강하며 정리한 GitHub Repository입니다.

 

GitHub - kiekk/inflearn-toby-spring-boot

Contribute to kiekk/inflearn-toby-spring-boot development by creating an account on GitHub.

github.com

 

이번 챕터에 대해 정리할 내용은 다음과 같습니다.

  • JdbcTemplate vs Connection
  • HikariCP

JdbcTemplate vs Connection

Spring을 사용하기 전에는 DB에 SQL문을 실행시키기 위해서는 Connection을 직접 생성하여 실행할 SQL문을 전달해야 했습니다.

(Connection, Statement, PreparedStatement...)

 

하지만 Spring에서는 이 기능을 추상화한 JdbcTemplate 객체를 제공합니다.

JdbcTemplateJdbcOperations interface를 구현하고 있는데, JdbcOperations에는 기본 JDBC 작업들의 집합이 정의되어 있습니다.

Connection 객체를 사용할 때의 단점은 개발자가 직접 Connection을 맺고 끊는 로직을 SQL문을 실행할 때마다 작성해야 한다는 점입니다.

 

이렇게 되면 매번 Connection open, close 로직이 매번 반복되어 불필요한 코드의 중복이 발생하며, Connection 사용 후 개발자가 close를 누락할 위험도 있습니다.

 

또한 Connection, Statement, ResultSet등의 객체를 사용하고 close할 때는 close 순서도 매우 중요하기 때문에 이러한 부분들을 개발자가 일일이 신경써야 한다는 점은 매우 큰 단점이며 실수로 누락할 시 애플리케이션의 성능이 크게 저하될 수 있습니다

public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        User user = null;
        try {
            c = connectionMaker.makeConnection();
            ps = c.prepareStatement(
                    "select * from users where id = ?"
            );

            ps.setString(1, id);

            rs = ps.executeQuery();


            if (rs.next()) {
                user = new User();
                user.setId(rs.getString("id"));
                user.setName(rs.getString("name"));
                user.setPassword(rs.getString("password"));
            }
        } catch (Exception e) {
            throw e;
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                }
            }
            if (c != null) {
                try {
                    c.close();
                } catch (SQLException e) {
                }
            }
        }

        if (user == null) {
            throw new EmptyResultDataAccessException(1);
        }

        return user;
    }

이 코드는 토비의 스프링 3.1 책에 나온 예제입니다.

 

코드를 보면 try-catch 내부에 있는 부분만이 실제 비지니스 로직이며, 그 외에는 전부 Connection open, close를 위한 로직입니다.

finally문에서 생성한 Connection, PreparedStatement, ResultSet 객체들을 close 해줘야 하는데, 이 때 아래와 같은 순서대로 close 해야 합니다.

ResultSet → PreparedStatement → Connection

또 하나의 단점은 위 코드에서도 볼 수 있듯이 예외가 추상화되어 있지 않다는 점입니다.

 

Connection open 시 발생할 수 있는 예외인 ClassNotFoundException, 또 SQL문을 실행하다 발생할 수 있는 SQLException으로 개발자가 일일이 예외 처리를 하기에 까다롭습니다.

 

반대로 Spring은 JDBC 관련 예외들을 전부 DataAccessException으로 추상화를 해두었기 때문에 개발자는 일일이 예외를 신경쓰지 않고 DataAccessException 하나로 전부 예외를 받아 처리할 수 있게 되는 장점이 있습니다.

 

HikariCP

HikariCP는 JDBC 커넥션 풀 프레임워크로 Spring Boot에서는 커넥션 풀 관리를 위해 HikariCP를 사용하고 있습니다.

// Spring
implementation 'org.springframework:spring-jdbc'
runtimeOnly 'hikari-cp:hikari-cp:3.0.1'

// Spring Boot
implementation 'org.springframework.boot:spring-boot-starter-jdbc'

Spring에서는 jdbc, hikariCP를 직접 추가해야 했지만, Spring Boot에서는 starter-jdbc를 추가하는 것으로 hikariCP까지도 함께 추가할 수 있습니다.

starter-jdbc 내부를 살펴보면 spring-jdbc와 HikariCP를 추가하도록 작성되어 있습니다.

HikariCP를 추가한 후 다시 프로젝트를 실행하면 위와 같이 HIkariCP가 사용된 것을 확인할 수 있습니다.

댓글