네이버클라우드 AIaaS 개발자 양성과정 1기/DBMS, SQL, JDBC, Servlet

[비트캠프] 74일차(16주차1일) - Servlet(보관소, 필터, 리스너, MVC 모델I), JSP(기초), myapp-44~46

끈기JK 2023. 2. 20. 13:03

 

44. Web Application Server 구조로 전환하기

 

### 44. Web Application Server 구조로 전환하기 
- 웹 기술 도입  - 웹 기술을 도입하여 애플리케이션 서버를 구현하는 방법 
- 서블릿 구동 원리 및 서블릿 만드는 방법

 

폴더 구조는 다음과 같이 된다.

AppServer 삭제한다. Handler 삭제 및 util 의 불필요 파일 삭제한다.

 

StudentDao 에서 findAll 을 keyword 받도록 수정한다.

public interface StudentDao {
  void insert(Student s);
  List<Student> findAll(String keyword);
  Student findByNo(int no);
  int update(Student s);
  int delete(int no);
}

 

servlet > student 패키지 파일 수정한다.

teacher 유사하므로 생략한다.

@WebServlet("/student/list")
public class StudentListServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private StudentDao studentDao;

  public StudentListServlet() {
    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));
      studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");

    out.println("<div><a href='form'>새 학생</a></div>");

    out.println("<table border='1'>");
    out.println("<tr>");
    out.println("  <th>번호</th> <th>이름</th> <th>전화</th> <th>재직</th> <th>전공</th>");
    out.println("</tr>");

    String keyword = request.getParameter("keyword");
    List<Student> students = this.studentDao.findAll(keyword);

    for (Student student : students) {
      out.println("<tr>");
      out.printf("  <td>%d</td> <td><a href='view?no=%d'>%s</a></td> <td>%s</td> <td>%s</td> <td>%s</td>\n",
          student.getNo(),
          student.getNo(),
          student.getName(),
          student.getTel(),
          student.isWorking() ? "예" : "아니오",
              getLevelText(student.getLevel())    );
      out.println("</tr>");
    }
    out.println("</table>");

    out.println("<form action='list' method='get'>");
    out.printf("<input type='text' name='keyword' value='%s'>\n", keyword != null ? keyword : "");
    out.println("<button>검색</button>");
    out.println("</form>");

    out.println("</body>");
    out.println("</html>");
  }

  private static String getLevelText(int level) {
    switch (level) {
      case 0: return "비전공자";
      case 1: return "준전공자";
      default: return "전공자";
    }
  }
}

 

@WebServlet("/student/view")
public class StudentViewServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private StudentDao studentDao;

  public StudentViewServlet() {
    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));
      studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    int studentNo = Integer.parseInt(request.getParameter("no"));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");

    Student student = this.studentDao.findByNo(studentNo);

    if (student == null) {
      out.println("<p>해당 번호의 학생이 없습니다.</p>");

    } else {
      out.println("<form id='student-form' action='update' method='post'>");

      out.println("<table border='1'>");

      out.println("<tr>");
      out.println("  <th>번호</th>");
      out.printf("  <td><input type='text' name='no' value='%d'></td>\n",  student.getNo());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>이름</th>");
      out.printf("  <td><input type='text' name='name' value='%s'></td>\n",  student.getName());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>이메일</th>");
      out.printf("  <td><input type='email' name='email' value='%s'></td>\n",  student.getEmail());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>암호</th>");
      out.println("  <td><input type='password' name='password'></td>");
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>전화</th>");
      out.printf("  <td><input type='tel' name='tel' value='%s'></td>\n",  student.getTel());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>우편번호</th>");
      out.printf("  <td><input type='text' name='postNo' value='%s'></td>\n",  student.getPostNo());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>기본주소</th>");
      out.printf("  <td><input type='text' name='basicAddress' value='%s'></td>\n",  student.getBasicAddress());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>상세주소</th>");
      out.printf("  <td><input type='tel' name='detailAddress' value='%s'></td>\n",  student.getDetailAddress());
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>재직여부</th>");
      out.printf("  <td><input type='checkbox' name='working' %s> 재직중</td>\n",  student.isWorking() ? "checked" : "");
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>성별</th>");
      out.printf("  <td><input type='radio' name='gender' value='M' %s> 남\n"
          + "<input type='radio' name='gender' value='W' %s> 여</td>\n"
          , student.getGender() == 'M' ? "checked" : ""
            , student.getGender() == 'W' ? "checked" : "");
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>전공</th>");
      out.printf("  <td><select name='level'>"
          + "<option value='0' %s>비전공자</option>"
          + "<option value='1' %s>준전공자</option>"
          + "<option value='2' %s>전공자</option>"
          + "</select></td>\n"
          , student.getLevel() == 0 ? "selected" : ""
            , student.getLevel() == 1 ? "selected" : ""
              , student.getLevel() == 2 ? "selected" : "");
      out.println("</tr>");

      out.println("<tr>");
      out.println("  <th>등록일</th>");
      out.printf("  <td>%s</td>\n",  student.getCreatedDate());
      out.println("</tr>");

      out.println("</table>");
    }

    out.println("<div>");
    out.println("  <button id='btn-list' type='button'>목록</button>");
    out.println("  <button>변경</button>");
    out.println("  <button id='btn-delete' type='button'>삭제</button>");
    out.println("</div>");

    out.println("</form>");

    out.println("<script>");
    out.println("document.querySelector('#btn-list').onclick = function() {");
    out.println("  location.href = 'list';");
    out.println("}");
    out.println("document.querySelector('#btn-delete').onclick = function() {");
    out.println("  var form = document.querySelector('#student-form');");
    out.println("  form.action = 'delete';");
    out.println("  form.submit();");
    out.println("}");
    out.println("</script>");

    out.println("</body>");
    out.println("</html>");

  }

}

 

@WebServlet("/student/form")
public class StudentFormServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");
    out.println("<form action='insert' method='post'>");
    out.println("<table border='1'>");
    out.println("<tr>");
    out.println("  <th>이름</th>");
    out.println("  <td><input type='text' name='name'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>이메일</th>");
    out.println("  <td><input type='email' name='email'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>암호</th>");
    out.println("  <td><input type='password' name='password'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>전화</th>");
    out.println("  <td><input type='tel' name='tel'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>우편번호</th>");
    out.println("  <td><input type='text' name='postNo'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>기본주소</th>");
    out.println("  <td><input type='text' name='basicAddress'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>상세주소</th>");
    out.println("  <td><input type='tel' name='detailAddress'></td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>재직여부</th>");
    out.println("  <td><input type='checkbox' name='working'> 재직중</td>");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>성별</th>");
    out.println("  <td><input type='radio' name='gender' value='M' checked> 남\n"
        + " <input type='radio' name='gender' value='W'> 여</td>\n");
    out.println("</tr>");

    out.println("<tr>");
    out.println("  <th>전공</th>");
    out.println("  <td><select name='level'>\n"
        + "<option value='0'>비전공자</option>\n"
        + "<option value='1'>준전공자</option>\n"
        + "<option value='2'>전공자</option>\n"
        + "</select></td>");
    out.println("</tr>");

    out.println("</table>");

    out.println("<div>");
    out.println("  <button>등록</button>");
    out.println("  <button id='btn-cancel' type='button'>취소</button>");
    out.println("</div>");

    out.println("</form>");

    out.println("<script>");
    out.println("document.querySelector('#btn-cancel').onclick = function() {");
    out.println("  location.href = 'list';");
    out.println("}");
    out.println("</script>");

    out.println("</body>");
    out.println("</html>");

  }
}

 

@WebServlet("/student/update")
public class StudentUpdateServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  public StudentUpdateServlet() {
    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));
      txManager = new TransactionManager(sqlSessionFactory);
      memberDao = new DaoGenerator(sqlSessionFactory).getObject(MemberDao.class);
      studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");

    Student student = new Student();
    student.setNo(Integer.parseInt(request.getParameter("no")));
    student.setName(request.getParameter("name"));
    student.setEmail(request.getParameter("email"));
    student.setPassword(request.getParameter("password"));
    student.setTel(request.getParameter("tel"));
    student.setPostNo(request.getParameter("postNo"));
    student.setBasicAddress(request.getParameter("basicAddress"));
    student.setDetailAddress(request.getParameter("detailAddress"));
    student.setWorking(request.getParameter("working") != null);
    student.setGender(request.getParameter("gender").charAt(0));
    student.setLevel(Byte.parseByte(request.getParameter("level")));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");


    txManager.startTransaction();
    try {
      if (memberDao.update(student) == 1 &&
          studentDao.update(student) == 1) {
        txManager.commit();
        out.println("<p>변경했습니다.</p>");

      } else {
        out.println("<p>해당 번호의 학생이 없습니다.</p>");
      }
    } catch (Exception e) {
      txManager.rollback();
      out.println("<p>변경 실패입니다.</p>");
      e.printStackTrace();
    }

    out.println("</body>");
    out.println("</html>");

    response.setHeader("Refresh", "1;url=list");
  }

}

 

@WebServlet("/student/insert")
public class StudentInsertServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  public StudentInsertServlet() {
    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));
      txManager = new TransactionManager(sqlSessionFactory);
      memberDao = new DaoGenerator(sqlSessionFactory).getObject(MemberDao.class);
      studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");

    Student student = new Student();
    student.setName(request.getParameter("name"));
    student.setEmail(request.getParameter("email"));
    student.setPassword(request.getParameter("password"));
    student.setTel(request.getParameter("tel"));
    student.setPostNo(request.getParameter("postNo"));
    student.setBasicAddress(request.getParameter("basicAddress"));
    student.setDetailAddress(request.getParameter("detailAddress"));
    student.setWorking(request.getParameter("working") != null);
    student.setGender(request.getParameter("gender").charAt(0));
    student.setLevel(Byte.parseByte(request.getParameter("level")));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<meta http-equiv='Refresh' content='1;url=list'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");

    txManager.startTransaction();
    try {
      memberDao.insert(student);
      studentDao.insert(student);
      txManager.commit();
      out.println("<p>입력 했습니다.</p>");

    } catch (Exception e) {
      txManager.rollback();
      out.println("<p>입력 실패입니다.</p>");
      e.printStackTrace();
    }

    out.println("</body>");
    out.println("</html>");
  }

}

 

@WebServlet("/student/delete")
public class StudentDeleteServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  public StudentDeleteServlet() {
    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));
      txManager = new TransactionManager(sqlSessionFactory);
      memberDao = new DaoGenerator(sqlSessionFactory).getObject(MemberDao.class);
      studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

    int studentNo = Integer.parseInt(request.getParameter("no"));

    response.setContentType("text/html;charset=UTF-8");
    PrintWriter out = response.getWriter();

    out.println("<!DOCTYPE html>");
    out.println("<html>");
    out.println("<head>");
    out.println("<meta charset='UTF-8'>");
    out.println("<title>비트캠프 - NCP 1기</title>");
    out.println("</head>");
    out.println("<body>");
    out.println("<h1>학생</h1>");

    txManager.startTransaction();
    try {
      if (studentDao.delete(studentNo) == 1 &&
          memberDao.delete(studentNo) == 1) {
        txManager.commit();
        out.println("<p>삭제했습니다.</p>");

      } else {
        out.println("<p>해당 번호의 학생이 없습니다.</p>");
      }
    } catch (Exception e) {
      txManager.rollback();
      out.println("<p>삭제 실패입니다.</p>");
      e.printStackTrace();
    }

    out.println("</body>");
    out.println("</html>");

    response.setHeader("Refresh", "1;url=list");
  }
}

 

 

 

45. 리스너 및 ServletContext 보관소 사용법

 

web app. 시작 하고 종료되는 동안 여러 보관소 객체가 생성된다.

  • ServletContext : web app. 시작시 ServletContext 생성된다. web app 이 실행되는 동안 유지. 서블릿들이 공유할 자원을 보관. 예) DAO, DB 커넥션 등
  • HttpSession : 로그인시 HttpSession 생성된다. 로그인 동안 유지. 로그인 동안 사용할 데이터 보관. 예) 로그인 사용자 정보
  • ServletRequest : 클라이언트가 요청시 ServletRequest 생성된다. 요청 처리를 하는 동안 유지. 요청 처리 중에 생성된 중간 결과 보관. 예) select 결과 등.
    s1 서블릿이 s2 를 include 하고 s2 에서 리턴해서 s1 에서 s3 로 forward 하고 응답해서 종료된다. 이때 s1, s2, s3 는 ServletRequest 를 요청을 처리하는 동안 공유.
  • JspContext : JSP 를 실행하는 동안 유지. 각 JSP 를 실행할 때마다 생성. 실행 완료 후 소멸

 

 

 

45 리스너

 

리스너 : 서버가 어떤 상태에 놓일때 실행하는 객체

《interface》ServletContextListener 구현체가 있다면, ServletContainer 가 call 하는데,

web app 시작할 때 → contextInitialized() 실행하고,

web app 종료할 때 → contextDestroyed() 실행한다.

 

《interface》ServletRequestListener 구현체가 있다면, ServletContainer 가 call 하는데,

요청이 들어왔을 때 → requestInitialized() 실행하고,

응답을 완료할 때 → requestDestroyed() 실행한다.

 

《interface》HttpSessionListener 구현체가 있다면, ServletContainer 가 call 하는데,

세션 생성할 때(로그인 할 때) → sessionCreated() 실행하고,

세션 종료할 때(로그아웃 할 때) → sessionDestroyed() 실행한다.

 

 

애너테이션이 무슨 의미인지 암기해야 한다.

 

 

 

45. 자원 공유 전/후

 

① 공유 전

BoardListServlet 이 BoardDao 를 포함(aggregation, 집합)한다.

BoardViewServlet 이 BoardDao 를 포함한다.

BoardInsertServlet 이 BoardDao 를 포함한다.

...

서블릿 마다 DAO 객체를 생성한 후 보유한다. → 메모리 낭비

 

② 공유 후

ServletContext 보관소가 BoardDao, MemberDao, StudentDao, TeacherDao, TransactionManager 를 포함(aggregation, 집합)한다.

BoardListServlet 이 사용한다.

BoardViewServlet 이 사용한다.

BoardInsertServlet 이 사용한다.

...

공유 → 메모리 낭비를 줄인다 → GoF 의 Flyweight 패턴 → 확장: Pooling 기법

 

 

 

45. 필터 구동과 사용법

 

 

서블릿 실행 전/후로 작업을 추가할 수 있다. = 필터

작업 예)

요청

① 요청 파라미터 값 디코딩, 압축해제

② 인증 및 권한 검사

③ 요청 로그 출력

응답

① 응답 데이터 압축, 압호화

② 응답 로그 출력

 

필터

 - GoF 의 chain of Responsibility 패턴

   작업1 → 작업2 → 작업3

- 기능(작업)을 추가/제거 하기 쉽다.

   기존 코드 손대지 않고

 

《interface》Filter 에 다음과 같은 메서드가 있다.

init()  ← 웹앱 시작 시 필터 생성

doFilter()  ← 요청이 들어왔을때

destroy()  ← 웹앱 종료 시 필터 제거

 

이를 상속한 《concrete》MyFilter 에 메서드 오버라이딩 한다.

사용 방법 : @WebFilter("URL패턴") → Servlet Container 에 필터를 등록 → deployment(배치) 방법

init() { - } : 필터를 실행하는 동안 사용할 객체를 준비

doFilter() { - } : 서블릿 실행 전/후에 수행할 작업

destroy() { - } : init() 에서 준비했던 자원을 해제

 

 

 

45 필터 실행 과정

 

ServletContainer 에 ① 요청 이 오면 Filter1 의 ② doFilter() 실행한다. Filter2 의 ③ doFilter() 실행한다. Filter3 의 ④ doFilter() 실행한다. 서블릿의 ⑤ service() 실행한다. 요청마다 ⑥ 실행한다. ⑦ 리턴 ⑧ 리턴 ⑨ 리턴 ⑩ 리턴 후 ⑪ 응답 한다.

 

doFilter(...) {

  // 다음 필터 실행 전에 수행할 작업

  nextchain.doFilter();  // 다음 필터 실행

  // 다음 필터 실행한 후에 수행할 작업

}

 

☆주의!

- 필터의 실행 순서는 제어할 수 없다

   → 필터 실행 순서를 고려한 프로그래밍은 하지말 것!

 

 

 

45 필터 실행 예 - http://localhost:8080/web/board/list

 

ServletContainer 로 요청한다. @WebFilter 설정이 "/*" 인 Filter1 의 doFilter() 실행된다. 이어서 "/board/*" 인 Filter2 의 doFilter() 실행된다. 그리고 맞는 필터 없으므로 BoardListServlet 의 Service() 작업 실행된다. return 하여 Filter2 로 간다. return 하여 Filter1 으로 간다. return 하여 ServletContainer 가 응답한다.

 

 

 

45 필터 실행 과정 - UML Interactive Diagram (Sequence Diagram)

 

사용자 요청이 ServletContainer 로 온다. 작업 후 Filter1 의 doFilter() 실행한다. 작업 후 Filter 2 의 doFilter() 실행한다. 작업 후 chain.doFilter() 에 의해 BoardListServlet 의 service() 실행한다. 작업 후 Filter2 로 return 한다. 작업 후 Filter1 으로 return 한다. 작업 후 ServletContainer 로 return 한다. 작업 후 응답한다.

 

 

 

서블릿 컨테이너와 컴포넌트

 

서블릿 컨테이너에 web app 들이 있다.

web app 안에 있는 Servlet, Filter, Listener 는 web app. 주요 components 이다.

 

 

 

45. 기타 서블릿 컴포넌트 사용하기

 

### 45. 기타 서블릿 컴포넌트 사용하기
- 리스너, 필터 등 
- 리스너 구동 원리 및 사용법 
- ServletContext 보관소 사용법 
- 필터 구동 원리 및 사용법

 

filter, listener 패키지 추가 및 클래스 추가한다.

 

리스너 파일 준비한다. 각 Servlet 의 공통 코드인 session 객체 생성 및 dao 생성 코드를 붙여넣는다.

// 웹 애플리케이션이 시작/종료 될 때 실행되는 객체
@WebListener
// 서블릿 컨테이너에게 이 클래스가 리스너 구현체임을 알려줘야 한다.
// 그래야만 서블릿 컨테이너는 이 클래스의 인스턴스를 생성한다.
// 그리고 웹앱이 시작되거나 종료될 때 메서드를 호출해 준다.
public class ContextLoaderListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    // 웹 애플리케이션이 시작될 때 서블릿 컨테이너가 호출한다.
    System.out.println("ContextLoaderListener.contextInitialized() 호출됨!");

    try {
      InputStream mybatisConfigInputStream = Resources.getResourceAsStream(
          "bitcamp/myapp/config/mybatis-config.xml");
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      BitcampSqlSessionFactory sqlSessionFactory = new BitcampSqlSessionFactory(
          builder.build(mybatisConfigInputStream));

      TransactionManager txManager = new TransactionManager(sqlSessionFactory);

      BoardDao boardDao = new DaoGenerator(sqlSessionFactory).getObject(BoardDao.class);
      MemberDao memberDao = new DaoGenerator(sqlSessionFactory).getObject(MemberDao.class);
      StudentDao studentDao = new DaoGenerator(sqlSessionFactory).getObject(StudentDao.class);
      TeacherDao teacherDao = new DaoGenerator(sqlSessionFactory).getObject(TeacherDao.class);

      // 서블릿 컨텍스트 보관소를 알아낸다.
      ServletContext ctx = sce.getServletContext();

      // 서블릿들이 공유할 객체를 이 보관소에 저장한다.
      ctx.setAttribute("txManager", txManager);

      ctx.setAttribute("boardDao", boardDao);
      ctx.setAttribute("memberDao", memberDao);
      ctx.setAttribute("studentDao", studentDao);
      ctx.setAttribute("teacherDao", teacherDao);

    } catch (Exception e) {
      System.out.println("웹 애플리케이션 자원을 준비하는 중에 오류 발생!");
      e.printStackTrace();
    }
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    // 웹 애플리케이션이 종료될 때 서블릿 컨테이너가 호출한다.
    System.out.println("ContextLoaderListener.contextDestroyed() 호출됨!");
  }
}

 

서블릿 실행 전에 사용할 필터 코드 입력한다.

@WebFilter("/*")
public class CharacterEncodingFilter implements Filter {
  // 요청 데이터를 꺼내기 전에, 즉 getParameter() 를 호출하기 전에
  // 클라이언트가 보낸 값이 어떤 문자집합으로 인코딩되었는지 알려준다.
  // POST 요청으로 한글 데이터가 들어왔을때 한글을 깨뜨리지 않고 꺼내는 방법

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    request.setCharacterEncoding("UTF-8");
    chain.doFilter(request, response);  // 다음 필터 실행. 다음 필터가 없으면 최종 목적지인 서블릿 실행.
  }
}

 

Board : form 제외하고 list, view, update, inesrt, delete Servlet 앞부분 수정한다.

@WebServlet("/board/list")
public class BoardListServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private BoardDao boardDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    boardDao = (BoardDao) ctx.getAttribute("boardDao");
  }

 

Student, Teacher : list, view 는 studentDao 또는 teacherDao 만 필요하다.

@WebServlet("/student/list")
public class StudentListServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }

 

Student, Teacher : insert, update, delete 는 memberDao, txManager 도 필요하다.

@WebServlet("/student/insert")
public class StudentInsertServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    txManager = (TransactionManager) ctx.getAttribute("txManager");
    memberDao = (MemberDao) ctx.getAttribute("memberDao");
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }

 

 

 

Java 와 JSP

 

class BoardListServlet 을 list..jsp 로 변경한다.

 

directive element : import  →  <%@ page import="-----" %>

declaration element : 필드 및 메서드 정의  →  <%! O %>

scriptlet : 자바코드 →  <% O %>

expression element : printf("-- %s")  →  <%= O %>

출력문자열 : println("-----");

 

 

 

46. JSP를 이용하여 출력문을 자동으로 생성하기: MVC 모델1 

 

### 46. JSP를 이용하여 출력문을 자동으로 생성하기: MVC 모델1 
- JSP의 동작원리 이해 
- JSP 사용법

 

webapp 폴더에 board, student, teacher 폴더 생성하고 각 jsp 파일 작성한다.

 

Student 의 jsp 코드를 예로 들면 다음과 같다.

 

list.jsp

<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%!
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }
  
  private static String getLevelText(int level) {
    switch (level) {
      case 0: return "비전공자";
      case 1: return "준전공자";
      default: return "전공자";
    }
  }
%>

<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>

<div><a href='form.jsp'>새 학생</a></div>

<table border='1'>
<tr>
  <th>번호</th> <th>이름</th> <th>전화</th> <th>재직</th> <th>전공</th>
</tr>

<%
    String keyword = request.getParameter("keyword");
    List<Student> students = this.studentDao.findAll(keyword);

    for (Student student : students) {
%>
  <tr>
      <td><%=student.getNo()%></td> 
      <td><a href='view.jsp?no=<%=student.getNo()%>'><%=student.getName()%></a></td> 
      <td><%=student.getTel()%></td> 
      <td><%=student.isWorking() ? "예" : "아니오"%></td> 
      <td><%=getLevelText(student.getLevel())%></td>
  </tr>
<%
    }
%>
</table>

<form action='list' method='get'>
  <input type='text' name='keyword' value='<%=keyword != null ? keyword : ""%>'>
<button>검색</button>
</form>

</body>
</html>

 

view.jsp

<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%!
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }
%>
<%
    int studentNo = Integer.parseInt(request.getParameter("no"));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>
<%
    Student student = this.studentDao.findByNo(studentNo);
    if (student == null) {
%>
  <p>해당 번호의 학생이 없습니다.</p>
<%
    } else {
%>
  <form id='student-form' action='update.jsp' method='post'>

  <table border='1'>

  <tr>
    <th>번호</th>
      <td><input type='text' name='no' value='<%=student.getNo() %>'></td>
  </tr>

  <tr>
    <th>이름</th>
      <td><input type='text' name='name' value='<%=student.getName() %>'></td>
  </tr>

  <tr>
    <th>이메일</th>
      <td><input type='email' name='email' value='<%=student.getEmail() %>'></td>
  </tr>

  <tr>
    <th>암호</th>
    <td><input type='password' name='password'></td>
  </tr>

  <tr>
    <th>전화</th>
      <td><input type='tel' name='tel' value='<%=student.getTel() %>'></td>
  </tr>

  <tr>
    <th>우편번호</th>
      <td><input type='text' name='postNo' value='<%=student.getPostNo() %>'></td>
  </tr>

  <tr>
    <th>기본주소</th>
      <td><input type='text' name='basicAddress' value='<%=student.getBasicAddress() %>'></td>
  </tr>

  <tr>
    <th>상세주소</th>
      <td><input type='tel' name='detailAddress' value='<%=student.getDetailAddress() %>'></td>
  </tr>

  <tr>
    <th>재직여부</th>
      <td><input type='checkbox' name='working' <%=student.isWorking() ? "checked" : "" %>> 재직중</td>
  </tr>

  <tr>
    <th>성별</th>
      <td><input type='radio' name='gender' value='M'
       <%=student.getGender() == 'M' ? "checked" : "" %>> 남
        <input type='radio' name='gender' value='W'
         <%=student.getGender() == 'W' ? "checked" : "" %>> 여</td>
  </tr>

  <tr>
    <th>전공</th>
      <td><select name='level'>
            <option value='0' <%=student.getLevel() == 0 ? "selected" : "" %>>비전공자</option>
            <option value='1' <%=student.getLevel() == 1 ? "selected" : "" %>>준전공자</option>
            <option value='2' <%=student.getLevel() == 2 ? "selected" : "" %>>전공자</option>
          </select>
      </td>
  </tr>

  <tr>
    <th>등록일</th>
      <td><%=student.getCreatedDate() %></td>
  </tr>

  </table>
<%
    }
%>
<div>
  <button id='btn-list' type='button'>목록</button>
  <button>변경</button>
  <button id='btn-delete' type='button'>삭제</button>
</div>

</form>

<script>
document.querySelector('#btn-list').onclick = function() {
  location.href = 'list.jsp';
}
document.querySelector('#btn-delete').onclick = function() {
  var form = document.querySelector('#student-form');
  form.action = 'delete.jsp';
  form.submit();
}
</script>

</body>
</html>

 

form.jsp

<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>
<form action='insert.jsp' method='post'>
<table border='1'>
<tr>
  <th>이름</th>
  <td><input type='text' name='name'></td>
</tr>

<tr>
  <th>이메일</th>
  <td><input type='email' name='email'></td>
</tr>

<tr>
  <th>암호</th>
  <td><input type='password' name='password'></td>
</tr>

<tr>
  <th>전화</th>
  <td><input type='tel' name='tel'></td>
</tr>

<tr>
  <th>우편번호</th>
  <td><input type='text' name='postNo'></td>
</tr>

<tr>
  <th>기본주소</th>
  <td><input type='text' name='basicAddress'></td>
</tr>

<tr>
  <th>상세주소</th>
  <td><input type='tel' name='detailAddress'></td>
</tr>

<tr>
  <th>재직여부</th>
  <td><input type='checkbox' name='working'> 재직중</td>
</tr>

<tr>
  <th>성별</th>
  <td><input type='radio' name='gender' value='M' checked> 남
        <input type='radio' name='gender' value='W'> 여</td>
</tr>

<tr>
  <th>전공</th>
  <td><select name='level'>
      <option value='0'>비전공자</option>
      <option value='1'>준전공자</option>
      <option value='2'>전공자</option>
      </select></td>
</tr>

</table>

<div>
  <button>등록</button>
  <button id='btn-cancel' type='button'>취소</button>
</div>

</form>

<script>
document.querySelector('#btn-cancel').onclick = function() {
  location.href = 'list.jsp';
}
</script>

</body>
</html>

 

insert.jsp

<%@page import="bitcamp.myapp.dao.MemberDao"%>
<%@page import="bitcamp.util.TransactionManager"%>
<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%!
  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    txManager = (TransactionManager) ctx.getAttribute("txManager");
    memberDao = (MemberDao) ctx.getAttribute("memberDao");
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }
%>
<%
    Student student = new Student();
    student.setName(request.getParameter("name"));
    student.setEmail(request.getParameter("email"));
    student.setPassword(request.getParameter("password"));
    student.setTel(request.getParameter("tel"));
    student.setPostNo(request.getParameter("postNo"));
    student.setBasicAddress(request.getParameter("basicAddress"));
    student.setDetailAddress(request.getParameter("detailAddress"));
    student.setWorking(request.getParameter("working") != null);
    student.setGender(request.getParameter("gender").charAt(0));
    student.setLevel(Byte.parseByte(request.getParameter("level")));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta http-equiv='Refresh' content='1;url=list.jsp'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>
<%
    txManager.startTransaction();
    try {
      memberDao.insert(student);
      studentDao.insert(student);
      txManager.commit();
%>
  <p>입력 했습니다.</p>
<%
    } catch (Exception e) {
      txManager.rollback();
%>
  <p>입력 실패입니다.</p>
<%
      e.printStackTrace();
    }
%>
</body>
</html>

 

update.jsp

<%@page import="bitcamp.myapp.dao.MemberDao"%>
<%@page import="bitcamp.util.TransactionManager"%>
<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%!
  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    txManager = (TransactionManager) ctx.getAttribute("txManager");
    memberDao = (MemberDao) ctx.getAttribute("memberDao");
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }
%>
<%
    Student student = new Student();
    student.setNo(Integer.parseInt(request.getParameter("no")));
    student.setName(request.getParameter("name"));
    student.setEmail(request.getParameter("email"));
    student.setPassword(request.getParameter("password"));
    student.setTel(request.getParameter("tel"));
    student.setPostNo(request.getParameter("postNo"));
    student.setBasicAddress(request.getParameter("basicAddress"));
    student.setDetailAddress(request.getParameter("detailAddress"));
    student.setWorking(request.getParameter("working") != null);
    student.setGender(request.getParameter("gender").charAt(0));
    student.setLevel(Byte.parseByte(request.getParameter("level")));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>
<%
    txManager.startTransaction();
    try {
      if (memberDao.update(student) == 1 &&
          studentDao.update(student) == 1) {
        txManager.commit();
%>
    <p>변경했습니다.</p>
<%
      } else {
%>
    <p>해당 번호의 학생이 없습니다.</p>
<%
      }
    } catch (Exception e) {
      txManager.rollback();
%>
  <p>변경 실패입니다.</p>
<%
      e.printStackTrace();
    }
%>
</body>
</html>
<%
    response.setHeader("Refresh", "1;url=list.jsp");
%>

 

delete.jsp

<%@page import="bitcamp.myapp.dao.MemberDao"%>
<%@page import="bitcamp.util.TransactionManager"%>
<%@ page import="bitcamp.myapp.vo.Student"%>
<%@ page import="java.util.List"%>
<%@ page import="bitcamp.myapp.dao.StudentDao"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%!
  private TransactionManager txManager;
  private MemberDao memberDao;
  private StudentDao studentDao;

  @Override
  public void init() {
    ServletContext ctx = getServletContext();
    txManager = (TransactionManager) ctx.getAttribute("txManager");
    memberDao = (MemberDao) ctx.getAttribute("memberDao");
    studentDao = (StudentDao) ctx.getAttribute("studentDao");
  }
%>
<%
    int studentNo = Integer.parseInt(request.getParameter("no"));
%>
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<title>비트캠프 - NCP 1기</title>
</head>
<body>
<h1>학생(JSP)</h1>
<%
    txManager.startTransaction();
    try {
      if (studentDao.delete(studentNo) == 1 &&
          memberDao.delete(studentNo) == 1) {
        txManager.commit();
%>
    <p>삭제했습니다.</p>
<%
      } else {
%>
    <p>해당 번호의 학생이 없습니다.</p>
<%
      }
    } catch (Exception e) {
      txManager.rollback();
%>
  <p>삭제 실패입니다.</p>
<%
      e.printStackTrace();
    }
%>

</body>
</html>
<%
    response.setHeader("Refresh", "1;url=list.jsp");
%>

 

 

 


 

조언

 

*전화번호부 앱을 100% 숙지하고 쓰지 않듯이, Servlet 의 모든 기능을 숙지하고 사용하려 하지 마라.

*초보때는 기억을 떠올려서 하지 말고, 언뜻언뜻 보면서 코딩하라. 

 

 


 

과제

 

학습

- eomcs-java\eomcs-servlet\app\src\main\webapp\jsp