프로그램의 기능을 개발할 때는 View 부터 만드는 게 좋습니다.
1. save-form.mustache (View)
{{> layout/header}}
<section>
<!-- http body : title=제목6&content=내용6
http header : application/x-www-form-urlencoded
key값은 input태그의 name, value값은 input태그에 사용자가 입력하는 값-->
<form action="/board/save" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="title" placeholder="제목"><br>
<input type="text" name="content" placeholder="내용"><br>
<button type="submit">글쓰기</button>
</form>
</section>
</body>
</html>
placeholder=”제목”
- 사용자가 필드에 아무것도 입력하지 않았을 때 placeholder내용이 화면에 표시됩니다.
enctype="application/x-www-form-urlencoded"
- request의 Content-Type을 설정합니다.
2. DTO (Data Transfer Object)
public class BoardRequest {
@Data // getter, setter, toString
public static class SaveDTO {
private String title;
private String content;
}
}
3. Controller
@GetMapping("/save-form")
public String saveForm() {
return "save-form";
}
@PostMapping("/board/save")
public String save(BoardRequest.SaveDTO saveDTO) {
// System.out.println(saveDTO);
boardService.게시글쓰기(saveDTO);
return "redirect:/";
}
save 메서드의 매개변수 타입이 객체가 가능한 이유
- application/x-www-form-urlencoded 타입의 request(요청)이 웹 서버를 통해 WAS(여기서는 tomcat)로 도착합니다.
- tomcat이 받은 데이터로 DTO 객체를 만듭니다.
- tomcat이 파싱한 DTO객체를 HttpServletRequest 안에 담아서 DispatcherServlet 에게 전달합니다.
System.out.println(saveDTO); 는 자바 문법 상
System.out.println(saveDTO.toString()); 과 같습니다.
리다이렉션 흐름(RPG 패턴)
- 클라이언트가 작성한 글 내용을 담은 POST 요청을 보냅니다.
- 서버가 클라이언트에게 302 리다이렉트 응답을 보냅니다.
- 이 응답은 클라이언트에게 새로운 위치(“/”)로 다시 요청하라고 지시합니다.
- HTTP 상태 코드는 302(Found)입니다.
- 클라이언트가 “/” 로 GET 요청을 보냅니다.
- 클라이언트는 서버의 지시에 따라 “/” 경로로 GET 요청을 보냅니다.
- 서버가 /에 대한 응답을 보냅니다.
- 이 요청에 대한 응답은 일반적으로 200 OK 상태 코드를 갖습니다.
- “/” 경로에 해당하는 리소스를 클라이언트에게 반환합니다.
따라서, 최종적으로 클라이언트는 302 리다이렉트를 통해 “/” 경로로 이동하며, 이 경로에 대한 요청에 200 응답을 받습니다.
개발자 도구 Network 탭에서 요청이 302 요청과 200 요청 총 2번 요청이 발생하는 것을 확인할 수 있습니다.
리다이렉션을 해야 하는 이유
- 글 쓰기 결과가 반영된 화면을 보여주기 위해서입니다.
4. Repository
public void save(String title, String content) {
Query q = em.createNativeQuery("insert into board_tb (title, content, created_at) values(?, ?, now())");
q.setParameter(1, title);
q.setParameter(2, content);
q.executeUpdate();
// commit 하지 않은 상태
}
5. Repository Test
@Test
public void save_test() {
// given
String title = "제목6";
String content = "내용6";
// when
boardRepository.save(title, content);
// then
Board board = boardRepository.findById(6);
System.out.println(board.getId());
System.out.println(board.getTitle());
System.out.println(board.getContent());
}
6. Service
@Transactional // commit 완료
public void 게시글쓰기(BoardRequest.SaveDTO saveDTO) {
boardRepository.save(saveDTO.getTitle(), saveDTO.getContent());
}
@Transactional 을 Repository가 아니라 Service에 붙이는 이유
- 게시글쓰기에 메서드가 여러 개 있을 경우 일부 메서드에 오류가 생길 때 rollback 시키기 위해서입니다.
Share article