학교에서 배운 JSP 내용을 정리해둡니다.

본 내용은 일반 JSP 환경에서 Reflection Method를 이용해 API 서블릿을 만드는 방법을 남겨둔 것입니다. 
파라미터와 반환값은 JSONObject를 사용하므로, 가벼운 JSON 관련 클래스를 사용했습니다.



본 기능의 목적은 아래와 같다 .


1. 특정 서블릿의 주소로 비동기 통신 요청 

2. 요청은 서블릿으로 받는다. ex) /api/user

3. 실행할 함수는 JSON 객체로 받는다 ex) { 'fn_nm' : 'get' , 'params' : {  ... } }


위 단계를 구현하기 위해서 먼저 스트링으로 받은 함수명을 reflection method로 실행하는 부분이 필요한데, 그부분은 아래와같이 클래스로 분리하였다.


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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
package util;
import java.lang.reflect.*;
import org.json.simple.*;
/*
 * API 서블릿에서 리플렉션을 사용하기 위한 클래스 
 * 
 * @author          KYEONGSOO YOO
 * @Date            2018-06-01
 * */
public class ReflectionMethod {
    // ================================================================================
    // Properties
    // ================================================================================
    private static Class<>; _parentClass;
    private static Method[] _parentMethods;
    private static Object _parentClassInstance;
    // ================================================================================
    // Constructor
    // ================================================================================
    /*
     * ReflectionMEthod 클래스의 생성자 함수이다. 인스턴스가 존재해야한다. 보통 실행할 함수가 속한 클래스의 this를 넘겨주면
     * 된다.
     * 
     * 수정 2018-06-19 KYEONGSOO YOO
     * 초기화 부분을 함수 실행시로 옮김 
     * 
     * @author                  KYEONGSOO YOO
     * 
     */
    public ReflectionMethod() {
        super();
    }
    
    // ================================================================================
    // private methods
    // ================================================================================
    /*
     * 내부 함수이다. 생성자가 제대로 동작했는지 체크한다.
     * 
     * @author                  KYEONGSOO YOO
     * @return                  생성자가 제대로 생성되었는지 여부 (Boolean)
     */
    private boolean chkState() {
        return !(_parentMethods == null || _parentMethods.length &lt; 0);
    }
    /*
     * 현재 클래스에서 String을 기준으로 함수를 가져온다. 만약 함수가 존재하지 않으면 null을 리턴한다.
     * 
     * @author                  KYEONGSOO YOO
     * @param   name            가져올 함수의 이름
     * @return                  가져온 함수 , 존재하지 않으면 null
     */
    private Method get(String name) {
        for (Method fn : _parentMethods) {
            if (fn.getName().equals(name)) {
                return fn;
            }
        }
        System.out.println("[RM] can not find method by [" + name + "]");
        return null;
    }
    
    private boolean setInstance(Object instance) {
        if(instance != null) {
            _parentClassInstance = instance;
            _parentClass = _parentClassInstance.getClass();
            _parentMethods = _parentClass.getDeclaredMethods();
            return true;
        }
        else {
            return false;
        }
        
    }
    
    /*
     * 현재 클래스에 함수가 존재하는지 여부를 확인한다.
     * 
     * @author                  KYEONGSOO YOO
     * @param   name            가져올 함수의 이름
     * @return                  함수의 존재 여부
     */
    private boolean exists(String fn_nm) {
        for (int i = 0; i &lt; _parentMethods.length; i++) {
            // System.out.println(_parentMethods[i]);
            if (_parentMethods[i].getName().equals(fn_nm)) {
                return true;
            }
        }
        return false;
    }
    /*
     * 현재 클래스에서 함수를 가져오고 실제로 실행한다. 변수들은 json으로 받고 그 결과도 json으로 넘겨준다.
     * 
     * @author              KYEONGSOO YOO
     * @param   fn_nm       가져올 함수의 이름
     * @param   params      함수에 넘겨줄 매개변수들, json
     * @return              함수 실행 결과, json
     */
    private JSONObject invoke(String fn_nm, JSONObject params) {
        JSONObject obj = new JSONObject();
        try {
            Method fn = get(fn_nm);
            Object value = fn.invoke(_parentClassInstance, params);
            obj.put("INVOKE_RESULT_CD"1);
            obj.put("INVOKE_DATA", value);
            return obj;
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            obj.put("INVOKE_RESULT_CD"-1);
            obj.put("INVOKE_ERR_MSG", e.getLocalizedMessage());
            return obj;
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            obj.put("INVOKE_RESULT_CD"-2);
            obj.put("INVOKE_ERR_MSG", e.getLocalizedMessage());
            return obj;
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            obj.put("INVOKE_RESULT_CD"-3);
            obj.put("INVOKE_ERR_MSG", e.getLocalizedMessage());
            return obj;
        }
    }
    /*
     * 클래스의 함수를 실행하고 결과 json까지 만들어준다. invoke, exists, get을 합친 것이다. 본 함수를 사용할
     * 
     * @author              KYEONGSOO YOO
     * @param   fn_nm       가져올 함수의 이름
     * @param   params      함수에 넘겨줄 매개변수들, json
     * @return              함수 실행 결과, json
     */
    private JSONObject callAndGetResult(String fn_nm, JSONObject params) {
        JSONObject jsonObj = new JSONObject();
        if (chkState()) {
            if (exists(fn_nm)) {
                JSONObject ret = invoke(fn_nm, params);
                int resultCD = (int) ret.get("INVOKE_RESULT_CD");
                if (resultCD &gt; 0) {
                    jsonObj.put("RESULT""SUCCESS");
                    jsonObj.put("RESULT_CD"1);
                    jsonObj.put("RESULT_DATA", ret.get("INVOKE_DATA"));
                } else {
                    jsonObj.put("RESULT""FAIL");
                    jsonObj.put("RESULT_CD"-2);
                    jsonObj.put("ERR_MSG""[RM] 요청된 함수 [" + fn_nm + "]의 실행 중 오류가 발생했습니다.");
                    jsonObj.put("INVOKE_ERR_MSG", ret.get("INVOKE_ERR_MSG"));
                    jsonObj.put("INVOKE_ERR_CD", ret.get("INVOKE_RESULT_CD"));
                }
            } else {
                jsonObj.put("RESULT""FAIL");
                jsonObj.put("RESULT_CD"-1);
                jsonObj.put("ERR_MSG""[RM] 요청된 함수 [" + fn_nm + "]이 존재하지 않습니다.");
                System.out.println("METHOD IS NOT EXISTS - start");
                for(int i = 0 ; i &lt; _parentMethods.length ; i++) {
                    System.out.println(_parentMethods[i].getName());
                }
                System.out.println("METHOD IS NOT EXISTS - end");
            }
        } else {
            jsonObj.put("RESULT""FAIL");
            jsonObj.put("RESULT_CD"0);
            jsonObj.put("ERR_MSG""[RM] 리플렉션 클래스가 정상적으로 초기화되지 않았습니다.");
        }
        return jsonObj;
    }
    
    // ================================================================================
    // public Methods
    // ================================================================================
    /*
     * 현재 클래스에 함수가 존재하는지 여부를 확인한다.
     * 
     * @author                  KYEONGSOO YOO
     * @param   name            가져올 함수의 이름
     * @param   instance        확인할 클래스의 인스턴스 
     * @return                  함수의 존재 여부
     */
    public boolean exists(String fn_nm , Object instance) {
        if(setInstance(instance)) {
            return exists(fn_nm);
        }
        else {
            return false;
        }
    }
    /*
     * 클래스의 함수를 실행하고 결과 json까지 만들어준다. invoke, exists, get을 합친 것이다. 본 함수를 사용할
     * 
     * @author              KYEONGSOO YOO
     * @param   fn_nm       가져올 함수의 이름
     * @param   params      함수에 넘겨줄 매개변수들, json
     * @param   instance        확인할 클래스의 인스턴스 
     * @return              함수 실행 결과, json
     */
    public JSONObject callAndGetResult(String fn_nm , JSONObject params, Object instance) {
        if(setInstance(instance)) {
            return callAndGetResult(fn_nm, params);
        }else {
            JSONObject ret = new JSONObject();
            ret.put("RESULT" , "FAIL");
            ret.put("RESULT_CD"1);
            ret.put("ERR_MSG" , "클래스의 인스턴스가 주어지지 않았습니다.");
            return ret;
        }
    }
}
 
cs



아래와 같이 분리한 이후에는 특정 주소를 처리 하는 서블릿을 만들고 아래와 같이 구현한다. 


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
package api;
 
import java.io.IOException;
 
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 javax.servlet.http.HttpSession;
import javax.xml.ws.http.HTTPException;
 
import java.sql.*;
import util.*;
import org.json.simple.*;
import org.json.simple.parser.*;
 
/**
 * Servlet implementation class users
 */
@WebServlet("/API/users")
public class users extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static ReflectionMethod rm;
    private CommonVariable cv;
 
    private HttpSession _session = null;
 
    /**
     * @see HttpServlet#HttpServlet()
     */
    public users() {
        super();
        // TODO Auto-generated constructor stub
        rm = new ReflectionMethod();
        cv = CommonVariable.getInstance();
    }
 
    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        _session = request.getSession();
 
        JSONParser parser = new JSONParser();
        JSONObject prms = null;
        try {
            prms = (JSONObject) parser.parse(request.getParameter("params"));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
        JSONObject jsonObj = rm.callAndGetResult(request.getParameter("fn"), prms ,this);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().append(jsonObj.toJSONString());
 
    }
 
    /**
     * @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);
        JSONParser parser = new JSONParser();
        JSONObject prms = null;
        try {
            prms = (JSONObject) parser.parse(request.getParameter("params"));
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
        _session = request.getSession();
 
        JSONObject jsonObj = rm.callAndGetResult(request.getParameter("fn"), prms, this);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().append(jsonObj.toJSONString());
 
    }
}
 
cs


위와 같이 구현한 경우 요청 시에는 {  fn_nm : 'mockup' , params: { ... } } 형태로 값을 넘기면, fn_nm에 문자열로 넘긴 함수명을 찾아 실행하게 된다. 

아래에 만약 mockup 이라는 함수를 구현해 사용한다면,

public JSONObject mockup(JSONObject params);

라는 형태로 함수를 구현하면 된다.  


아래는 mysql과 연동 하였을 경우의 예제이다. 


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
/*
     * 예시 함수 
     * 조회를 시행해야 하는 경우.
     * 메소드 리플렉션과 데이터베이스 요청을 포함하기 때문에 예외처리 항목에 
     * ClassNotFoundException / SQLException 항목이 포함되어야 한다. 
     * NOTE : 로그인을 예제로 사용함  
     * */
    public JSONObject mockup_select (JSONObject params) throws ClassNotFoundException, SQLException{
        JSONObject ret = new JSONObject();
           
        
        /*
         * JSONObject params에 아래와 같은 변수들이 들어있는 경우 
         * */
        String mail = (String) params.get("userMail");
        String pw = (String) params.get("userPW");
        
        // 데이터 베이스 요청을 위한 문자열 
        // 잦은 String append는 성능과 가독성에 좋지 않아 String.format을 사용하도록 함 
        String query = String.format("select * from tb_user where userMail = '%s' and userPw = '%' ", mail , pw);
        
        // DB에 연결하기 위함 클래스를 초기화한다.
        // 본 클래스는 util 패키지에 들어있다.
        DBConnector dc = new DBConnector();
        
        // 셀렉트 결과를 가져온다. 
        // 셀렉트시에는 반드시 execute함수를 사용해야 한다.
        // 아래 함수는 insert , update , delete시 사용하지 않도록 조심해야한다.
        // DB 연결 함수들은 항상 try catch문으로 감싸두어야 한다. 오류 검출을 위
        JSONArray db_result = null;
        try {
            db_result = dc.excute(query); 
        }
        catch(SQLException e) {
            // 실제 에러가 발생한 부분을 서버 콘솔상에 출력한다. 
            e.printStackTrace(System.out);
            
            // 실패한 결과에는 반드시 ret JSON에 아래 키들을 포함해야한다. 
            /*
             * METHOD_RESULT_CD         integer             : 결과 코드이다. 실패한 경우 0보다 작은 값을 순서대로 넣는다. 
             * METHOD_ERR_MSG           String              : 요구 사항에 따라 직접 메시지를 삽입.
             * METHOD_ERR_LOCALE_MSG    String              : 예외처리를 통해 오류가 발생한 경우에만 삽입한다. 
             * */
            ret.put("METHOD_RESULT_CD" , -1);
            ret.put("METHOD_ERR_LOCALE_MSG", e.getLocalizedMessage());
        }
        
        // JSONArray 객체는 배열이 아니라 리스트 클래스를 상속받는다.
        // 배열처럼 사용하기 위해서는 먼저 변환해주어야 한다. 
        Object[] items = db_result.toArray();
        
        if(items.length > 0) {
            // 하나 이상 값이 조회되었을 경우 1보다 갯수가 크다. 
            // 로그인이 성공한 것으로 판단하고 데이터를 돌려보낸다.
            
            // 성공한 결과에는 반드시 ret JSON에 아래 키들을 포함해야한다. 
            /*
             * METHOD_RESULT_CD     integer                 : 결과 코드이다. 성공한 경우 1을 출력한다. 
             *                                              만약 실패 한다면 0보다 작은 값으로 코드를 변경한다. (ex : -1)
             * METHOD_RESULT_DATA   JSONObject/JSONArray    : 조회된 결과 데이터이다. JSONObject 혹은 JSONArray를 넣어주도록 한다.
             * */
            ret.put("METHOD_RESULT_CD"1);
            ret.put("METHOD_RESULT_DATA", db_result);
        }
        else {
            // 로그인이 실패한 경우이다. 
            // 이 경우에는 직접 메시지를 넣어주면된다. 서버 오류로 인한 예외가 아니라 사용자 입력에 따른 예외기 떄
            ret.put("METHOD_RESULT_CD" , -2);
            ret.put("METHOD_ERR_MSG""아이디 혹은 비밀번호를 확인해주세요");   
        }
        
        
        return ret;
    }
    
cs


'DEV > JSP' 카테고리의 다른 글

[JSP 1.x] DB 사용부를 클래스로 분리하기  (0) 2018.06.20

+ Recent posts