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 &nbsp;
        </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 학원 강의 ]

+ Recent posts