화면 동작 구조
- 우선 구현하려는 웹페이지의 동작 구조를 다시 한번 확인해보겠습니다.
- 회원가입에 대한 요청을 '*.do' URL로 요청하면 Controller 클래스가 응답합니다.
- Controller 클래스는 요청 정보를 Action 클래스에 넘겨줍니다.
- Action 클래스는 요청 정보를 이용해 DB에 데이터를 요청합니다.
- Action 클래스는 처리된 데이터를 View 페이지에 전달합니다.
- View 페이지는 데이터를 이용해 화면을 구현합니다.
web.xml
- 위의 과정을 거치기 위해서 우선 '*.do'로 URI 요청이 오는 경우 일차적으로 Controller가 응답할 수 있도록
Controller 서블릿 클래스와 URL을 매핑하는 과정을 거칩니다.
- WEB-INF 폴더 아래 web.xml에 다음과 같이 정보를 입력합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>MemberController</servlet-name>
<servlet-class>jsp.front.controller.MemberController</servlet-class>
<init-param>
<param-name>propertyConfig</param-name>
<param-value>(절대경로)\JSP_BOARD\src\jsp\member\properties\member.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>MemberController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
|
cs |
- '*.do'로 오는 모든 요청은 MemberController를 거칩니다.
- member.properties 정보를 파라미터 값으로 전달해주기 위해 6~9번째 줄을 작성합니다.
- value값을 입력할 때는 현재 프로젝트의 절대 경로를 입력합니다.
member.properties
1
2
3
4
5
6
7
8
|
# Form Change
main.do=jsp.member.action.MemberFormChangeAction
SignUpForm.do=jsp.member.action.MemberFormChangeAction
# Action
MemberjoinAction.do=jsp.member.action.MemberJoinAction
|
cs |
- member.properties의 값은 위와 같이 입력해줍니다.
- 프로퍼티에서 '='을 기준으로 좌측은 key, 우측은 value입니다.
- 예를 들어 'main.do'라는 key 값에 대한 value는 'jsp.member.action.MemberFormChangeAction'이 됩니다.
- 프로퍼티를 사용하는 이유는 각각 다른 요청 URL에 ( ex) main.do, SignUpForm.do ) 대해
어떤 Action 클래스를 실행시킬지 결정하기 위함입니다.
- 만약 모든 'do' URL에 대한 요청을 컨트롤러에서 처리한다면 컨트롤러가 너무 비대해집니다.
- 컨트롤러에서는 일차적으로 'do'요청을 받아들이고
어떤 Action 클래스가 실행되어야 하는지 결정하는 역할을 합니다.
- 따라서 프로퍼티에 요청 URL 정보를 key값으로, 응답해야 하는 Action 클래스를 value값으로 설정합니다.
MemberController.java
- 모든 '*.do' 요청에 대한 일차적인 응답 객체입니다.
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
|
package jsp.front.controller;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jsp.common.action.Action;
import jsp.common.action.ActionForward;
import jsp.member.action.MemberFormChangeAction;
// 회원 관련 컨트롤러
public class MemberController extends HttpServlet {
private HashMap<String, Action> commandMap;
//명령어와 처리클래스가 매핑된 properties 파일을 읽어 Map객체에 저장
public void init(ServletConfig config) throws ServletException { //서블릿 초기화
super.init(config);
String props = config.getInitParameter("propertyConfig"); //web.xml에서 설정한 프로퍼티 name
Properties properties = new Properties(); //명령어와 처리클래스의 매핑 정보를 저장할 프로퍼티 객체
FileInputStream fis = null;
commandMap = new HashMap<String, Action>();
try {
fis = new FileInputStream(props); //member.properties 파일을 fis에 넣기
properties.load(fis); //member.properties를 properties 객체에 저장하기
} catch (IOException e) {
e.printStackTrace(); //오류 처리
} finally {
if(fis!=null) { //fis에 값이 있다면
try {
fis.close(); //FileInputStream 닫기
} catch (Exception e) {
e.printStackTrace();
}
}
}
Iterator iterator = properties.keySet().iterator(); //프로퍼티 객체의 키 값을 iterator 객체에 저장
while(iterator.hasNext()) {
String key = (String)iterator.next(); //프로퍼티 key 값
String value = properties.getProperty(key); //프로터피 value 값
System.out.println(key); ///ex) LoginForm.do
System.out.println(value); //ex) jsp.member.action.MemberFormChangeAction
try {
Class commandClass = Class.forName(value); //해당 문자열을 클래스로 만들기
Action actionInstance = (Action)commandClass.getDeclaredConstructor().newInstance(); //클래스의 객체를 생성
System.out.println(actionInstance);
if(value.equals("jsp.member.action.MemberFormChangeAction")) { //value 값이 jsp.member.action.MemberFormChangeAction이라면
MemberFormChangeAction changeAction = (MemberFormChangeAction) actionInstance; //MemberFormChangeAction 클래스 객체 생성
changeAction.setCommand(key); //changeAction 객체에 key값 세팅
}
commandMap.put(key, actionInstance); //map에 키값과 value값 객체 저장
} catch (Exception e) {
e.printStackTrace();
}
}
}
//post나 get요청이 들어오면 실행되는 메소드
protected void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String URI = null;
ActionForward forward = null;
Action action = null;
try {
URI = request.getRequestURI(); //요청한 URI를 가져오기
System.out.println(URI);// ex) /JSP_BOARD/LoginForm.do
String command = URI.substring(URI.lastIndexOf("/")+1);
System.out.println(command);// ex) LoginForm.do
/*
* 예를 들어 MemberLoginAction.do로 요청이 들어온다면
* value 값이 jsp.member.action.MemberLoginAction 객체화되어
* commandMap에 저장된다.
* 따라서 get(command)를 하면
* action = new (jsp.member.action.MemberLoginAction)이 된다.
* 즉 action = new MemberLoginAction()이 된다.
* 따라서 get이나 post 요청이 들어오면 설정된 properties에 의해
* 구현된 Action 클래스가 실행된다.
*/
action = commandMap.get(command); //command = KEY값, key값으로 value값 얻기
System.out.println(action);
if(action == null) { //properties에 해당 key값에 대한 value값 설정이 안되어있을 때
System.out.println("not found : "+command);
return;
}
forward = action.execute(request, response); //execute() 호출
String path = forward.getPath(); //경로설정
System.out.println("path "+path);
System.out.println(forward);
if(forward != null) {
if(forward.isRedirect()) {
response.sendRedirect(forward.getPath());
} else {
//getRequestDispatcher(상대경로)
RequestDispatcher dispatcher = request.getRequestDispatcher(forward.getPath());
dispatcher.forward(request, response);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
process(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
process(request, response);
}
}
|
cs |
- 컨트롤러는 서블릿을 초기화하고 properties 값에 의해 Action 객체를 인스턴스화 하는 메서드와
get요청이나 post 요청이 들어오면 실행되는 메서드로 구성됩니다.
- member.properties에 저장된 key값과 value 값을
FileInputStream을 이용해 properties 객체에 저장합니다.(27~46번째 줄)
- properties에 저장된 key값을 iterator 객체에 저장합니다.(48번째 줄)
- 해당 value값을 Action클래스의 인스턴스의 객체로 만듭니다.(55~56번째 줄)
- process 메서드를 이용해 post나 get 요청에 응답하는 메소드를 구현합니다.
- 전달받은 URI를 이용해 Action 클래스를 구현하여 동작시킵니다.(91번째 줄)
- 공통 구현 메서드인 execute()를 실행합니다.(98번째 줄)
- forward.getPath()로 경로를 가져옵니다. 경로 설정은 각 구현 Action 클래스에서 정의됩니다.(105번째 줄)
- forward.isRedirect()가 true인 경우 설정된 경로로 'redirect'합니다.
- forward.isRedirect()가 true가 아닌 경우 상대 경로를 얻어 'forward'합니다.
Action.java
1
2
3
4
5
6
7
8
|
package jsp.common.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
|
cs |
- Action 클래스입니다.
- '~~ Action'으로 들어오는 모든 요청은 Action 인터페이스를 구현해야 합니다.(다형성의 이유)
- 구현된 Action 클래스는 execute() 메서드를 통해 요청(request)과 응답(response) 객체를 전달받고
경로와 리다이렉트 정보를 가지고 있는 ActionForward 객체를 리턴하도록 정의합니다.
ActionForward.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
|
package jsp.common.action;
// 페이지 이동을 처리하기 위한 클래스
public class ActionForward {
private boolean isRedirect = false;
private String path = null; // 이동할 다음 화면
//Redirect 사용여부, false이면 Forward 사용
public boolean isRedirect() {
return isRedirect;
}
public void setRedirect(boolean isRedirect) {
this.isRedirect = isRedirect;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
|
cs |
- ActionForward 클래스입니다.
- 이동할 화면 경로 정보와 Redirect에 대한 정보를 가지고 있습니다.
MemberFormChangeAction.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
|
package jsp.member.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jsp.common.action.Action;
import jsp.common.action.ActionForward;
//Action 클래스를 구현한다.
public class MemberFormChangeAction implements Action{
//contentPage = 파라미터값
//나중에 main.jsp에서 contentPage의 값을 가져와 화면을 구성한다.
private String form = "main.jsp?contentPage=views/member/";
private String path;
public void setCommand(String command) {
int idx = command.indexOf("."); //ex) LoginForm.do에서 .의 인덱스를 가져온다.
path = command.substring(0, idx)+".jsp"; // ex) 가져온 index를 이용해 LoginForm.jsp와 같이 만든다.
System.out.println(path);
}
@Override
public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
ActionForward forward = new ActionForward();
forward.setRedirect(false);
if(path.equals("main.jsp")) { //가져온 경로가 main.jsp와 같으면
forward.setPath(path); //그대로 main.jsp를 경로로 설정한다.
System.out.println(path);
} else { //가져온 경로가 main.jsp와 다르면
forward.setPath(form+path); // ex) 'main.jsp?contentPage=view/member/LoginForm.jsp'와 같이 경로를 설정한다.
}
return forward;
}
}
|
cs |
- MemberFormChangeAction.java 클래스입니다.
- 해당 클래스에서는 MemberController 클래스에서 전달받은 key값을 이용합니다.
- 전달받은 key값을 이용해서 변경할 화면의 주소를 설정합니다.
- 해당 클래스에 의해 호출될 페이지는 request 객체와 response 객체를 공유해야 하기 때문에
forward 방식으로 동작해야 합니다. 따라서 'forward.setRedirect(false);'를 입력합니다.
흐름 정리해보기
- LoginForm.do라는 요청이 왔다고 예를 들어보겠습니다.
- web.xml에서 '*.do'로 들어오는 모든 요청은 MemberController가 처리하도록 매핑했습니다.
- MemberController에서는 우선 member.properties의 정보를 가져옵니다.
- properties의 값은 다음과 같습니다.
'LoginForm.do = jsp.member.action.MemberFormChangeAction'
여기서 왼쪽이 key, 오른쪽이 value입니다.
- value값이 MemberFormChangeAction이기 때문에
MemberController에서 MemberFormChangeAction의 setCommand(key값) 메서드를 실행합니다.
- setCommand의 실행결과 'main.jsp?contentPage=view/member/LoginForm.jsp'의 경로가 설정됩니다.
- process() 메서드에 의해 MemberFormChangeAction.java에서 설정한 경로를 가져올 수 있습니다.
- MemberFormChangeAction에서 redirect를 false로 설정했기 때문에
상대 경로를 이용해 설정한 주소 값을 넘겨줍니다.
- 'main.jsp?contentPage=view/member/LoginForm.jsp' 경로가 'LoginForm.do'로 실행됩니다.
댓글