Notice
Recent Posts
Recent Comments
Link
«   2026/03   »
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
Tags
more
Archives
Today
Total
관리 메뉴

brograming

DispatcherServlet / commandHandler / commands.properties_Day38 본문

Kosta

DispatcherServlet / commandHandler / commands.properties_Day38

brograming 2023. 4. 11. 14:36

<커맨드 기반 개발 패턴>

1. DispatcherServlet을 서블릿 파일로 생성. 이 파일이 모든 요청을 받는다. url을 " *.do"로 등록했기 때문에 요청 url이 .do로 끝나면 이 요청들은 DispatcherServlet 이 받는다.

 

이 파일은 요청을 받고 처리함

요청을 실제 처리하는 클래스는 ~Handler라는 일반 클래스 들이다.

각 핸들러 클래스는 요청을 처리하고 뷰페이지 경로를 반환한다.

 

이 핸들러 클래스들을 /WEB-INF/commands.properties 파일에 url과 함께 등록해야 한다.

 

DispatcherServlet의 init() : 이 페이지가 서버에 처음 올라갈때 한번만 실행된다. commands.properties파일의 데이터를 하나씩 읽어서 맵에 저장. 맵 : private Map<String, Handler> map = new HashMap<>(); 

Handler : 값의 타입이 Handler

map.put("/member/Join.do", new JoinHandler());  맵에 이런 형태로 들어간다.

 

Handler handle = map.get("/member/login.do");

 

String view = handle.process(req, res);

 

/member/a.jsp  => forward로 이동redirect/member/a.jsp => sendredirect로 이동

 

 

 

index.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
${sessionScope.loginId }<br/>
<a href = "${pageContext.request.contextPath }/member/Join.do">회원가입</a>
<a href = "${pageContext.request.contextPath }/member/login.do">로그인</a>
<a href = "${pageContext.request.contextPath }/member/edit.do?id=${sessionScope.loginId}">내정보확인</a>
<a href = "${pageContext.request.contextPath }/member/logout.do">로그아웃</a>
<a href = "${pageContext.request.contextPath }/member/out.do?id=${sessionScope.loginId}">탈퇴</a>
</body>
</html>

회원가입 ~ 탈퇴 모두 ~.do로 끝나기 때문에 DispatcherServlet.java로 이동

 

 

 

DispatcherServlet.java

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package req;
 
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
 
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import handler.Handler;
import handler.member.JoinHandler;
import handler.member.LoginHandler;
 
 
/**
 * Servlet implementation class DispatcherServlet
 */
@WebServlet("*.do")//뷰 페이지 요청을 제외한 .do로 끝나는 모든 요청이 여기로 온다.
public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    //commands.properties 파일의 url과 명령어를 읽어서 저장할 map
    private Map<String, Handler> map = new HashMap<>(); //map의 키와 값을 저장. Handler에 Handler타입의 객체를 생성해서 집어넣음. process 호출???
       
 
    // init : 서블릿 생성시 딱 한번 실행되는 메서드.
    @Override
    public void init() throws ServletException {
        // TODO Auto-generated method stub
        Properties prop = new Properties(); 
        //Properties 클래스는 Hashtables의 하위 클래스이다.
        //Hashtables를 상속 받았기 때문에 Map의 속성 즉, Key와 Value를 갖는다
        //HashMap과 큰 차이가 없지만, Properties 클래스는 파일 입출력을 지원한다.
        //key=value 형식으로 작성 된 파일을 key와 value 로 나누어 저장할 때 유용하다.
        
        
        //파라미터 경로를 웹에서 사용하는 실제 경로로 변환
        String path = this.getServletContext().getRealPath("/WEB-INF/commands.properties"); // 실제 경로를 가지고옴
        try {
            //commands.properties 파일의 키, 값을 로드해서 prop에 저장
            prop.load(new FileReader(path));  //load() : 메서드의 이름 그대로 파일의 내용을 읽어서 키-값의 형태로 분류해서 맵에 보관
            Iterator iter = prop.keySet().iterator();
            
            while(iter.hasNext()) {
                String url = (String) iter.next(); // 키 뽑아내기 /member/Join.do
                String className = prop.getProperty(url); //getProperty(url) : 키로 값 뽑아내기.  handler.member.JoinHandler  
                //className 을 객체로 생성해야 process
                System.out.println(url + " / " + className); //확인용 출력
                
                //Class : 클래스에 대한 정보 갖음. 클래스 이름, 멤버변수이름/타입, 메서드 이름/프로토타입
                try {
                    //지정한 클래스 정보를 갖는 Class 객체 반환
                    Class<?> handlerClass = Class.forName(className);  //Class.forName(className) : className 클래스이 정보를 가져온다.
                    //지정한 클래스의 객체를 생성할 생성자 반환
                    Constructor<?> cons = handlerClass.getConstructor(null); //생성자 가져오기
                    //handler 객체 생성
                    Handler handler = (Handler) cons.newInstance();
                    //생성한 handler객체를 url과 같이 map에 저장
                    
//                    Handler handler = new JoinHandler(); 바로 위 3줄과 같다.
                    map.put(url,handler); // 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();
        }
    }
    
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DispatcherServlet() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        //요청을 처리한 뒤 뷰페이지 이동
        String url = request.getServletPath(); // 요청 url반환. get방식일때'?name='빼고 path만 줌
//        response.getWriter().append("url : " + url); // url : /*.do
        System.out.println(url);
//        // 주소창에 http://localhost:8787/FileDown/join.do치면 url : /join.do 출력
//        // ~~ .do로 끝나면 다 여기로 요청이 들어온다.
//        
        
//        Handler handler = null; // 인터페이스로 선언.인터페이스 타입. 업캐스팅
        Handler handler = map.get(url); //업캐스팅. 맵은 값을 키로 접근한다.공통으로 상속받은 Handler로 업캐스팅
       //commands.properties에 등록되지 않은 url이면 handler에 null 반환.
        String view = null;
        if(handler != null) {
        view = handler.process(request, response);
        if(view.startsWith("redirect")) {
            String[] path = view.split(":"); //split() : String클래스에서 제공하는 메서드
            response.sendRedirect(request.getContextPath()+path[1]);
        } else {
            RequestDispatcher dis = request.getRequestDispatcher(view);
            dis.forward(request, response);
        }
        }else { // handler가 null이면 등록되지 않은 url
            response.getWriter().append("404 not found url");
        }
        
        //init에서 작업하기 때문에 더이상 필요 없다.
//        if(url.startsWith("/member/Join.do")) { // startsWith : 앞에 이 값으로 시작하냐? /member/Join.do?abcd이렇게 해도 true반환
//            handler = new JoinHandler();
//        }else if(url.startsWith("/member/login.do")) {
//            handler = new LoginHandler();
//        }
        
        
//        if(handler == null) {
//            response.getWriter().append("404 not found null");
//        }else {
//        //요청 처리 메서드 호출
//         view = handler.process(request,response); //결과페이지 url반환
//         
//         if(view.startsWith("redirect")) {
//             String [] path = view.split(":");
//             response.sendRedirect(request.getContextPath()+path[1]);
//         }else {
//        RequestDispatcher dis = request.getRequestDispatcher(view);
//        dis.forward(request, response);
//        }
//        }
    
        }
 
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
 
 
}
cs

28줄 : @WebServlet("*.do") // 뷰 페이지 요청을 제외한 ~.do로 끝나는 모든 요청이 여기로 온다.

 

55줄 :String url = (String) iter.next();

 반복자(Iterator)에서 뽑은 요소는 object로 반환되기 때문에 url에 String으로 형변환하여 초기화 해준다.

 

 

commands.properties

/member/Join.do=handler.member.JoinHandler
/member/login.do=handler.member.LoginHandler
/member/edit.do=handler.member.EditHandler
/member/logout.do=handler.member.LogoutHandler
/member/out.do=handler.member.OutHandler

58줄 : System.out.println(url + " / " + className);

1. properites 객체 생성.

2. key=value 형식으로 작성 된 commands.properties의 경로를 path 에 저장

3. prop.load() : 파일의 내용을 읽어서 키-값의 형태로 분류해서 맵에 보관

4. keySet() : prop의 전체 key를 꺼내 반복자 변수 iter에 담는다.

5. iter.hasNext() : 다음 요소 있으면 true, 아니면 false반환. iter의 마지막 데이터까지 반복한다.

6. iter.next() : 다음 요소 추출 / 키 뽑아내서 변수 url에 담는다. 

7. prop.getProperty(url) : url(key)를 찾아 해당 값을 변수 className에 저장한다.

8. ▼ System.out.println(url + " / " + className);  출력 결과

/member/out.do / handler.member.OutHandler

/member/edit.do / handler.member.EditHandler

/member/login.do / handler.member.LoginHandler

/member/logout.do / handler.member.LogoutHandler

/member/Join.do / handler.member.JoinHandler

63~67줄 : 생성자를 이용한 인스턴스 생성

63줄 : Class<?> handlerClass = Class.forName(className);

// 로딩단계(클래스조사),메모리에 올라오지는 않음(newInstance()해줘야함)

forName() : 물리적인 클래스 파일명을 인자로 넣어주면 이에 해당하는 클래스를 반환. 클래스를 조사하기 위한 클래스

                    Class.forName(className) : className 클래스의 정보를 가져온다.

<?> : 메소드 매개변수의 자료형에 사용되는 제네릭. <? extends Object>의 줄임 표현.

         어떤 자료형의 객체도 매개변수로 받겠다는 의미. 모든 클래스나 인터페이스 타입이 올 수 있다.

 

65줄 : Constructor<?> cons = handlerClass.getConstructor(null);

//지정한 클래스의 객체를 생성할 생성자 반환

 

67줄 : Handler handler = (Handler) cons.newInstance();

// 생성자로 인스턴스 만들기

 

121줄 : System.out.println(url);  // 회원가입 클릭 시 /member/Join.do 출력

 

131줄 : view = handler.process(request, response);

             JoinHandler의 process메서드 실행. 링크로 이동했으니 Method = GET.

136줄 :  view = /member/Join.jsp를 가지고 DispatcherServlet이동. 그리고  /member/Join.jsp로 이동

             가입 폼 입력 후 submit method = POST방식으로 JoinHandler로 다시 이동.

              view = "redirect : /index.jsp"를 가지고 DispatcherServlet이동

134줄 :  /index.jsp로 이동

 

133줄 : split() : String클래스에서 제공하는 메서드. 문자열을 배열로 반환한다.

String [ ] str = "aa:bb:cc".split(":");

str[0] // "aa"

str[1] // "bb"

str[2] // "cc"

            

 

JoinHandler.java

package handler.member;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import handler.Handler;
import member.MemberService;
import member.MemberVo;

public class JoinHandler implements Handler {

	@Override
	public String process(HttpServletRequest request, HttpServletResponse response) {
		// 요청 처리에 사용할 req, res를 파라미터로 받고, 결과페이지 경로를 리턴값으로 반환
		// String은 뷰페이지(결과페이지) 경로를 String으로 반환
		// TODO Auto-generated method stub
//		System.out.println("get방식(으로 왔다 치고) 요청 처리");
		
		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;
	}

}

request.getMethod() : 요청방식(GET / POST)값 반환

 

 

 

Handler.java(interface)

package handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//모든 요청 처리 클래스의 부모
public interface Handler { //파라미터로 request, response 받음
	String process(HttpServletRequest request, HttpServletResponse response);//반환타입 string
   // url을 반환해야 하기 때문에 반환 타입을 String으로 설정
}

 

/member/Join.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
<script>
function idcheck(){
	//윈도우 창 객체 만들기
	let wid = open('idcheck.jsp', 'win','width = 400, height = 400, top = 300, left = 300')  // open() : 새 창 띄우기
	//open(param1, param2, param3). param1: 이 창에 띄울 페이지, param2 : 새 윈도우 이름, param3 : 윈도우 설정
	// 윈도우 설정 : width, height, top :수직위치, left:수평위치, ...
	//리던값 : 생성된 윈도우 객체.
	win.document.ff.id.value = f.id.value;
	//win.close(); //팝업창 닫음
}
</script>
</head>
<body>
<h3>회원가입</h3>
	${pageContext.request.contextPath }  : 이 프로젝트의 기본 경로
	<form action = "${pageContext.request.contextPath }/member/Join.do" method = "post" name = "f" > 
		<table border = "1">
			<tr><th> ID</th>
			<td>
				<input type = "text" name = "id">
				<input type = "button" value = "id중복체크" onclick = "idcheck()">
			</td></tr>
			<tr><th> PWD</th><td><input type = "password" name = "pwd"></td></tr>
			<tr><th> NAME</th><td><input type = "text" name = "name"></td></tr>
			<tr><th> EMAIL</th><td><input type = "text" name = "email"></td></tr>
			<tr><th> 가입</th><td><input type = "submit" name = "가입"></td></tr>

		</table>
	</form>

</body>
</html>

<form action = "${pageContext.request.contextPath }/member/Join.do" method = "post" name = "f" > 

요청 .do로 끝나면 모두 DispatcherServlet으로 간다.

 

<스프링에서의 컨트롤러>

Handler(inderface)

String process(req, res);

                   ↑

JoinHandler implements Handler

 

 

class상속은 browse, interface는 add

Handler.java - interface 로 만들기

JoinHandler.java - class로 만들기, implements Handler

LoginHandler.java - class로 만들기, implements Handler

EditHandler.java - class로 만들기, implements Handler
LogoutHandler.java - class로 만들기, implements Handler
OutHandler.java - class로 만들기, implements Handler

 

 

 

직접 요청을 받는 것은 DispatcherServlet이다.

'Kosta' 카테고리의 다른 글

Properties. load(), keySet()  (0) 2023.04.11
CommandHandler_Day38  (0) 2023.04.11
파일 다운로드_Day38  (0) 2023.04.11
로그인(세션과 쿠키)_Day31, Day32  (0) 2023.03.30
[JAVASCRIPT]_Day25  (0) 2023.03.22