Servlet 기초
서블릿의 응답 처리
doGet()이나doPost()메서드 안에서 처리javax.servlet.http.HttpServletResponse객체 이용setContentType()을 이용해 클라이언트에게 전송할 데이터 종류(MIME_TYPE)를 지정- 브라우저(클라이언트)와 서블릿의 통신은 자바 IO 스트림을 이용
package ex01;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
public void init() {
System.out.println("init 메소드 호출");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 웹브라우저에서 전송된 데이터의 인코딩 설정
response.setContentType("text/html;charset=utf-8"); // 응답할 데이터 종류를 html로 설정
PrintWriter out = response.getWriter(); // 출력 스트림 객체 받아옴
String user_id = request.getParameter("user_id");
String user_pw = request.getParameter("user_pw");
String data = "<html>";
data += "<body>";
data += "아이디 : " + user_id;
data += "<br>";
data += "비밀번호 : " + user_pw;
data += "</body>";
data += "</html>";
out.print(data); // PrintWriter의 print()를 이용해 HTML 태그 문자열을 웹브라우저로 출력
}
public void destory() {
System.out.println("destroy 메소드 호출");
}
}
위의 코드에서는 GET 방식으로 데이터를 처리했다.
GET과 POST 비교
GET 방식과 POST 방식을 비교해보자.
| GET 방식 | POST 방식 | |
| 서블릿에 데이터를 전송 시 | 데이터가 URL 뒤에 name=value 형태로 전송됨. 여러 개의 데이터 전송 시 &로 구분해서 전송 |
TCP/IP 프로토콜 데이터의 HEAD 영역에 숨겨진 채 전송됨 |
| 보안 | 보안에 취약 | 보안에 유리 |
| 전송 데이터 용량 | 데이터의 최대길이는 255자 | 무제한 |
| 속도 | POST 방식보다 빠름 | 전송 시 서블릿에서는 또 다시 가져오는 작업을 해야하므로 처리 속도가 GET 방식보다 느림 |
| 데이터 처리 | 서블릿에서 doGet()으로 전송된 데이터 처리 | 서블릿에서 doPost()를 이용해 데이터 처리 |
POST 방식으로 변경
package ex01;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
public void init() {
System.out.println("init 메소드 호출");
}
// doGet 메서드를 doPost 메서드로 변경
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8"); // 웹브라우저에서 전송된 데이터의 인코딩 설정
response.setContentType("text/html;charset=utf-8"); // 응답할 데이터 종류를 html로 설정
PrintWriter out = response.getWriter(); // 출력 스트림 객체 받아옴
String user_id = request.getParameter("user_id");
String user_pw = request.getParameter("user_pw");
String data = "<html>";
data += "<body>";
data += "아이디 : " + user_id;
data += "<br>";
data += "비밀번호 : " + user_pw;
data += "</body>";
data += "</html>";
out.print(data); // PrintWriter의 print()를 이용해 HTML 태그 문자열을 웹브라우저로 출력
}
public void destory() {
System.out.println("destroy 메소드 호출");
}
}
login.html도 form 태그의 method를 post로 바꾼다.
이번엔 자바스크립트를 이용해 서블릿을 요청해봤다.
로그인 버튼을 클릭했을 때 fn_validate() 함수가 실행되어 아이디, 비밀번호의 유효값을 검증하고 submit()을 호출해 /login 경로로 데이터를 보낸다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인 창</title>
<script>
function fn_validate(){
var f = document.frmLogin;
var user_id = f.user_id.value;
var user_pw = f.user_pw.value;
if((user_id.length == 0 || user_id == "") || (user_pw.length == 0 || user_pw == "")){
alert("아이디와 비밀번호는 필수입니다");
} else {
f.method = "post";
f.action = "login";
f.submit();
}
}
</script>
</head>
<body>
<form name="frmLogin" method="post" action="login" encType="UTF-8">
<input type="hidden" name="address" value="서울시 성동구">
아이디 : <input type="text" name="user_id"><br>
비밀번호 : <input type="password" name="user_pw"><br>
<input type="checkbox" name="subject" value="java" checked> 자바
<input type="checkbox" name="subject" value="python" > 파이썬
<input type="checkbox" name="subject" value="JSP" > JSP
<input type="checkbox" name="subject" value="kotlin" > 코틀린<br>
<input type="button" onclick="fn_validate();" value="로그인"><input type="reset" value="reset">
</form>
</body>
</html>
GET 방식, POST 방식 동시 처리
전송된 방식에 따라 doGet()이나 doPost()로 처리한 후 다시 doHandle()을 호출해서 모든 기능을 구현할 수도 있다.
데이터베이스 연동
오라클에서 SQL Developer를 다운로드한 후 압축 풀고 exe 파일을 실행한다.
그리고 +버튼을 눌러 DB서버 접속을 만든다.
테스트 버튼 눌러서 성공하면 접속한다.
1. 테이블 생성 및 오라클 드라이버 추가
오라클 SQL Developer에서 쿼리문을 다음과 같이 작성한다.
CREATE TABLE t_member (
id varchar2(10) PRIMARY KEY,
pwd varchar2(10),
name varchar2(50),
email varchar2(50),
joinDate date DEFAULT SYSDATE
)
INSERT INTO t_member
VALUES('mok', '1212', '황시목', 'mok@gmail.com', sysdate);
INSERT INTO t_member
VALUES('yeojin', '1111', '한여진', 'jin@gmail.com', sysdate);
INSERT INTO t_member
VALUES('dongjae', '1234', '서동재', 'seo@gmail.com', sysdate);
commit;
오라클은 자동 커밋이 아니기 때문에 반드시 commit을 해줘야 한다.
그리고 ojdbc6.jar를 다운로드 받아 WEB-INF/lib 안에 복사한다.
ojdbc6.jar: 자바(Java) 애플리케이션이 오라클 데이터베이스와 연동(연결 및 SQL 실행)할 수 있도록 도와주는 자바 라이브러리 파일(JDBC 드라이버)
2. 예제 코드 작성
웹 브라우저에서 /member URL로 회원 정보를 요청하면 브라우저에 회원 정보를 테이블로 만들어서 출력하는 예제를 실습한다.
- 웹 브라우저가 서블릿에게 회원 정보를 요청
- MemberServlet은 요청을 받은 후 MemberDAO 객체를 생성하여 listMembers() 메서드를 호출
- listMembers()에서 다시 connDB() 메서드를 호출하여 DB와 연결한 후 SQL문을 실행해 회원 정보를 조회
- 조회된 회원 정보를 MemberVO 속성에 설정한 후 다시 ArrayList에 저장
- ArrayList를 다시 메서드를 호출한 MemberServlet으로 반환한 후 ArrayList의 MemberVO를 차례대로 가져와 회원 정보를 HTML 태그의 문자열로 만듦
- 만들어진 HTML 태그를 웹 브라우저로 전송해서 회원 정보를 출력
MemberVO.java
package ex02;
import java.sql.Date;
// Member Value Object의 줄임말
public class MemberVO {
private String id;
private String pwd;
private String name;
private String email;
private Date joinDate;
public MemberVO() {
System.out.println("MemberVO 생성자 호출");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getJoinDate() {
return joinDate;
}
public void setJoinDate(Date joinDate) {
this.joinDate = joinDate;
}
}
MemberDAO.java
package ex02;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
//Member Data Access Object의 줄임말
public class MemberDAO {
private static final String driver = "oracle.jdbc.driver.OracleDriver";
private static final String url = "오라클 서버 url";
private static final String user = "사용자 이름";
private static final String pwd = "비밀번호";
private Connection con;
private Statement stmt;
public List<MemberVO> listMembers(){
List<MemberVO> list = new ArrayList<MemberVO>();
try {
connDB(); // 네 가지 정보로 DB를 연결
String query = "select * from t_member";
System.out.println(query);
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String id = rs.getString("id");
String pwd = rs.getString("pwd");
String name = rs.getString("name");
String email = rs.getString("email");
Date joinDate = rs.getDate("joinDate");
MemberVO vo = new MemberVO();
vo.setId(id);
vo.setPwd(pwd);
vo.setName(name);
vo.setEmail(email);
vo.setJoinDate(joinDate);
list.add(vo);
}
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
e.printStackTrace();
}
return list; // 조회한 레코드의 개수만큼 ArrayList를 반환
}
private void connDB() {
try {
Class.forName(driver);
System.out.println("Oracle 드라이버 로딩 성공");
con = DriverManager.getConnection(url, user, pwd);
System.out.println("Connection 생성 성공");
stmt = con.createStatement();
System.out.println("Statement 생성 성공");
} catch (Exception e) {
e.printStackTrace();
}
}
}
MemberServlet.java
package ex02;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/member") // '/member' 경로로 들어오면 이 서블릿이 동작됨
public class MemberServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
MemberDAO dao = new MemberDAO();
List<MemberVO> list = dao.listMembers();
out.print("<html><body>");
out.print("<table border=1><tr align='center' bgcolor='lightgreen'>");
out.print("<td>아이디</td><td>비밀번호</td><td>이름</td><td>이메일</td><td>가입일</td><td>삭제</td></tr>");
// 조회한 회원 정보를 리스트로 출력
for(int i = 0; i < list.size(); i++) {
MemberVO memberVO = (MemberVO)list.get(i);
String id = memberVO.getId();
String pwd = memberVO.getPwd();
String name = memberVO.getName();
String email = memberVO.getEmail();
Date joinDate = memberVO.getJoinDate();
out.print("<tr><td>" + id + "</td><td>"
+ pwd + "</td><td>"
+ name + "</td><td>"
+ email + "</td><td>"
+ joinDate + "</td><td><a href='/ServletEx/member?command=delMember&id="+id+"'>삭제</a></td></tr>");
}
out.print("</table></body></html>");
}
}
3. 결과 확인

PreparedStatement
위의 코드에서는 Statement 인터페이스를 이용해 DB와 연동했다. 이 경우 매번 SQL문을 컴파일해야 하므로 속도가 느리다.
PreparedStatement 인터페이스를 사용하면 SQL문을 미리 컴파일해서 재사용하므로 Statement 인터페이스보다 빠르게 DB작업 수행이 가능하다.
Statement로 3번 조회하는 경우
Statement stmt = con.createStatement();
for(int i = 1; i <= 3; i++) {
String query = "SELECT * FROM member WHERE id=" + i;
ResultSet rs = stmt.executeQuery(query);
while(rs.next()) {
System.out.println(rs.getString("name"));
}
}
DB 내부 흐름
Java
↓
SELECT * FROM member WHERE id=1
↓
DB
↓
1. SQL 파싱(해석)
2. SQL 컴파일
3. 실행
4. 결과 반환
Java
↓
SELECT * FROM member WHERE id=2
↓
DB
↓
1. SQL 파싱(해석)
2. SQL 컴파일
3. 실행
4. 결과 반환
Java
↓
SELECT * FROM member WHERE id=3
↓
DB
↓
1. SQL 파싱(해석)
2. SQL 컴파일
3. 실행
4. 결과 반환
이렇게 매번 SQL문을 해석하고 컴파일 한다.
PreparedStatement로 3번 조회하는 경우
String query = "SELECT * FROM member WHERE id = ?";
PreparedStatement pstmt = con.prepareStatement(query);
for (int i = 1; i <= 3; i++) {
pstmt.setInt(1, i);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("name"));
}
}
DB 내부 흐름
// 처음 준비할 때
Java
↓
SELECT * FROM member WHERE id = ?
↓
DB
↓
1. SQL 파싱
2. SQL 컴파일
3. 실행 계획 저장
// 1~3번째 실행
값 전달
? = 1
↓
이미 컴파일된 SQL 실행
↓
결과 반환
값 전달
? = 2
↓
이미 컴파일된 SQL 실행
↓
결과 반환
값 전달
? = 3
↓
이미 컴파일된 SQL 실행
↓
결과 반환
총 1번 컴파일 한다.
정리하면,
Statement → SQL 자체가 매번 새로 생성PreparedStatement → SQL 구조는 고정, 값만 변경
따라서, DB연동 시 또는 빠른 반복 처리가 필요할 때 PreparedStatement 인터페이스를 사용해야 한다.
❕느낀점
너무 어렵지만 재밌기도 하다. 데이터베이스 연동이라는게 엄청 어려울 줄 알았는데 생각보다는 간단해서 좋았다.
jdbc를 잘 익혀서 팀 과제에 사용할 수 있게 해야겠다.
'Java' 카테고리의 다른 글
| [TIL-260312] Servlet & JSP 입문: JSP 내장 객체, 액션 태그, 모델2 (0) | 2026.03.13 |
|---|---|
| [TIL-260311] Servlet & JSP 입문: 커넥션 풀, JSP 정의, JSP 스크립트 (1) | 2026.03.12 |
| [TIL-260309] Servlet & JSP 입문: JSP 실습, Servlet 기초 (0) | 2026.03.10 |
| [TIL-260306] Servlet & JSP 입문: 자바와 웹, 웹 애플리케이션, HTTP (0) | 2026.03.08 |
| [TIL-260212] 자바 기초: 내부 클래스, 스레드, 람다, 스트림 (0) | 2026.02.17 |