1. 서비스 활용하기
- 대부분의 규모 있는 스프링 부트 프로젝트는 컨트롤러에서 리포지터리를 직접 호출하지 않고 중간에 서비스를 두어 데이터를 처리
1) 서비스가 필요한 이유
1-1) 복잡한 코드 모듈화 가능
- A,B 컨트롤러가 C 리포지터리의 메서드 a,b,c를 호출해 사용하는 중복된 코드를 가지게 되는 경우
- C 리포지터리의 a,b,c 메서드를 호출하는 기능을 서비스로 만들고 컨트롤러에서 이 서비스를 호출하여 사용할 수 있음
1-2) 엔티티 객체를 DTO 객체로 변환 가능
- 엔티티 클래스는 컨트롤러에서 사용하지 않도록 설계
- 엔티티 클래스를 대신해 사용할 DTO(Data Transfer Obejct) 클래스 필요
- 서비스는 컨트롤러와 리포지터리의 중간에서 엔티티 객체와 DTO 객체를 서로 변환하여 양방향에 전달하는 역할
2) 서비스 만들기
QuestionService.java
package com.example.demo.question;
import java.util.List;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class QuestionService {
private final QuestionRepository questionRepository;
public List<Question> getList() {
return this.questionRepository.findAll();
}
}
- 스프링 부트는 @Service를 붙은 클래스는 서비스로 인식
- 질문 목록 데이터를 조회하여 리턴하는 getList 메서드를 추가
3) 컨트롤러에서 서비스 사용하기
QuestionController.java
2. 상세 페이지 만들기
1) 질문 목록에 링크 추가하기
- 질문 목록의 제목을 클릭하면 상세화면이 호출되도록 제목에 링크 추가
/templates/question_list.html
<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>
- 제목에 상세 페이지 URL을 연결하기 위해 타임리프의 th:herf 속성을 사용
- URL은 반드시 @와 { 와 } 문자 사이에 입력해야 함
- 타임 리프에서는 /question/detail/과 같은 문자열과 ${question.id}와 같은 자바 객체의 값을 더할 때는 반드시 |로 좌우를 감싸 주어야 함
2) 상세 페이지 컨트롤러 만들기
QuestionController.java
- 질문 상세 페이지 URL을 매핑
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 lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class QuestionController{
private final QuestionService questionService;
@GetMapping("/question/list")
public String list(Model model) {
List<Question> questionList = this.questionService.getList();
model.addAttribute("questionList",questionList);
return "question_list";
}
@GetMapping("/question/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
return "question_detail";
}
}
- 변하는 id값을 얻을 때에는 @PathVariable을 사용
- @GetMapping(value="/question/detail/{id}")에서 사용한 id와 @PathVariable("id")의 매개변수 이름이 이와 같이 동일해야 함
templates/question_detail.html
<h1>제목</h1>
<div>내용</div>
3) 상세 페이지에 서비스 사용하기
- 질문 데이터의 제목(subject)과 내용(content) 출력
QuestionService.java
package com.example.demo.question;
import java.util.List;
import java.util.Optional;
import com.example.demo.DataNotFoundException;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class QuestionService {
private final QuestionRepository questionRepository;
public List<Question> getList() {
return this.questionRepository.findAll();
}
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이 실행되도록 함
DataNotFoundException.java
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="entity not found")
public class DataNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public DataNotFoundException(String message) {
super(message);
}
}
- 데이터베이스에서 특정 엔티티 또는 데이터를 찾을 수 없을 때 발생시키는 예외 클래스로 만듬
- 이 예외가 발생하면 스프링 부트는 설정된 HTTP 상태 코드(HttpStatus.NOT_FOUND)와 이유("entity not found")를 포함한 응답을 생성하여 클라이언트에게 반환
- RuntimeException 클래스를 상속하는 것은 사용자 정의 예외 클래스를 정의하는 방법 중 하나
QuestionController.java
...
@GetMapping("/question/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
Question question = this.questionService.getQuestion(id);
model.addAttribute("question", question);
return "question_detail";
}
}
- QuestionService의 getQuestion 메서드를 호출하여 Question 객체를 템플릿에 전달
4) 상세 페이지 출력하기
templates/question_detail.html
<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
'SpringBoot' 카테고리의 다른 글
[Do it] 스프링 부트 기본 기능 익히기(6) (1) | 2024.01.22 |
---|---|
[Do it] 2장 스프링 부트 기본 기능 익히기(5) (0) | 2024.01.21 |
[Do it] 2장 스프링 부트 기본 익히기(3) (0) | 2024.01.19 |
[Do it] 2장 스프링 부트 기본 기능 익히기(2) (1) | 2024.01.19 |
[Do it] 2장 스프링 부트 기본 기능 익히기 (1) (0) | 2024.01.18 |