5 min to read
[스프링부트 시리즈13] 상세페이지 생성하기
질문의 제목을 클릭하면 해당질문과 관련된 상세 페이지로 넘어가는 기능을 추가해보자.
상세 페이지 만들기
이 장에는 질문 목록의 제목을 클릭하면 관련된 상세 내용 페이지로 넘어가는 기능을 추가한다.
질문 목록에 링크 추가하기
먼저, 질문 목록의 제목을 클릭하면 상세 화면이 호출되도록 제목에 링크를 추가하자.
다음과 같이 질문 목록 템플릿인 question_list.html
을 수정해 보자.
[파일명: /templates/question_list.html]
<table>
<thead>
<tr>
<th>제목</th>
<th>작성일시</th>
</tr>
</thead>
<tbody>
<tr th:each="question, index : ${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>
태그를 통해 질문 목록의 제목을 텍스트로 출력하던 것에서 질문의 상세 내용이 담긴 웹 페이지로 이동할 수 있는 링크로 변경했다.
제목에 상세 페이지 URL을 연결하기 위해 타임리프의 th:href
속성을 사용한다.
이때 URL은 반드시 @{
와 }
문자 사이에 입력해야 한다.
여기서는 문자열 /question/detail/
과 ${question.id}
값이 조합되어 /question/detail/${question.id}
로 작성했다.
만약 좌우에 |
없이 다음과 같이 사용하면 오류가 발생한다.
<a th:href="@{/question/detail/${question.id}}" th:text="${question.subject}"></a>
타임리프에서는 /question/detail/
과 같은 문자열과 ${question.id}
와 같은 자바 객체의 값을 더할 때는 반드시 다음처럼 |
로 좌우를 감싸 주어야 한다.
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
상세 페이지 컨트롤러 만들기
1) 브라우저를 통해 질문 목록 페이지에 접속해 링크를 클릭해보자.
위와 같이 404 오류가 발생한다.
아직 http://localhost:8080/question/detail/2
를 매핑하지 않았기 때문에 404 오류가 발생한다.
URL에 끝에 ‘2’가 붙는 이유는 질문 상세 링크에 question.id가 포함되어 있기 때문이다.
2) 다음과 같이 오류를 해결하기 위해 QuestionController에 질문 상세 페이지 URL을 매핑해 보자.
[파일명:/question/QuestionController.java] ```java (… 생략 …) import org.springframework.web.bind.annotation.PathVariable; (… 생략 …) public class QuestionController {
(... 생략 ...)
@GetMapping(value = "/question/detail/{id}")
public String detail(Model model, @PathVariable("id") Integer id) {
return "question_detail";
} } ``` 요청한 URL인 `http://localhost:8080/question/detail/2`의 숫자 2처럼 변하는 id값을 얻을 때에는 `@PathVariable` 애너테이션을 사용한다. 이때 `@GetMapping(value = "/question/detail/{id}")`에서 사용한 id와 `@PathVariable("id")`의 매개변수 이름이 이와 같이 동일해야 한다. 다시 로컬 서버를 실행한 후, 브라우저에서 URL을 입력해 보자.
3) 그런데 코드를 수정하고 다시 URL을 호출하면 이번에는 404 대신 500 오류가 발생할 것이다. 왜냐하면 응답으로 리턴한 question_detail
템플릿이 없기 때문이다. templates
에 question_detail.html
파일을 새로 만들어 다음 내용을 작성해 보자.
[파일명:/templates/question_detail.html]
<h1>제목</h1>
<div>내용</div>
1) 로컬 서버를 재시작하고 URL을 요청하면 다음과 같은 화면이 나온다.
상세 페이지에 서비스 사용하기
이제 화면에 출력한 ‘제목’과 ‘내용’ 문자열 대신 질문 데이터의 제목(subject)과 내용(content)을 출력해 보자. 먼저, 제목과 내용에 들어갈 질문 데이터를 조회해 보자.
1) 질문 데이터를 조회하기 위해서 QuestionService.java
를 다음과 같이 수정해 보자.
[파일명:/question/QuestionService.java]
(... 생략 ...)
import java.util.Optional;
import com.mysite.sbb.DataNotFoundException;
(... 생략 ...)
public class QuestionService {
(... 생략 ...)
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값)가 존재하는지 검사하는 과정이 필요하다. 만약 id값에 해당하는 질문 데이터가 없을 경우에는 예외 클래스인 DataNotFoundException
이 실행되도록 했다.
2) 사실 DataNotFoundException
클래스는 아직 존재하지 않아 컴파일 오류가 발생한다. 다음과 같이 com.mysite.sbb
패키지에 자바 파일을 추가로 만들어 DataNotFoundException
클래스를 정의해 보자.
[파일명: DataNotFoundException.java]
package com.mysite.sbb;
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);
}
}
DataNotFoundException
은 데이터베이스에서 특정 엔티티 또는 데이터를 찾을 수 없을 때 발생시키는 예외 클래스로 만들었다.
이 예외가 발생하면 스프링 부트는 설정된 HTTP 상태 코드(HttpStatus.NOT_FOUND
)와 이유(“entity not found”)를 포함한 응답을 생성하여 클라이언트에게 반환하게 된다. 여기서는 404 오류를 반환하도록 작성했다.
3) 그리고 QuestionController.java
로 돌아가 QuestionService
의 getQuestion
메서드를 호출하여 Question
객체를 템플릿에 전달할 수 있도록 다음과 같이 수정하자.
[파일명:/question/QuestionController.java]
(... 생략 ...)
public class QuestionController {
(... 생략 ...)
@GetMapping(value = "/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";
}
}
상세 페이지 출력하기
‘제목’과 ‘내용’ 문자열 대신 질문 데이터의 제목(subject)과 내용(content)을 화면에 출력해 보자.
1) 상세 페이지 템플릿인 question_detail.html
파일로 돌아가 다음과 같이 수정해 보자. 이때 QuestionController
의 detail 메서드에서 Model
객체에 ‘question’이라는 이름으로 Question
객체를 저장했으므로 다음과 같이 작성할 수 있다.
[파일명:/templates/question_detail.html]
<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
2) 이제 다시 상세 페이지를 요청해 보자. 다음과 같은 화면이 나타날 것이다.
조회한 질문 데이터의 제목과 내용이 화면에 잘 출력된 것을 확인할 수 있다. 3) 이번에는 33과 같은 존재하지 않는 id값을 입력해 페이지를 요청해 보자.
http://localhost:8080/question/detail/33
이와 같이 존재하지 않는 데이터를 조회하려고 할 경우에는 DataNotFound Exception
이라는 예외 클래스가 실행되어 404 Not found
오류가 발생하는 것을 확인할 수 있다.