[Thymeleaf] Form 태그 값 전달 받기

SEOP·2024년 7월 1일
post-thumbnail

th:form을 사용하여,
Form 태그에 작성된 정보를 컨트롤러에서 전달 받고자 한다.

1. 게시글 VO 생성

import lombok.*;

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class Article {
    //ID
    private int articleId;
    //제목
    private String title;
    //내용
    private String content;
    //작성자
    private String name;
    //조회 수
    private String viewCnt;
    //작성일
    private String regDate;
}

우선 임의의 게시글 VO 객체를 생성한다.

2. 게시글 Dto 생성

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.*;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ArticleDto {
    private int articleId;
    private String title;
    private String content;
    private String name;
    private String viewCnt;
    private String regDate;
}

필요로 하는 데이터만 전달할 DTO를 생성한다.
예제는 매우 쉽게 진행할 생각으로,
VO랑 동일하게 작성하였지만

사용자 요구에 따라서,
DTO는 어떻게든 변경될 수 있다.

3. 빈 객체 전달하기

//게시글 등록 양식
@GetMapping("/articleForm")
public String articleForm(Model model){
    model.addAttribute("article",new ArticleDto());
    return "pages/articles/articleForm";
}

게시글 등록 양식 폼으로 이동할 때,
Model 객체에 게시글DTO 객체를 추가하여 이동한다.

4. 회원가입 페이지

    <form action="/articles/articleForm" th:object="${article}" method="post">
      <div>
        <input type="hidden" id="name" name="name" class="form-control" value="testme" placeholder="제목을 입력하세요.">
      </div>
      <div>
        <label for="title">제목</label>
        <input type="text" id="title" th:field="*{title}" class="form-control" placeholder="제목을 입력하세요.">
      </div>
      <div>
        <label for="content">내용</label>
        <textarea cols="150" rows="10" id="content" th:field="*{content}" class="form-control" placeholder="내용을 입력하세요."></textarea>
      </div>

      <hr class="my-4">
      <div class="row">
        <div class="col">
          <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button>
        </div>
        <div class="col">
          <button class="w-100 btn btn-secondary btn-lg"
                  onclick="location.href='/articles/'"
                  type="button">취소</button>
        </div>
      </div>
    </form>

Form 태그의th:object 에 Controller에서 전달 받은 객체 이름을 기입한다.

th:field="*{속성명}" 을 통해,
전달 받은 객체와 매핑을 해준다.

4.1. th:field="*{속성}"

Thymeleaf를 쓰지 않았다면,
Form 태그 내 input에 id,name 속성을 일일이 설정해 줘야 했다.

th:field="*{속성}"를 사용하면,
위 설명한 항목을 자동으로 설정해준다.

해당 예시에는 id=를 작성해주긴 했다.
컴파일 단계에서 경고?에러?로 IDE가 인식하여,
적어두긴 했다.

5. 값 전달 및 확인

//게시글 등록
@PostMapping("/articleForm")
public RedirectView articleAdd(@ModelAttribute ArticleDto articleDto, Model model){

    //API 호출
    Map<String, Object> response = articleService.articleAdd(articleDto);

    // 결과 확인
    log.info(response.toString());

    return new RedirectView("/articles/");
}

이전 Form태그에서 방식은 Post 방식이었다.
그래서 @PostMapping을 사용했다.

파라미터로는
@ModelAttribute 를 사용하여 매핑된 객체를 가져온다.

게시글 Service에서 DB에 데이터를 추가하는 과정이 있으나,
여기서는 중요하지 않으므로 생략하겠다.

Redirect를 통해,
메소드가 끝날 때 페이지 이동을 시킨다.

번외) NULL..............

프로젝트를 시작했을 때,
간단한 예시이기 때문에 VO와 DTO를 동일하게 하자고 생각했다.
그래서 해당 예제에 굳이 DTO가 필요할까 라는 생각에 DTO를 생성하지 않았다.

폼 태그에 바인딩 될 것으로 예상한 정보들이 컨트롤러에 전달이 되지 않는 문제가 발생하였다.
대체 뭐가 문제일까 이리저리 수정을 해보며 시간을 매우 낭비했다.

문제는 @Setter 였다.
이전 과정을 살펴보면,
Controller 에서 VO객체를 게시글 등록 뷰 페이지로 전달했다.(#3)
뷰 페이지에서는 전달된 객체를 th:object를 통해 바인딩을 하고
th:field를 이용하여 VO객체의 속성들에 바인딩을 해주는데.....
여기서 VO객체에 @Setter 어노테이션이 없으니 바인딩이 안돼,,,,,,,,,,,,,,,
컨트롤러에서 값을 받아오지 못해 Null 값이 떴다.

Thymeleaf의 Form을 사용하기 위해서 VO든 DTO든 @Setter를 잊지말자


참고: https://velog.io/@kim-ju-ho/SpringBoot-thymeleaf-form-%EC%82%AC%EC%9A%A9

0개의 댓글