Follow Work/SpringbootBoard

[StringBoot] 질문 목록과 템플릿 (4)

ReCode.B 2022. 8. 11. 23:27
728x90

참고 링크 : https://wikidocs.net/book/7601

오라클DB와 intelliJ로 작업하였습니다


템플릿 설정하기

스프링 진영에서 추천하는 타임리프(Thymleaf) 템플릿 엔진을 사용할 것이다.

 

Thymeleaf

Integrations galore Eclipse, IntelliJ IDEA, Spring, Play, even the up-and-coming Model-View-Controller API for Java EE 8. Write Thymeleaf in your favourite tools, using your favourite web-development framework. Check out our Ecosystem to see more integrati

www.thymeleaf.org

 

타임리프를 사용하려면 설치가 필요하다. 다음과 같이 build.gradle 파일을 수정하자.

[파일명: /sbb/build.gradle]

(... 생략 ...)

dependencies {
    (... 생략 ...)
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
}

(... 생략 ...)

위와 같이 수정하고 "Refresh Gradle Project"로 필요한 라이브러리를 설치하자.


데이터 조회하여 템플릿에 전달하기

템플릿에 질문 목록을 조회하여 출력해 보자. 질문 목록을 조회하기 위해서는 Question 리포지터리를 사용해야 한다. Question 리포지터리로 조회한 질문 목록은 Model 클래스를 사용하여 템플릿에 전달할수 있다. 

package com.gosari.repick_project.question;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;


@RequiredArgsConstructor
@Controller
public class QuestionController {

    private final QuestionRepository questionRepository;

    @RequestMapping("/question/list")
    public String list(Model model){
        List<Question> questionList = this.questionRepository.findAll();
        model.addAttribute("questionList" , questionList);
        return "question_list";
    }
}

@RequiredArgsConstructor 애너테이션으로 questionRepository 속성을 포함하는 생성자를 생성하였다. @RequiredArgsConstructor는 롬복이 제공하는 애너테이션으로 final이 붙은 속성을 포함하는 생성자를 자동으로 생성하는 역할을 한다. 롬복의 @Getter, @Setter가 자동으로 Getter, Setter 메서드를 생성하는 것과 마찬가지@RequiredArgsConstructor는 자동으로 생성자를 생성한다. 따라서 스프링 의존성 주입 규칙에 의해 questionRepository 객체가 자동으로 주입된다.

 

의존성 주입(Dependency Injection) 방식 3가지
@Autowired 속성 - 속성에 @Autowired 애너테이션을 적용하여 객체를 주입하는 방식
생성자 - 생성자를 작성하여 객체를 주입하는 방식 (권장하는 방식)
Setter - Setter 메서드를 작성하여 객체를 주입하는 방식 (메서드에 @Autowired 애너테이션 적용이 필요하다.)

그리고 Question 리포지터의 findAll 메서드를 사용하여 질문 목록 데이터인 questionList를 생성하고 Model 객체에 "questionList" 라는 이름으로 값을 저장했다. Model 객체는 자바 클래스와 템플릿 간의 연결고리 역할을 한다. Model 객체에 값을 담아두면 템플릿에서 그 값을 사용할 수 있다.

Model 객체는 따로 생성할 필요없이 컨트롤러 메서드의 매개변수로 지정하기만 하면
스프링부트가 자동으로 Model 객체를 생성한다.


템플릿에서 전달받은 데이터 사용하기

Model 객체에 저장한 값을 템플릿에서 사용할 수 있다고 했다. 어떻게 사용할수 있을까? 다음과 같이 question_list.html 템플릿을 작성해보자

<!DOCTYPE html>
<html lang="en" xmlns:th="//www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <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>
</body>
</html>

질문 목록을 HTML의 테이블 구조로 표시되게 하였다.

템플릿 파일에 입력된 th:each="question : ${questionList}"와 같은 특이한 표현이 눈에띌 것이다.

 th: 로 시작하는 속성은 타임리프 템플릿 엔진이 사용하는 속성이다.

바로 이 부분이 자바 코드와 연결된다. question_list.html 파일에 사용한 타임리프 속성들을 잠시 살펴보자.

<tr th:each="question : ${questionList}">

QuestionController의 list 메서드에서 조회한 질문 목록 데이터"questionList"라는 이름으로 Model 객체에 저장했다. 타임리프는 Model 객체에 저장된 값을 읽을 수 있으므로 템플릿에서 questionList를 사용할수 있게 되는 것이다. 위의 코드는 <tr> ... </tr> 엘리먼트를 questionList의 갯수만큼 반복하여 출력하는 역할을 한다. 그리고 questionList에 저장된 데이터를 하나씩 꺼내 question 객체에 대입하여 반복구간 내에서 사용할수 있게 한다.

자바의 for each 문을 떠올리면 쉽게 이해할 수 있을 것이다.

다음 코드는 바로 앞의 for 문에서 얻은 question 객체의 제목을 <td> 엘리먼트의 텍스트로 출력한다.

<td th:text="${question.subject}"></td>

다음 코드도 같은 맥락으로 이해할 수 있다.

<td th:text="${question.createDate}"></td>

http://localhost:8080/question/list


자주 사용하는 타임리프의 속성

타임리프의 자주 사용하는 속성에는 다음 3가지 유형이 있다. 이 3가지 유형만 알아도 여러 기능을 충분히 만들 수 있다.

1. 분기문 속성

분기문 속성은 다음과 같이 사용한다.

th:if="${question != null}"

위의 경우 question 객체가 null 이 아닌 경우에 해당 엘리먼트가 표시된다.

2. 반복문 속성

반복문은 반복횟수만큼 해당 엘리먼트를 반복하여 표시한다. 반복문 속성은 자바의 for each 문과 유사하다.

th:each="question : ${questionList}"

반복문은 다음과 같이 사용할 수도 있다.

th:each="question, loop : ${questionList}"

추가한 loop 객체를 이용하여 루프 내에서 다음과 같은 속성을 사용할수 있다.

  • loop.index - 반복 순서, 0부터 1씩 증가
  • loop.count - 반복 순서, 1부터 1씩 증가
  • loop.size - 반복 객체의 요소 갯수 (예: questionList의 요소 갯수)
  • loop.first - 루프의 첫번째 순서인 경우 true
  • loop.last - 루프의 마지막 순서인 경우 true
  • loop.odd - 루프의 홀수번째 순서인 경우 true
  • loop.even - 루프의 짝수번째 순서인 경우 true
  • loop.current - 현재 대입된 객체 (예: 위의 경우 question과 동일)

3. 텍스트 속성

th:text=값 속성은 해당 엘리먼트의 텍스트로 "값"을 출력한다.

th:text="${question.subject}"

텍스트는 th:text 속성 대신에 다음처럼 대괄호를 사용하여 값을 직접 출력할수 있다.

<tr th:each="question : ${questionList}">
    <td>[[${question.subject}]]</td>
    <td>[[${question.createDate}]]</td>
</tr>

 

728x90