1. ModelMapper 라이브러리
- TodoService와 TodoDTO에 Lombok을 적용시키는 것이 더 좋기 때문에 다시 구성하기
// TodoDTO
package org.zerock.jdbcex.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
@Builder
// @Data는 getter / setter / toString / equals / hashCode 등을 모두 컴파일할 때 생성
// VO에서 getter만 사용하여 읽기 전용으로 구성하는 것과 차이가 있음
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TodoDTO {
private Long tno;
private String title;
private LocalDate dueDate;
private Boolean finished;
}
// TodoVO
package org.zerock.jdbcex.domain;
import lombok.*;
import java.time.LocalDate;
@Getter
@Builder
@ToString
// 파라미터가 없는 생성자와 모든 필드값이 필요한 생성자를 만들어냄
@AllArgsConstructor
@NoArgsConstructor
public class TodoVO {
private Long tno;
private String title;
private LocalDate dueDate;
private boolean finished;
}
- DTO → VO, VO → DTO 변환은 ModelMapper 라이브러리를 이용해 처리
- ModelMapper 라이브러리는 build.gradle파일에서 추가
- dependencies에 " implementation group: 'org.modelmapper', name: 'modelmapper', version: '3.0.0' " 추가
- 프로젝트에 util 패키지 추가 > ModelMapper의 설정을 변경하고 쉽게 사용할 수 있는 MapperUtil을 enum으로 생성
package org.zerock.jdbcex.util;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
public enum MapperUtil {
INSTANCE;
private ModelMapper modelMapper;
MapperUtil() {
this.modelMapper = new ModelMapper();
// ModelMapper의 설정을 변경하려면 getConfiguration()을 이용해서 private로 선언된 필드도 접근할 수 있도록 설정을 변경
this.modelMapper.getConfiguration()
.setFieldMatchingEnabled(true)
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
.setMatchingStrategy(MatchingStrategies.STRICT);
}
// get()으로 ModelMapper를 사용할 수 있도록 구성
public ModelMapper get() {
return modelMapper;
}
}
[자바 웹 개발 워크북] 2.2 - 프로젝트 내 JDBC 구현
1. Lombok 라이브러리 Lombok을 사용하면 자바에서 클래스 작성 시 getter / setter 생성 또는 생성자 함수 정의하는 작업을 간단한 annotation 추가만으로 끝낼 수 있음 getter / setter: @Getter, @Setter, @Data 등을
data-science-study.tistory.com
실습(이전 글에 이어서)
7) TodoService와 ModelMapper 테스트
- DTO와 VO 둘 다 이용해야 하는 TodoService를 구성하고 ModelMapper의 동작 확인
// TodoService
package org.zerock.jdbcex.service;
import org.modelmapper.ModelMapper;
import org.zerock.jdbcex.dao.TodoDAO;
import org.zerock.jdbcex.domain.TodoVO;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.MapperUtil;
public enum TodoService {
INSTANCE;
private TodoDAO dao;
private ModelMapper modelMapper;
TodoService() {
dao = new TodoDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
// TodoDTO를 파라미터로 받아서 TodoVO로 변환하는 과정을 ModelMapper로 처리
// print하여 잘 변환됐는지 확인하고
// TodoDAO를 이용해서 TodoVO를 insert
public void register(TodoDTO todoDTO) throws Exception {
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
System.out.println("todoVO: " + todoVO);
dao.insert(todoVO);
// insert는 int를 반환하므로 이를 이용한 예외처리도 가능
}
}
- TodoServieTests 에서 테스트용 코드 작성
package org.zerock.service;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import java.time.LocalDate;
public class TodoServiceTests {
private TodoService todoService;
@BeforeEach
public void ready() {
todoService = TodoService.INSTANCE;
}
@Test
public void testRegister() throws Exception {
TodoDTO todoDTO = TodoDTO.builder()
.title("JDBC Test Title")
.dueDate(LocalDate.now())
.build();
todoService.register(todoDTO);
}
}
2. Log4j2와 @Log4j2
- 레벨을 설정하여 개발할 때 필요한 레벨의 로그와 실제 운영 시 필요한 레벨의 로그를 구분
- 예를 들어, System.out.println은 개발 시에는 확인용으로 많이 사용하지만, 개발이 끝난 후 운영 시에는 필요 x
- Log4j2에는 Appender를 사용하여 어떤 방식으로 기록할 것인지 결정(System.out.println 대신 Consol Appender 사용)
- Log4j2의 레벨은 중요도의 개념, 로그의 레벨을 지정하면 해당 레벨 이상의 로그들만 출력되어, 개발할 때는 로그의 레벨을 INFO 이하로 개발, 운영할 때는 ERROR나 WARN 이상으로 개발
- 레벨이 INFO이면 로그에 INFO, WARN, ERROR, FATAL이 출력
- 레벨이 ERROR이면 로그에 ERROR, FATAL이 출력
- Log4j2 사용을 위해 build.gradle에 라이브러리 설치
// build.gradle의 dependencies에 추가
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.2'
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.17.2'
implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.17.2'
1) log4j2.xml 설정 파일
- log4j2 라이브러리의 설정은 log4j2.xml 파일 이용, 해당 파일에 레벨이나 Appender 지정
- 프로젝트의 resources 폴더 > log4j2.xml 생성
// log4j2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status = "WARN">
<Appenders>
<Console name = "Console" target = "SYSTEM_OUT">
<PatternLayout pattern = "%d{HH:mm:ss.SSS} [%t] %-5level %logger{36}- %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level = "info">
<Appender ref = "Console"/>
</Root>
</Loggers>
</Configuration>
2) Log4j2 어노테이션
- 기존 TodoService 코드에 @Log4j2 어노테이션 추가하고, System.out.println을 log.info()로 변경
package org.zerock.jdbcex.util.service;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.zerock.jdbcex.dao.TodoDAO;
import org.zerock.jdbcex.domain.TodoVO;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.MapperUtil;
@Log4j2
public enum TodoService {
INSTANCE;
private TodoDAO dao;
private ModelMapper modelMapper;
TodoService() {
dao = new TodoDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
public void register(TodoDTO todoDTO) throws Exception {
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
// System.out.println("todoVO: " + todoVO);
log.info(todoVO);
dao.insert(todoVO);
}
}
- TodoServiceTests에서 testRegister()를 실행하면 변경된 로그가 출력됨
- 또한, HikariCP의 로그도 다르게 출력됨(HikariCP가 내부적으로 slf4j 라이브러리를 이용하고 있고, log4j-slf4j-impl 라이브러리가 Log4j2를 이용할 수 있도록 설정되기 때문)
- 테스트 환경에서 @Log4j2 사용하기
- 테스트 환경에서 @Log4j2 기능을 활용하기 위해 테스트 환경에서도 어노테이션을 처리하는 testAnnotationprocessor와 testCompileOnly 설정 추가
// build gradle의 dependencies에 추가
testCompileOnly group: 'org.projectlombok', name: 'lombok', version: '1.18.24'
testAnnotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.24'
- TodoServiceTests에 테스트 코드를 @Log4j2를 이용하도록 수정
package org.zerock.service;
import lombok.extern.log4j.Log4j2;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.service.TodoService;
import java.time.LocalDate;
@Log4j2
public class TodoServiceTests {
private TodoService todoService;
@BeforeEach
public void ready() {
todoService = TodoService.INSTANCE;
}
@Test
public void testRegister() throws Exception {
TodoDTO todoDTO = TodoDTO.builder()
.title("JDBC Test Title")
.dueDate(LocalDate.now())
.build();
// 테스트 코드에서 Log4j2가 사용됨을 확인
log.info("---------------------------------");
// TodoService에서 한 번, TodoServiceTests에서 한 번, 총 두 번 출력되도록 입력
log.info(todoDTO);
todoService.register(todoDTO);
}
}
- "---------------------------------"가 출력되어 TodoServiceTests에서도 log.info()가 동작함을 확인
- TodoDTO의 내용이 두 번 출력되어 위의 출력은 TodoServiceTests에서, 밑의 출력은 TodoService에서 된 것을 확인
3. 컨트롤러와 서비스 객체 연동
- TodoService와 TodoDAO의 연동을 확인한 후, 마지막으로 Servlet으로 작성된 Controller와 TodoService 연동
- 실습 ↓↓↓↓↓
실습
1) 목록 기능 구현
- TodoListcontroller
- Get 방식
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "todoListController", value = "/todo/list")
@Log4j2
public class TodoListController extends HttpServlet {
private TodoService todoService = TodoService.INSTANCE;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("todo list.................");
}
}
- TodoService의 목록 기능 구현(개발은 DAO → Service → Controller 순서로 하며, TodoDAO는 개발 완료했으므로 TodoService 에 listAll() 기능 추가)
// TodoService
package org.zerock.jdbcex.service;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.zerock.jdbcex.dao.TodoDAO;
import org.zerock.jdbcex.domain.TodoVO;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.MapperUtil;
import java.util.List;
import java.util.stream.Collectors;
@Log4j2
public enum TodoService {
INSTANCE;
private TodoDAO dao;
private ModelMapper modelMapper;
TodoService() {
dao = new TodoDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
// TodoDTO를 파라미터로 받아서 TodoVO로 변환하는 과정을 ModelMapper로 처리
// print하여 잘 변환됐는지 확인하고
// TodoDAO를 이용해서 TodoVO를 insert
public void register(TodoDTO todoDTO) throws Exception {
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
// System.out.println("todoVO: " + todoVO);
log.info(todoVO);
dao.insert(todoVO);
// insert는 int를 반환하므로 이를 이용한 예외처리도 가능
}
// listAll() 메서드 작성
public List<TodoDTO> listAll() throws Exception {
List<TodoVO> voList = dao.selectAll();
log.info("voList....................");
log.info(voList);
List<TodoDTO> dtoList = voList.stream()
.map(vo -> modelMapper.map(vo, TodoDTO.class))
.collect(Collectors.toList());
return dtoList;
}
}
- listAll()은 TodoDAO에서 가져온 TodoVO 목록을 모두 TodoDTO로 변환해서 반환
- 이때, ModelMapper와 Java Stream의 map()을 이용해 간단히 처리
- TodoListController 수정
- TodoListController에서 HttpServletRequest의 setAttribute()를 이용해서 TodoService 객체가 반환하는 데이터를 저장하고 RequestDipatcher를 이용해서 JSP로 전달
// TodoListController
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet(name = "todoListController", value = "/todo/list")
@Log4j2
public class TodoListController extends HttpServlet {
private TodoService todoService = TodoService.INSTANCE;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("todo list.................");
try {
List<TodoDTO> dtoList = todoService.listAll();
req.setAttribute("dtoList", dtoList);
req.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(req, resp);
} catch (Exception e) {
log.error(e.getMessage());
throw new ServletException("list error");
}
}
}
- WEB-INF 폴더 > todo 디렉토리 생성 > list.jsp 파일 작성
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix ="c" %>
<html>
<head>
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<ul>
<c:forEach items = "${dtoList}" var = "dto">
<li>${dto}</li>
</c:forEach>
</ul>
</body>
</html>
- 결과
- tbl_todo 테이블에 있는 데이터들을 jsp에서 출력
2) 등록 기능 구현
- GET 방식으로 등록 화면을 확인
- <form> 태그 내에 입력 항목 채운 뒤
- POST 방식으로 처리
- 처리 후에는 목록화면으로 redirect하는 PRG(Post - Redirect - get) 패턴 방식
- TodoService의 등록 기능 구현(이전에 만들어 둔 register() 메서드가 TodoService의 등록 기능임)
package org.zerock.jdbcex.service;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.zerock.jdbcex.dao.TodoDAO;
import org.zerock.jdbcex.domain.TodoVO;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.MapperUtil;
import java.util.List;
import java.util.stream.Collectors;
@Log4j2
public enum TodoService {
INSTANCE;
private TodoDAO dao;
private ModelMapper modelMapper;
TodoService() {
dao = new TodoDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
// TodoDTO를 파라미터로 받아서 TodoVO로 변환하는 과정을 ModelMapper로 처리
// print하여 잘 변환됐는지 확인하고
// TodoDAO를 이용해서 TodoVO를 insert
public void register(TodoDTO todoDTO) throws Exception {
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
// System.out.println("todoVO: " + todoVO);
log.info(todoVO);
dao.insert(todoVO);
// insert는 int를 반환하므로 이를 이용한 예외처리도 가능
}
// listAll() 메서드 작성
public List<TodoDTO> listAll() throws Exception {
List<TodoVO> voList = dao.selectAll();
log.info("voList....................");
log.info(voList);
List<TodoDTO> dtoList = voList.stream()
.map(vo -> modelMapper.map(vo, TodoDTO.class))
.collect(Collectors.toList());
return dtoList;
}
}
- TodoRegisterController 구현
- controller 패키지의 HttpServlet을 상속받도록 선언
- GET / POST 모두 사용
// TodoRegisterController
package org.zerock.jdbcex.controller;
import com.sun.tools.javac.comp.Todo;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.time.format.DateTimeFormatter;
@WebServlet(name = "todoRegisterController", value = "/todo/register")
@Log4j2
public class TodoRegisterController extends HttpServlet {
private TodoService todoService = TodoService.INSTANCE;
private final DateTimeFormatter DATEFORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
// GET 방식으로 호출되는 경우, "/WEB-INF/todo/register.jsp" 파일에서 입력 화면을 보여줌
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info("/todo/register GET .......");
req.getRequestDispatcher("/WEB-INF/todo/register.jsp").forward(req, resp);
}
// '/todo/register'에서 <form> 태그 내에 title과 dueDate를 POST 방식으로 전송ㄱ
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
TodoDTO todoDTO = TodoDTO.builder()
.title(req.getParameter("title"))
.dueDate(LocalDate.parse(req.getParameter("dueDate"), DATEFORMATTER))
.build();
log.info("/todo/register POST .......");
log.info(todoDTO);
try {
todoService.register(todoDTO);
} catch (Exception e) {
e.printStackTrace();
}
resp.sendRedirect("/todo/list");
}
}
3) 조회 기능 구현
- GET 방식으로 동작
- '/todo/read?tno=12'와 같이 쿼리 스트링으로 tno 값을 전달
- TodoService에서 TodoDTO를 반환하고 이를 Controller에서 HttpServletRequest에 담아 JSP에서 출력
// TodoReadController
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "todoReadController", value = "/todo/read")
@Log4j2
public class TodoReadController extends HttpServlet {
private TodoService todoService = TodoService.INSTANCE;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Long tno = Long.parseLong(req.getParameter("tno"));
TodoDTO todoDTO = todoService.get(tno);
// dto라는 이름으로 TodoDTO 데이터 담기
req.setAttribute("dto", todoDTO);
req.getRequestDispatcher("/WEB-INF/todo/read.jsp").forward(req, resp);
} catch (Exception e){
log.error(e.getMessage());
throw new ServletException("read error");
}
}
}
<!-- read.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Todo Read</title>
</head>
<body>
<div>
<input type = "text" name = "tno" value = "${dto.tno}" readonly>
</div>
<div>
<input type = "text" name = "title" value = "${dto.title}" readonly>
</div>
<div>
<input type = "date" name = "dueDate" value = "${dto.dueDate}">
</div>
<div>
<input type = "checkbox" name = "finished" value = ${dto.finished ? "checked": ""} readonly>
</div>
<div>
<!-- 수정/삭제 또는 목록으로 갈 수 있는 링크 -->
<a href = "/todo/modify?tno=${dto.tno}">Modify/Remove</a>
<a href = "/todo/list">List</a>
</div>
</body>
</html>
- 목록에서 조회 링크 처리
- 조회 기능이 정상임이 확인됐으면 목록 페이지에서 각 목록을 누르면 각 게시물로 바로 이동되도록 링크 걸어주기
<!-- list.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix ="c" %>
<html>
<head>
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<ul>
<c:forEach items = "${dtoList}" var = "dto">
<li>
// 목록에서 숫자부분을 클릭하면 해당 번호가 쿼리 스트링으로 전달된 read 경로로 조회됨
<span><a href = "/todo/read?tno=${dto.tno}">${dto.tno}</a></span>
<span>${dto.title}</span>
<span>${dto.dueDate}</span>
<span>${dto.finished? "DONE": "NOT YET"}</span>
</li>
</c:forEach>
</ul>
</body>
</html>
4) 수정 / 삭제 기능 구현
- 두 기능 모두 POST 방식으로 처리
- 화면에 두 개의 <form> 태그 작성해서 처리 또는 자바스크립트를 이용해 하나의 <form> 태그의 action 속성을 변경해서 처리
- TodoService의 수정 / 삭제 기능 구현
// TodoService
package org.zerock.jdbcex.service;
import com.sun.tools.javac.comp.Todo;
import lombok.extern.log4j.Log4j2;
import org.modelmapper.ModelMapper;
import org.zerock.jdbcex.dao.TodoDAO;
import org.zerock.jdbcex.domain.TodoVO;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.util.MapperUtil;
import java.util.List;
import java.util.stream.Collectors;
@Log4j2
public enum TodoService {
INSTANCE;
private TodoDAO dao;
private ModelMapper modelMapper;
TodoService() {
dao = new TodoDAO();
modelMapper = MapperUtil.INSTANCE.get();
}
...
// remove() 메서드 작성
// remove()는 번호만 파라미터로 받아서 해당 번호에 해당하는 데이터 삭제
public void remove(Long tno) throws Exception {
log.info("tno: " + tno);
dao.deleteOne(tno);
}
// modify() 메서드 작성
// modify()는 TodoDTO 타입 전체를 파라미터로 받음
public void modify(TodoDTO todoDTO) throws Exception {
log.info("todoDTO: " + todoDTO);
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
dao.updateOne(todoVO);
}
}
- TodoModifyController의 구현
// TodoModifyController
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@WebServlet(name = "todoModifyController", value = "/todo/modify")
@Log4j2
public class TodoModifyController extends HttpServlet{
private TodoService todoService = TodoService.INSTANCE;
private final DateTimeFormatter DATEFORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Long tno = Long.parseLong(req.getParameter("tno"));
TodoDTO todoDTO = todoService.get(tno);
// 데이터 담기
req.setAttribute("dto", todoDTO);
req.getRequestDispatcher("/WEB-INF/todo/modify.jsp").forward(req, resp);
} catch (Exception e) {
log.error(e.getMessage());
throw new ServletException("modify get... error");
}
}
}
- 수정 작업이 이루어지는 'WEB-INF/todo/modify.jsp'
<!-- modify.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Todo Modify</title>
</head>
<body>
<form id = 'form1' action = '/todo/modify' method = 'post'>
<div>
<input type = 'text' name = 'tno' value = '${dto.tno}' readonly>
</div>
<div>
<input type = "text" name = "title" value = "${dto.title}">
</div>
<div>
<input type = "date" name = "dueDate" value = "${dto.dueDate}">
</div>
<div>
<input type = "checkbox" name = "finished" value = ${dto.finished ? "checked": ""}>
</div>
<div>
<button type = 'submit'>Modify</button>
</div>
</form>
<form id = 'form2' action = '/todo/remove' method = 'post'>
<input type = 'hidden' name = 'tno' value = '${dto.tno}' readonly>
<div>
<button type = 'submit'>Remove</button>
</div>
</form>
</body>
</html>
- TodoModifyController에서 POST 방식으로 동작하는 doPost() 이용해서 처리
// TodoModifyController
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.dto.TodoDTO;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@WebServlet(name = "todoModifyController", value = "/todo/modify")
@Log4j2
public class TodoModifyController extends HttpServlet{
...
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String finishedStr = req.getParameter("finished");
TodoDTO todoDTO = TodoDTO.builder()
.tno(Long.parseLong(req.getParameter("tno")))
.title(req.getParameter("title"))
.dueDate(LocalDate.parse(req.getParameter("dueDate"), DATEFORMATTER))
.finished(finishedStr != null && finishedStr.equals("on"))
.build();
log.info("/todo/modify POST...");
log.info(todoDTO);
try {
todoService.modify(todoDTO);
} catch (Exception e) {
e.printStackTrace();
}
resp.sendRedirect("/todo/list");
}
}
- <form> 태그에서 전송된 title, finished 등을 이용해서 TodoDTO를 구성
- 만들어진 TodoDTO는 TodoService 객체로 전달되고 목록 화면으로 다시 이동하여 수정된 결과를 볼 수 있음
- TodoRemoveController의 구현
package org.zerock.jdbcex.controller;
import lombok.extern.log4j.Log4j2;
import org.zerock.jdbcex.service.TodoService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "todoRemoveController", value = "/todo/remove")
@Log4j2
public class TodoRemoveController extends HttpServlet {
private TodoService todoService = TodoService.INSTANCE;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Long tno = Long.parseLong(req.getParameter("tno"));
log.info("tno: "+ tno);
try {
todoService.remove(tno);
} catch (Exception e) {
log.error(e.getMessage());
throw new ServletException("read error");
}
resp.sendRedirect("/todo/list");
}
}
1) 코드의 개선 사항들
- 웹 MVC 구조를 사용하면 확실하게 책임과 역할을 구분할 수 있다는 장점
- 여러 개의 코드를 만들어야 한다는 단점
- 개선 사항
- 여러 개의 Controller를 작성하는 번거로움: TodoDAO나 TodoService와 달리 HttpServlet을 상속하는 여러 개의 Controller 작성해야 함
- 동일 로직 반복 사용: 게시물 조회나 수정 작업은 둘 다 GET 방식으로 동작, 결과를 보여주는 JSP만 다름, 결국 동일 코드 여러 번 작성
- 예외 처리 부재: 예외 발생 시 처리에 대한 설계가 없어 비정상적 호출 발생 시 대비 x
- 반복적 메서드 호출: HttpServletRequset나 HttpServletResponse를 이용 해 TodoDTO를 구성하는 작업 등이 동일한 코드들로 작성되어 개선 필요, Long.parseLong() 드으이 코드도 많이 반복
- 자바의 객체지향 기법을 좀 더 사용할 필요 o
'back-end > Java' 카테고리의 다른 글
[자바 웹 개발 워크북] 3.2 - 사용자 정의 쿠키 (1) | 2023.01.11 |
---|---|
[자바 웹 개발 워크북] 3.1 - 세션과 필터 (0) | 2023.01.09 |
[자바 웹 개발 워크북] 2.2 - 프로젝트 내 JDBC 구현 (0) | 2023.01.05 |
[자바 웹 개발 워크북] 2.1 - JDBC 프로그래밍 준비 (0) | 2023.01.04 |
[자바 웹 개발 워크북] 1.5 - 모델(Model) (0) | 2023.01.03 |