[Spring DB] JDBC 이해

2025. 5. 5. 06:21코딩 도구/백엔드 개발 (Backend Development)

반응형

[Spring] DB JDBC 이해 정리

이 글은 인프런 김영한님의 "스프링 DB 1편 - 데이터 접근 핵심 원리" 강의를 수강하고 정리한 내용이다.

 

이번 정리에서는 JDBC의 등장 배경부터 실제 데이터베이스 연결과 CRUD 구현까지, JDBC를 활용한 데이터 접근의 전반적인 흐름을 다룬다. 또한 JDBC의 한계와 이를 보완하는 최신 기술까지 정리한다.


1. JDBC 등장 배경과 개요

데이터베이스 연결 문제

애플리케이션이 데이터베이스와 통신하려면 다음과 같은 과정을 거쳐야 한다:

  1. 커넥션 연결 (주로 TCP/IP)
  2. SQL 전달
  3. 결과 응답 수신

하지만 데이터베이스 종류마다 위 과정이 다르게 구현되어 있어 다음과 같은 문제가 발생한다:

  • DB 변경 시 코드 수정 필요
  • 개발자가 DB마다 다른 API와 연결 방법을 학습해야 함

이 문제를 해결하기 위해 JDBC(Java Database Connectivity)라는 자바 표준 API가 등장했다.

JDBC란?

  • 자바에서 다양한 관계형 데이터베이스에 접근할 수 있도록 만든 표준 인터페이스이다.
  • 핵심 인터페이스: Connection, Statement, ResultSet

각 DB 벤더는 이 인터페이스를 구현한 JDBC 드라이버를 제공한다. 예를 들어 MySQL은 MySQL 드라이버를, Oracle은 Oracle 드라이버를 제공한다.

JDBC의 효과

  • DB 변경에 유연하다: DB만 교체하면 코드 대부분 변경 없이 사용 가능하다.
  • 학습 비용이 낮다: JDBC만 배우면 모든 DB에 동일하게 적용할 수 있다.

하지만 SQL 문법이나 데이터 타입 등의 차이는 여전히 존재하므로 DB 교체 시 SQL 수준의 수정은 불가피하다.


2. JDBC와 최신 데이터 접근 기술

JDBC는 강력하지만 불편한 점도 많다. 이를 보완하기 위해 등장한 기술이 다음과 같다:

SQL Mapper

  • JDBC를 편리하게 사용할 수 있도록 도와주는 기술이다.
  • 개발자가 SQL은 직접 작성하지만 나머지 처리를 자동화해준다.
  • 대표 기술: Spring JdbcTemplate, MyBatis

ORM (Object-Relational Mapping)

  • 객체와 데이터베이스를 자동으로 매핑해준다.
  • SQL을 직접 작성하지 않아도 되므로 생산성이 높다.
  • 대표 기술: JPA (Java Persistence API), 하이버네이트 등

정리

  • SQL Mapper는 SQL은 직접 작성하지만 반복 작업을 줄여준다.
  • ORM은 SQL도 자동으로 생성해주지만 학습 난이도는 더 높다.
  • 모두 내부적으로는 JDBC를 사용하므로, JDBC의 원리를 이해하는 것이 매우 중요하다.

3. 데이터베이스 연결

DB 접속 정보 관리

public abstract class ConnectionConst {
    public static final String URL = "jdbc:h2:tcp://localhost/~/test";
    public static final String USERNAME = "sa";
    public static final String PASSWORD = "";
}

DB 접속 정보를 상수로 관리해 여러 클래스에서 쉽게 사용할 수 있도록 한다.

DB 커넥션 유틸 클래스

public class DBConnectionUtil {
    public static Connection getConnection() {
        try {
            Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            log.info("get connection={}, class={}", connection, connection.getClass());
            return connection;
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        }
    }
}

DriverManager.getConnection()을 사용해 DB 커넥션을 얻는다. 이때 등록된 드라이버 중 URL을 인식 가능한 드라이버가 연결을 담당한다.


4. JDBC 개발 - 등록, 조회, 수정, 삭제

회원 도메인 클래스

@Data
public class Member {
    private String memberId;
    private int money;

    public Member(String memberId, int money) {
        this.memberId = memberId;
        this.money = money;
    }
    public Member() {}
}

1) 회원 등록

public Member save(Member member) throws SQLException {
    String sql = "insert into member(member_id, money) values(?, ?)";
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setString(1, member.getMemberId());
        pstmt.setInt(2, member.getMoney());
        pstmt.executeUpdate();
        return member;
    } finally {
        close(con, pstmt, null);
    }
}
  • ?는 SQL Injection 방지를 위한 바인딩 처리이다.
  • PreparedStatement 사용을 권장한다.
  • 주의: 리소스 정리는 반드시 finally 구문에 작성해야 한다. 이 구문을 놓치면 커넥션이 닫히지 않아 계속 유지되는 문제가 발생하며, 이를 리소스 누수라고 한다. 결과적으로 커넥션 풀이 고갈되어 장애가 발생할 수 있다.

2) 회원 조회

public Member findById(String memberId) throws SQLException {
    String sql = "select * from member where member_id = ?";
    Connection con = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setString(1, memberId);
        rs = pstmt.executeQuery();

        if (rs.next()) {
            Member member = new Member();
            member.setMemberId(rs.getString("member_id"));
            member.setMoney(rs.getInt("money"));
            return member;
        } else {
            throw new NoSuchElementException("member not found: " + memberId);
        }
    } finally {
        close(con, pstmt, rs);
    }
}
  • ResultSet은 조회된 데이터 테이블을 순회하는 구조이다.
  • 커서는 .next()로 이동하면서 데이터를 읽을 수 있다.

3) 회원 수정

public void update(String memberId, int money) throws SQLException {
    String sql = "update member set money=? where member_id=?";
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setInt(1, money);
        pstmt.setString(2, memberId);
        pstmt.executeUpdate();
    } finally {
        close(con, pstmt, null);
    }
}

4) 회원 삭제

public void delete(String memberId) throws SQLException {
    String sql = "delete from member where member_id=?";
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        con = getConnection();
        pstmt = con.prepareStatement(sql);
        pstmt.setString(1, memberId);
        pstmt.executeUpdate();
    } finally {
        close(con, pstmt, null);
    }
}

리소스 정리 메서드 예시

private void close(Connection con, Statement stmt, ResultSet rs) {
    JdbcUtils.closeResultSet(rs);
    JdbcUtils.closeStatement(stmt);
    JdbcUtils.closeConnection(con);
}
  • 스프링에서 제공하는 JdbcUtils를 사용하면 예외를 내부에서 처리해주므로 안전하게 리소스를 닫을 수 있다.

5. 마무리 정리

  • JDBC는 자바에서 데이터베이스를 다루기 위한 표준 기술로, 다양한 DB에 유연하게 대응할 수 있도록 해준다.
  • 반복 코드와 복잡한 사용 방식 때문에 SQL Mapper, ORM 기술이 등장했지만, 이들 또한 내부에서는 JDBC를 사용하고 있다.
  • 따라서 JDBC의 구조와 흐름을 정확히 이해하는 것이 고급 데이터 접근 기술의 기초가 된다.
  • 실무에서도 성능 분석, 문제 해결, 최적화 등을 위해 JDBC 레벨의 이해가 필수이다.
  • 특히 리소스 정리를 철저히 하지 않으면 커넥션 누수로 인한 장애가 발생할 수 있으므로, 항상 finally 구문을 통해 커넥션을 닫는 습관을 가져야 한다.
반응형