Statement와 PreparedStatement의 차이
Statement와 PreparedStatement의 차이
목차
시작하며
JDBC 라는 API의 핵심 객체 세가지 (Connection, Statement, ResultSet) 에 대해 개념을 정리하고, Statement와 PreparedStatement의 차이에 대해서 알아본다. 그리고 해당 세가지 객체를 활용하여 실제 DB에 연결하는 코드를 작성해본다.
1. JDBC 란?
1-1. JDBC (Java Database Connectivity) 정의 : API
- java 프로그램이 DB(oracle, mysql 등)와 연결하여 데이터를 주고 받을 수 있도록 해주는 자바 표준 API
1-2. 특징 : DB 독립성
- 개발자는 JDBC 표준 사용법만 익히면, 오라클이든 MySQL이든 DB 종류에 상관없이 똑같은 자바 코드로 연결할 수 있음 (각 DB에 맞는 드라이버만 갈아 끼우면 됨)
1-3. 역할 : 다리(Bridge)
“자바 애플리케이션” – “데이터베이스” 사이의 다리 역할
2. JDBC 핵심 객체
2-1. - Connection (연결 통로)
- 역할:
DB 서버와자바 프로그램사이의 물리적인 연결(Session)을 맺는 객체
1
Conncetion con = null;
비유: 전화를 걸어서 상대방(DB)과 “통화가 연결된 상태”
생성: DriverManager.getConnection(url, id, pw) 메소드로 얻어옴.
2-2. Statement (운반 트럭)
- 역할: 연결된 통로(Connection)를 통해 SQL 문(쿼리)을 DB에 전달하고, 실행 결과를 받아오는 객체
- 비유: 주문서(SQL)를 싣고 주방(DB)으로 달리는 “트럭”
- 종류:
- Statement: 일반 트럭 (완성된 SQL을 그대로 운반, 보안 취약)
- PreparedStatement: 보안 트럭 (SQL 틀을 미리 준비하고 값만 실어 나름, 권장 👍)
2-3. ResultSet (결과 상자)
- 역할: SELECT 문을 실행했을 때, DB로부터 찾아온 데이터 결과표(Table)를 담고 있는 객체
- 비유: 주방(DB)에서 요리가 완료되어 나온 “음식 쟁반”
- 특징:
3. Statement 와 PreparedStatement 차이
1. 기존 방식인 Statement
: 완성된 SQL 문자열을 통째로 DB에 전송
- 단점 1 (가독성) 변수 값을 넣을 때 따옴표(‘)와 더하기 기호(+)를 복잡하게 연결해야 해서 오타가 나기 쉬움
- 단점 2 (보안) SQL 인젝션(SQL Injection) 공격에 취약 (해커가 입력값에 SQL 명령어를 섞으면 그대로 실행됨)
1
2
3
4
5
String name = "SCOTT";
// 문자열 결합 연산(+)으로 인해 가독성이 떨어지고, 작은따옴표(') 누락 실수가 잦음
String sql = "SELECT * FROM emp WHERE ename = '" + name + "'";
stmt = con.createStatement();
rs = stmt.executeQuery(sql);
2. 개선된 방식인 PreparedStatement
: SQL 문장의 틀(골격)을 먼저 컴파일해두고, 실행 시에 값(Value)만 바꿔 끼워 넣는 방식
- 장점 1 (가독성): 복잡한 따옴표(‘) 처리 없음
- 장점 2 (보안): 입력값을 단순 문자로 취급하므로, SQL 인젝션을 원천 차단
- 장점 3 (성능): 동일한 쿼리는 미리 컴파일된 것을 재사용하므로 속도가 빠름
1
2
3
4
5
6
7
8
9
10
11
12
13
String name = "SCOTT";
// 1. SQL 작성: 값이 들어갈 자리를 물음표(?)인 'Placeholder'로 비워둠
String sql = "SELECT * FROM emp WHERE ename = ?";
// 2. 준비(Prepare): SQL 문법을 미리 DB에 전송하여 준비시킴
pstmt = con.prepareStatement(sql);
// 3. 파라미터 바인딩: 첫 번째(1) 물음표(?)에 값을 채워 넣음
// 알아서 따옴표(') 처리를 해주며, 해킹 코드가 들어와도 단순 문자로 인식
pstmt.setString(1, name);
// 4. 실행: 이미 준비된 쿼리를 실행 (괄호 안에 sql을 넣지 않음)
rs = pstmt.executeQuery();
4. DAO 란?
: 데이터베이스에 접근하여 데이터를 조회하거나 조작(CRUD)하는 기능을 전담하는 객체 (Data Access Object)
- 목적: 메인 비즈니스 로직(Controller)과 지저분한 SQL 처리 로직을 분리하기 위해 사용
4-1. 짝꿍: DTO (Data Transfer Object)
- DAO가 창고지기(동작, Method) 라면, DTO는 데이터를 담아 나르는 이삿짐 박스(Data, Variable) (VO 라고도 부름)
- DB에서 꺼낸 데이터를 자바 객체(DeptDTO)에 담아서 메인 프로그램으로 리턴해줌
5. 객체 활용 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// DeptDTO.java
// Dept(부서)의 정보를 담고있는 DB 구조
package model.domain;
// DeptDTO : 부서 테이블(dept)의 1개의 행(row) 데이터를 담는 그릇
public class DeptDTO {
private int deptno;
private String dname;
private String loc;
}
// 기본 생성자 (No-args Constructor)
// - **다른 생성자가 하나도 없을 때만** 가시적으로 작성하지 않아도 자동으로 생김
// - private으로 만들고 싶으면 : private DeptDTO() {} 라고 명시해야 함
// -> 외부에서 new를 못 하게 막는 경우 (예: 싱글톤 패턴, 유틸리티 클래스)
// - public DeptDTO(int deptno, ...) 처럼 파라미터가 있는 생성자를 만든 경우 (오버로딩) -> 자동으로 만들어주던 기본 생성자는 사라짐
// -> 반드시 기본 생성자(public DeptDTO() {})를 직접 써줘야 함
// - 생성자는 객체(Instance)를 만드는 역할이라서, 절대 static을 붙일 수 없음
public DeptDTO() {}
// 파라미터 생성자 (All-args Constructor)
public DeptDTO(int deptno, String dname, String loc) {
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package model.dao;
public class DeptDAO {
// 1. 데이터 삽입 (INSERT) - 결과가 '몇 건 처리됨(int)'으로 출력
public boolean insertDept(DeptDTO deptdto) throws SQLException {
Conncetion con = null;
PreparedStatement pstmt = null;
boolean result = false;
try {
con = DBUtil.getConnection();
// 값을 넣을 곳을 ? 로 비워둠
String sql = "INSERT INTO dept VALUES (?, ?, ?)";
pstmt = con.preparedStatement(sql);
//물음표 부분 채우기 (DTO 박스에서 꺼내서 넣기)
pstmt.setInt(1, deptdto.getDeptno());
pstmt.setString(2, deptdto.getDname());
pstmt.setString(3, deptdto.getLoc());
// 실행 (INSERT, DELETE, UPDATE [DML]는 executeUpdate로 실행)
int count = pstmt.executeUpdate();
if (count > 0) {
result = true;
}
} finally {
DBUtil.close(con, pstmt);
}
return result;
}
// 2. 전체 조회 (SELECT) - 결과가 '데이터 목록(ArrayList)'로 나옴
public ArrayList<DeptDTO> getAllDepts() {
// 연결 객체, 반환 객체 초기화
Connection con = null;
PreparedStatement pstmt= null;
ResultSet rs = null;
ArrayList<DeptDTO> selectList = new Arraylist<>();
try {
con = DBUtil.getConnection();
String sql = "SELECT * FROM dept ORDER BY deptno ASC";
pstmt = con.preparedStatement(sql);
// 실행 (SELECT -> executeQuery 사용!)
rs = pstmt.executeQuery();
while (rs.next()) {
// DB에서 꺼낸 데이터를 DTO 박스에 포장
DeptDTO deptdto = new DeptDTO(
rs.getInt("deptno"),
rs.getString("dname"),
rs.getString("loc")
);
//리스트에 추가
selectList.add(deptdto);
}
}finally {
DBUtil.close(con, pstmt, rs);
}
return selectList;
}
}
1
2
3
4
5
package util;
public class DBUtil {
}
[!INFO] Change Log
- 2026-01-12: 최초 작성
- 2026-01-12: 코드 수정
- 2026-01-13: 최종 수정
This post is licensed under CC BY 4.0 by the author.



