[JDBC] 기본 구성 요소 및 문법

DU·2025년 6월 6일
0

JDBC (Java Database Connectivity) API를 사용하는 방법이 궁금하여 정리해보고자 합니다. JDBC는 Java 애플리케이션이 다양한 관계형 데이터베이스와 상호작용할 수 있도록 표준화된 방법을 제공합니다.


JDBC 기본 구성 요소 및 문법

JDBC를 사용하여 SQL 쿼리를 실행하려면 다음과 같은 단계를 거칩니다.

  1. JDBC 드라이버 로드
  2. 데이터베이스 연결 설정
  3. Statement 또는 PreparedStatement 객체 생성
  4. SQL 쿼리 실행
  5. 결과 처리 (SELECT 문인 경우)
  6. 자원 해제 (Connection, Statement, ResultSet 닫기)

1. JDBC 드라이버 로드 (Class.forName())

데이터베이스와 통신하려면 해당 데이터베이스 벤더가 제공하는 JDBC 드라이버를 JVM에 로드해야 합니다.

try {
    Class.forName("com.mysql.cj.jdbc.Driver"); // MySQL 드라이버
    // Class.forName("oracle.jdbc.driver.OracleDriver"); // Oracle 드라이버
    // Class.forName("org.postgresql.Driver"); // PostgreSQL 드라이버
    System.out.println("JDBC 드라이버 로드 성공!");
} catch (ClassNotFoundException e) {
    System.err.println("JDBC 드라이버를 찾을 수 없습니다: " + e.getMessage());
    e.printStackTrace();
}
  • 설명: Class.forName() 메소드를 사용하여 드라이버 클래스를 메모리에 로드합니다. 이렇게 하면 드라이버가 스스로 등록됩니다. (JDBC 4.0 이상에서는 이 단계가 필수는 아니지만, 명시적으로 로드하는 것이 명확합니다.)

2. 데이터베이스 연결 설정 (DriverManager.getConnection())

로드된 드라이버를 사용하여 특정 데이터베이스에 연결합니다.

String url = "jdbc:mysql://localhost:3306/userdb?useSSL=false&serverTimezone=UTC"; // JDBC URL
String user = "root"; // 데이터베이스 사용자 이름
String password = "1234"; // 데이터베이스 비밀번호

Connection conn = null; // Connection 객체 선언

try {
    conn = DriverManager.getConnection(url, user, password);
    System.out.println("데이터베이스 연결 성공!");
} catch (SQLException e) {
    System.err.println("데이터베이스 연결 실패: " + e.getMessage());
    e.printStackTrace();
}
  • url (JDBC URL): 연결할 데이터베이스의 위치와 속성을 지정합니다. jdbc:mysql:// 부분은 MySQL 드라이버를 사용한다는 의미이고, localhost:3306은 데이터베이스 서버의 주소와 포트, /userdb는 데이터베이스 이름입니다. ? 이후는 추가 옵션입니다.
  • user, password: 데이터베이스 접속에 필요한 사용자 이름과 비밀번호입니다.
  • Connection 객체: 데이터베이스 세션을 나타냅니다. 모든 SQL 작업은 이 연결을 통해 이루어집니다.

3. Statement 또는 PreparedStatement 객체 생성

SQL 쿼리를 실행하기 위한 객체를 생성합니다.

  • Statement (정적 SQL 쿼리용):

    Statement stmt = null;
    try {
        stmt = conn.createStatement();
        System.out.println("Statement 객체 생성 성공!");
    } catch (SQLException e) {
        e.printStackTrace();
    }
    • 용도: SQL 쿼리 문자열이 실행 시점에 변하지 않는 (매개변수가 없는) 간단한 쿼리에 사용됩니다.
    • 단점: SQL 인젝션 공격에 취약할 수 있으며, 반복 실행 시 성능 저하가 발생할 수 있습니다.
  • PreparedStatement (매개변수 있는 동적 SQL 쿼리용 - 권장):

    String sqlInsert = "INSERT INTO users (name, email) VALUES (?, ?)"; // '?' 자리표시자
    PreparedStatement pstmt = null;
    try {
        pstmt = conn.prepareStatement(sqlInsert);
        System.out.println("PreparedStatement 객체 생성 성공!");
    } catch (SQLException e) {
        e.printStackTrace();
    }
    • 용도: SQL 쿼리 문자열에 물음표(?)와 같은 자리표시자가 포함되어 실행 시점에 값을 바인딩해야 하는 쿼리에 사용됩니다. 보안(SQL 인젝션 방지) 및 성능 향상 측면에서 Statement보다 훨씬 권장됩니다.

4. SQL 쿼리 실행

생성된 Statement 또는 PreparedStatement 객체를 사용하여 SQL 쿼리를 실행합니다.

  • INSERT, UPDATE, DELETE (데이터 변경):
    Statement 사용 시:

    String sqlUpdate = "UPDATE users SET email = 'new_email@example.com' WHERE name = 'Alice'";
    try {
        int rowsAffected = stmt.executeUpdate(sqlUpdate);
        System.out.println(rowsAffected + " 개의 행이 업데이트되었습니다.");
    } catch (SQLException e) {
        e.printStackTrace();
    }

    PreparedStatement 사용 시:

    String sqlUpdate = "UPDATE users SET email = ? WHERE name = ?";
    try {
        pstmt = conn.prepareStatement(sqlUpdate);
        pstmt.setString(1, "new_email@example.com"); // 첫 번째 '?'
        pstmt.setString(2, "Alice"); // 두 번째 '?'
        int rowsAffected = pstmt.executeUpdate();
        System.out.println(rowsAffected + " 개의 행이 업데이트되었습니다.");
    } catch (SQLException e) {
        e.printStackTrace();
    }
    • executeUpdate(): INSERT, UPDATE, DELETE와 같이 데이터베이스의 데이터를 변경하는 SQL 쿼리를 실행할 때 사용합니다. 실행된 행의 수를 int 타입으로 반환합니다.
  • SELECT (데이터 조회):
    Statement 사용 시:

    String sqlSelect = "SELECT id, name, email FROM users WHERE id > 10";
    ResultSet rs = null;
    try {
        rs = stmt.executeQuery(sqlSelect);
        // 5. 결과 처리 부분으로 이어짐
    } catch (SQLException e) {
        e.printStackTrace();
    }

    PreparedStatement 사용 시:

    String sqlSelect = "SELECT id, name, email FROM users WHERE id > ?";
    ResultSet rs = null;
    try {
        pstmt = conn.prepareStatement(sqlSelect);
        pstmt.setInt(1, 10); // 첫 번째 '?'
        rs = pstmt.executeQuery();
        // 5. 결과 처리 부분으로 이어짐
    } catch (SQLException e) {
        e.printStackTrace();
    }
    • executeQuery(): SELECT 쿼리를 실행하여 데이터를 조회할 때 사용합니다. 쿼리 결과를 ResultSet 객체로 반환합니다.

5. 결과 처리 (ResultSet - SELECT 문인 경우)

SELECT 쿼리의 결과를 ResultSet 객체로부터 읽어들입니다.

// 위에서 얻은 ResultSet rs 객체를 사용
if (rs != null) { // rs가 null이 아닌지 확인 (쿼리 실패 방지)
    try {
        while (rs.next()) { // 다음 행이 존재하는 동안 반복
            int id = rs.getInt("id"); // "id" 컬럼의 정수 값 가져오기
            String name = rs.getString("name"); // "name" 컬럼의 문자열 값 가져오기
            String email = rs.getString("email"); // "email" 컬럼의 문자열 값 가져오기

            // 컬럼 인덱스로도 가져올 수 있습니다. (성능은 약간 좋지만 가독성은 떨어짐)
            // int id = rs.getInt(1); // 첫 번째 컬럼
            // String name = rs.getString(2); // 두 번째 컬럼

            System.out.printf("ID: %d, 이름: %s, 이메일: %s\n", id, name, email);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
  • rs.next(): ResultSet의 커서를 다음 행으로 이동시키고, 다음 행이 존재하면 true, 없으면 false를 반환합니다. while 루프와 함께 사용하여 모든 행을 순회합니다.
  • rs.get데이터타입("컬럼이름") 또는 rs.get데이터타입(컬럼인덱스): 현재 커서가 가리키는 행에서 지정된 컬럼의 값을 Java 타입으로 가져옵니다.

6. 자원 해제 (Connection, Statement, ResultSet 닫기)

데이터베이스 자원은 사용 후 반드시 닫아주어 누수를 방지해야 합니다. try-with-resources 문을 사용하는 것이 가장 권장되는 방법입니다.

// try-with-resources 사용 (Java 7 이상)
// 괄호 안에 선언된 자원들은 블록이 끝나면 자동으로 close() 메소드가 호출됩니다.
try (Connection conn = DriverManager.getConnection(url, user, password);
     Statement stmt = conn.createStatement(); // 또는 PreparedStatement
     ResultSet rs = stmt.executeQuery("SELECT * FROM users")) { // SELECT 쿼리인 경우

    // SQL 쿼리 실행 및 결과 처리 코드

} catch (SQLException e) {
    System.err.println("SQL 에러 발생: " + e.getMessage());
    e.printStackTrace();
} catch (ClassNotFoundException e) { // Class.forName() 에서 발생 가능
    System.err.println("드라이버 로드 에러: " + e.getMessage());
    e.printStackTrace();
}
  • try-with-resources: AutoCloseable 인터페이스를 구현하는 객체(여기서는 Connection, Statement, ResultSet)를 try 문 괄호 안에 선언하면, try 블록이 정상적으로 종료되거나 예외가 발생하더라도 해당 객체들의 close() 메소드가 자동으로 호출됩니다. 이 방법이 자원 관리의 가장 안전하고 효율적인 방법입니다.

전체 예시 코드 (SELECT 문):

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement; // 또는 java.sql.Statement
import java.sql.ResultSet;
import java.sql.SQLException;

public class JdbcSelectExample {

    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/userdb?useSSL=false&serverTimezone=UTC";
        String user = "root";
        String password = "1234";

        // try-with-resources를 사용하여 자원 자동 해제
        try (Connection conn = DriverManager.getConnection(url, user, password);
             // PreparedStatement 사용 예시 (더 권장)
             PreparedStatement pstmt = conn.prepareStatement("SELECT id, name, email FROM users WHERE id > ?")) {

            // 드라이버 로드는 JDBC 4.0+ 에서는 필수가 아니지만, 명시적으로 넣어줄 수 있습니다.
            // Class.forName("com.mysql.cj.jdbc.Driver"); 
            
            System.out.println("데이터베이스 연결 성공!");

            // PreparedStatement에 값 바인딩
            pstmt.setInt(1, 0); // id가 0보다 큰 모든 사용자 (WHERE id > 0)

            // 쿼리 실행 및 결과 받기
            ResultSet rs = pstmt.executeQuery();

            System.out.println("📝 저장된 사용자 목록:");
            // ResultSet에서 데이터 한 행씩 읽기
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                System.out.printf("👉 [%d] %s / %s\n", id, name, email);
            }

        } catch (SQLException e) {
            System.err.println("SQL 에러 발생: " + e.getMessage());
            e.printStackTrace();
        } 
        // ClassNotFoundException은 DriverManager.getConnection() 호출시 드라이버 로드가
        // 실패했을 때 발생할 수 있지만, 요즘은 대부분의 경우 자동으로 로드되므로 생략하기도 합니다.
        // 하지만 Class.forName()을 명시적으로 사용한다면 필요합니다.
        /* catch (ClassNotFoundException e) {
            System.err.println("JDBC 드라이버를 찾을 수 없습니다: " + e.getMessage());
            e.printStackTrace();
        }*/
    }
}

이것이 SQL과 관련된 Java (JDBC) 코드의 기본적인 문법과 흐름입니다. 실제 프로젝트에서는 Connection Pool, ORM (JPA/Hibernate, MyBatis) 등을 사용하여 JDBC 코드를 직접 작성하는 것을 최소화하기도 하지만, JDBC의 기본 원리를 이해하는 것은 매우 중요합니다.

0개의 댓글