[사이드 프로젝트]나만의 도서 관리 서비스5

zwon·2023년 8월 29일
0

개발일지

목록 보기
7/23

이 부분은 검증과 타임리프가 제공하는 입력 폼 처리 기능에 대해 적용을 했는데 이때 헷갈린 부분들에 대해서 정리할려고 한다.

수정폼 검증 메시지 출력에 있어서 약간 헤맸는데 나는 기존 입력값은 보여주되, 값을 수정했을 경우 수정된 값이 editBookDto에 적용되도록 하고싶었다. 기존 입력값을 보여줄려는 이유는 사용자 입장에서 도서명과 저자명은 수정할 일이 별로 없다고 생각했기때문이다. 하지만 오타가 날 확률이 있기때문에 우선 수정은 가능하게 했지만 오타가 나지 않은 한 수정은 안할꺼같다고 생각하여 다시 입력하는 번거러움을 없애기위해 값을 화면에 보여주고싶었다.

그래서 모델의 2개의 값을 담아서 전송을 했고,

@GetMapping("/{BookId}/edit")
  public String editForm(@PathVariable Long BookId, Model model) {
    Book book = bookService.findOne(BookId);

    model.addAttribute("book", book);
    model.addAttribute("updateBookDto", new UpdateBookDto());
    return "book/editForm";
 }

editForm.html은 다음과 같이 작성했다.

<form th:action th:object="${updateBookDto}" method="post">

    <div th:if="${#fields.hasGlobalErrors()}">
      <p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">글로벌 오류 메시지</p>
    </div>

    <div>
      <label for="bookName">도서명</label>
      <input type="text" id="bookName" th:field="*{bookName}">
      <div class="field-error" th:errors="*{bookName}"></div>
    </div>
    <div>
      <label for="author">저자명</label>
      <input type="text" id="author" th:field="*{author}">
      <div class="field-error" th:errors="*{author}"></div>
    </div>
    <div>
      <label for="curr_page">현재 Page</label>
      <input type="text" id="curr_page" th:field="*{curr_page}">
    </div>

    <div>
      <label for="end">종료일</label>
      <input type="date" id="end" th:field="*{end}">
    </div>
    ...

페이지 소스 보기 결과 value에 값이 담겨있지 않았다.


th:field가 정상 상황에선 모델의 값을 출력하고 비정상 상황에서는 FiledError에서 보관한 값을 사용해서 값을 출력한다고 알고있었고 th:filed가 값을 출력해줄 것으로 생각을 했었다.

그래서 th:object를 설정해주지 않고 바로 th:field를 사용했다.
나는 th:filed="*{bookName}"이 book객체의 bookName을 가져오는 것으로 착각을 했다.
근데 계속 안되길래th:value=${book.bookName} 등 별 난리를 다쳤지만,,, 알고보니 th:filed="{bookName}"에서 {...} 표현법을 사용하고있어 bookName은 updateBookDto를 가르키고있다.

그래서 컨트롤러에서 model.addAttribute("updateBookDto", new UpdateBookDto()); 즉, 비어있는 updateBookDto를 넘겨주니까 당연히 빈 값이 나오게 되었던 것이다.
그래서 컨트롤러에서 다음과 같이 값을 세팅해주었다.

그리고 수정 폼에서 사용하는 객체는 book이 아니라 UpdatBookDto를 사용하기 때문에 th:object로 book 객체를 사용하지 않았다.

이 문제를 해결하는 과정에서 타임리프에서 제공하는 입력 폼 객체 기능 등 타임리프에 대한 지식이 부족하다고 느꼈다.

@GetMapping("/{BookId}/edit")
  public String editForm(@PathVariable Long BookId, Model model) {
    Book book = bookService.findOne(BookId);

    UpdateBookDto updateBookDto = new UpdateBookDto();
    UpdateBookDto.setBookName(book.getBookName());
    UpdateBookDto.setAuthor(book.getAuthor());
    UpdateBookDto.setCurr_page(book.getCurr_page());

    model.addAttribute("UpdateBookDto", UpdateBookDto);
    return "book/editForm";
  }

도서 등록 폼 검증

도서 수정 폼 검증



타임리프 입력폼 처리 기능

  • th:object : 커맨드 객체 지정
  • th:field : id, name, value 속성을 자동으로 처리
  • *{....} : th:object에서 선택한 객체에 접근

우선 입력폼과 등록폼에서 모두 동일한 addForm을 사용한다.
위에서 우선 addForm을 조회할 때 빈 객체를 생성해서 넘겨줬다.

model.addAttribute("updateBookDto", new UpdateBookDto());

그 이유는 addForm.html에서 사용할 객체를 th:object를 통해서 지정해줘야하기 때문이다. 그래서 th:object="${updateBookDto}\"를 통해 폼에서 사용할 객체를 지정해줬고,

addForm.html에 th:field="${updateBookDto.bookName}" 이런식으로 UpdateBookDto 프로퍼티에 접근할 수 있다.
이미 우리는 th:object로 updateBookDto를 가리키고있기 때문에 다음과 같이 압축해서 표현할 수 있다.

th:field="*{.bookName}"

그리고 바인딩 오류도 쉽게 잡을 수 있어서 편리하다.


역시 강의만 듣는다고 공부가 아니다.
실제로 사용하고 공부해보는게 내가 뭘 모르고 어디서 헷갈리는지를 알 수 있다.
타임리프나 Spring MVC 구조에 대해서 정리가 필요하다고 느껴 잠깐 프로젝트는 멈추고 Spring MVC와 thymeleaf에 대해서 정리좀 하고 와야겠다.

profile
Backend 관련 지식을 정리하는 Back과사전

0개의 댓글