implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
@RequestMapping("/question")
@Controller
public class QuestionController {
@GetMapping("/list")
public String list() {
return "question_list";
}
}
@RequiredArgsConstructor
@RequestMapping("/question")
@Controller
public class QuestionController {
private final QuestionRepository questionRepository;
@GetMapping("/list")
public String list(Model model) {
List<Question> questionList = this.questionRepository.findAll();
model.addAttribute("questionList", questionList);
return "question_list";
}
}
매개변수로
model지정시 객체가 자동으로 생성
@RequiredArgsController 애너테이션으로
생성자 방식 QuestionRepository 객체 주입
스프링 부트가 내부적으로 QuestionController 를 생성할 때,
롬복으로 만들어진 생성자에 의해
QuestionRepository 객체가 자동으로 주입
QuestionRepository 의 findAll 메서드를 사용
질문 목록 데이터 questionList 를 생성
Model 객체에 'questionList' 라는 이름으로 저장
Model 객체에 값을 담아두면 템플릿에서 그 값을 사용할 수 있음
Model 객체는 따로 생성할 필요 없이 컨트롤러의 메서드에 매개변수로
지정하기만 하면 스프링 부트가 자동으로 Model 객체 생성
Model 객체는 자바 클래스와 템플릿을 이어주는 연결고리 역할
<table>
<thead>
<tr>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question : ${questionList}">
<td th:text="${question.subject}"></td>
<td th:text="${question.createDate}"></td>
</tr>
</tbody>
</table>
타임리프는 다음에 배우기
@GetMapping("/")
public String root() {
return "redirect:/question/list";
}
root 메서드에 / URL 매핑
이렇게 하면 기본적으로 /question/list 페이지가 나옴
@RequiredArgsConstructor
@Service
public class QuestionService {
private QuestionRepository questionRepository;
public List<Question> getList() {
return this.questionRepository.findAll();
}
}
@Service 어노테이션 붙이기
@RequiredArgsConstructor = questionRepository 생성자 방식 주입
getList() 조회 메서드 만들기
<table>
<thead>
<tr>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question : ${questionList}">
<td>
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
</td>
<td th:text="${question.createDate}"></td>
</tr>
</tbody>
</table>
<td th:text="${question.subject}"></td>
요게 없어지고 저게 들어감
질문 목록의 제목을 텍스트로 출력하던 것을
질문의 상세 내용이 담긴 웹 페이지로 이동할 수 있는 링크로 변경
@GetMapping(value = "/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
return "question_list";
}
detail/2 처럼 변하는 id 값을 얻기 위해선 @PathVariable("id") 를 사용
이때 (value= "/detail/{id}") 의 {id} 와
@PathVariable("id") 가 같아야함
value의 id = Variable = id
id가 아니더라도 매개변수의 이름이 이와 같이 동일해야 한다.
질문 데이터 조회를 위해 서비스 수정
public Question getQuestion(Integer id) {
Optional<Question> question = this.questionRepository.findById(id);
if (question.isPresent()) {
return question.get();
} else {
throw new DataNotFoundException("question not found");
}
}
id 값으로 질문 데이터를 조회하기 위해 getQuestion 메서드 추가
리포지터리로 얻은 Question 객체 = Optional 객체
if, else 문을 통해 isPresent 메서드로
해당 데이터가 존재하는지 검사하는 과정 추가
id 값에 해당하는 데이터가 없다면,
예외 클래스인 DataNotFoundException 이 실행
public class DataNotFoundException extends RuntimeException{
private static final long serialVersionUTD = 1L;
public DataNotFoundException(String message) {
super(message);
}
}
데이터베이스에서 특정 엔티티 또는 데이터를 찾을 수 없을 때
발생시키는 예외 클래스
이게 발생하면 스프링 부트는 설정된
HTTP 상태 코드 (HTTPStatus.NOT_FOUND) 와
이유 ("entity not found") 를 포함한 응답을 생성해 클라이언트에게 반환
여기선 404 오류를 반환하도록 작성
DataNotFoundException이RuntimeException을 상속받는 이유는, 데이터가 없다는 상황을 언체크 예외로 처리함으로써 개발자가 예외를 명시적으로 처리하지 않아도 되게 하기 위함입니다.
이를 통해 더 깔끔하고 간결한 코드를 작성할 수 있습니다.
@GetMapping(value = "/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
Question question = this.questionService.getQuestion(id);
model.addAttribute("question", question);
return "question_detail";
}
Service 안에 있는 getQuestion 메서드를 호출하여,
Question 객체를 템플릿이랑 이어주는 model.addAttribute 사용
<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
'제목', '내용' 문자열 대신 subject (제목) , content (내용) 을 출력