** 이 포스팅은 DB 연결이 완료된 후의 상황을 기준으로 합니다. 만약 DB 연결이 되지 않은 경우 아래 포스팅을 참고해주세요. **
** DB는 Oracle을 기준으로 합니다. **
[JSP] DB 연결 방법(DriverManager, Connection Pool)
** 본 포스팅은 Oracle 11g을 기준으로 작성되었습니다 **1. JDBC 파일 필요자바에서 데이터베이스에 접속하기 위해선 JDBC를 사용해야 한다. JDBC(Java Database Connectivity)란 자바에서 데이터베이스와 연결
semin1127.tistory.com
DB 연결은 Connection Pool 방식으로 하는 것이 좋지만, 이 포스팅에서는 단순 DB 연결을 1번만 맺어 쿼리를 실행하는 것을 가정하므로 DriverManager 방식을 사용하도록 하겠다.
1. DB 연결 (by. DriverManager)
<%@ page language="java" contentType="text/html; charset=EUC-KR"
pageEncoding="EUC-KR"%>
<%@ page import="java.sql.Connection" %>
<%@ page import="java.sql.PreparedStatement" %>
<%@ page import="java.sql.Statement" %>
<%@ page import="java.sql.ResultSet" %>
<%@ page import="java.sql.DriverManager" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>Query Test</title>
</head>
<body>
<%
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String user = "ORCLSTUDY";
String pwd = "1234";
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", user, pwd);
/*
* 이 부분에서 쿼리 실행
*/
} catch(Exception e) {
e.printStackTrace();
} finally {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
%>
</body>
</html>
DriverManager을 사용하여 Oracle DB Connection을 얻는 과정이다. 자원을 닫아주지 않으면 누수가 발생하고, 최대 커넥션 수에 도달하면 더 이상 커넥션을 얻을 수 없어 무한 로딩 현상이 발생하므로 반드시 자원을 닫아줘야 한다.
2. 쿼리
2-1. Statement 사용
첫 번째 방법은 Statement 클래스를 사용하는 것이다. 코드는 다음과 같다. JSP 부분만 담았다.
<%
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String user = "ORCLSTUDY";
String pwd = "1234";
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String sql = "SELECT * FROM member";
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", user, pwd);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println("id: " + rs.getString("ID"));
System.out.println("name: " + rs.getString("NAME"));
System.out.println();
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
%>
- stmt = conn.createStatement() : Statement 객체 생성
- rs = stmt.executeQuery(sql) : 쿼리 실행 결과 저장
- while (rs.next()) {...} : 데이터 하나씩 순회하며 추출
ResultSet 클래스는 쿼리 실행 결과를 저장할 때 사용되는 것으로 행을 순회하는 커서가 존재한다. 그러므로 rs.next( ) 메서드를 사용하여 행을 순차적으로 탐색할 수 있고, 만약 모든 데이터를 다 탐색했다면 이 메서드는 false를 반환한다.
2-2. PreparedStatement 사용
두 번째 방법은 PreparedStatement 클래스를 사용하는 것이다. Statement와는 다르게 객체를 생성할 때 sql 문을 파라미터로 넣어줘야 한다. 코드는 크게 다르지 않다.
<%
Connection conn = null;
Statement stmt = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String user = "ORCLSTUDY";
String pwd = "1234";
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String sql = "SELECT * FROM member";
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", user, pwd);
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("id: " + rs.getString("ID"));
System.out.println("name: " + rs.getString("NAME"));
System.out.println();
}
} catch(Exception e) {
e.printStackTrace();
} finally {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
%>
- pstmt = conn.prepareStatement(sql) : 미리 선언된 sql을 컴파일하여 객체를 생성
- rs = pstmt.executeQuery( ) : 쿼리 실행 결과 저장
- while (rs.next()) {...} : 데이터 하나씩 순회하며 추출
Statement를 사용했을 때와의 차이는 Connection으로부터 객체를 생성할 때 sql문을 파라미터로 설정하여 컴파일 시점에 미리 객체를 생성한다는 것이다. 컴파일 시점에 객체를 생성하는 것이므로 Statement보다 성능이 좋다.
3. Statement VS PreparedStatement
3-1. 객체 생성 시점에 따른 성능 차이
Statement는 런타임 시점에 쿼리를 사용할 때마다 계속해서 컴파일이 이뤄지지만, PreparedStatement는 실행 전에 최초 1회 컴파일 된 후 이후에는 컴파일이 이뤄지지 않는다. 따라서 PreparedStatement의 성능이 더 좋다.
3-2. 쿼리 파라미터 설정 방법 차이 (feat. SQL Injection 공격)
3-2-1. Statement
Statement는 쿼리 파라미터를 설정할 때 쿼리문 내부에 설정해야 한다. 만약 사용자가 입력한 값을 쿼리에 추가해야 한다면 사용자 입력 값이 그대로 들어간다.
// try 부분만 추출
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String id = request.getParameter("id");
String passwd = request.getParameter("passwd");
String sql = "SELECT * FROM member WHERE id=" + id + " AND passwd=" + passwd;
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", user, pwd);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println("id: " + rs.getString("ID"));
System.out.println("name: " + rs.getString("NAME"));
System.out.println();
}
}
request.getParameter("...") 메서드는 이전 페이지에서 전달된 값을 반환한다. 이는 GET 방식이나 POST 방식의 요청 모두 동일하다. 실제 로그인 요청은 POST 방식으로 처리되지만 여기선 단순성을 위해 직접 URL에 파라미터 값을 입력해주었다.
그러나 이처럼 sql에 사용자가 입력한 값을 그대로 설정하는 것은 문제가 될 수 있다. 요즘에는 기본적으로 방어가 되어 있는 SQL Injection 공격에 뚫릴 수 있는 것이다. SQL에서 주석은 --로 설정하는데 이를 이용해서 다음과 같이 입력하면 쿼리의 조건이 true가 되어 결과를 반환한다.
분명 비밀번호를 입력하지 않았는데도 쿼리가 실행되었다. 이는 쿼리가 다음과 같이 해석되었기 때문에 가능한 것이다.
SELECT * FROM member WHERE id=3-- AND passwd=
이처럼 -- 뒷부분이 전부 주석이 되어버리기 때문에 id만 일치한다면 쿼리가 실행되버리는 것이다. 물론 Java 코드에서 추가적인 조치를 할 수는 있겠지만 근본적으로 Statement의 위험성이 있다는 것을 말하고자 하는 것이다.
3-2-2. PreparedStatement
PreparedStatement는 Statement와 달리 메서드로 쿼리 파라미터를 설정한다. 값의 타입에 따라 setString( ), setInt( ), setDate( ) 등의 메서드를 사용하여 값을 설정하면 된다.
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
String id = request.getParameter("id");
String passwd = request.getParameter("passwd");
// :id, :passwd 대신 물음표(?)를 써도 되고 :foawfwo, :lajlfwlie 이렇게 아무거나 입력해도 되긴 된다
String sql = "SELECT * FROM member WHERE id=:id AND passwd=:passwd";
conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", user, pwd);
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
pstmt.setString(2, passwd);
rs = pstmt.executeQuery();
while (rs.next()) {
System.out.println("id: " + rs.getString("ID"));
System.out.println("name: " + rs.getString("NAME"));
System.out.println();
}
}
두 파라미터 타입이 모두 String이므로 setString( ) 메서드를 사용하였다. 파라미터는 앞에서부터 1번의 번호를 갖는다. 그러므로 파라미터를 설정할 땐 다음과 같이 설정해야 한다.
pstmt.set...({파라미터 번호}, {파라미터 값});
이번에도 앞에서 했던 것처럼 이상한 값을 전달해보겠다.
PreparedStatement는 쿼리가 미리 컴파일되므로 쿼리 파라미터는 단순 값의 바인딩만 일어난다. 메서드에 파라미터를 설정하는 것처럼 생각하면 되는 것이다. 그러므로 id에 3-- 값이 들어오더라도 주석으로 설정되는 것이 아니라 통문자열인 '3--' 으로 설정되기에 SQL Injection 공격이 기본적으로 먹히지 않는 것이다.
'[Programming Language] > [JSP]' 카테고리의 다른 글
[JSP] Excel 파일 업로드 후 내용 파싱하여 DB에 저장하기(Apache Tika, Apache POI) (1) | 2025.06.05 |
---|---|
[JSP] DB 연결 방법(DriverManager, Connection Pool) (0) | 2025.05.23 |
[JSP] 데이터 주고 받는 방법(form, Fetch API) (0) | 2025.05.19 |
[JSP] 태그 종류(scriptlet, directive, declaration, expression, comment, action) (0) | 2025.05.15 |
[JSP] JSP란? JSP와 Servlet? 기본 예제 (0) | 2025.05.11 |