본문 바로가기

SpringBoot

[Do it] 2장 스프링 부트 기본 기능 익히기(5)

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