1. URL 프리픽스
- 프리픽스(preifx)란 URL의 접두사 또는 시작 부분을 가리키는 말로, QuestionController에 속하는 URL 매핑은 항상 /question 프리픽스로 시작하도록 설정 가능
QuestionController.java
package com.example.demo.question;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import lombok.RequiredArgsConstructor;
@RequestMapping("/question")
@RequiredArgsConstructor
@Controller
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 question = this.questionService.getQuestion(id);
model.addAttribute("question", question);
return "question_detail";
}
}
- QuestionController 클래스명 위에 @RequestMapping("/question")을 추가하고, 메서드 단위에서 /question을 생략하고 그 뒷부분만을 적으면 됨
- list 메서드의 URL 매핑은 /list이지만 @RequestMapping에서 이미 /question URL을 매핑했기 때문에 /question+/list가 되어 최종 URL 매핑은 /question/list가 됨
2. 답변 기능 만들기
- 질문에 답변을 입력하고, 입력한 답변을 질문 상세 페이지에서 확인할 수 있도록 구현
1) 텍스트 창과 등록 버튼 만들기
templates/question_detail.html
<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
<form th:action="@{|/answer/create/${question.id}|}" method="post">
<textarea name ="content" id="content" rows="15"></textarea>
<input type="submit" value="답변 등록">
</form>
- form의 action은 타임리프의 th:action 속성으로 생성
- 텍스트 창에 답변을 작성하고, 답변 등록 버튼을 클릭하면 /answer/create/2와 같은 URL이 post 방식으로 호출됨
2) 답변 컨트롤러 만들기
AnswerController.java
package com.example.demo.answer;
import com.example.demo.question.Question;
import com.example.demo.question.QuestionService;
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;
import lombok.RequiredArgsConstructor;
@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
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 question = this.questionService.getQuestion(id);
//답변 저장 코드
return String.format("redirect:/question/detail/%s", id);
}
}
- /answer/create/{id}와 같은 URL 요청시 createAnswer 메서드가 호출되도록 @PostMapping으로 매핑
@RequestParam(value="content") String content
- question_detail.html에서 답변으로 입력한 내용(content)을 얻으려고 추가
- 템플릿의 답변 내용에 해당하는 <textarea>의 name 속성명이 content이므로 여기서도 변수명을 content로 사용
- /create/{id}에서 {id}는 질문 엔티티의 id이므로 이 id값으로 질문을 조회하고 값이 없을 경우에는 404 오류 발생
3) 답변 서비스 만들기
AnswerService.java
package com.example.demo.answer;
import java.time.LocalDateTime;
import com.example.demo.question.Question;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class AnswerService {
private final AnswerRepository answerRepository;
public void create(Question question, String content) {
Answer answer = new Answer();
answer.setContent(content);
answer.setCreateDate(LocalDateTime.now());
answer.setQuestion(question);
this.answerRepository.save(answer);
}
}
- 답변(Answer)을 생성하기 위해 create 메서드를 추가
- craete 메서드는 입력받은 2개의 매개변수인 question과 content를 사용하여 Answer 객체를 생성하여 저장
AnswerController.java
- 작성한 create 메서드를 사용하여 답변 저장
...
@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
public class AnswerController {
private final QuestionService questionService;
private final AnswerService answerService;
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id")Integer id,
@RequestParam(value="content")String content) {
Question question = this.questionService.getQuestion(id);
this.answerService.create(question,content);
return String.format("redirect:/question/detail/%s", id);
}
}
4) 상세 페이지에 답변 표시하기
templates/question_detail.html
<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
<h5 th:text="|${#lists.size(question.answerList)}개의 답변이 있습니다.|"></h5>
<div>
<ul>
<li th:each="answer : ${question.answerList}" th:text="${answer.content}"></li>
</ul>
</div>
<form th:action="@{|/answer/create/${question.id}|}" method="post">
<textarea name ="content" id="content" rows="15"></textarea>
<input type="submit" value="답변 등록">
</form>
3. 웹 페이지 디자인하기
1) 스태틱 디렉터리와 스타일 시트
- CSS 파일은 HTML 파일과 달리 스태틱(static) 디렉터리에 저장해야함
/static/style.css
textarea{
width:100%;
}
input[type=submit]{
margin-top:10px;
}
2) 템플릿에 스타일 적용하기
- 작성한 스타일 시트 파일을 질문 상세 페이지 템플릿에 적용
- question_detail.html 파일 상단에 style.css를 사용할 수 있는 링크를 추가하여 스타일시트 파일을 상세 페이지 템플릿에 적용
/templates/question_detail.html
'SpringBoot' 카테고리의 다른 글
[Do it] 3장 스프링 부트 고급 기능 익히기(1) (0) | 2024.01.22 |
---|---|
[Do it] 스프링 부트 기본 기능 익히기(6) (1) | 2024.01.22 |
[Do it] 2장 스프링 부트 기본 기능 익히기 (4) (0) | 2024.01.21 |
[Do it] 2장 스프링 부트 기본 익히기(3) (0) | 2024.01.19 |
[Do it] 2장 스프링 부트 기본 기능 익히기(2) (1) | 2024.01.19 |