1. Todo의 삭제 기능 개발
수정과 삭제는 GET 방식으로 조회한 후에 POST 방식으로 처리
사실상 GET 방식의 내용은 조회 화면과 같지만 스프링 MVC에는 여러 개의 경로를 배열과 같은 표기법을 이용해서 하나의 @GetMapping으로 처리할 수 있기 때문에 read() 기능을 수정해서 수정과 삭제에도 같은 메서드를 이용하도록 작업
TodoController의 read()를 다음과 같이 수정해서 "/todo/modify?tno=xxx"의 경로를 처리하도록 수정
@GetMapping({"/read", "/modify"})
public void read(Long tno, Model model) {
// 1) request로 전달 받은 tno를 서비스에 전달해서 2)TodoDTO를 반환받아서 3)View 에 전달
TodoDTO todoDTO = todoService.getOne(tno);
log.info(todoDTO);
model.addAttribute("dto", todoDTO);
}
/WEB-INF/view/todo 폴더에 있는 read.jsp를 그대로 복사해서 modify.jsp를 구성
👾 modify.jsp에서는 수정과 삭제 작업이 POST 방식으로 처리될 예정이므로 이를 위한 <form> 태그를 구성하고 수정이 가능한 항목들은 편집이 가능하도록 함
<form action="/todo/modify" method="post">
<div class="input-group mb-3">
<span class="input-group-text">Tno</span>
<input type="text" name="tno" class="form-control" value="${dto.tno}" readonly>
</div>
<div class="input-group mb-3">
<span class="input-group-text">Title</span>
<input type="text" name="title" class="form-control" value="${dto.title}">
</div>
<div class="input-group mb-3">
<span class="input-group-text">DueDate</span>
<input type="date" name="dueDate" class="form-control" value="${dto.dueDate}">
</div>
<div class="input-group mb-3">
<span class="input-group-text">Writer</span>
<input type="text" name="writer" class="form-control" value="${dto.writer}" readonly>
</div>
<div class="form-check">
<label class="form-check-label">
Finished
</label>
<input type="checkbox" name="finished"
class="form-check-input" ${dto.finished ? "checked" : ""}>
</div>
<div class="my-4">
<div class="float-end">
<button type="button" class="btn btn-danger">Remove</button>
<button type="button" class="btn btn-primary">Modify</button>
<button type="button" class="btn btn-secondary">List</button>
</div>
</div>
</form>
✓ 제목 / 만료일 / 완료는 수정이 가능하도록 수정
✓ 화면 아래 버튼은 삭제, 수정, 목록 버튼이 추가
✓ 브라우저를 통해 "/todo/modify?tno=xx"와 같은 경로로 화면이 나오는 것 확인
1) Remove 버튼의 처리
Remove 버튼의 클릭은 자바 스크립트를 이용해서 <form> 태그의 action을 조정하는 방식으로 동작하게 구성
<script>
const frmModify = document.querySelector('form');
document.querySelector('.btn-danger').addEventListener('click', function () {
frmModify.action = '/todo/remove';
frmModify.method = 'post';
frmModify.submit();
});
</script>
✓ Remove 버튼은 class 속성이 "btn-danger"이므로 이를 이용해서 클릭 이벤트를 처리
TodoController에는 POST 방식으로 동작하는 remove() 메서드를 설계
@PostMapping("/remove")
public String remove(Long tno, RedirectAttributes redirectAttributes) {
log.info("-----remove----");
log.info("tno: " + tno);
todoService.remove(tno);
return "redirect:/todo/list";
}
✓ 우선 tno 파라미터가 정상적으로 전달되는지 확인하고 목록으로 이동하도록 구성
✓ Remove 버튼을 누르면 다음과 같은 로그 출력 확인
2) TodoMapper와 TodoService 처리
TodoMapper에는 delete() 메서드를 추가하고 TodoMapper.xml에는 sql을 추가
public interface TodoMapper {
String getTime();
void insert(TodoVO todoVO);
List<TodoVO> selectAll();
TodoVO selectOne(Long tno);
void delete(Long tno);
}
<delete id="delete">
DELETE FROM tbl_todo WHERE tno = #{tno}
</delete>
test 코드 작성
@Test
public void testDelete() {
// 1) tno로 데이터를 반환해서 정상 출력 확인 2) 삭제 3) 다시 tno로 데이터를 반환해서 삭제 확인.
Long tno = 2L;
TodoVO todoVO = todoMapper.selectOne(tno);
log.info(todoVO);
todoMapper.delete(tno);
todoVO = todoMapper.selectOne(tno);
log.info(todoVO);
}
TodoServce / TodoServiceImpl에는 remove() 메서드를 작성
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
void remove(Long tno);
}
@Override
public void remove(Long tno) {
todoMapper.delete(tno);
}
TodoController 에서 TodoService의 remove()를 호출하는 코드를 추가
@PostMapping("/remove")
public String remove(Long tno, RedirectAttributes redirectAttributes) {
log.info("-----remove----");
log.info("tno: " + tno);
todoService.remove(tno);
return "redirect:/todo/list";
}
✓ 브라우저를 통해서 특정한 번호를 가진 게시물이 삭제 되는지 확인
2. Todo의 수정 기능 개발
Todo의 수정 기능은 수정이 가능한 항목들만 변경되어야 하므로 SQL이 복잡해짐
TodoMapper에 메서드 추가
public interface TodoMapper {
String getTime();
void insert(TodoVO todoVO);
List<TodoVO> selectAll();
TodoVO selectOne(Long tno);
void delete(Long tno);
void update(TodoVO todoVO);
}
TodoMapper.xml 코드 추가
<update id="update">
UPDATE tbl_todo SET title = #{title}, dueDate = #{dueDate},
finished = #{finished} WHERE tno = #{tno}
</update>
test 코드 추가
@Test
public void testUpdate() {
Long tno = 12L;
TodoVO todoVO = TodoVO.builder()
.tno(tno)
.title("수정")
.dueDate(LocalDate.parse("2024-4-26"))
.finished(true)
.build();
todoMapper.update(todoVO);
log.info(todoMapper.selectOne(tno));
}
TodoService / TodoServiceImpl 에서는 TodoDTO를 TodoVO로 변환해서 처리
public interface TodoService {
void register(TodoDTO todoDTO);
List<TodoDTO> getAll();
TodoDTO getOne(Long tno);
void remove(Long tno);
void modify(TodoDTO todoDTO);
}
@Override
public void modify(TodoDTO todoDTO) {
log.info("...modify()...");
TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
log.info(todoVO);
todoMapper.update(todoVO);
}
1) checkbox를 위한 Formatter
👾 수정 작업에서는 화면에서 체크박스를 이용해서 완료여부 finished (boolean) 를 처리하게 됨
👾 문제는 브라우저가 체크박스가 클릭된 상태일때 전송되는 값은 "on"이라는 값을 전달
👾 TodoDTO로 데이터를 수집할 때에는 문자열 "on"을 boolean 타입으로 처리할 수 있어야 하므로 컨트롤러에서 데이터를 수집할 때 타입을 변경해 주기 위한 CheckboxFormatter 를 formatter 패키지에 추가해서 개발
public class CheckBoxFormatter implements Formatter<Boolean> {
@Override
public Boolean parse(String text, Locale locale) throws ParseException {
if (text == null) return false;
return text.equals("on");
}
@Override
public String print(Boolean object, Locale locale) {
return object.toString();
}
}
servlet-context.xml에 format 라이브러리 추가
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<set>
<bean class="com.example.spring_ex_01_2404.controller.formatter.LocalDateFormatter"/>
<bean class="com.example.spring_ex_01_2404.controller.formatter.CheckBoxFormatter"/>
</set>
</property>
</bean>
2) TodoController의 modify()
@PostMapping("/modify")
public String midify(@Valid TodoDTO todoDTO,
BindingResult bindingResult,
RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
// 유효성 검사 결과 에러가 있으면 수정 페이지로 돌아감
log.info("has error");
redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
redirectAttributes.addAttribute("tno", todoDTO.getTno());
return "redirect:/todo/mofidy";
}
log.info(todoDTO);
todoService.modify(todoDTO);
return "redirect:/todo/list";
}
✓ @Valid를 이용해서 필요한 내용들을 검증하고 문제가 있는 경우에는 다시 "/todo/modify"로 이동시키는 방식을 사용
✓ "/todo/modify"로 이동할 때는 tno 파라미터가 필요하기 때문에 RedirectAttributes의 addAttribute을 이용
/WEB-INF/view/todo/modify.jsp 에 검증된 정보를 처리하는 코드를 추가
👾 <form> 태그가 끝난 후에는 <script> 태그를 이용
<script>
const serverValidResult = {};
<c:forEach items="${errors}" var="error">
serverValidResult['${error.getField()}'] = '${error.defaultMessage}';
</c:forEach>
console.log(serverValidResult);
</script>
<script>
const frmModify = document.querySelector('form');
document.querySelector('.btn-danger').addEventListener('click', function () {
frmModify.action = '/todo/remove';
frmModify.method = 'post';
frmModify.submit();
});
document.querySelector('.btn-primary').addEventListener('click', function () {
frmModify.action = '/todo/modify';
frmModify.method = 'post';
frmModify.submit();
});
document.querySelector('.btn-secondary').addEventListener('click', function () {
self.location = '/todo/list';
});
</script>
[ 내용 참고 : IT 학원 강의 ]
'Spring & Spring Boot' 카테고리의 다른 글
[Spring] 목록 데이터를 위한 DTO와 서비스 계층 (0) | 2024.04.29 |
---|---|
[Spring] 페이징 처리를 위한 TodoMapper (0) | 2024.04.28 |
[Spring] Todo 목록 | 조회 기능 개발 (0) | 2024.04.28 |
[Spring] Todo 기능 개발 | 한글 처리 필터 설정 | 유효성 검사 @Valid (0) | 2024.04.28 |
[Spring] 프로젝트 구현 준비 | 부트스트랩 적용 | DB 처리 (0) | 2024.04.27 |