Dispatcher Servlet 이전에는 하나의 jsp마다 Servlet을 만들어 사용했다. 그러한 번거로움을 해결하기 위해 Dispatcher Servlet을 이용하는 법을 배웠다. (맞나?)
<파일/폴더 만들기>
아래의 파일과 폴더들을 만들 것이다. 이 포스팅의 handler.member 에서는 join만 할테니 나머지는 혼자 해보도록 하십쇼~ 포항항
Java Resources - src/main/java - handler / handler.member / req
src - main - webapp - member
src - main - webapp - web-inf - lib - commands.properties
Handler.java
Handler.java 는 interface이다. 앞으로 이 인터페이스는 모든 요청 처리 클래스의 부모가 될 것이다.
public interface Handler {
String process(HttpServletRequest request, HttpServletResponse response);
}
commends.properties
일반 File 로 생성한다. 이 문자열을 Dispatcher 에서 읽어와 링크로 만들어줄 것이다.
/member/join.do=handler.member.JoinHandler
/member/login.do=handler.member.LoginHandler
/member/edit.do=handler.member.EditHandler
/member/out.do=handler.member.OutHandler
/member/logout.do=handler.member.LogoutHandler
DispatcherServlet
디스패처 서블릿의 주소는 "*.do"이다. *는 전체를 의미한다. 즉, 뷰 페이지 요청을 제외한 모든 요청이 여기로 올 것이다.
private Map<String, Handler> map = new HashMap<>(); | commands.properties 파일의 login을 예시로 들면 String : /member/login.do Handler : handler.member.LoginHandler 이렇게 map 에 키로 저장한다. |
@WebServlet("*.do") // 뷰 페이지 요청을 제외한 모든 요청이 여기로 온다.
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String, Handler> map = new HashMap<>();
init 은 source - override/implement Methods - general Servlet - init 을 체크해서 자동으로 생성할 수 있다.
init의 최종 목적은 map.put이다. 나는 properties의 문장들의 왼쪽을 버튼, 오른쪽을 버튼을 누르면 나오는 값(경로) 로 만들어주는 과정이라고 생각했다. 때문에 서블릿 생성 시 (서버에 처음 올라갔을 때 ) 딱 한번 실행되는 메소드이다.
Properties prop = new Properties(); | properties에 (키, 값) / (키, 값) ,Map 방식으로 저장된다. |
String path = this.getServletContext().getRealPath("WEB-INF/commands.properties"); | 파라메터에 있는 경로를 웹에서 실제로 사용하는 경로로 변환한다. |
try { prop.load(new FileReader(path)); |
commands.properties 파일의 (키, 값)을 불러내 prop에 저장한다. |
Iterator iter = prop.keySet().iterator(); | prop.keySet().iterator() 문법을 이용해 변수 iter에 (properties 파일의 키값)을 저장한다. |
while (iter.hasNext()) { | iter.이 다음 값을 가질때까지 (변수 iter에 값이 있을때까지) |
String url = (String) iter.next(); | /member/login.do |
String className = prop.getProperty(url); System.out.println(url + "/" + className); |
key 값을 이용해서 value 값을 뽑아낸다. getProperty : url(key)의 value를 뽑음 |
try { Class<?> handlerClass = Class.forName(className); Constructor<?> cons = handlerClass.getConstructor(null); Handler handler = (Handler) cons.newInstance(); |
String으로 나온 value 값을 handler로 변환하기 위한 과정이다. 1. 지정한 클래스 정보를 갖는 Class 객체를 반환한다. 2. 지정한 클래스의 객체를 생성할 생성자를 반환한다. 3. handler 객체를 생성한다. // => JoinHandler h = new JoinHandler(); //위 세줄이 이 한줄과 동일 , 프로그램에 의해서 객체가 생성되기 때문에 복잡하게 써야함. |
map.put(url, handler); //생성한 객체를 값으로 저장 | 생성한 handler 객체를 url과 같이 map에 저장한다. 즉 이제 url 버튼을 누르면 해당하는 handler로 연결된다고 보면 된다. |
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
Properties prop = new Properties();
String path = this.getServletContext().getRealPath("WEB-INF/commands.properties");
try {
prop.load(new FileReader(path));
Iterator iter = prop.keySet().iterator();
while (iter.hasNext()) {
String url = (String) iter.next();
String className = prop.getProperty(url);
System.out.println(url + "/" + className);
try {
Class<?> handlerClass = Class.forName(className); //정보 수집
Constructor<?> cons = handlerClass.getConstructor(null); //생성자 가져옴
Handler handler = (Handler) cons.newInstance(); //객체 생성
// => JoinHandler h = new JoinHandler(); //위 세줄이 이 한줄과 동일 , 프로그램에 의해서 객체가 생성되기 때문에 복잡하게 써야함.
map.put(url, handler); //생성한 객체를 값으로 저장
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
위에서 properties 파일을 실제 움직이는 경로로 만들었다면, 이제 실제 서블릿 역할의 doGet을 만들것이다.
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { |
핸들러는 handler 인터페이스를 상속받는다. |
String url = request.getServletPath(); String view = null; |
index의 로그인을 누르면 /member/login.do , 즉 loginHandler을 불러온다. |
Handler handler = map.get(url); | map.get에 url (키 값)을 넣어 handler 값 (키에 해당하는 값)을 받아온다. |
if(handler != null) { view = handler.process(request, response); |
handler에 값이 있으면 해당하는 핸들러로 가서 실행하고, 값을 받아온다. 결국 받아온 값은 view가 된다. (밑의 각 handler 참고) |
if(view.startsWith("redirect")) { String[] path = view.split(":"); response.sendRedirect(request.getContextPath()+path[1]); } |
만약 view가 redirect로 시작한다면 sendRedirect로 이동하고 |
else { RequestDispatcher dis = request.getRequestDispatcher(view); dis.forward(request, response); } |
만약 redirect로 시작하지 않는다면 forward로 이동한다. |
} else { response.getWriter().append("404 not found url"); } |
handler에 값이 없으면 404 not found url을 표시한다. |
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
String url = request.getServletPath();
String view = null;
Handler handler = map.get(url);
if(handler != null) {
view = handler.process(request, response);
if(view.startsWith("redirect")) {
String[] path = view.split(":");
response.sendRedirect(request.getContextPath()+path[1]);
}else {
RequestDispatcher dis = request.getRequestDispatcher(view);
dis.forward(request, response);
}
} else {
response.getWriter().append("404 not found url");
}
}
Index.jsp
인덱스에는 회원가입, 로그인, 내정보확인, 로그아웃, 탈퇴 등이 있다.
아래 접은글은 인덱스 코드 힌트이다. 필요한사람만 보세요. 그냥 보세요.
힌트
내정보확인, 로그아웃, 탈퇴에서는 현재 로그인된 아이디를 받아와야한다.
<a href="${pageContext.request.contextPath }/member/join.do">회원가입</a>
join/do -> joinHandler 등장~
String view = ""; if(request.getMethod().equals("GET")) { view = "/member/join.jsp"; } |
만약 보내진 방식이 get이면 view 의 경로는 /member/join.jsp로 이동한다. Dispatcher에 따르면 forward 방식으로 이동한다. |
else { String id = request.getParameter("id"); String pwd = request.getParameter("pwd"); String name = request.getParameter("name"); String email = request.getParameter("email"); MemberService service = new MemberService(); service.join((new MemberVo(id,pwd,name,email))); view = "redirect:/index.jsp"; } |
보내진 방식이 post 이면 id,pwd,name, email 받아와서 service 객체를 생성한다. view 는 redirect가 된다. 즉, Dispatcher에 따르면 redirect 방식으로 이동한다. " : " 은 문자를 나눠, redirect : 0번방, index,jsp : 1번방 |
return view ; | 둘 중에 나온 view 를 보내준다. |
@Override
public String process(HttpServletRequest request, HttpServletResponse response) {
//요청 처리에 사용할 req, res를 파람으로 받고, 결과 페이지 경로를 리턴 값으로 반환
// TODO Auto-generated method stub
String view = "";
if(request.getMethod().equals("GET")) {
view = "/member/join.jsp";
} else {
String id = request.getParameter("id");
String pwd = request.getParameter("pwd");
String name = request.getParameter("name");
String email = request.getParameter("email");
MemberService service = new MemberService();
service.join((new MemberVo(id,pwd,name,email)));
view = "redirect:/index.jsp";
}
return view ;
}