th:form을 사용하여,
Form 태그에 작성된 정보를 컨트롤러에서 전달 받고자 한다.
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 객체를 생성한다.
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는 어떻게든 변경될 수 있다.
//게시글 등록 양식
@GetMapping("/articleForm")
public String articleForm(Model model){
model.addAttribute("article",new ArticleDto());
return "pages/articles/articleForm";
}
게시글 등록 양식 폼으로 이동할 때,
Model 객체에 게시글DTO 객체를 추가하여 이동한다.
<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="*{속성명}" 을 통해,
전달 받은 객체와 매핑을 해준다.
Thymeleaf를 쓰지 않았다면,
Form 태그 내 input에 id,name 속성을 일일이 설정해 줘야 했다.
th:field="*{속성}"를 사용하면,
위 설명한 항목을 자동으로 설정해준다.
해당 예시에는 id=를 작성해주긴 했다.
컴파일 단계에서 경고?에러?로 IDE가 인식하여,
적어두긴 했다.
//게시글 등록
@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를 통해,
메소드가 끝날 때 페이지 이동을 시킨다.
프로젝트를 시작했을 때,
간단한 예시이기 때문에 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