학교에서 배운 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 < 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 < _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 > 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 < _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 |
---|