본문 바로가기
(개발) 프로젝트/Java로 직접 만드는 WAS

[스프링 서버 구현하기] 외부 데이터베이스 도입과 JDBC

by yuiee 2024. 3. 27.

스프링 서버를 직접 구현하며, 데이터를 저장하는 방식을 메모리에 저장하는 방식에서 JDBC API를 이용해서 외부 데이터베이스에 저장하는 방식으로 바꿨습니다. 그 이유는 메모리에 저장하게 되면, 애플리케이션이 종료될 경우 모든 데이터가 사라지게 되게 되기 때문입니다. 따라서 데이터를 영구적으로 저장할 수 있게 데이터베이스를 사용하기로 결정했고, 그 과정에서 학습한 내용을 기록했습니다.

 

 

 

JDBC란?


https://jenkov.com/tutorials/jdbc/index.html

JDBC는 Java Database Connectivity의 약자로 Java에서 데이터베이스에 접속할 수 있도록 해주는 Java API입니다.

 

 

 

JDBC를 통해 데이터베이스에 접근하는 과정


JDBC를 통해 데이터베이스에 접근하는 과정은 다음과 같습니다.

  1. 드라이버 로드
  2. DataSource를 통해 데이터베이스 연결 풀(pool)에서 연결 요청.
  3. PreparedStatement를 통해 쿼리 수행
  4. 쿼리 실행에 대한 결과물이 있다면 ResultSet 객체를 통해 반환
  5. 관련 자원을 모두 해제

 

 

이 과정에서 데이터베이스 연결 풀에서 연결을 관리하는 역할은 프로젝트 내에서 DriverManager로 클래스를 만들고 DataSource를 사용해서 관리했습니다. 그 이유는 javax.sql.DriverManger는 매 연결 요청마다 새로운 연결을 생성하기 때문입니다. 반면 DataSource는 연결 풀(Connection Pool)과 함께 사용되어 연결을 재사용할 수 있기 때문입니다.

 

@Component
public class DriverManager {
    private final DataSource dataSource;

    public DriverManager(DataSourceConfig dataSourceConfig) {
        this.dataSource = createDataSource(dataSourceConfig);
    }

    private DataSource createDataSource(DataSourceConfig dataSourceConfig) {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName(dataSourceConfig.getDriverClassName());
        basicDataSource.setUrl(
            dataSourceConfig.getUrl());
        basicDataSource.setUsername(dataSourceConfig.getUsername());
        basicDataSource.setPassword(dataSourceConfig.getPassword());
        return basicDataSource;
    }

    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

 

 

데이터베이스에 대한 설정 정보는 설정 정보가 담겨 있는 파일을 읽어와서 DataSourceConfig 객체에 매핑해서 관리했습니다.

 

 

Jdbc Template을 사용하는 이유


JDBC를 사용하게 되면 기본적으로 구현해야 하는 기능과 반복적으로 사용하는 기능이 존재합니다. JdbcTemplate을 직접 구현해서 JDBC를 직접 사용할 때 발생하는 반복작업을 줄이고 코드를 재사용하고자 했습니다.

 

처음에 작성한 코드는 아래와 같습니다. DriverManger 객체에서 커넥션을 얻어오는 방식으로 데이터베이스 연결이 되면 SQL을 작성해서 데이터베이스에 연결을 하고, 데이터베이스로부터 user에 대한 정보를 얻어온 후 자원 해제를 하는 과정입니다.

 

이때, Connection과 PreparedStatement는 autocloseable 구현체입니다. 따라서 Java 7부터는 try-with-resources 가 적용되어 try 선언 시 자동적으로 자원이 해제되게 됩니다.

 

    @Override
    public Optional<User> findById(Long id) {
        User user = null;
        try {
            Connection connection = driverManager.getConnection();
            String sql = "SELECT * FROM user WHERE id = ?";
            PreparedStatement statement = connection.prepareStatement(sql);
            statement.setLong(1, id);
            ResultSet resultSet = statement.executeQuery();
            while (resultSet.next()) {
                Long userId = resultSet.getLong("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                String password = resultSet.getString("password");
                String phoneNumber = resultSet.getString("phoneNumber");
                user = new User(userId, name, password, email, phoneNumber);
            }
            return Optional.ofNullable(user);
        } catch (SQLException e) {
            throw new IllegalArgumentException("SQL exception occurs", e);
        }
    }

 

위의 코드는 기능상으로는 문제가 없습니다. 하지만 데 매번 데이터베이스 Connection을 얻어오는 부분과 같이 일부 과정이 중복되게 되고 중복되는 과정을 수정할 때, 일부 메소드만 수정하게 된다면 예상치 못한 버그가 발생할 수 있습니다.

 

따라서 변하지 않는 부분과 변하는 부분을 분리하고 변하지 않는 부분을 재사용하기 위해 디자인 패턴을 적용할 필요가 있습니다. 이러한 디자인 패턴으로는 템플릿 메소드 패턴과 템플릿 콜백 메소드 패턴이 있습니다. 이와 관련된 자세한 내용은 다음 글에서 알아보고자 합니다.

댓글