하이어코딩 RSS 태그 관리 글쓰기 방명록 mahiru
2025-07-05 15:41:53
728x90
반응형

🎯 개요

Java 애플리케이션에서 SQLite 데이터베이스를 사용하기 위한 JDBC 연결 설정부터 실제 사용까지의 전체 과정을 다룹니다. Maven 프로젝트 기준으로 설명하며, 실무에서 바로 활용할 수 있는 코드 예제를 포함합니다.

📋 목차

  1. JDBC란 무엇인가?
  2. Maven 의존성 설정
  3. SQLite JDBC 드라이버 설정
  4. 데이터베이스 연결 구현
  5. 연결 테스트
  6. 실무 팁과 Best Practices
  7. 문제 해결

🔍 JDBC란 무엇인가?

**JDBC (Java Database Connectivity)**는 Java 애플리케이션이 데이터베이스와 상호작용할 수 있게 해주는 API입니다.

JDBC의 핵심 구성요소

  • DriverManager: 데이터베이스 드라이버 관리
  • Connection: 데이터베이스 연결 인터페이스
  • Statement/PreparedStatement: SQL 실행
  • ResultSet: 쿼리 결과 처리

SQLite를 선택하는 이유

  • 경량: 별도 서버 설치 불필요
  • 파일 기반: 단일 파일로 데이터베이스 관리
  • 크로스 플랫폼: 다양한 운영체제 지원
  • 개발 친화적: 빠른 프로토타이핑과 테스트에 적합

🛠️ Maven 의존성 설정

1단계: pom.xml 기본 설정

 
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>jdbc-sqlite-example</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

2단계: SQLite JDBC 의존성 추가

 
xml
<dependencies>
    <!-- SQLite JDBC Driver -->
    <dependency>
        <groupId>org.xerial</groupId>
        <artifactId>sqlite-jdbc</artifactId>
        <version>3.42.0.1</version>
    </dependency>
    
    <!-- 테스트를 위한 JUnit (선택사항) -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3단계: Maven 플러그인 설정

 
xml
<build>
    <plugins>
        <!-- Java 컴파일러 플러그인 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.11.0</version>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
        
        <!-- 실행 가능한 JAR 생성 플러그인 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.example.Main</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

🔗 SQLite JDBC 드라이버 설정

JDBC URL 형식 이해하기

 
java
// 기본 형식
jdbc:sqlite:경로/파일명.db

// 예시들
jdbc:sqlite:mydata.db                    // 현재 디렉터리
jdbc:sqlite:/absolute/path/to/mydata.db  // 절대 경로
jdbc:sqlite:./relative/path/mydata.db    // 상대 경로
jdbc:sqlite::memory:                     // 메모리 DB (테스트용)

프로젝트 구조에 맞는 DB 경로 설정

 
project-root/
├── src/
│   ├── main/
│   │   ├── java/
│   │   └── resources/
│   │       └── database/
│   │           └── mydata.db    ← SQLite 파일 위치
│   └── test/
├── target/
└── pom.xml

권장 JDBC URL:

 
java
private static final String DB_URL = "jdbc:sqlite:src/main/resources/database/mydata.db";

💻 데이터베이스 연결 구현

1단계: 연결 유틸리티 클래스 생성

 
java
package com.example.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 데이터베이스 연결 관리 유틸리티 클래스
 * SQLite 데이터베이스와의 연결, 초기화, 종료를 담당
 */
public class DatabaseUtil {
    
    // 데이터베이스 연결 URL
    private static final String DB_URL = "jdbc:sqlite:src/main/resources/database/mydata.db";
    
    /**
     * 데이터베이스 연결을 반환합니다.
     * @return Connection 객체
     * @throws SQLException 연결 실패시 예외 발생
     */
    public static Connection getConnection() throws SQLException {
        try {
            // SQLite JDBC 드라이버가 자동으로 로드됩니다 (JDBC 4.0+)
            return DriverManager.getConnection(DB_URL);
        } catch (SQLException e) {
            System.err.println("데이터베이스 연결 실패: " + e.getMessage());
            throw e;
        }
    }
    
    /**
     * 데이터베이스와 테이블을 초기화합니다.
     * 애플리케이션 시작시 한 번 호출됩니다.
     */
    public static void initializeDatabase() {
        // 테이블 생성 SQL (존재하지 않을 경우에만)
        String createTableSQL = """
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                email TEXT UNIQUE NOT NULL,
                created_at TEXT NOT NULL DEFAULT (datetime('now'))
            )
            """;
        
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement()) {
            
            // 테이블 생성
            stmt.execute(createTableSQL);
            System.out.println("✅ 데이터베이스 초기화 완료");
            
        } catch (SQLException e) {
            System.err.println("❌ 데이터베이스 초기화 실패: " + e.getMessage());
        }
    }
    
    /**
     * 데이터베이스 연결을 안전하게 종료합니다.
     * @param conn 종료할 Connection 객체
     */
    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
                System.out.println("🔒 데이터베이스 연결 종료");
            } catch (SQLException e) {
                System.err.println("연결 종료 중 오류 발생: " + e.getMessage());
            }
        }
    }
    
    /**
     * 데이터베이스 연결 상태를 확인합니다.
     * @return 연결 성공시 true, 실패시 false
     */
    public static boolean testConnection() {
        try (Connection conn = getConnection()) {
            if (conn != null && !conn.isClosed()) {
                System.out.println("✅ 데이터베이스 연결 테스트 성공");
                return true;
            }
        } catch (SQLException e) {
            System.err.println("❌ 연결 테스트 실패: " + e.getMessage());
        }
        return false;
    }
}

2단계: 연결 테스트 클래스

 
java
package com.example.test;

import com.example.util.DatabaseUtil;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

/**
 * JDBC 연결 테스트를 위한 클래스
 * 데이터베이스 연결 상태와 드라이버 정보를 확인합니다.
 */
public class ConnectionTest {
    
    public static void main(String[] args) {
        System.out.println("🚀 JDBC 연결 테스트 시작");
        System.out.println("=" .repeat(50));
        
        testBasicConnection();
        testDatabaseInfo();
        testDatabaseOperations();
        
        System.out.println("=" .repeat(50));
        System.out.println("✅ 모든 테스트 완료");
    }
    
    /**
     * 기본 연결 테스트
     */
    private static void testBasicConnection() {
        System.out.println("1️⃣ 기본 연결 테스트");
        
        try (Connection conn = DatabaseUtil.getConnection()) {
            if (conn != null) {
                System.out.println("   ✅ SQLite 연결 성공!");
                System.out.println("   📍 연결 상태: " + !conn.isClosed());
            }
        } catch (SQLException e) {
            System.err.println("   ❌ 연결 실패: " + e.getMessage());
        }
        System.out.println();
    }
    
    /**
     * 데이터베이스 정보 조회 테스트
     */
    private static void testDatabaseInfo() {
        System.out.println("2️⃣ 데이터베이스 정보 조회");
        
        try (Connection conn = DatabaseUtil.getConnection()) {
            DatabaseMetaData metaData = conn.getMetaData();
            
            System.out.println("   📊 데이터베이스 제품: " + metaData.getDatabaseProductName());
            System.out.println("   🔢 제품 버전: " + metaData.getDatabaseProductVersion());
            System.out.println("   🚗 드라이버 이름: " + metaData.getDriverName());
            System.out.println("   📝 드라이버 버전: " + metaData.getDriverVersion());
            System.out.println("   🔗 JDBC URL: " + metaData.getURL());
            
        } catch (SQLException e) {
            System.err.println("   ❌ 정보 조회 실패: " + e.getMessage());
        }
        System.out.println();
    }
    
    /**
     * 데이터베이스 기본 작업 테스트
     */
    private static void testDatabaseOperations() {
        System.out.println("3️⃣ 데이터베이스 작업 테스트");
        
        // 데이터베이스 초기화
        DatabaseUtil.initializeDatabase();
        
        // 간단한 쿼리 실행 테스트
        try (Connection conn = DatabaseUtil.getConnection()) {
            // SQLite 버전 확인 쿼리
            var stmt = conn.createStatement();
            var rs = stmt.executeQuery("SELECT sqlite_version()");
            
            if (rs.next()) {
                System.out.println("   ✅ SQLite 버전: " + rs.getString(1));
            }
            
        } catch (SQLException e) {
            System.err.println("   ❌ 작업 테스트 실패: " + e.getMessage());
        }
        System.out.println();
    }
}

3단계: 실제 사용 예제

 
java
package com.example.dao;

import com.example.util.DatabaseUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * User 데이터 접근 객체 (DAO)
 * CRUD 작업을 담당합니다.
 */
public class UserDao {
    
    /**
     * 사용자 추가
     */
    public void insertUser(String name, String email) throws SQLException {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        
        try (Connection conn = DatabaseUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            
            pstmt.setString(1, name);
            pstmt.setString(2, email);
            
            int rowsAffected = pstmt.executeUpdate();
            System.out.println("✅ " + rowsAffected + "명의 사용자가 추가되었습니다.");
            
        } catch (SQLException e) {
            System.err.println("❌ 사용자 추가 실패: " + e.getMessage());
            throw e;
        }
    }
    
    /**
     * 모든 사용자 조회
     */
    public List<User> selectAllUsers() throws SQLException {
        String sql = "SELECT id, name, email, created_at FROM users ORDER BY created_at DESC";
        List<User> users = new ArrayList<>();
        
        try (Connection conn = DatabaseUtil.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            
            while (rs.next()) {
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setEmail(rs.getString("email"));
                user.setCreatedAt(rs.getString("created_at"));
                users.add(user);
            }
            
        } catch (SQLException e) {
            System.err.println("❌ 사용자 조회 실패: " + e.getMessage());
            throw e;
        }
        
        return users;
    }
}

🧪 연결 테스트

Maven으로 테스트 실행

 
bash
# 의존성 확인
mvn dependency:tree

# 컴파일
mvn clean compile

# 연결 테스트 실행
mvn exec:java -Dexec.mainClass="com.example.test.ConnectionTest"

성공적인 테스트 결과 예시

 
🚀 JDBC 연결 테스트 시작
==================================================
1️⃣ 기본 연결 테스트
   ✅ SQLite 연결 성공!
   📍 연결 상태: true

2️⃣ 데이터베이스 정보 조회
   📊 데이터베이스 제품: SQLite
   🔢 제품 버전: 3.42.0
   🚗 드라이버 이름: SQLite JDBC
   📝 드라이버 버전: 3.42.0.1
   🔗 JDBC URL: jdbc:sqlite:src/main/resources/database/mydata.db

3️⃣ 데이터베이스 작업 테스트
   ✅ 데이터베이스 초기화 완료
   ✅ SQLite 버전: 3.42.0
==================================================
✅ 모든 테스트 완료

💡 실무 팁과 Best Practices

1. 연결 관리 Best Practices

 
java
// ✅ 좋은 예: try-with-resources 사용
try (Connection conn = DatabaseUtil.getConnection();
     PreparedStatement pstmt = conn.prepareStatement(sql)) {
    // 작업 수행
} catch (SQLException e) {
    // 예외 처리
}

// ❌ 나쁜 예: 수동 연결 관리
Connection conn = null;
try {
    conn = DatabaseUtil.getConnection();
    // 작업 수행
} finally {
    if (conn != null) conn.close(); // 누락 가능성
}

2. SQL Injection 방지

 
java
// ✅ 안전: PreparedStatement 사용
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userName);

// ❌ 위험: 문자열 연결
String sql = "SELECT * FROM users WHERE name = '" + userName + "'";

3. 트랜잭션 관리

 
java
public void transferData() throws SQLException {
    Connection conn = DatabaseUtil.getConnection();
    
    try {
        conn.setAutoCommit(false); // 트랜잭션 시작
        
        // 여러 DB 작업 수행
        insertUser(conn, "user1", "user1@example.com");
        updateUserStatus(conn, 1, "active");
        
        conn.commit(); // 트랜잭션 커밋
        System.out.println("✅ 트랜잭션 성공");
        
    } catch (SQLException e) {
        conn.rollback(); // 트랜잭션 롤백
        System.err.println("❌ 트랜잭션 실패, 롤백됨");
        throw e;
        
    } finally {
        conn.setAutoCommit(true); // 자동 커밋 복원
        conn.close();
    }
}

4. 연결 풀링 (고급)

 
xml
<!-- HikariCP 연결 풀 의존성 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>
 
java
public class ConnectionPool {
    private static HikariDataSource dataSource;
    
    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:sqlite:src/main/resources/database/mydata.db");
        config.setMaximumPoolSize(10);
        config.setMinimumIdle(2);
        dataSource = new HikariDataSource(config);
    }
    
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

🚨 문제 해결

자주 발생하는 오류와 해결책

1. ClassNotFoundException: org.sqlite.JDBC

원인: SQLite JDBC 드라이버를 찾을 수 없음

해결책:

 
bash
# Maven 의존성 재다운로드
mvn clean install -U

# 의존성 확인
mvn dependency:tree | grep sqlite

2. SQLException: Database is locked

원인: 다른 프로세스가 데이터베이스 파일을 사용 중

해결책:

 
java
// 연결 URL에 타임아웃 설정 추가
private static final String DB_URL = 
    "jdbc:sqlite:mydata.db?busy_timeout=30000";

3. Text blocks are not supported in -source 8

원인: Java 8에서 Text Blocks 사용

해결책:

 
java
// Java 8 호환 코드로 변경
String sql = "CREATE TABLE IF NOT EXISTS users (" +
            "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
            "name TEXT NOT NULL" +
            ")";

4. 한글 깨짐 현상

해결책:

 
java
// 연결 URL에 인코딩 설정
private static final String DB_URL = 
    "jdbc:sqlite:mydata.db?encoding=UTF-8";

디버깅 팁

 
java
// 연결 상태 로깅
public static void logConnectionInfo(Connection conn) throws SQLException {
    DatabaseMetaData meta = conn.getMetaData();
    System.out.println("🔍 디버깅 정보:");
    System.out.println("   - 연결 상태: " + !conn.isClosed());
    System.out.println("   - 자동 커밋: " + conn.getAutoCommit());
    System.out.println("   - 트랜잭션 격리: " + conn.getTransactionIsolation());
    System.out.println("   - 카탈로그: " + conn.getCatalog());
}

📚 참고 자료

  • SQLite JDBC 공식 문서
  • Oracle JDBC 튜토리얼
  • SQLite 공식 문서
  • Maven 의존성 관리

🎯 마무리

이 가이드를 통해 Java와 SQLite JDBC 연결을 성공적으로 구현했습니다. 핵심 포인트를 요약하면:

  1. Maven 의존성: sqlite-jdbc 라이브러리 추가
  2. 연결 관리: try-with-resources로 안전한 연결 관리
  3. 보안: PreparedStatement로 SQL Injection 방지
  4. 예외 처리: 적절한 SQLException 처리
  5. 테스트: 연결 상태 및 기능 검증

실제 프로덕션 환경에서는 연결 풀링, 트랜잭션 관리, 로깅 등을 추가로 고려해야 합니다. 이 기초를 바탕으로 더 복잡한 데이터베이스 애플리케이션을 구축할 수 있을 것입니다.

728x90
반응형
이 페이지는 리디주식회사에서 제공한 리디바탕 글꼴이 사용되어 있습니다.