본문 바로가기
spring

[Spring] Spring Boot를 통한 JDBC, JdbcTemplate, JPA의 이해와 비교

by goblin- 2024. 11. 22.

1. JDBC (Java Database Connectivity)

 

개념 및 동작 방식

 

JDBC는 자바 애플리케이션에서 관계형 데이터베이스에 연결하고 SQL 쿼리를 실행하며 결과를 처리하기 위한 표준 API입니다. JDBC를 사용하면 다양한 데이터베이스 시스템(MySQL, PostgreSQL, Oracle 등)에 독립적으로 접근할 수 있습니다.

 

DriverManager: JDBC 드라이버를 로드하고 데이터베이스 연결을 관리합니다.

Connection: 데이터베이스와의 연결을 나타내는 객체입니다.

StatementPreparedStatement: SQL 문을 실행하기 위한 객체입니다.

ResultSet: 쿼리 실행 결과를 담는 객체로, 결과 데이터를 처리할 때 사용합니다.

 

동작 방식:

 

1. 드라이버 로드: 데이터베이스에 맞는 JDBC 드라이버 클래스를 로드합니다.

2. 연결 생성: DriverManager.getConnection()을 사용하여 데이터베이스에 연결합니다.

3. SQL 실행: StatementPreparedStatement를 통해 SQL 문을 실행합니다.

4. 결과 처리: ResultSet을 사용하여 쿼리 결과를 처리합니다.

5. 자원 해제: close() 메서드를 사용하여 연결 및 자원을 해제합니다.

 

Spring Boot에서의 사용

 

Spring Boot에서 JDBC를 직접 사용할 수 있지만, 반복적인 코드와 예외 처리로 인해 복잡해질 수 있습니다. 아래 예시는 JDBC를 직접 사용하여 데이터베이스에 접근하는 DAO 클래스입니다.

 

예시 코드:

// DataSourceConfig.java
@Configuration
public class DataSourceConfig {
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driver;

    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(url);
        config.setUsername(username);
        config.setPassword(password);
        config.setDriverClassName(driver);
        HikariDataSource hikariDataSource = new HikariDataSource(config);
        return hikariDataSource;
    }
}

// UserJdbcApiDao.java
@Repository
@RequiredArgsConstructor
public class UserJdbcApiDao {
    private final DataSource dataSource;

    public User findById(int userId) throws SQLException {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = dataSource.getConnection();
            statement = connection.prepareStatement("SELECT * FROM \"user\" WHERE id = ?");
            statement.setInt(1, userId);
            resultSet = statement.executeQuery();
            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").toLocalDateTime()
                );
            }
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "유저 정보가 존재하지 않습니다 - id : " + userId);
        } catch (SQLException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "데이터베이스 오류", e);
        } finally {
            if (resultSet != null) resultSet.close();
            if (statement != null) statement.close();
            if (connection != null) connection.close();
        }
    }
    // 다른 메서드들도 동일한 방식으로 구현
}

 

설명:

 

DataSourceConfig 클래스에서 DataSource를 설정하여 데이터베이스 연결을 관리합니다.

UserJdbcApiDao 클래스에서 JDBC를 사용하여 데이터베이스에 접근하고 있습니다.

Connection, PreparedStatement, ResultSet 등을 수동으로 관리하며, 예외 처리와 자원 해제를 직접 처리해야 합니다.

 

장단점

 

장점:

 

데이터베이스와의 직접적인 연결로 인해 세밀한 제어가 가능합니다.

표준 JDBC API를 사용하므로 모든 자바 개발자가 이해할 수 있습니다.

 

단점:

 

반복적인 코드가 많아 가독성이 떨어집니다.

자원 관리와 예외 처리를 수동으로 해야 하므로 코드가 복잡해집니다.

생산성이 낮아지고 유지보수가 어렵습니다.

 

2. JdbcTemplate

 

개념 및 필요성

 

JdbcTemplate은 Spring Framework에서 제공하는 클래스로, JDBC의 반복적인 작업(연결, 예외 처리, 자원 해제 등)을 추상화하여 개발 생산성을 높여줍니다.

 

필요성:

 

JDBC를 직접 사용할 때 발생하는 반복적인 코드를 줄여줍니다.

예외 처리를 간소화하고 일관성 있는 예외 변환을 제공합니다.

자원 관리를 자동으로 처리하여 코드의 안정성을 높입니다.

 

주요 기능

 

SQL 실행 및 결과 매핑: SQL 쿼리를 실행하고 결과를 객체로 매핑합니다.

예외 변환: SQLException을 스프링의 DataAccessException으로 변환하여 일관된 예외 처리를 제공합니다.

자원 관리 자동화: Connection, Statement, ResultSet 등의 자원을 자동으로 관리합니다.

 

사용 예제

 

예시 코드:

// UserJdbcTemplateDao.java
@Repository
@RequiredArgsConstructor
public class UserJdbcTemplateDao {
    private final JdbcTemplate jdbcTemplate;

    public User findById(int userId) {
        String sql = "SELECT * FROM \"user\" WHERE id = ?";
        return this.jdbcTemplate.queryForObject(
            sql,
            (resultSet, rowNum) -> new User(
                resultSet.getInt("id"),
                resultSet.getString("name"),
                resultSet.getInt("age"),
                resultSet.getString("job"),
                resultSet.getString("specialty"),
                resultSet.getTimestamp("created_at").toLocalDateTime()
            ),
            userId
        );
    }
    // 다른 메서드들도 동일한 방식으로 구현
}

 

설명:

 

JdbcTemplate을 사용하여 SQL 쿼리를 실행하고 결과를 객체로 매핑합니다.

람다 표현식을 사용하여 RowMapper를 간결하게 작성합니다.

자원 관리와 예외 처리가 자동으로 이루어지므로 코드가 간결해집니다.

 

dbcTemplate은 따로 DataSource를 설정할 필요가 없음:

 

DataSource: 위의 데이터베이스 접속 정보를 기반으로 Spring Boot가 자동으로 생성합니다.

기본적으로 HikariCP를 사용하여 커넥션 풀링을 제공합니다.

JdbcTemplate: DataSource 빈이 존재하면 Spring Boot가 자동으로 생성합니다.

EntityManagerFactoryTransactionManager: spring-boot-starter-data-jpa 의존성이 있고, JPA 관련 설정이 존재하면 자동으로 생성됩니다.

 

장단점

 

장점:

 

코드의 간결성과 가독성이 향상됩니다.

자원 관리와 예외 처리가 자동으로 처리됩니다.

개발 생산성이 높아집니다.

 

단점:

 

여전히 SQL 문을 직접 작성해야 합니다.

객체와 데이터베이스 간의 매핑을 수동으로 처리해야 합니다.

 

3. JPA (Java Persistence API)

 

개념 및 동작 방식

 

JPA는 자바 객체와 관계형 데이터베이스의 테이블을 매핑하여 ORM(Object-Relational Mapping)을 구현하기 위한 표준 인터페이스입니다.

 

엔티티(Entity): 데이터베이스 테이블과 매핑되는 자바 클래스입니다.

엔티티 매니저(EntityManager): 엔티티의 생성, 수정, 삭제, 조회 등을 관리합니다.

JPQL(Java Persistence Query Language): 엔티티 객체를 대상으로 하는 쿼리 언어입니다.

 

동작 방식:

 

1. 엔티티 클래스 정의: 데이터베이스 테이블과 매핑될 클래스를 정의하고 어노테이션을 사용하여 매핑 정보를 설정합니다.

2. 엔티티 매니저를 통해 작업 수행: 엔티티 매니저를 사용하여 엔티티의 생명주기를 관리하고 데이터베이스 작업을 수행합니다.

3. 트랜잭션 관리: 데이터의 일관성을 유지하기 위해 트랜잭션을 관리합니다.

 

ORM과 Hibernate

 

ORM(Object-Relational Mapping): 객체 지향 프로그래밍의 객체와 관계형 데이터베이스의 테이블을 매핑하는 기술입니다.

Hibernate: JPA의 대표적인 구현체로, 다양한 추가 기능과 최적화를 제공합니다. Spring Boot에서 기본적으로 사용하는 JPA 구현체입니다.

 

Spring Boot에서의 사용

 

Spring Boot에서는 Spring Data JPA를 통해 JPA를 간편하게 사용할 수 있습니다.

 

예시 코드:

// User.java
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private Integer age;
    private String job;
    private String specialty;
    private LocalDateTime createdAt;
    // getters and setters
}

// UserRepository.java
public interface UserRepository extends JpaRepository<User, Integer> {
    // 기본적인 CRUD 메서드가 제공됩니다.
}

// UserService.java
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;

    public User findById(int userId) {
        return userRepository.findById(userId)
            .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "유저 정보를 찾을 수 없습니다 - id: " + userId));
    }
    // 다른 메서드들도 동일한 방식으로 구현
}

설명:

 

엔티티 클래스에서 @Entity 어노테이션을 사용하여 데이터베이스 테이블과 매핑합니다.

리포지토리 인터페이스에서 JpaRepository를 상속받아 기본적인 CRUD 메서드를 사용할 수 있습니다.

서비스 클래스에서 비즈니스 로직을 구현하며, UserRepository를 주입받아 사용합니다.

SQL을 직접 작성하지 않아도 되며, 메서드 이름으로 쿼리를 생성하거나 @Query 어노테이션을 사용하여 JPQL 또는 네이티브 쿼리를 작성할 수 있습니다.

 

장단점

 

장점:

 

객체 지향적인 코드 작성이 가능하여 비즈니스 로직에 집중할 수 있습니다.

생산성이 높아지고 유지보수가 용이합니다.

데이터베이스 벤더에 종속적이지 않은 코드를 작성할 수 있습니다.

 

단점:

 

학습 곡선이 있으며, ORM의 동작 방식을 이해해야 합니다.

복잡한 쿼리나 성능 최적화가 필요한 경우 직접 튜닝이 필요합니다.

추상화로 인해 발생하는 오버헤드를 고려해야 합니다.

 

4. 세 기술의 비교

 

기능 및 사용성 비교

특징 JDBC JdbcTemplate JPA
SQL 직접 작성 필요  필요 보통 필요 없음
코드의 간결성 낮음 중간 높음
자원 관리 수동 처리 자동 처리 자동 처리
예외 처리 수동 처리 자동 처리 자동 처리
학습 난이도 낮음  낮음 높음
생산성 낮음 중간 높음
객체-관계 매핑 수동 매핑 수동 매핑 자동 매핑

 

 

언제 어떤 것을 선택해야 하는지

 

JDBC:

데이터베이스에 대한 세밀한 제어가 필요한 경우

직접 SQL을 작성하고 최적화해야 하는 경우

작은 규모의 애플리케이션이나 간단한 작업에 적합

JdbcTemplate:

JDBC의 반복적인 코드를 줄이고 싶을 때

SQL은 직접 작성하되, 자원 관리와 예외 처리를 자동화하고 싶은 경우

중간 규모의 애플리케이션에 적합

JPA:

객체 지향적인 방식으로 데이터베이스를 다루고 싶은 경우

생산성과 유지보수성을 높이고 싶은 경우

대규모 애플리케이션이나 복잡한 비즈니스 로직이 필요한 경우

 

5. 결론

 

Spring Boot를 통해 데이터베이스와 연결할 때 JDBC, JdbcTemplate, JPA 세 가지 방법을 사용할 수 있습니다. 각 기술은 고유한 장단점이 있으며, 프로젝트의 요구사항과 규모, 팀의 역량에 따라 적절한 기술을 선택해야 합니다.

 

JDBC는 데이터베이스에 대한 세밀한 제어가 가능하지만, 반복적인 코드와 자원 관리로 인해 생산성이 낮습니다.

JdbcTemplate은 JDBC의 단점을 보완하여 코드의 간결성과 생산성을 높여주지만, 여전히 SQL을 직접 작성해야 합니다.

JPA는 객체 지향적인 방식으로 데이터베이스를 다룰 수 있어 생산성과 유지보수성이 높지만, 학습 곡선이 있고 성능 최적화에 신경 써야 합니다.

 

제공해주신 예시 코드를 통해 JDBC와 JdbcTemplate의 사용 방법을 살펴보았습니다. JPA를 사용하면 더 간결하고 유지보수하기 쉬운 코드를 작성할 수 있으므로, 프로젝트의 특성에 맞게 기술을 선택하시기 바랍니다.

 

추가로 궁금하신 사항이나 도움이 필요한 부분이 있으시면 언제든지 말씀해주세요!