열혈강의 자바 웹 애플리케이션 보고 정리
제4장 서블릿과 JDBC
이번 장에서는 GenericServlet 클래스를 확장한 HttpServlet 클래스를 이용하여 서블릿을 만들어 보겠음.
클라이언트의 요청을 GET과 POST 등으로 구분하여 처리하는 방법과 리다이렉트, 리프래시를 다루는 방법을 배웁니다. 초기화 매개변수를 이용하여 설정 정보를 외부 파일에 두는 방법과 서블릿에서 이를 참고하는 방법을 알아봅니다.
4.1 데이터베이스에서 데이터 가져오기
서블릿이 하는 주된 일은 클라이언트가 요청한 데이터를 다루는 일입니다. 데이터베이스는 개발자들이 쉽게 데이터를 저장하고 꺼낼 수 있도록 도와주는 프로그램입니다. 데이터베이스가 없다면, 개발자들이 직접 팡리 입출력 API를 사용하여 데이터를 다뤄야 합니다.
개발자는 SQL로 데이터베이스가 할 일을 작성하고, JDBC를 사용하여 데이터베이스로 SQL을 보내고 결과를 받습니다.
서블릿 <-> JDBC Driver <-> DB
4.1.1 회원 목록 조회 구현
회원 테이블 생성
-- 회원기본정보 테이블 members 생성 create table members( mno integer not null comment '회원일련번호', email varchar(40) not null comment '이메일', pwd varchar(100) not null comment '암호', mname varchar(50) not null comment '이름', cre_date datetime not null comment '가입일', mod_date datetime not null comment '마지막암호변경일' ) comment '회원기본정보';
-- members 테이블의 기본 키 정의 alter table members add constraint pk_members primary key (mno);
-- members의 email 칼럼을 유니크로 지정 create unique index uix_members on members (email asc);
-- members의 pk 자동증가 alter table members modify column mno integer not null auto_increment comment '회원일련번호';
-- 데이터 입력 insert into members(email, pwd, mname, cre_date, mod_date) values ('s1@test.com','1111','홍길동',now(),now()); insert into members(email, pwd, mname, cre_date, mod_date) values ('s2@test.com','1111','임꺽정',now(),now()); insert into members(email, pwd, mname, cre_date, mod_date) values ('s3@test.com','1111','일지매',now(),now()); insert into members(email, pwd, mname, cre_date, mod_date) values ('s4@test.com','1111','이몽룡',now(),now()); insert into members(email, pwd, mname, cre_date, mod_date) values ('s5@test.com','1111','성춘향',now(),now());
-- 데이터 입력 확인 select * from members; |
JDBC 드라이버 준비
‘회원 목록 조회’ 서블릿 만들기
package spms.servlets;
import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;
import javax.servlet.GenericServlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebServlet;
@WebServlet("/member/list") public class MemberListServlet extends GenericServlet {
@Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub Connection conn = null; Statement stmt = null; ResultSet rs = null;
try { DriverManager.registerDriver(new com.mysql.jdbc.Driver()); conn = DriverManager.getConnection("jdbc:mysql://localhost/sample", "root", "mysql"); stmt = conn.createStatement(); rs = stmt.executeQuery("select MNO,MNAME,EMAIL,CRE_DATE from MEMBERS order by mno asc"); response.setContentType("Text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html><head><title>회원목록</title></head>"); out.println("<body><h1>회원목록</h1>"); while (rs.next()) { out.println(rs.getInt("MNO") + rs.getString("MNAME") + rs.getString("EMAIL") + rs.getDate("CRE_DATE") + "<br>"); } out.println("</body></html>"); } catch (Exception e) { // TODO Auto-generated catch block throw new ServletException(e); } finally { try { if (rs != null) rs.close(); } catch (Exception e) { } try { if (stmt != null) stmt.close(); } catch (Exception e) { } try { if (conn != null) conn.close(); } catch (Exception e) { } } }
}
|
데이터베이스 관련 코드를 위한 try~ catch~
JDBC API를 사용할 때 예외가 발생할 수 있으므로 try~ catch~ 블록 준비
Try 블록에서 예외가 발생하면 그 예외를 servletException 객체에 담아서 이 메서드를 호출한 서블릿 컨테이너에 던집니다. 서블릿 컨테이너는 예외에 따른 적절한 화면을 생성하여 클라이언트에게 출력
DriverManager가 사용할 JDBC 드라이버 등록
WEB-INF/lib에 mysql-connect-java-5.1.26-bin.jar를 넣고.
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
데이터베이스에 연결
DriverManager의 getConnection()을 호출하여 MySQL 서버에 연결할 수 있습니다.
conn = DriverManager.getConnection("jdbc:mysql://localhost/studydb",//JDBC URL "study", //DBMS 사용자 아이디 "study"); //DBMS 사용자 암호 |
SQL 실행 객체 준비
Connection 구현체를 이용하여 SQL문을 실행할 객체를 준비합니다.
Stmt = conn.createStatement();
데이터베이스에 SQL 문을 보내기
Java.sql.ResultSet 인터페이스의 구현체
이 인터페이스의 주요 메서드는 다음과 같습니다.
…
next()는 서버에서 다음 레코드를 가져옵니다.
…
SELECT 결과 가져오기
while (rs.next()) { out.println(rs.getInt("MNO") + rs.getString("MNAME") + rs.getString("EMAIL") + rs.getDate("CRE_DATE") + "<br>"); }} |
ResultSet 객체를 통해 next()를 호출하면 서버에서 레코드(Record)를, 다른 말로 ‘행(Row)’을 가져온다. 서버에서 받은 레코드는 ResultSet 객체에 보관된다. Next()는 서버에서 레코드를 받으면 true, 레코드가 없다면 false를 반환한다.
JDBC 프로그래밍의 마무리
반드시 자원 해제를 수행해야 합니다. 자원을 해제하기 가장 좋은 위치는 finally 블록입니다. Finally 블록은 try나 catch를 벗어나기 전에 반드시 수행됩니다.
자원을 해제할 때는 역순으로 처리합니다.
4.1.2 서블릿 배치 정보 설정
4.1.3 ‘회원 목록 조회’ 서블릿 테스트
4.2 HttpServlet으로 GET 요청 다루기
지금까지 서블릿 클래스를 만들 때 service() 메서드를 정의하였는데, HttpServlet 클래스를 상속받게 되면 service() 대신 doGet()이나 doPost()를 정의합니다.
4.2.1 회원 목록 화면에 ‘신규 회원’ 링크 추가
out.println("<p><a href='add'>신규 회원</a></p>");
신규 회원 링크를 클릭하면 404가 뜬다. 아직
회원 정보 입력폼 만들기
Spms.servlets 패키지에 MemberAddServlet 클래스 작성하기 GenericServlet 추상 클래스 대신 HttpServlet 추상 클래스를 상속받습니다.
package spms.servlets;
import java.io.IOException; import java.io.PrintWriter;
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/add") public class MemberAddServlet extends HttpServlet {
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html><head><title>회원 등록</title></head>"); out.println("<body><h1>회원 등록</h1>"); //<form> 태그의 action 속성은 실행할 서블릿의 URL 주소입니다. 상대 경로 'add'를 사용했기 때문에 실제 URL은 http://localhost:9999/web04/member/add 입니다. //<form> 태그의 method 속성은 서버에 요청하는 방식을 지정합니다. 기본값은 'get'입니다. out.println("<form action='add' method='post'>"); out.println("이름: <input type='text' name='name'><br>"); out.println("이메일: <input type='text' name='email'><br>"); //<form> 태그의 입력 항목은 총 세개가 있습니다. 암호는 보이지 않게 '*'가 대신 나타나도록 input 태그의 type 속성을 'password'라고 설정. out.println("암호: <input type='password' name='password'><br>"); //서블릿에 입력폼의 값을 전달하는 방법은 간단합니다. 'submit' 타입의 <input> 태그를 사용하면 서버에 요청을 보내는 버튼을 만들 수 있습니다. out.println("<input type='submit' value='추가'>"); //'reset' 타입의 <input> 태그는 입력폼을 초기화시키는 버튼을 생성합니다. out.println("<input type='reset' value='취소'>"); out.println("</form>"); out.println("</body></html>"); }
} |
doGet() 메서드 오버라이딩
HttpServlet 클래스에 정의도니 메서드 중에서 doGet() 메서드를 오버라이딩 합니다. doGet() 메서드 안에 이 클래스가 해야 할일을 작성합니다.
클라이언트 요청이 들어오면, 첫째로 상속받은 HttpServlet의 service() 메서드가 호출되고, 둘째로 service()는 클라이언트 요청 방식에 따라 doGet()이나 doPost(), doPut() 등의 메서드를 호출합니다. 따라서 HttpServlet을 상속받을 때 service() 메서드를 직접 구현하기보다는 클라이언트의 요청 방식에 따라 doXXX() 메서드를 오버라이딩 합니다.
회원 정보 입력폼 실행
회원 등록 입력폼의 POST 요청
‘추가’를 클릭하면 다음과 같은 오류 출력
톰캣 서버가 클라이언트로부터 /member/add 요청을 받으면, MemberAddServlet의 service() 메서드를 호출합니다. Service() 에서는 요청 방식에 따라 GET 요청이면 doGet()을 호출하고, POST 요청이면 doPost()를 호출합니다. 현재 MemberAddServlet 클래스에 doGet()은 있지만 doPost()는 존재하지 않습니다. 그래서 오류 화면이 출력된 것입니다.
HttpServlet을 상속받아 서블릿을 만들 때는 클라이언트 요청ㅇ PEK라 적절한 doXXX() 메서드를 정의해야 합니다.
4.3 HttpServlet으로 POST 요청 다루기
4.3.1 doPost() 오버라이딩
JDBC 객체를 위한 참조 변수 선언
Inert SQL 문을 실행하기 때문에 ResultSet의 참조 변수는 선언하지 않는다.
PreparedStatement는 반복적인 질의를 하거나, 입력 매개변수가 많은 경우에 유용합니다. 특히 이미지와 같은 바이너리 데이터를 저장하거나 변경할 때는 PreparedStatement만이 가능합니다.
입력 매개변수의 값 설정
입력 매개변수는 SQL 문에서 ‘?” 문자로 표시된 입력 항목을 가리키는 말입니다. 입력 항목의 값은 SQL 문을 실행하기 전에 setXXX() 메서드를 호출하여 설정합니다.
stmt.setString(1, request.getParameter("email"));
stmt.setString(2, request.getParameter("password"));
stmt.setString(3, request.getParameter("name"));
Sql 질의 수행
executeUpdate()를 호출하여 SQL을 실행합니다.
stmt.executeUpdate();
결과를 반환하지 않는 select 문을 실행할 때는 executeQuery()를 호출하고, insert처럼 결과 레코드를 만들지 않은 ddl이나 의 종류의 sql문을 실행할 때는 executeUpdate()를 호출합니다.
- 에러
Type mismatch: cannot convert from java.sql.Connection to com.mysql.jdbc.Connection
에러 해결: import java.sql.Connection; 추가
- 에러
The import java.sql.Connection collides with another import statement
에러 해결: import com.mysql.jdbc.Connection 삭제
회원 등록 테스트
정상적으로 등록되었는지 ‘회원 목록’을 출력
Statement vs preparedStatement
비교 항목 |
Statement |
PreparedStatement |
실행 속도 |
질의할 때마다 SQL 문을 컴파일한다. |
SQL문을 미리 준비하여 컴파일해둔다. 입력 매개변수 값만 추가하여 서버에 전송한다. 특히 여러 번 반복하여 질의하는 경우, 실행속도가 빠름 |
바이너리 데이터 전송 |
불가능 |
가능 |
프로그래밍 편의성 |
SQL문 안에 입력 매개변수 값이 포함되어 있어서 SQL 문이 복잡하고 매개변수가 여러 개인 경우 코드 관리가 힘들다. |
SQL문과 입력 매개변수가 분리되어 있어서 코드 작성이 편리하다. |
4.4 요청 매개변수의 한글 깨짐 처리
4.4.1 한글 깨짐 현상
4.4.2 한글 입력값이 깨진 이유
웹 브라우저가 웹 서버에 데이터를 보낼 때 문자형식
사용자가 입력한 값은 UTF-8로 인코딩되어 서버에 전달됩니다.
서블릿에서 데이터를 꺼낼 때 문자형식
서블릿에서 getParameter()를 호출하면 이 메서드는 기본적으로 매개변수의 값이 ISO-8859-1로 인코딩되었다고 가정하고 각 바이트를 유니코드로 바꾸고 나서 반환합니다. 즉 클라이언트가 보낸 문자를 영어라고 간주하고 유니코드로 바꿉니다. 바로 여기서 문제가 발생합니다.
UTF-8은 한글 한 자를 3바이트로 표현합니다. 서블릿이 이 3바이트를 하나의 문자로 인식하지 않고 각각의 바이트를 개별 문자로 취급하여 유니코드로 변환했기 때문에 한글이 깨진 것입니다.
4.4.3 한글 깨짐 해결책
다시 등록하고 톰캣 서버 재시작 하면 한글이 제대로 출력된 것을 확인할 수 있다.
그래도 한글이 깨진다면?
톰캣 서버의 server.xml에서 URILEncoding=”UTF-8”을 추가하면 해결
톰캣 서버를 설치 폴더에서 독립적으로 실행하는 경우
톰캣이 설치된 폴더에서 conf/server.xml을 편집
톰캣 서버를 이클립스에서 실행하는 경우
Servers 프로젝트 폴더에 server.xml 파일 편집
4.5 리프래시
4.5.1 자동으로 회원 목록을 출력하기
리프래시 실습 시나리오
회원 등록 결과를 화면에 출력한 후 1초가 지난 다음에 자동으로 회원 목록을 출력하게 만들기. MemberAddServlet에서 회원 등록 결과를 웹 브라우저로 보낼 때 리프래시 정보를 보내면 됩니다.
HttpServletResponse의 addHeader()는 HTTP 응답 정보에 헤더를 추가하는 메서드입니다.
Refresh의 숫자’1’은 응답 본문을 출력하고 나서 1초 뒤에 다시 서비스를 요청하라는 뜻입니다. 이때 url은 다시 요청할 서비스 주소입니다.
HTML의 meta 태그를 이용한 리프래시
<meta> 태그는 반드시 <head> 태그 안에 선언해야 합니다.
만약 작업 결과를 출력하지 않고 다른 페이지로 이동할 때는 ‘리다이렉트’로 처리해야 합니다.
4.6 리다이렉트
회원 정보를 등록하고 나서 그 결과를 출력하지 않고 즉시 회원 목록 화면으로 이동하게 하는 방법을 배워 보겠습니다. 이러한 기법을 리다이렉트라고 합니다.
4.6.1 리다이렉트 실습하기
MemberAddServlet은 데이터베이스에 회원 정보를 저장하고 나서 응답 결과를 출력하지 않고 대신 이동 페이지의 URL 정보를 웹 브라우저에게 보냅니다. 리다이렉트 정보는 응답 헤더에 설정합니다.
리다이렉트 메서드 sendRedired()
HTML을 출력하는 코드 주석이나 제거 후 HttpServletResponse의 sendRedirect() 호출 코드 추가
4.7 서블릿 초기화 매개변수
서블릿 초기화 매개변수란? 서블릿을 생성하고 초기화할 때, 즉 init()를 호출할 때 서블릿 컨테이너가 전달하는 데이터입니다. 보통 데이터베이스 연결 정보와 같은 정적인 데이터를 서블릿에 전달할 때 사용합니다.
서블릿 초기화 매개변수는 DD파일(web.xml)의 서블릿 배치 정보에 설정할 수 있고, 애노테이션을 사용하여 서블릿 소스 코드에 설정할 수 있습니다. 가능한 소스 코드에서 분리하여 외부 파일에 두는 것을 추천하는데 이는 외부 파일에 두면 변경하기 쉽기 때문입니다.(실무에서도 외부 파일에 둠)
4.7.1 회원 정보 조회와 변경
회원의 상세 정보를 조회하고 값을 변경하는 서블릿 작성. 또한 서블릿 초기화 매개변수를 이용하여 소스 코드에 박혀 있던 데이터베이스 연결 정보를 web.xml 파일로 옮기겠습니다.
4.7.2 회원 목록 페이지에 상세 정보 링크 추가
상세 정보를 조회하려면 회원 번호가 필요하므로 이 링크에 ‘no’ 매개변수를 포함시킵니다.
웹 브라우저에서 회원 목록 페이지를 출력하면 각 회원 이름에 링크가 걸려있는 것을 확인할 수 있다.
4.7.3 DD파일에 서블릿 초기화 매개변수 설정
<?xml version="1.0" encoding="UTF-8"?>
xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0"> <display-name>web04</display-name>
<!-- 서블릿 선언 --> <servlet> <servlet-name>MemberUpdateServlet</servlet-name> <servlet-class>spms.servlets.MemberUpdateServlet</servlet-class> <init-param> <param-name>driver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </init-param> <init-param> <param-name>url</param-name> <param-value>jdbc:mysql://localhost/sample</param-value> </init-param> <init-param> <param-name>username</param-name> <param-value>root</param-value> </init-param> <init-param> <param-name>password</param-name> <param-value>mysql</param-value> </init-param> </servlet>
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app> |
Web.xml의 내용 중에 <init-param>가 서블릿 초기화 매개변수를 설정하는 태그입니다. 이 엘리먼트(태그)는 <servlet>의 자식 엘리먼트입니다. <init-param>에도 두 개의 자식 엘리먼트가 있는데, <param-name>에는 매개변수의 이름을 지정하고, <param-value>에는 매개변수의 값을 지정합니다.
서블릿 초기화 매개변수를 설정하는 엘리먼트
<init-param>
<param-name>매개변수 이름</param-name>
<param-value>매개변수 값</param-value>
</init-param>
예제처럼 소스 파일 밖에 DB 정보를 두면 나중에 DB정보가 바뀌더라도 web.xml만 편집하면 되기 때문에 유지보수가 쉬워집니다. 실무에서도 이처럼 변경되기 쉬운 값들은 XML 파일(.xml)이나 프로퍼티 파일(.properties)에 두어 관리합니다.
4.7.4 회원 상세 정보 출력하는 서블릿 작성
Spms.servlets 패키지에 MemberUpdateServlet 클래스 작성
초기화 매개변수를 이용하여 JDBC 드라이버 로딩
이전 방식
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
현재 방식
Class.forName()을 사용하여 JDBC 드라이버 클래스, 즉 java.sql.Driver를 구현한 클래스를 로딩합니다.
Class.forName()은 인자값으로 클래스 이름을 넘기면 해당 클래스를 찾아 로딩합니다. 클래스 이름은 반드시 패키지 이름을 포함해야 합니다. 보통 영어로 ‘fully qualified name’ 또는 ‘QName’이라고 합니다. 자바 API에서 많이 등장하는 용어이니 반드시 기억하기
서블릿 초기화 매개변수의 값 꺼내기
This.getInitParameter(/* 매개변수 이름 */)
getInitParameter()는 해당 서블릿의 배치 정보가 있는 web.xml로부터 <init-param>의 매개변수의 값을 꺼내 줍니다. getInitParameter()가 반환하는 값은 문자열입니다.
서블릿 초기화 매개변수를 꺼내는 getInitParameter() 메서드
This.getInitParameter(“driver”) ---à com.mysql.jdbc.Driver
서블릿 초기화 매개변수를 이용한 JDBC 드라이버 클래스의 로딩
Class.forName(this.getInitParameter(“driver”));
자바 소스 코드 안에 JDBC 드라이버 클래스를 직접 작성하는 것보다. 이렇게 초기화 매개변수를 이용하는 방식이 유지 보수하기 훨씬 낫습니다. 드라이버를 바꾸고 싶을 때는 간단히 web.xml만 변경하면 됩니다.
초기화 매개변수를 이용하여 데이터베이스 연결
데이터베이스에 연결할 때도 예제와 같이 초기화 매개변수를 이용합니다.
conn = DriverManager.getConnection(this.getInitParameter("url"), this.getInitParameter("username"),
this.getInitParameter("password"));
회원 상세 정보 질의
요청 매개변수로 넘어온 회원 번호를 가지고 회원 정보를 질의합니다.
rs = stmt.executeQuery(
"select mno,email,mname,cre_Date from members where mno=" + request.getParameter("no"));
rs.next();
단 한 명의 회원 정보를 가져오기 때문에 next()를 한 번만 호출합니다.
회원 번호의 입력상자는 readonly 속성을 추가하여 읽기 전용으로 설정하였습니다. readonly 속성은 값 없이 속성 이름만 추가해도 됩니다.
번호: <input type=’text’ name=’no’ value=’1’ readonly<br>
‘취소’ 버튼을 눌렀을 때 회원 목록 페이지로 이동하고자 onclick 속성에 자바스크립트를 넣었습니다.
<input type=’button’ value=’취소’ onclick=’location.href=”list”’>
Location은 웹 브라우저의 페이지 이동을 관리하는 자바스크립트 객체입니다. href 프로퍼티는 웹 브라우저가 출력할 페이지의 URL을 설정합니다. 즉 ‘취소’ 버튼을 누르면 /member/list 요청을 발생시킵니다.
<form> 태그의 action 값은 회원 상세 페이지와 동일한 URL입니다. 즉 입력폼에서 ‘저장’ 버튼을 누르면 현재 페이지와 동일한 /member/update로 요청합니다. 다만, method가 ‘post’이기 때문에 서버에 요청할 때 POST 요청을 보냅니다.
회원 상세 정보 페이지 테스트
4.7.5 회원 정보 변경하기
4.7.6 애노테이션으로 서블릿 초기화 매개변수 설정
언제라도 바뀔 수 있는 정보는 소스 파일이 아닌 외부 파일에 두어야 합니다. 그렇게 해야 개발자가 아닌 시스템 운영자도 변경할 수 있습니다.
4.8 컨텍스트 초기화 매개변수
JDBC 드라이버와 데이터베이스 연결 정보에 대한 초기화 매개변수를 각 서블릿마다 별도로 설정해줘야 합니다. 그러나 여러 서블릿이 사용하는 JDBC 드라이버와 DB 연결 정보가 같다면, 각각의 서블릿마다 초기화 매개변수를 선언하는 것이 매우 번거롭고 낭비적이기 때문에 이런 경우에 컨텍스트 초기화 매개변수를 사용합니다.
<web.xml>
<!-- 컨텍스트 매개변수 -->
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost/sample</param-value>
</context-param>
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>mysql</param-value>
</context-param>
<*.java>
ServletContext sc = this.getServletContext(); Class.forName(sc.getInitParameter("driver")); conn = DriverManager.getConnection(sc.getInitParameter("url"), // JDBC URL sc.getInitParameter("username"), // DBMS 사용자 아이디 sc.getInitParameter("password")); // DBMS 사용자 암호 |
this -> sc로 변경
실력 향상 과제
1. MemberAddServlet과 MemberListServlet도 컨텍스트 초기화 매개변수를 사용하는 것으로 바꾸시오.
l 컨텍스트 초기화 매개변수의 선언[web.xml]
ß 컨텍스트 매개변수 à <context-param> <param-name> <param-value> </context-param>
l 컨텍스트 초기화 매개변수의 사용
ServletContext sc = this.getServletContext(); Class.forName(sc.getInitParameter("driver")); conn = DriverManager.getConnection(sc.getInitParameter("url"), // JDBC URL sc.getInitParameter("username"), // DBMS 사용자 아이디 sc.getInitParameter("password")); // DBMS 사용자 암호 |
2. MemberListServlet의 슈퍼 클래스를 GenericServlet에서 HttpServlet으로 교체하시오. 또한 service() 대신 doGet()을 오버라이딩 하시오.
귀찮아서 안할래….
3. 회원 정보를 삭제하는 서블릿을 만드시오.
Ø 삭제요청 URL:/member/delete?no=삭제할회원번호
Ø 명함정보삭제 서블릿:MemberDeleteServlet
와 이것도 못하겠다… 난 멍청이가 분명해 ㅋㅋㅋㅋㅋㅋㅋ
우선 2가지를 수정하니까 되었음 (결국 정답을 보고 말았다는 소리 ㅇㅂㅇ..)
1) Sql문 수정
<변경전>
<변경후>
하하하 입력 매개변수의 값을 설정하지도 않고 “?”만 했으니… 가져오지 않는게 당연하죠
입력 매개변수의 값 설정
입력 매개변수는 SQL 문에서 ‘?” 문자로 표시된 입력 항목을 가리키는 말입니다. 입력 항목의 값은 SQL 문을 실행하기 전에 setXXX() 메서드를 호출하여 설정합니다.
stmt.setString(1, request.getParameter("email"));
stmt.setString(2, request.getParameter("password"));
stmt.setString(3, request.getParameter("name"));
2) doPost -> doGet으로 수정
그리고 doPost에서 doGet으로 변경하니까 됨… 왜 ? 두개의 차이점이 뭐지?
doPost로 하면 이상하게 아래와 같은 에러가 발생하고 doGet으로 하면 잘됨.
4. 다음 그림과 같이 회원 목록 페이지에서 각 항목의 끝에 삭제 링크를 추가하세요. 링크를 클릭하면 회원 정보가 삭제되고, 회원 목록 페이지를 자동으로 갱신합니다.
l MemberListServlet
n 내가 작성한 MemberListServlet
n 정답 MemberListServlet
다른거는 rs.getInt(“MNO”)를 또 넣어줬다는 것이네… 그리고 “ ‘ 잘 써야겠네
5. 다음 그림과 같이 회원 정보 변경 양식에서 삭제 버튼을 추가하세요. 삭제를 클릭하면 회원 정보를 삭제하고, 회원 목록 페이지로 이동합니다.
Anyway 삭제 잘됨
여기 삭제도 잘된다!
4.9 필터 사용하기
<에러>
nullPointerException 발생함.. 왜그러지?
<에러 해결>
init() 메서드의 매개변수인 FilterConfig 객체가 틀렸음 이 객체를 통해 필터 초기화 매개변수의 값을 꺼낼 수 있음 이 객체를 doFilter()에서 사용하기 위해 인스턴스 변수에 저장함
this.config = config;
필터는 서블릿 실행 전후에 어떤 작업을 하고자 할 때 사용하는 기술입니다. 예를 들면 클라이언트가 보낸 데이터의 암호를 해제한다거나, 서블릿이 실행되기 전에 필요한 자원을 미리 준비한다거나, 서블릿 실행될때마다 로그를 남긴다거나 하는 작업을 필터를 통해 처리할 수 있습니다.
4.9.1 필터
getParameter()를 호출하면 한글이 깨지는 것을 방지하기 위해 아래와 같이 작성
request.setCharacterEncoding(“UTF-8”);
각 서블릿 마다 앞의 코드를 작성하는 것은 매우 번거로운 일입니다.
4.9.2 필터 만들기
매개변수의 문자집합을 설정하는 필터를 작성하겠습니다. 모든 클라이언트 요청에 대해 필터가 적용되도록 설정해 보겠습니다. 필터 클래스는 다음 그림과 같이 javax.servlet.Filter 인터페이스를 구현해야 합니다.
Spms.filters 패키지를 만들고 CharacterEncodingFilter 클래스를 생성합니다.
Init()
Init() 메서드는 필터 객체가 생성되고 나서 준비 작업을 위해 딱 한번 호출됩니다. 이 메서드의 매개변수는 FilterConfig 객체입니다. 이 객체를 통해 필터 초기화 매개변수의 값을 꺼낼 수 있습니다. 예제 코드를 보면 이 객체를 doFilter()에서 사용하기 위해 인스턴스 변수에 저장하였습니다.
this.config = config;
doFilter()
필터와 연결된 URL에 대해 요청이 들어오면 doFilter()가 항상 호출됩니다. 이 메서드에 필터가 할 일을 작성하면 됩니다.
1) nextFilter 매개변수는 다음 필터를 가리킵니다.
2) nextFilter.doFilter()는 다음 필터를 호출합니다. 다음 필터가 없다면 내부적으로 서블릿의 service() 메서드를 호출합니다.
3) 만약 서블릿이 실행되기 전에 처리할 작업이 있다면 nextFilter.doFilter() 호출 전에 작성해야 합니다.
4) 만약 서블릿이 실행된 후에 처리할 작업이 있다면 nextFilter.doFilter()를 호출하는 코드 다음에 작성하세요.
예제 소스에서는 서블릿이 실행되기 전에 메시지 바디의 문자 집합을 먼저 설정하기 위하여 nextFilter.doFilter()를 호출하기 전에 setCharacterEncodig()를 먼저 호출하였습니다.
4.9.3 필터의 구동
1) 서블릿 컨테이너는 웹 애플리케이션을 시작할 때 배치기술서(web.xml)에 등록된 필터의 인스턴스를 생성합니다.
2) 또한 생성된 필터에 대해 준비작업을 할 수 있도록 init()를 호출합니다.
3) 클라이언트의 요청이 들어오면 그 요청에 해당하는 필터의 doFilter() 메서드를 호출합니다.
4) doFilter()에서는 필터로서 해야 할 작업을 실행하고 다음 필터의 doFilter() 메서드를 호출합니다. 마지막 필터까지 이 과정을 반복합니다.
5) 마지막 필터는 내부적으로 서블릿의 service()를 호출합니다.
6) 서블릿의 service() 호출이 끝나면 service()를 호출했던 이전 필터로 돌아갑니다. 이런 식으로 반복해서 제일 처음 호출되었던 필터까지 돌아갑니다.
7) 마지막으로 클라이언트에게 응답 결과를 보냅니다.
doFilter() 호출 -> doFilter() -> doFilter() -> service()
필터의 적용 사례
사전 작업
- 문자집합 설정
- 압축해제
- 암호화된 데이터 복원
- 로그 작성
- 사용자 검증
- 사용권한 확인
è nextFilter.doFilter()
사후 작업
- 응답 데이터 압축
- 응답 데이터 암호화
- 데이터 형식 변환
4.9.4 필터의 배치
필터의 배치 방법도 서블릿과 마찬가지로 두 가지가 있습니다. DD 파일에 기술하는 방법과 애노테이션으로 기술하는 방법이 있습니다.
*.java에서 setCharacterEncoding() 호출 부분 제거
//request.setCharacterEncoding(“UTF-8”);
애노테이션을 이용한 필터 배치
CharacterEncodingFilter 클래스에 @WebFilter 애노테이션을 작성합니다.
4.10 정리
JDBC API를 이용하여 데이터베이스에 연결하고, 질의하고, 결과를 가져오는 서블릿을 만들어봄
또한, HTTP 프로토콜의 요청 형식 중에서 GET과 POST 요청을 다루는 방법에 대해서도 알아보았습니다. 데이터 조회나 삭제 요청처럼 간단한 데이터를 보내는 경우에는 GET 방식이 적합하고, 데이터 등록이나 변경, 로그인과 같으 대량의 데이터를 보내거나 브라우저의 주소창에 노출되지 말아야 할 데이터를 보내는 경우에는 POST 방식이 적합합니다.
특히 HttpServet을 상속받아서 서블릿을 만들면 GET과 POST 요청을 다룬느 방법에 대해서도 알아보았습니다.
클라이언트에서 보낸 한글 데이터가 깨지는 것을 경험했는데 이것은 서블릿에서 데이터를 꺼낼 때 클라이언트가 보낸 데이터를 ISO-8859-1이라 가정하고 유니코드로 변환하기 때문입니다. 이를 교정하려면 getParameter()를 호출하여 데이터를 꺼내기 전에 setCharacterEncoding()을 호출하여 클라이언트가 보낸 데이터가 UTF-8임을 먼저 지정해야 합니다.
자동으로 회원 목록 화면으로 가는 방법을 배웠습니다. 리프래시 하는 방법과 리다이렉트 하는 방법이 있습니다. 리프래시는 응답 헤더에 Refresh를 설정하거나, HTML meta 태그로 설정할 수 있습니다. 리다이렉트는 ServletResponse의 sendRedirect()를 호출하면 됩니다. 리프래시와 리다이렉트의 차이점은 리프래시는 본문을 보낸다는 것이고, 리다이렉트는 본문을 보내지 않는다는 것입니다.
여러 서블릿에서 공통으로 사용할 정보라면 컨텍스트 초기화 매개변수를 사용하세요.
서블릿을 실행하기 전이나 후에 특별한 작업을 수행해야 한다면 필터를 사용하세요. 매개변수의 문자 집합을 지정하는데 필터를 사용하면 서블릿마다 코드를 작성할 필요가 없어 매우 편리합니다.
'JAVA > spring' 카테고리의 다른 글
Mybatis Log Setting (0) | 2020.06.16 |
---|---|
MVC Architecture (0) | 2020.02.24 |
Servlet Programming (0) | 2020.01.09 |
Web programming (0) | 2020.01.03 |
Web Applications (0) | 2020.01.02 |
댓글