본문 바로가기
내가 공부하려고 올리는/JSP\Servlet

[JSP 게시판] Model 2 JSP/Servlet 게시판 만들기(2) - properties/MemberController/Action/ActionForward

by 결딴력 2022. 1. 15.
반응형

화면 동작 구조

 

- 우선 구현하려는 웹페이지의 동작 구조를 다시 한번 확인해보겠습니다.

Model2 동작 구조

- 회원가입에 대한 요청을 '*.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'로 실행됩니다.

 

 

반응형

댓글