[비트캠프] 79일차(17주차1일) - Servlet(리플렉션 API, 애노테이션), myapp-54~58
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
- 프로젝트 선정 이유
- 현황 및 문제점
- 해결 방안 및 이점
- UI 프로토타입 제시
- 주제 후보 제안 발표 : 2023-03-06 (월)
조언
*신입에게 중요한 것은 업무 파악이다. 어떤 테이블이 어떤 의미인지 알아야 코딩을 할 수 있다. 업무 파악 시간 주는 회사는 좋은 회사이다.
*프로젝트시 기능 단위로 인원 분배 해야한다. 수평적으로 인원 분배하면 안된다.
*처음부터 4주짜리로 기획하면 안되고 크게 기획하고 우선 중요한 기능을 구현해야 한다.
과제
학습
- com.eomcs.reflect
- com.eomcs.annotation