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

[비트캠프] 79일차(17주차1일) - Servlet(리플렉션 API, 애노테이션), myapp-54~58

끈기JK 2023. 2. 27. 12:54

 

54. 페이지 컨트롤러 생성 자동화: Reflection API 활용

 

### 54. 페이지 컨트롤러 생성 자동화: Reflection API 활용
- 리플랙션 API를 이용하여 클래스의 인스턴스 생성을 자동화하는 방법

 

웹 앱 클래스 폴더 알아내서 그 중 PageController 구현 클래스를 찾는다.

PageController 의 인스턴스를 생성한다. 생성자로 파라미터 배열 알아내서 이를 이용해 인스턴스 생성한다.

각 PageController 구현체에 스태틱 필드 path 를 선언하여 요청 경로를 저장하였다.

@WebListener
public class ContextLoaderListener implements ServletContextListener {

  List<Class<?>> controllerClasses = new ArrayList<>();

  List<Object> servicePool = new ArrayList<>();

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    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);
      BoardFileDao boardFileDao = new DaoGenerator(sqlSessionFactory).getObject(BoardFileDao.class);

      servicePool.add(new DefaultBoardService(txManager, boardDao, boardFileDao));
      servicePool.add(new DefaultStudentService(txManager, memberDao, studentDao));
      servicePool.add(new DefaultTeacherService(txManager, memberDao, teacherDao));

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

      // 웹 애플리케이션 클래스가 배치되어 있는 폴더 알아내기
      // 배치 폴더에서 PageController 구현 클래스 찾기
      findPageController(new File(ctx.getRealPath("/WEB-INF/classes")), "");

      // 페이지 컨트롤러의 인스턴스 생성하기
      createPageControllers(ctx);

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

  private void findPageController(File dir, String packageName) throws Exception {
    File[] files = dir.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class"));

    if (packageName.length() > 0) {
      packageName += ".";
    }

    for (File file : files) {
      String qName = packageName + file.getName(); // 패키지명 + 파일명  예) bitcamp.myapp.vo
      if (file.isDirectory()) {
        findPageController(file, qName);
      } else {
        Class<?> clazz = Class.forName(qName.replace(".class", ""));
        if (clazz.isInterface()) { // 인터페이스는 제외
          continue;
        }
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> c : interfaces) {
          if (c == PageController.class) {
            controllerClasses.add(clazz);
            break;
          }
        }
      }
    }
  }

  private void createPageControllers(ServletContext ctx) throws Exception {
    for (Class<?> c : controllerClasses) {
      Constructor<?> constructor = c.getConstructors()[0];
      Parameter[] params = constructor.getParameters();
      Object[] arguments = prepareArguments(params);
      Object controller = constructor.newInstance(arguments);

      try {
        Field field = c.getDeclaredField("path");
        ctx.setAttribute((String) field.get(null), controller);
      } catch (Exception e) {}
    }
  }

  private Object[] prepareArguments(Parameter[] params) {
    Object[] arguments = new Object[params.length];
    for (int i = 0; i < params.length; i++) {
      for (Object obj : servicePool) {
        if (params[i].getType().isInstance(obj)) {
          arguments[i] = obj;
          break;
        }
      }
    }
    return arguments;
  }
}

 

클라이언트 요청 URL 로 페이지 컨트롤러를 찾는다.

@MultipartConfig(maxFileSize = 1024 * 1024 * 50)
@WebServlet("/app/*")
public class DispatcherServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  public void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 요청 URL => http://localhost:8080/web/app/board/list
    // - Context Root: /web
    // - ServletPath: /app
    // - PathInfo: /board/list
    String pathInfo = request.getPathInfo();

    // 페이지 컨트롤러 실행
    if (pathInfo.equals("/")) {
      response.sendRedirect(request.getContextPath() + "/");
      return;
    }

    ServletContext ctx = getServletContext();

    // 클라이언트가 요청한 URL을 가지고 페이지 컨트롤러를 찾는다.
    PageController controller = (PageController) ctx.getAttribute(pathInfo);
    if (controller == null) {
      request.getRequestDispatcher("/NotFoundController.jsp").forward(request, response);
      return;
    }

    // 페이지 컨트롤러 실행
    String view = controller.execute(request, response);

    // 쿠키 처리
    @SuppressWarnings("unchecked")
    List<Cookie> cookies = (List<Cookie>) request.getAttribute("cookies");
    if (cookies != null) {
      for (Cookie cookie : cookies) {
        response.addCookie(cookie);
      }
    }

    // 뷰 컴포넌트 실행
    if (view != null) {
      if (view.startsWith("redirect:")) {
        response.sendRedirect(view.substring(9));
      } else {
        request.getRequestDispatcher(view).forward(request, response);
      }
    }
  }
}

 

각 페이지 컨트롤러에 스태틱 필드로 path 에 요청 URL 저장한다.

public class BoardListController implements PageController {

  public static String path = "/board/list";
  private BoardService boardService;

  public BoardListController(BoardService boardService) {
    this.boardService = boardService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("boards",
        boardService.list(request.getParameter("keyword")));
    return "/board/list.jsp";
  }
}
public class BoardFormController implements PageController {

  public static String path = "/board/form";

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) {
    return "/board/form.jsp";
  }
}

나머지 생략

 

 

 

### 55. 애노테이션을 활용하여 페이지 컨트롤러의 경로 지정하기

 

### 55. 애노테이션을 활용하여 페이지 컨트롤러의 경로 지정하기
- 애노테이션을 정의하고 활용하는 방법
- 애노테이션 값을 조회하는 방법

 

RequestMapping 애노테이션 정의한다.

@Retention 매개변수에 RUNTIME 지정해야 실행 중 값 꺼낼 수 있다.

package bitcamp.util;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 애노테이션 유지 정책 변경하기
// => CLASS : 컴파일 했을 때 바이트코드에 유지하기 (기본)
// => RUNTIME : CLASS + 실행 중에 Reflection API 를 사용해서 값 꺼내기
// => SOURCE : 컴파일 했을 때 제거하기. 즉 소스 파일에서만 유지하기.
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
  String value(); // 필수 속성
}

 

스태틱 필드 path 가져오던 것에서 애노테이션 객체 가져오는 것으로 변경한다.

@WebListener
public class ContextLoaderListener implements ServletContextListener {

/* 중략 */

  private void createPageControllers(ServletContext ctx) throws Exception {
    for (Class<?> c : controllerClasses) {
      Constructor<?> constructor = c.getConstructors()[0];
      Parameter[] params = constructor.getParameters();
      Object[] arguments = prepareArguments(params);
      Object controller = constructor.newInstance(arguments);

      RequestMapping anno = c.getAnnotation(RequestMapping.class);
      if (anno != null) {
        ctx.setAttribute(anno.value(), controller);
      }
    }
  }

/* 후략*/

}

 

애노테이션 지정하고 괄호에 요청 URL 입력한다.

애노테이션 필드가 한 개이고, 필드명이 value 일 때, (value=" ") 에서 (" ") 처럼 사용 가능하다.

@RequestMapping("/board/list")
public class BoardListController implements PageController {

  private BoardService boardService;

  public BoardListController(BoardService boardService) {
    this.boardService = boardService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("boards",
        boardService.list(request.getParameter("keyword")));
    return "/board/list.jsp";
  }
}
@RequestMapping("/board/form")
public class BoardFormController implements PageController {

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) {
    return "/board/form.jsp";
  }
}

나머지 생략

 

 

 

### 56. 페이지 컨트롤러 생성 자동화 II: 애노테이션 활용

 

### 56. 페이지 컨트롤러 생성 자동화 II: 애노테이션 활용
- 인터페이스 대신 애노테이션을 이용하여 페이지 컨트롤러를 지정하는 방법

 

 

package bitcamp.util;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 페이지 컨트롤러를 표시하는 용도
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
  String value() default "";
}

 

Controller 찾는 코드를 아래와 같이 애노테이션을 이용해 찾도록 바꾼다.

public class ContextLoaderListener implements ServletContextListener {

/* 중략 */

  private void findPageController(File dir, String packageName) throws Exception {
    File[] files = dir.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class"));

    if (packageName.length() > 0) {
      packageName += ".";
    }

    for (File file : files) {
      String qName = packageName + file.getName(); // 패키지명 + 파일명  예) bitcamp.myapp.vo
      if (file.isDirectory()) {
        findPageController(file, qName);
      } else {
        Class<?> clazz = Class.forName(qName.replace(".class", ""));
        if (clazz.isInterface()) { // 인터페이스는 제외
          continue;
        }
        Controller anno = clazz.getAnnotation(Controller.class);
        if (anno != null) {
          controllerClasses.add(clazz);
        }
      }
    }
  }
  
/* 후략 */
}

 

Controller 클래스에 @Controller 애노테이션 붙인다.

@Controller
@RequestMapping("/board/list")
public class BoardListController implements PageController {

  private BoardService boardService;

  public BoardListController(BoardService boardService) {
    this.boardService = boardService;
  }

  @Override
  public String execute(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("boards",
        boardService.list(request.getParameter("keyword")));
    return "/board/list.jsp";
  }
}

나머지 생략

 

 

 

### 57. 페이지 컨트롤러의 요청 핸들러를 지정하기: 애노테이션 활용 

 

### 57. 페이지 컨트롤러의 요청 핸들러를 지정하기: 애노테이션 활용 
- 인터페이스 대신 애노테이션을 이용하여 요청 핸들러를 지정하는 방법 
- 한 페이지 컨트롤러에 여러 개의 요청 핸들러를 두는 방법

 

RequestHandlerMapping 객체 정의하고 controller, method 를 받는다.

package bitcamp.util;

import java.lang.reflect.Method;

public class RequestHandlerMapping {
  public Object controller;
  public Method method;

  public RequestHandlerMapping() {
  }

  public RequestHandlerMapping(Object controller, Method method) {
    this.controller = controller;
    this.method = method;
  }
}

 

@RequestMapping 붙인 메서드를 요청 핸들러 등록되도록 한다.

@WebListener
public class ContextLoaderListener implements ServletContextListener {

/* 중략 */

  private void createPageControllers(ServletContext ctx) throws Exception {
    for (Class<?> c : controllerClasses) {
      Constructor<?> constructor = c.getConstructors()[0];
      Parameter[] params = constructor.getParameters();
      Object[] arguments = prepareArguments(params);
      Object controller = constructor.newInstance(arguments);

      // 페이지 컨트롤러에서 RequestMapping 애노테이션이 붙은 메서드를 찾아
      // ServletContext 보관소에 저장한다.
      Method[] methods = c.getDeclaredMethods();
      for (Method m : methods) {
        RequestMapping anno = m.getAnnotation(RequestMapping.class);
        if (anno == null) continue;
        ctx.setAttribute(anno.value(), new RequestHandlerMapping(controller, m));
        System.out.println(c.getName() + "." + m.getName() + "() 요청 핸들러 등록!");
      }
    }
  }
  
/* 후략 */
}

 

 

RequestHandlerMapping 을 찾아서 method.invoke() 실행하며 매개변수에 controller, request, response 넘겨준다.

@MultipartConfig(maxFileSize = 1024 * 1024 * 50)
@WebServlet("/app/*")
public class DispatcherServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  public void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      // 요청 URL => http://localhost:8080/web/app/board/list
      // - Context Root: /web
      // - ServletPath: /app
      // - PathInfo: /board/list
      String pathInfo = request.getPathInfo();

      // 페이지 컨트롤러 실행
      if (pathInfo.equals("/")) {
        response.sendRedirect(request.getContextPath() + "/");
        return;
      }

      ServletContext ctx = getServletContext();

      // 클라이언트가 요청한 URL을 가지고 페이지 컨트롤러의 요청 핸들러를 찾는다.
      RequestHandlerMapping requestHandlerMapping = (RequestHandlerMapping) ctx.getAttribute(pathInfo);
      if (requestHandlerMapping == null) {
        request.getRequestDispatcher("/NotFoundController.jsp").forward(request, response);
        return;
      }

      // 요청 핸들러 실행
      String view = (String) requestHandlerMapping.method.invoke(
          requestHandlerMapping.controller, request, response); // controller.execute(request, response)

      // 쿠키 처리
      @SuppressWarnings("unchecked")
      List<Cookie> cookies = (List<Cookie>) request.getAttribute("cookies");
      if (cookies != null) {
        for (Cookie cookie : cookies) {
          response.addCookie(cookie);
        }
      }

      // 뷰 컴포넌트 실행
      if (view != null) {
        if (view.startsWith("redirect:")) {
          response.sendRedirect(view.substring(9));
        } else {
          request.getRequestDispatcher(view).forward(request, response);
        }
      }
    } catch (Exception e) {
      request.getRequestDispatcher("/ServerError.jsp").forward(request, response);
      e.printStackTrace();
    }
  }
}

 

커맨드 패턴 적용하며 분리했던 클래스를 관련 컨트롤러 메서드끼리 하나의 파일로 묶는다.

메서드 명 execute 에서 요청 URL 에 맞게 변경한다.

@Controller
public class AuthController {

  private StudentService studentService;
  private TeacherService teacherService;

  public AuthController(StudentService studentService, TeacherService teacherService) {
    this.studentService = studentService;
    this.teacherService = teacherService;
  }

  @RequestMapping("/auth/form")
  public String form(HttpServletRequest request, HttpServletResponse response) {
    return "/auth/form.jsp";
  }

  @RequestMapping("/auth/login")
  public String login(HttpServletRequest request, HttpServletResponse response) {

    String usertype = request.getParameter("usertype");
    String email = request.getParameter("email");
    String password = request.getParameter("password");

    if (request.getParameter("saveEmail") != null) {
      Cookie cookie = new Cookie("email", email);
      cookie.setMaxAge(60 * 60 * 24 * 30); // 30일 동안 유지
      response.addCookie(cookie);

    } else {
      Cookie cookie = new Cookie("email", "");
      cookie.setMaxAge(0);
      response.addCookie(cookie);
    }

    Member member = null;
    switch (usertype) {
      case "student":
        member = studentService.get(email, password);
        break;
      case "teacher":
        member = teacherService.get(email, password);
        break;
    }

    if (member != null) {
      HttpSession session = request.getSession();
      session.setAttribute("loginUser", member);
      return "redirect:../";
    } else {
      request.setAttribute("error", "loginfail");
      return "/auth/form.jsp";
    }

  }

  @RequestMapping("/auth/logout")
  public String logout(HttpServletRequest request, HttpServletResponse response) {
    request.getSession().invalidate();
    return "redirect:../";
  }

  @RequestMapping("/auth/fail")
  public String fail(HttpServletRequest request, HttpServletResponse response) {
    return "/auth/fail.jsp";
  }
}

 

@Controller
public class BoardController {

  private BoardService boardService;

  public BoardController(BoardService boardService) {
    this.boardService = boardService;
  }

  @RequestMapping("/board/form")
  public String form(HttpServletRequest request, HttpServletResponse response) {
    return "/board/form.jsp";
  }

  @RequestMapping("/board/insert")
  public String insert(HttpServletRequest request, HttpServletResponse response) {
    try {
      DiskFileItemFactory factory = new DiskFileItemFactory();
      ServletFileUpload upload = new ServletFileUpload(factory);
      List<FileItem> items = upload.parseRequest(request);
      Map<String,String> paramMap = new HashMap<>();
      List<FileItem> files = new ArrayList<>();

      for (FileItem item : items) {
        if (item.isFormField()) {
          paramMap.put(item.getFieldName(), item.getString("UTF-8"));
        } else {
          files.add(item);
        }
      }

      Board board = new Board();
      board.setTitle(paramMap.get("title"));
      board.setContent(paramMap.get("content"));

      Member loginUser = (Member) request.getSession().getAttribute("loginUser");
      Member writer = new Member();
      writer.setNo(loginUser.getNo());
      board.setWriter(writer);

      List<BoardFile> boardFiles = new ArrayList<>();
      for (FileItem file : files) {
        if (file.getSize() == 0) {
          continue;
        }
        String filename = UUID.randomUUID().toString();
        file.write(new File(request.getServletContext().getRealPath("/board/upload/" + filename)));

        BoardFile boardFile = new BoardFile();
        boardFile.setOriginalFilename(file.getName());
        boardFile.setFilepath(filename);
        boardFile.setMimeType(file.getContentType());
        boardFiles.add(boardFile);
      }
      board.setAttachedFiles(boardFiles);

      boardService.add(board);

    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }
    return "/board/insert.jsp";
  }

  @RequestMapping("/board/list")
  public String list(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("boards",
        boardService.list(request.getParameter("keyword")));
    return "/board/list.jsp";
  }

  @RequestMapping("/board/view")
  public String view(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("board",
        boardService.get(Integer.parseInt(request.getParameter("no"))));
    return"/board/view.jsp";
  }

  @RequestMapping("/board/update")
  public String update(HttpServletRequest request, HttpServletResponse response) {
    try {
      Member loginUser = (Member) request.getSession().getAttribute("loginUser");

      Board board = new Board();
      board.setNo(Integer.parseInt(request.getParameter("no")));
      board.setTitle(request.getParameter("title"));
      board.setContent(request.getParameter("content"));

      Board old = boardService.get(board.getNo());
      if (old.getWriter().getNo() != loginUser.getNo()) {
        return "redirect:../auth/fail";
      }

      Collection<Part> parts = request.getParts();
      List<BoardFile> boardFiles = new ArrayList<>();
      for (Part part : parts) {
        if (!part.getName().equals("files") || part.getSize() == 0) {
          continue;
        }

        String filename = UUID.randomUUID().toString();
        part.write(request.getServletContext().getRealPath("/board/upload/" + filename));

        BoardFile boardFile = new BoardFile();
        boardFile.setOriginalFilename(part.getSubmittedFileName());
        boardFile.setFilepath(filename);
        boardFile.setMimeType(part.getContentType());
        boardFile.setBoardNo(board.getNo());
        boardFiles.add(boardFile);
      }
      board.setAttachedFiles(boardFiles);

      boardService.update(board);

    }  catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }

    return "/board/update.jsp";
  }

  @RequestMapping("/board/delete")
  public String delete(HttpServletRequest request, HttpServletResponse response) {
    try {
      Member loginUser = (Member) request.getSession().getAttribute("loginUser");
      int boardNo = Integer.parseInt(request.getParameter("no"));

      Board old = boardService.get(boardNo);
      if (old.getWriter().getNo() != loginUser.getNo()) {
        return "redirect:../auth/fail";
      }
      boardService.delete(boardNo);

    }  catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }
    return "/board/delete.jsp";
  }

  @RequestMapping("/board/filedelete")
  public String filedelete(HttpServletRequest request, HttpServletResponse response) {

    Member loginUser = (Member) request.getSession().getAttribute("loginUser");
    int boardNo = Integer.parseInt(request.getParameter("boardNo"));

    Board old = boardService.get(boardNo);
    if (old.getWriter().getNo() != loginUser.getNo()) {
      return "redirect:../auth/fail";
    } else {
      boardService.deleteFile(Integer.parseInt(request.getParameter("fileNo")));
      return "redirect:view?no=" + boardNo;
    }
  }
}

 

@Controller
public class DownloadController {

  private BoardService boardService;

  public DownloadController(BoardService boardService) {
    this.boardService = boardService;
  }

  @RequestMapping("/download/boardfile")
  public String execute(HttpServletRequest request, HttpServletResponse response) {

    try {
      int fileNo = Integer.parseInt(request.getParameter("fileNo"));

      BoardFile boardFile = boardService.getFile(fileNo);
      if (boardFile == null) {
        throw new RuntimeException("파일 정보 없음!");
      }

      File downloadFile = new File(
          request.getServletContext().getRealPath("/board/upload/" + boardFile.getFilepath()));
      if (!downloadFile.exists()) {
        throw new RuntimeException("파일이 존재하지 않음!");
      }

      response.setContentType(boardFile.getMimeType());

      response.setHeader("Content-Disposition",
          String.format("attachment; filename=\"%s\"", boardFile.getOriginalFilename()));

      try (
          BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(downloadFile));
          BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());) {

        int b;
        while ((b = fileIn.read()) != -1) {
          out.write(b);
        }
        out.flush();
      }

    } catch (Exception e) {
      e.printStackTrace();
      return "/downloadfail.jsp";
    }
    return null;
  }
}

 

@Controller
public class StudentController {

  private StudentService studentService;

  public StudentController(StudentService studentService) {
    this.studentService = studentService;
  }

  @RequestMapping("/student/form")
  public String form(HttpServletRequest request, HttpServletResponse response) {
    return "/student/form.jsp";
  }

  @RequestMapping("/student/insert")
  public String insert(HttpServletRequest request, HttpServletResponse response) {
    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")));

    try {
      studentService.add(student);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/insert.jsp";
  }

  @RequestMapping("/student/list")
  public String list(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("students", studentService.list(request.getParameter("keyword")));
    return "/student/list.jsp";
  }

  @RequestMapping("/student/view")
  public String view(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("student",
        studentService.get(Integer.parseInt(request.getParameter("no"))));
    return"/student/view.jsp";
  }

  @RequestMapping("/student/update")
  public String update(HttpServletRequest request, HttpServletResponse response) {
    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")));

    try {
      studentService.update(student);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/update.jsp";
  }

  @RequestMapping("/student/delete")
  public String delete(HttpServletRequest request, HttpServletResponse response) {
    try {
      studentService.delete(Integer.parseInt(request.getParameter("no")));
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/delete.jsp";
  }
}

 

@Controller
public class TeacherController {

  private TeacherService teacherService;

  public TeacherController(TeacherService teacherService) {
    this.teacherService = teacherService;
  }

  @RequestMapping("/teacher/form")
  public String form(HttpServletRequest request, HttpServletResponse response) {
    return "/teacher/form.jsp";
  }

  @RequestMapping("/teacher/insert")
  public String insert(HttpServletRequest request, HttpServletResponse response) {
    Teacher teacher = new Teacher();
    teacher.setName(request.getParameter("name"));
    teacher.setEmail(request.getParameter("email"));
    teacher.setPassword(request.getParameter("password"));
    teacher.setTel(request.getParameter("tel"));
    teacher.setDegree(Integer.parseInt(request.getParameter("degree")));
    teacher.setSchool(request.getParameter("school"));
    teacher.setMajor(request.getParameter("major"));
    teacher.setWage(Integer.parseInt(request.getParameter("wage")));

    try {
      teacherService.add(teacher);

    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/insert.jsp";
  }

  @RequestMapping("/teacher/list")
  public String list(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("teachers", teacherService.list());
    return "/teacher/list.jsp";
  }

  @RequestMapping("/teacher/view")
  public String view(HttpServletRequest request, HttpServletResponse response) {
    request.setAttribute("teacher",
        teacherService.get(Integer.parseInt(request.getParameter("no"))));
    return "/teacher/view.jsp";
  }

  @RequestMapping("/teacher/update")
  public String update(HttpServletRequest request, HttpServletResponse response) {
    Teacher teacher = new Teacher();
    teacher.setNo(Integer.parseInt(request.getParameter("no")));
    teacher.setName(request.getParameter("name"));
    teacher.setEmail(request.getParameter("email"));
    teacher.setPassword(request.getParameter("password"));
    teacher.setTel(request.getParameter("tel"));
    teacher.setDegree(Integer.parseInt(request.getParameter("degree")));
    teacher.setSchool(request.getParameter("school"));
    teacher.setMajor(request.getParameter("major"));
    teacher.setWage(Integer.parseInt(request.getParameter("wage")));

    try {
      teacherService.update(teacher);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/update.jsp";
  }

  @RequestMapping("/teacher/delete")
  public String delete(HttpServletRequest request, HttpServletResponse response) {
    try {
      teacherService.delete(Integer.parseInt(request.getParameter("no")));
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/delete.jsp";
  }
}

 

 

 

### 58. 요청 핸들러의 파라미터 값 자동으로 주입하기: 애노테이션 활용 

 

### 58. 요청 핸들러의 파라미터 값 자동으로 주입하기: 애노테이션 활용 
- 요청 핸들러의 파라미터를 분석하여 호출에 필요한 아규먼트를 자동으로 주입한다.

 

파라미터 주입을 위한 애노테이션 정의한다.

@Target 으로 파라미터용이라 알린다.

package bitcamp.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface RequestParam {
  String value() default "";
}

 

prepareRequestHandlerArguments(...) 메서드 정의한다.

파라미터 배열에서 하나씩 꺼내서 어떤 클래스인지 비교해서 객체를 넣어준다.

@RequestParam 애노테이션 붙은 경우 paramName 에 따라 요청으로 들어온 값을 넣어준다.

@MultipartConfig(maxFileSize = 1024 * 1024 * 50)
@WebServlet("/app/*")
public class DispatcherServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  public void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      // 요청 URL => http://localhost:8080/web/app/board/list
      // - Context Root: /web
      // - ServletPath: /app
      // - PathInfo: /board/list
      String pathInfo = request.getPathInfo();

      // 페이지 컨트롤러 실행
      if (pathInfo.equals("/")) {
        response.sendRedirect(request.getContextPath() + "/");
        return;
      }

      ServletContext ctx = getServletContext();

      // 클라이언트가 요청한 URL을 가지고 페이지 컨트롤러의 요청 핸들러를 찾는다.
      RequestHandlerMapping requestHandlerMapping = (RequestHandlerMapping) ctx.getAttribute(pathInfo);
      if (requestHandlerMapping == null) {
        request.getRequestDispatcher("/NotFoundController.jsp").forward(request, response);
        return;
      }

      // 요청 핸들러 실행
      Object[] arguments = prepareRequestHandlerArguments(requestHandlerMapping.method, request, response);
      String view = (String) requestHandlerMapping.method.invoke(
          requestHandlerMapping.controller, arguments); // controller.execute(request, response)

      // 쿠키 처리
      @SuppressWarnings("unchecked")
      List<Cookie> cookies = (List<Cookie>) request.getAttribute("cookies");
      if (cookies != null) {
        for (Cookie cookie : cookies) {
          response.addCookie(cookie);
        }
      }

      // 뷰 컴포넌트 실행
      if (view != null) {
        if (view.startsWith("redirect:")) {
          response.sendRedirect(view.substring(9));
        } else {
          request.getRequestDispatcher(view).forward(request, response);
        }
      }
    } catch (Exception e) {
      request.getRequestDispatcher("/ServerError.jsp").forward(request, response);
      e.printStackTrace();
    }

  }

  private Object[] prepareRequestHandlerArguments(
      Method method,
      HttpServletRequest request,
      HttpServletResponse response) throws Exception {

    Parameter[] params = method.getParameters();
    Object[] arguments = new Object[params.length];

    System.out.println("파라미터 값 준비함!");
    for (int i = 0; i < params.length; i++) {
      Class<?> paramType = params[i].getType();

      // 파라미터의 타입에 따라 전달될 아규먼트(값)을 준비한다.
      if (paramType == HttpSession.class) {
        arguments[i] = request.getSession();
        System.out.println("    - 세션 객체 준비");
      } else if (paramType  == HttpServletRequest.class || paramType  == ServletRequest.class) {
        arguments[i] = request;
        System.out.println("    - 요청 객체 준비");
      } else if (paramType  == HttpServletResponse.class || paramType == ServletResponse.class) {
        arguments[i] = response;
        System.out.println("    - 응답 객체 준비");
      } else {
        RequestParam anno = params[i].getAnnotation(RequestParam.class);
        if (anno != null) {
          String paramName = anno.value();
          String paramValue = request.getParameter(paramName);

          if (paramType == String.class) {
            arguments[i] = paramValue;
          } else if (paramType == byte.class || paramType == Byte.class) {
            arguments[i] = Byte.parseByte(paramValue);
          } else if (paramType == char.class || paramType == Character.class) {
            arguments[i] = paramValue.charAt(0);
          } else if (paramType == int.class || paramType == Integer.class) {
            arguments[i] = Integer.parseInt(paramValue);
          } else if (paramType == float.class || paramType == Float.class) {
            arguments[i] = Float.parseFloat(paramValue);
          } else if (paramType == boolean.class || paramType == Boolean.class) {
            arguments[i] = paramValue != null;
          } else if (paramType == Part.class) {
            arguments[i] = request.getPart(paramName);
          } else if (paramType == Part[].class) {
            Collection<Part> parts = request.getParts();
            List<Part> paramParts = new ArrayList<>();
            for (Part part : parts) {
              if (!part.getName().equals(paramName)) {
                continue;
              }
              paramParts.add(part);
            }
            arguments[i] = paramParts.toArray(new Part[] {});
          }
          System.out.println("    - 요청 파라미터 값 준비");
        } else {
          arguments[i] = null;
        }
      }
    }
    return arguments;
  }
}

 

컨트롤러에 getParameter 로 전달받을때 @RequestParam 애노테이션 사용한다.

HttpServlet, Session 객체 또한 DispatcherServlet 이 값 넣어준다.

@Controller
public class AuthController {

  private StudentService studentService;
  private TeacherService teacherService;

  public AuthController(StudentService studentService, TeacherService teacherService) {
    this.studentService = studentService;
    this.teacherService = teacherService;
  }

  @RequestMapping("/auth/form")
  public String form() {
    return "/auth/form.jsp";
  }

  @RequestMapping("/auth/login")
  public String login(
      @RequestParam("usertype") String usertype,
      @RequestParam("email") String email,
      @RequestParam("password") String password,
      @RequestParam("saveEmail") String saveEmail,
      HttpServletRequest request,
      HttpServletResponse response,
      HttpSession session) {

    if (saveEmail != null) {
      Cookie cookie = new Cookie("email", email);
      cookie.setMaxAge(60 * 60 * 24 * 30); // 30일 동안 유지
      response.addCookie(cookie);

    } else {
      Cookie cookie = new Cookie("email", "");
      cookie.setMaxAge(0);
      response.addCookie(cookie);
    }

    Member member = null;
    switch (usertype) {
      case "student":
        member = studentService.get(email, password);
        break;
      case "teacher":
        member = teacherService.get(email, password);
        break;
    }

    if (member != null) {
      session.setAttribute("loginUser", member);
      return "redirect:../";
    } else {
      request.setAttribute("error", "loginfail");
      return "/auth/form.jsp";
    }

  }

  @RequestMapping("/auth/logout")
  public String logout(HttpSession session) {
    session.invalidate();
    return "redirect:../";
  }

  @RequestMapping("/auth/fail")
  public String fail() {
    return "/auth/fail.jsp";
  }
}

 

@Controller
public class BoardController {

  private BoardService boardService;

  public BoardController(BoardService boardService) {
    this.boardService = boardService;
  }

  @RequestMapping("/board/form")
  public String form() {
    return "/board/form.jsp";
  }

  @RequestMapping("/board/insert")
  public String insert(
      @RequestParam("title") String title,
      @RequestParam("content") String content,
      @RequestParam("files") Part[] files,
      HttpServletRequest request,
      HttpSession session) {
    try {
      Board board = new Board();
      board.setTitle(title);
      board.setContent(content);

      Member loginUser = (Member) session.getAttribute("loginUser");
      Member writer = new Member();
      writer.setNo(loginUser.getNo());
      board.setWriter(writer);

      List<BoardFile> boardFiles = new ArrayList<>();
      for (Part part : files) {
        if (part.getSize() == 0) {
          continue;
        }

        String filename = UUID.randomUUID().toString();
        part.write(request.getServletContext().getRealPath("/board/upload/" + filename));

        BoardFile boardFile = new BoardFile();
        boardFile.setOriginalFilename(part.getSubmittedFileName());
        boardFile.setFilepath(filename);
        boardFile.setMimeType(part.getContentType());
        boardFiles.add(boardFile);
      }
      board.setAttachedFiles(boardFiles);

      boardService.add(board);

    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }
    return "/board/insert.jsp";
  }

  @RequestMapping("/board/list")
  public String list(
      @RequestParam("keyword") String keyword,
      HttpServletRequest request) {

    request.setAttribute("boards", boardService.list(keyword));
    return "/board/list.jsp";
  }

  @RequestMapping("/board/view")
  public String view(
      @RequestParam("no") int no,
      HttpServletRequest request) {

    request.setAttribute("board", boardService.get(no));
    return"/board/view.jsp";
  }

  @RequestMapping("/board/update")
  public String update(
      @RequestParam("no") int no,
      @RequestParam("title") String title,
      @RequestParam("content") String content,
      @RequestParam("files") Part[] files,
      HttpServletRequest request,
      HttpSession session) {
    try {
      Member loginUser = (Member) session.getAttribute("loginUser");

      Board board = new Board();
      board.setNo(no);
      board.setTitle(title);
      board.setContent(content);

      Board old = boardService.get(board.getNo());
      if (old.getWriter().getNo() != loginUser.getNo()) {
        return "redirect:../auth/fail";
      }

      List<BoardFile> boardFiles = new ArrayList<>();
      for (Part part : files) {
        if (part.getSize() == 0) {
          continue;
        }

        String filename = UUID.randomUUID().toString();
        part.write(request.getServletContext().getRealPath("/board/upload/" + filename));

        BoardFile boardFile = new BoardFile();
        boardFile.setOriginalFilename(part.getSubmittedFileName());
        boardFile.setFilepath(filename);
        boardFile.setMimeType(part.getContentType());
        boardFile.setBoardNo(board.getNo());
        boardFiles.add(boardFile);
      }
      board.setAttachedFiles(boardFiles);

      boardService.update(board);

    }  catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }

    return "/board/update.jsp";
  }

  @RequestMapping("/board/delete")
  public String delete(
      @RequestParam("no") int boardNo,
      HttpServletRequest request,
      HttpSession session) {
    try {
      Member loginUser = (Member) session.getAttribute("loginUser");

      Board old = boardService.get(boardNo);
      if (old.getWriter().getNo() != loginUser.getNo()) {
        return "redirect:../auth/fail";
      }
      boardService.delete(boardNo);

    }  catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "data");
    }
    return "/board/delete.jsp";
  }

  @RequestMapping("/board/filedelete")
  public String filedelete(
      @RequestParam("boardNo") int boardNo,
      @RequestParam("fileNo") int fileNo,
      HttpSession session) {

    Member loginUser = (Member) session.getAttribute("loginUser");

    Board old = boardService.get(boardNo);
    if (old.getWriter().getNo() != loginUser.getNo()) {
      return "redirect:../auth/fail";
    } else {
      boardService.deleteFile(fileNo);
      return "redirect:view?no=" + boardNo;
    }
  }
}

 

@Controller
public class DownloadController {

  private BoardService boardService;

  public DownloadController(BoardService boardService) {
    this.boardService = boardService;
  }

  @RequestMapping("/download/boardfile")
  public String execute(
      @RequestParam("fileNo") int fileNo,
      HttpServletRequest request,
      HttpServletResponse response) {

    try {
      BoardFile boardFile = boardService.getFile(fileNo);
      if (boardFile == null) {
        throw new RuntimeException("파일 정보 없음!");
      }

      File downloadFile = new File(
          request.getServletContext().getRealPath("/board/upload/" + boardFile.getFilepath()));
      if (!downloadFile.exists()) {
        throw new RuntimeException("파일이 존재하지 않음!");
      }

      response.setContentType(boardFile.getMimeType());

      response.setHeader("Content-Disposition",
          String.format("attachment; filename=\"%s\"", boardFile.getOriginalFilename()));

      try (
          BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream(downloadFile));
          BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());) {

        int b;
        while ((b = fileIn.read()) != -1) {
          out.write(b);
        }
        out.flush();
      }

    } catch (Exception e) {
      e.printStackTrace();
      return "/downloadfail.jsp";
    }
    return null;
  }
}

 

@Controller
public class StudentController {

  private StudentService studentService;

  public StudentController(StudentService studentService) {
    this.studentService = studentService;
  }

  @RequestMapping("/student/form")
  public String form() {
    return "/student/form.jsp";
  }

  @RequestMapping("/student/insert")
  public String insert(
      @RequestParam("name") String name,
      @RequestParam("email") String email,
      @RequestParam("password") String password,
      @RequestParam("tel") String tel,
      @RequestParam("postNo") String postNo,
      @RequestParam("basicAddress") String basicAddress,
      @RequestParam("detailAddress") String detailAddress,
      @RequestParam("working") boolean working,
      @RequestParam("gender") char gender,
      @RequestParam("level") byte level,
      HttpServletRequest request) {

    Student student = new Student();
    student.setName(name);
    student.setEmail(email);
    student.setPassword(password);
    student.setTel(tel);
    student.setPostNo(postNo);
    student.setBasicAddress(basicAddress);
    student.setDetailAddress(detailAddress);
    student.setWorking(working);
    student.setGender(gender);
    student.setLevel(level);

    try {
      studentService.add(student);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/insert.jsp";
  }

  @RequestMapping("/student/list")
  public String list(
      @RequestParam("keyword") String keyword,
      HttpServletRequest request) {
    request.setAttribute("students", studentService.list(keyword));
    return "/student/list.jsp";
  }

  @RequestMapping("/student/view")
  public String view(
      @RequestParam("no") int no,
      HttpServletRequest request) {
    request.setAttribute("student", studentService.get(no));
    return"/student/view.jsp";
  }

  @RequestMapping("/student/update")
  public String update(
      @RequestParam("no") int no,
      @RequestParam("name") String name,
      @RequestParam("email") String email,
      @RequestParam("password") String password,
      @RequestParam("tel") String tel,
      @RequestParam("postNo") String postNo,
      @RequestParam("basicAddress") String basicAddress,
      @RequestParam("detailAddress") String detailAddress,
      @RequestParam("working") boolean working,
      @RequestParam("gender") char gender,
      @RequestParam("level") byte level,
      HttpServletRequest request) {

    Student student = new Student();
    student.setNo(no);
    student.setName(name);
    student.setEmail(email);
    student.setPassword(password);
    student.setTel(tel);
    student.setPostNo(postNo);
    student.setBasicAddress(basicAddress);
    student.setDetailAddress(detailAddress);
    student.setWorking(working);
    student.setGender(gender);
    student.setLevel(level);

    try {
      studentService.update(student);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/update.jsp";
  }

  @RequestMapping("/student/delete")
  public String delete(
      @RequestParam("no") int no,
      HttpServletRequest request) {
    try {
      studentService.delete(no);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/student/delete.jsp";
  }
}

 

@Controller
public class TeacherController {

  private TeacherService teacherService;

  public TeacherController(TeacherService teacherService) {
    this.teacherService = teacherService;
  }

  @RequestMapping("/teacher/form")
  public String form() {
    return "/teacher/form.jsp";
  }

  @RequestMapping("/teacher/insert")
  public String insert(
      @RequestParam("name") String name,
      @RequestParam("email") String email,
      @RequestParam("password") String password,
      @RequestParam("tel") String tel,
      @RequestParam("degree") int degree,
      @RequestParam("school") String school,
      @RequestParam("major") String major,
      @RequestParam("wage") int wage,
      HttpServletRequest request) {

    Teacher teacher = new Teacher();
    teacher.setName(name);
    teacher.setEmail(email);
    teacher.setPassword(password);
    teacher.setTel(tel);
    teacher.setDegree(degree);
    teacher.setSchool(school);
    teacher.setMajor(major);
    teacher.setWage(wage);

    try {
      teacherService.add(teacher);

    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/insert.jsp";
  }

  @RequestMapping("/teacher/list")
  public String list(HttpServletRequest request) {
    request.setAttribute("teachers", teacherService.list());
    return "/teacher/list.jsp";
  }

  @RequestMapping("/teacher/view")
  public String view(
      @RequestParam("no") int no,
      HttpServletRequest request, HttpServletResponse response) {

    request.setAttribute("teacher", teacherService.get(no));
    return "/teacher/view.jsp";
  }

  @RequestMapping("/teacher/update")
  public String update(
      @RequestParam("no") int no,
      @RequestParam("name") String name,
      @RequestParam("email") String email,
      @RequestParam("password") String password,
      @RequestParam("tel") String tel,
      @RequestParam("degree") int degree,
      @RequestParam("school") String school,
      @RequestParam("major") String major,
      @RequestParam("wage") int wage,
      HttpServletRequest request) {

    Teacher teacher = new Teacher();
    teacher.setNo(no);
    teacher.setName(name);
    teacher.setEmail(email);
    teacher.setPassword(password);
    teacher.setTel(tel);
    teacher.setDegree(degree);
    teacher.setSchool(school);
    teacher.setMajor(major);
    teacher.setWage(wage);

    try {
      teacherService.update(teacher);
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/update.jsp";
  }

  @RequestMapping("/teacher/delete")
  public String delete(
      @RequestParam("no") int no,
      HttpServletRequest request) {
    try {
      teacherService.delete(Integer.parseInt(request.getParameter("no")));
    } catch (Exception e) {
      e.printStackTrace();
      request.setAttribute("error", "other");
    }
    return "/teacher/delete.jsp";
  }
}

 

 

 

최종 프로젝트

 

  • 주제 : 팀 별 자유 주제
  • 기간 : 2023-04-01 ~ 2023-05-04
  • 수행 :
    • 주제 후보 제안 발표 : 2023-03-06 (월) 
      • UI 프로토타입 제시
        • 핵심 UI
      • 프로젝트 선정 이유
        • 현황 및 문제점
        • 해결 방안 및 이점

 

 

 


 

 

조언

 

*신입에게 중요한 것은 업무 파악이다. 어떤 테이블이 어떤 의미인지 알아야 코딩을 할 수 있다. 업무 파악 시간 주는 회사는 좋은 회사이다.

*프로젝트시 기능 단위로 인원 분배 해야한다. 수평적으로 인원 분배하면 안된다.

*처음부터 4주짜리로 기획하면 안되고 크게 기획하고 우선 중요한 기능을 구현해야 한다.

 

 


 

과제

 

학습

- com.eomcs.reflect

- com.eomcs.annotation