사이먼's 코딩노트
[SpringBoot] Prefix 적용 / Form 만들기 본문
[Prefix 적용]
- 현재까지 구현된 QuestionController.java 클래스를 살펴보면 2개의 URL 매핑이 되어있는 것을 확인할 수 있다.
- @GetMapping("/question/list")와 @GetMapping("/question/detail/{id}") 두 개다.
- Prefix란, 접두사 또는 시작부분을 가르키는 의미로 여기선 URL의 프리픽스가 모두 /question 으로 시작한다는 것을 알 수 있다.
- 다시 말해, QuestionController.java 클래스에 속하는 URL은 모두 프리픽스가 /question 으로 시작하도록 설정할 수 있다.
- 아래는 수정된 QuestionController.java 클래스의 코드이다.
package com.sbs.sbb.Question;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RequestMapping("/question")
@Controller
@RequiredArgsConstructor
public class QuestionController {
private final QuestionService questionService;
@GetMapping("/list")
public String list(Model model) {
List<Question> questionList = this.questionService.getList();
model.addAttribute("questionList", questionList);
return "question_list";
}
@GetMapping("/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
Question q = this.questionService.getQuestion(id);
model.addAttribute("question", q);
return "question_detail";
}
}
- QuestionController.java 클래스명 위에 @RequestMapping("/question") 어노테이션을 추가하면 메서드 안에 @GetMapping을 할 때 /question을 생략하고 그 뒷부분의 URL을 작성하면 된다.
[Form 만들기]
- question detail 페이지에서 답변을 입력하는 Form을 만들고 답변을 등록하기 위한 버튼까지 템플릿에 추가해봅시다.
- 여기서 말하는 Form(폼)이란 회원가입에서 자주 볼 수 있는 태그의 형식으로서 정보를 제출하기 위한 대화형 컨트롤을 포함하는 문서 구획을 나타낸다.
- 폼 태그 속성에는 name, action, method, target 등이 있다.
- action 속성은 폼을 전송할 서버 쪽 스크립트 파일을 지정한다.
- name 속성은 폼을 식별하기 위한 이름을 지정한다.
- target 속성은 action에서 지정한 스크립트 파일을 현재 창이 아닌다른 위치에 열도록 지정한다.
- accept-charset 속성은 폼 전송에 사용할 문자 인코딩을 지정한다.
- method 속성은 폼을 서버에 전송할 HTTP 메서드를 정한다.
- HTTP 메서드에는 GET 방식과 POST 방식이 있다.
- GET 방식은 정보를 요청하기 위해 사용되는 메서드로, URL에 받은 데이터가 고스란히 찍히기 때문에 보안성은 없지만 서버에서 처리할 필요없는 단순한 검색기능이나 링크이동, 브라우저의 주소창 입력 같은 곳에 사용된다.
- POST 방식은 정보를 생성 및 업데이트하기 위해 서버에 데이터를 보내는 메서드로, URL에 받은 데이터가 찍히지 않기 때문에 보안성을 챙길 수 있어 로그인이나 글 작성/수정, 댓글 작성/수정, 좋아요/취소와 같은 민감한 기능에 사용된다.
- 아래는 question_detail.html 템플릿에 폼 태그를 추가 작성한 코드이다.
<h2>
<h3 th:text="${question.id}"></h3>
<div th:text="${question.subject}"></div>
<div th:text="${question.content}"></div>
</h2>
<!--입력 폼 만들기-->
<form th:action="@{|/answer/create/${question.id}|}" method="POST">
<div>
<span>내용</span>
<br>
<textarea name="content" placeholder="내용" rows="5"></textarea>
</div>
<button type="submit">답변등록</button>
</form>
- '답변등록' 이라는 버튼을 누르게 되면 전송되는 Form의 action은 타임리프의 th:action 속성으로 생성한다.
- 텍스트 창에 답변을 작성하고 버튼을 클릭하면 '/answer/create/id번호' 와 같은 URL이 POST 방식으로 호출된다.
- 다시 로컬 서버를 실행하여 1번 question의 상세 내용 페이지인 localhost:8090/question/detail/1로 접속하게 되면 아래와 같이 답변 작성이 가능한 폼과 버튼이 생성되어있다.
- 지금 상황에서 버튼을 누르게 되면 'answer/create/id번호' URL은 아직 매핑이 되어있지 않기 때문에 404 에러가 발생한다.
[AnswerController.java 생성]
- 그렇다면 answer 관련 URL을 매핑하기 위해서 답변 관련 컨트롤러를 새로 생성하여 코드를 작성해봅시다.
- 아래는 AnswerController.java 클래스를 생성하여 새로 작성된 코드이다.
package com.sbs.sbb.Answer;
import com.sbs.sbb.Question.Question;
import com.sbs.sbb.Question.QuestionRepository;
import com.sbs.sbb.Question.QuestionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@RequestMapping("/answer")
@Controller
@RequiredArgsConstructor
public class AnswerController {
private final QuestionService questionService;
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id") Integer id, @RequestParam(value="content") String content) {
Question q = this.questionService.getQuestion(id);
return "redirect:/question/detail/%d".formatted(id);
}
}
- question_detail.html 에서 '/answer/create/id번호' 와 같은 URL 요청 시 createAnswer() 메서드가 호출되도록 @PostMapping 어노테이션을 사용하여 매핑하였다.
- POST 요청을 처리하는 경우이기 때문에 @GetMapping이 아닌 @PostMapping을 사용한 점을 잘 봐두면 좋다.
- createAnswer() 메서드의 매개변수로는 Model 객체와 여러개의 id가 존재하는 것을 생각하여 @PathVariable 어노테이션이 동일하게 사용되었다.
- 그리고 여기에 @RequestParam(value="content") 와 String content가 추가되었다. 이는 앞서 작성한 question_detail.html 템플릿에서 답변으로 입력한 내용인 name = "content"을 얻으려고 추가하였다. (textarea의 name 속성명과 변수명을 동일하게 생성)
- 폼을 통해 내용을 작성하고 버튼을 클릭했을 때 정상적으로 데이터가 전송되었다면 return 값으로 해당 id의 question 상세 내용 페이지 URL를 반환하여 리다이렉트하도록 하였다.
- 실제로 실행하게 되면 버튼 클릭시 localhost:8090/answer/create/1로 작성한 답변 내용이 전달되고, 해당 URL에서 다시 리다이렉트하여 실제 웹 페이지에서는 localhost:8090/question/detail/1 화면이 나타나게 된다.
- 아래 사진은 실제로 데이터가 주고 받은 로그이며, HTTP 상태 코드가 200번, 300번 대로 성공되는 모습을 볼 수 있다.
- 현재 URL인 localhost:8090/question/detail/1 에서 폼의 textarea에 값을 '전송 완료' 로 채우고 답변등록 버튼을 클릭한다.
- '전송 완료' 라는 content가 POST 방식으로 localhost:8090/answer/create/1에 요청을 하면 위의 사진에서 볼 수 있듯이 Payload의 content 부분에 '전송 완료'가 제대로 보내진 것을 확인할 수 있다.
- 브라우저에서는 하얀 화면이 나오는 중이며 이 때, 코드상으로는 AnswerController.java 클래스의 createAnswer() 메서드가 실행된다.
- 요청 처리는 questionService.getQuestion(id) 메서드를 통해 findById() 메서드를 호출하고 응답으로 localhost:8090/question/detail/1 URL에 리다이렉트하게 된다.
- 브라우저에서 받은 답변 내용을 랜더링하려 했지만, HTTP 상태 코드가 302라서 헤더에 적힌 URL로 새 요청을 보낸다.
- 새 요청을 보낼 URL은 localhost:8090/question/detail/1 이고 QuestionController.java 클래스에 의해 detail() 메서드를 실행하고, 요청을 날리기 전과 동일한 화면이 우리 눈에는 보이게 된다.
반응형
'Java > SpringBoot' 카테고리의 다른 글
[SpringBoot] Bootstrap 및 폰트 적용 / 공통 layout 추가 (0) | 2024.05.12 |
---|---|
[SpringBoot] Answer 저장 및 출력 / 스타일시트 적용 (0) | 2024.05.09 |
[SpringBoot] Detail 링크 및 페이지 추가 / 예외처리 (0) | 2024.05.09 |
[SpringBoot] Thymeleaf / 데이터 전달 및 랜더링 / Redirect (0) | 2024.05.08 |
[SpringBoot] Repository로 DB 관리하기(3) (0) | 2024.05.07 |