• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java EE 정리 25

02 Apr 2019

Reading time ~9 minutes

Java EE 정리 25 - Model2(2)


Model 2 작성 방법

  • 진입점을 하나로 설정
    • 통제의 편의성
    • Servlet(Main Controller) 하나 생성
    • DD(web.xml)에 security constraint 설정
      • JSP를 직접 요청하지 못하도록 막음
  • 객체다형성(inheritance)
  • 페이지 이동

Model 2 작성 방법 - JSP 요청 막기

  • 통제의 편의성
  • secufrity constraint(보안제약) 설정

01

<!-- DD(web.xml) -->
...
  <security-constraint>
      <web-resource-collection>
            <web-resource-name></web-resource-name>
            <url-pattern>*.jsp</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
            <role-name></role-name>
      </auth-constraint>
  </security-constraint>
</web-app>

02

  • JSP를 바로 접근 시 에러페이지 처리
    • 에러 이미지를 보여주는 JSP페이지로 이동시킴
    • 서비스할 때만 사용, 개발할 땐 막지 않는다.
    • 에러 코드에 따라 다른 에러 페이지를 보여줄 수 있다.

03

<!-- err_403.jsp -->
...
<img src="http://localhost:8080/model2_prj/common/error/1.PNG"/>
...
<!-- web.xml -->
...
  </welcome-file-list>
  
  <error-page>
      <error-code>403</error-code>
      <location>/common/error/err_403.jsp</location>
  </error-page>
  
  <security-constraint>
...

04

Model 2 작성 방법 - 진입점을 하나로 만들기

  • 요청을 처리한다는 의미로 ‘do’가 붙음
  • 어떤 do가 불리든 Main Controller로 가서 진입점을 하나로 만듦
<!-- view.jsp -->
...
요청
<a href="main.do">main</a>
<a href="a.do">a</a>
<a href="b.do">b</a>
<a href="yana.do">yanado</a>
...
public class MainController extends HttpServlet {
    
      protected void doGet(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            // 진입점을 하나로 만들기 위해
            // get 요청이 와도 post으로 처리됨
            doPost(request,response);
      }
      
      protected void doPost(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {

            RequestDispatcher rd =  request.getRequestDispatcher("date0402/view.jsp");
            rd.forward(request, response);
      }
}
<!-- DD, web.xml -->
...
  <servlet>
    <description></description>
    <display-name>MainController</display-name>
    <servlet-name>MainController</servlet-name>
    <servlet-class>date0401.MainController</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>MainController</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

05

  • *.do가 url로 표기 되므로 index.do가 표기되도록 변경처리
    • welcome-file로 index.html을 만들고 페이지 이동처리
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
      location.href="index.do";
</script>
</head>
<body>
</body>
</html>

Model 2 작성 방법 - 페이지 이동

  • Servlet(MainController)에서 JSP로 이동할 땐 forward를 사용한다.
    • redirect로 페이지 이동하면 JSP 직접요청으로 브라우저가 이동을 알게되고 에러페이지로 이동해버린다.
public class MainController extends HttpServlet {
    
      protected void doGet(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            // 진입점을 하나로 만들기 위해
            // get 요청이 와도 post으로 처리됨
            doPost(request,response);
      }
      
      protected void doPost(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            
            RequestDispatcher rd =  request.getRequestDispatcher("date0401/hello.jsp");
            rd.forward(request, response);
      }
}
<!-- hello.jsp -->
...
      안녕하세요?<br/>
      <img src="../common/images/img.png"/>
...

06

  • forward로 페이지 이동 시 url이 변경되지 않음
    • 이미지의 소스는 상대 경로가 아닌 절대 경로로 잡아야 한다.
    • 어디에 있든 서비스가 되게 하려면 url로 설정해야 한다.
<img src="common/images/img.png"/>

07

Model 2 작성 방법 - 객체 다형성

  • 클래스가 다르지만 같은 메소드를 갖고 있는 경우

08

public class SeonUi {
      public String execute() {
            return "어~~";
      }
}
public class JaeHyun {
      public String execute() {
            return "저요";
      }
}
public class TeackSung {
      public String execute() {
            return "한공기 추가요";
      }
}
  • key에 따라 다른 객체를 갖기 때문에 다른 객체를 캐스팅하면 에러가 발생
Map<String, Object> map = new HashMap<String, Object>();

map.put("gong", new SeonUi());
map.put("lee", new JaeHyun());
map.put("jung", new TeackSung());

String key = "lee";
SeonUi su = (SeonUi)map.get(key);
System.out.println(su.execute());

09

  • 에러가 발생하지 않기 위해선 if로 물어봐야 함
    • 사람 수가 늘어날 수록 코드가 많아진다.
if ("gong".equals(key)) {
    SeonUi su = (SeonUi)map.get(key);
    su.execute();
} else if ("lee".equals(key)) {
    JaeHyun jh = (JaeHyun)map.get(key);
    jh.execute();
} else if ("jung".equals(key)) {
    TeackSung ts = (TeackSung)map.get(key);
    ts.execute();
}

09-01

  • inheritance를 사용(객체다형성)하면 코드를 줄일 수 있다.
// is a, 객체다형성을 이용하면 코드의 길이가 줄어든다.
Controller c = (Controller)map.get(key);
c.execute(); // Override된 메소드 호출
  • 객체다형성 사용 예
public interface Person { // 인터페이스!
      public String execute();
}
public class SeonUi implements Person{
      public String execute() {
            return "어~~";
      }
}
public class JaeHyun implements Person {
      public String execute() {
            return "저요";
      }
}
public class TeackSung implements Person {
      public String execute() {
            return "한공기 추가요";
      }
}
  • 이때 사용되는 key를 Model2에선 Command(입력하는 값)라고 한다.
Map<String, Person> map = new HashMap<String, Person>();

map.put("gong", new SeonUi());
map.put("lee", new JaeHyun());
map.put("jung", new TeackSung());

String key = "gong";

if (map.containsKey(key)) { // NullPointerException 예외처리
      // 객체 다형성을 사용하여 부모에 얻어낸 자식을 저장
      Person person = map.get(key);
      System.out.println(person.execute());
}
// 사람이 더 늘어도 4줄로 다 처리가 가능하다.

Model2 예제

10

  • 주석으로 클래스다이어그램에 보여줄 JSP를 명시할 수 있다.
  • 컨트롤러에서 직접 DAO를 연결하지 않는다.
  • Command
    • D001 : DeptController
    • E001 : EmpController

11

  • MainController 만들기
    • 진입점(Spring의 DispatcherServlet)
      • 모든 요청을 받고 요청을 처리할 Controller를 찾아 실행을 함
      • View로 이동

12

public class MainController extends HttpServlet {
      protected void doGet(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            doPost(request,response); // GET방식의 요청이 있어도  doPost로 처리
      }
      protected void doPost(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            // Controller 구현 후 추가 예정
      }
}
  • welcome-file을 index.html만 남기고 삭제, JSP를 직접 요청하지 못하도록 설정
    • DD 변경(web.xml)
...
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  ...
  <security-constraint>
      <web-resource-collection>
            <web-resource-name></web-resource-name>
            <url-pattern>*.jsp</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
            <role-name></role-name>
      </auth-constraint>
  </security-constraint>
</web-app>
  • MainController로 요청이 가도록 welcome-file 생성
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
      location.href="index.do";
</script>
</head>
<body>
</body>
</html>
  • Controller 인터페이스 생성
public interface Controller {
      /**
       * 요청, 응답 처리, 관계유지 객체(Session, Cookie)의 사용
       * @param request 웹 파라미터 요청처리
       * @param response 응답처리
       * @throws ServletException
       * @throws IOException
       */
      public void execute(HttpServletRequest request,  HttpServletResponse response)
            throws ServletException, IOException;
      
      /**
       * 처리한 결과를 보여줄 JSP명을 반환
       * @return
       */
      public String moveURL();
      
      /**
       * 처리한 결과를 보여줄 JSP, HTML로 이동하는 이동방식 선정
       * @return
       */
      public boolean isForward();
}
  • VO 생성
public class DeptVO {
      private int deptno;
      private String dname, loc;
      // 생성자, getter, setter 생성
      ...
public class EmpVO {
      private int empno, sal;
      private String name, job, hiredate;
      // 생성자, getter, setter 생성
      ...
  • DAO 생성, Server.xml에 DBCP 설정
public class Model2DAO {
      
      private static Model2DAO j_dao;
      
      private Model2DAO() {}
      
      public static Model2DAO getInstance() {
            if (j_dao == null) {
                  j_dao = new Model2DAO();
            }
            
            return j_dao;
      }
      
      public Connection getConn() throws SQLException {
            Connection con = null;
            
            try {
                  // DBCP
                  Context ctx = new InitialContext();
                  DataSource ds =  (DataSource)ctx.lookup("java:comp/env/jdbc/model2_dbcp");
                  con = ds.getConnection();
                  
            } catch (NamingException e) {
                  e.printStackTrace();
            }
                              
            return con;
      }
      
      public List<EmpVO> selectEmpList(int deptno) throws SQLException {
            List<EmpVO> list = new ArrayList<EmpVO>();
            Connection con = null;
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            
            try {
                  con = getConn();
                  
                  StringBuilder selectEmp = new StringBuilder();
                  selectEmp
                  .append(" select empno, ename, job,  to_char(hiredate,'yyyy-MM-dd') hiredate, sal ")
                  .append(" from emp ")
                  .append(" where deptno = ? ")
                  .append(" order by sal ");
                  
                  pstmt = con.prepareStatement(selectEmp.toString());
                  pstmt.setInt(1, deptno);
                  
                  rs = pstmt.executeQuery();
                  
                  EmpVO evo = null;
                  while(rs.next()) {
                        evo = new EmpVO(rs.getInt("empno"),  rs.getInt("sal"),
                                    rs.getString("ename"),  rs.getString("job"),
                                    rs.getString("hiredate"));
                        
                        list.add(evo);
                  }
                  
            } finally {
                  if (rs != null) { rs.close(); }
                  if (pstmt != null) { pstmt.close(); }
                  if (con != null) { con.close(); }
            }
            
            return list;
      }
      
      public List<DeptVO> selectDeptList() throws SQLException {
            List<DeptVO> list = new ArrayList<DeptVO>();
            
            Connection con = null;
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            
            try {
                  con = getConn();
                  
                  StringBuilder selectDept = new StringBuilder();
                  selectDept
                  .append(" select deptno, dname, loc ")
                  .append(" from dept ");
                  
                  pstmt = con.prepareStatement(selectDept.toString());
                  
                  rs = pstmt.executeQuery();
                  
                  DeptVO dvo = null;
                  while(rs.next()) {
                        dvo = new DeptVO(rs.getInt("deptno"),
                                    rs.getString("dname"),  rs.getString("loc"));
                        
                        list.add(dvo);
                  }
                  
            } finally {
                  if (rs != null) { rs.close(); }
                  if (pstmt != null) { pstmt.close(); }
                  if (con != null) { con.close(); }
            }
            
            return list;
      }
}
<!-- server.xml -->
...
<Context docBase="model2_emp" path="/model2_emp" reloadable="true"  source="org.eclipse.jst.jee.server:model2_emp">
      <Resource auth="Container"  driverClassName="oracle.jdbc.OracleDriver" maxIdle="10" maxTotal="20"  maxWaitMillis="-1" name="jdbc/model2_dbcp" password="tiger"  type="javax.sql.DataSource" url="jdbc:oracle:thin:@localhost:1521:orcl"  username="scott"/>
</Context>
...
  • Service 생성
    • Business Logic을 처리하는 클래스, DAO 단의 클래스를 사용
public class DeptService {
      
      public List<DeptVO> searchAllDept() {
            List<DeptVO> list = null;
            
            Model2DAO m_dao = Model2DAO.getInstance();
            
            try {
                  list = m_dao.selectDeptList();
            } catch (SQLException e) {
                  e.printStackTrace();
            }
            
            return list;
      }
}
public class EmpService {
      
      public List<EmpVO> searchEmp(int deptno) {
            List<EmpVO> list = null;
            
            Model2DAO m_dao = Model2DAO.getInstance();
            
            try {
                  list = m_dao.selectEmpList(deptno);
                  
                  for(EmpVO ev : list) { // 사원명에 '님'을 붙인다.
                        ev.setName(ev.getName()+"님");
                  }
                  
            } catch (SQLException e) {
                  e.printStackTrace();
            }
            
            return list;
      }
}
  • 요청 하나당 클래스 하나가 처리한다.(올드한 방식)
    • Spring에선 한 클래스에서 요청마다 다른 메서드로 처리한다.
public class DeptController implements Controller {
      private String url;
      private boolean forwardFlag;

      @Override
      public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            
            DeptService ds = new DeptService();
            List<DeptVO> deptList = ds.searchAllDept();
            
            // 처리된 데이터를 JSP에서 보여주기 위해 Scope객체에  설정한다.
            request.setAttribute("deptList", deptList);
            
            url="dept/dept.jsp";
            forwardFlag = true;
      }
      @Override
      public String moveURL() {
            return url;
      }
      @Override
      public boolean isForward() {
            return forwardFlag;
      }
}
public class EmpController implements Controller {
      private String url;
      private boolean forwardFlag;

      @Override
      public void execute(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            String deptno = request.getParameter("deptno");
            int intDeptno = Integer.parseInt(deptno);
            
            EmpService es = new EmpService();
            List<EmpVO> empList = es.searchEmp(intDeptno);
            
            request.setAttribute("empList", empList);
            
            url="emp/emp.jsp";
            forwardFlag=true;
      }

      @Override
      public String moveURL() {
            return url;
      }

      @Override
      public boolean isForward() {
            return forwardFlag;
      }
}
  • MainController
    • static 영역으로 JVM이 실행되면서 미리 Command를 저장한 Map 변수를 만듦
public class MainController extends HttpServlet {
      private static Map<String, Controller> distMap;
      
      static { // static영역
            distMap = new HashMap<String, Controller>();
            
            distMap.put("D001", new DeptController()); // 부서조회 업무
            distMap.put("E001", new EmpController()); // 사원조회 업무
      }
      
      protected void doGet(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            doPost(request,response); // GET방식의 요청이 있어도  doPost로 처리
      }

      protected void doPost(HttpServletRequest request,  HttpServletResponse response) throws ServletException, IOException {
            request.setCharacterEncoding("UTF-8"); // POST 한글처리
            
            String cmd = request.getParameter("cmd");
            
            if (cmd == null || "".equals("")) { // cmd가 없을 때
                  cmd = "D001";
            }
            
            Controller controller  = distMap.get(cmd);

            if (controller == null) { // cmd를 외부에서 임의로 변경했을  때
                  // 메인을 제공한다.
                  controller = distMap.get("D001"); 
            }
      
            
            // 웹 파라미터 처리, 관계유지, 업무처리 결과를 Scope객체에  설정하는 일을 수행
            controller.execute(request, response);
            // 이동할 페이지의 URL받기
            String url = controller.moveURL();
            // 이동방식 받기
            boolean forwardFlag = controller.isForward();
            
            pageMove(request, response, url, forwardFlag);
      }
      
      public void pageMove(HttpServletRequest request,  HttpServletResponse response, String url, boolean forwardFlag)
                  throws ServletException, IOException {
            
            if (forwardFlag) { // forward 이동
                  RequestDispatcher rd =  request.getRequestDispatcher(url);
                  rd.forward(request, response);
            } else { // redirect 이동
                  response.sendRedirect(url);
            }
      }
}
  • View 생성 * Model2에서 JSP의 역할은 보여주는 역할만 한다.
    • 전달되는 deptList가 어디서 전달되는건지 확인하기가 어렵다.
    • 부서번호를 클릭했을 때 emp정보를 보는 페이지로 이동할 것
      • “emp.do” 처럼 do 앞은 뭐가 와도 상관없고 중요한건 Web Parameter
<!-- dept.jsp -->
...
<table>
      <tr>
            <th width="40">번호</th>
            <th width="80">부서번호</th>
            <th width="100">부서명</th>
            <th width="100">위치</th>
      </tr>
      <c:if test="${ empty deptList }">
      <tr>
            <td colspan="4" align="center">부서정보가 존재하지  않습니다.</td>
      </tr>
      </c:if>
      <c:forEach var="dept" items="${ deptList }" >
      <c:set var="i" value="${ i+1 }"/>
      <tr>
            <td><c:out value="${ i }"/></td>
            <td><a href="emp.do?cmd=E001&deptno=${ dept.deptno }"><c:out value="${  dept.deptno }"/></a></td>
            <td><c:out value="${ dept.dname }"/></td>
            <td><c:out value="${ dept.loc }"/></td>
      </tr>
      </c:forEach>
</table>
...

13

<!-- emp.jsp -->
...
<strong>${ param.deptno }</strong>번 부서조회<br/>
<a href="dept.do?cmd=D001">뒤로</a>
<table border="1">
      <tr>
            <th width="40">번호</th>
            <th width="80">사원번호</th>
            <th width="100">사원명</th>
            <th width="80">연봉</th>
            <th width="80">직무</th>
            <th width="150">입사일</th>
      </tr>
      <c:if test="${ empty empList }">
            <tr>
                  <td colspan="6" align="center">
                        해당 부서는 사원이 존재하지 않거나, 부서가  존재하지 않을 수 있습니다.
                  </td>
            </tr>
      </c:if>
      <c:forEach var="emp" items="${ empList }">
      <c:set var="i" value="${ i+1 }"></c:set>
      <tr>
            <td>${ i }</td>
            <td><c:out value="${ emp.empno }"/></td>
            <td><c:out value="${ emp.name }"/></td>
            <td><c:out value="${ emp.sal }"/></td>
            <td><c:out value="${ emp.job }"/></td>
            <td><c:out value="${ emp.hiredate }"  escapeXml="false"/></td>
      </tr>
      </c:forEach>
</table>          
...

14

Model 2 흐름 재정리

15

16

17



Java EEModel2