Skip to main content

02G. ์งˆ๋ฌธ ๋ชฉ๋ก๊ณผ ํ…œํ”Œ๋ฆฟ

2023๋…„ 12์›” 27์ผAbout 2 minJavaSpringAWScrashcoursejavajdkjdk8streamspringspringframeworkspringbootawsaws-ec2

02G. ์งˆ๋ฌธ ๋ชฉ๋ก๊ณผ ํ…œํ”Œ๋ฆฟ ๊ด€๋ จ


2-07. ์งˆ๋ฌธ ๋ชฉ๋ก๊ณผ ํ…œํ”Œ๋ฆฟ

์ ํ”„ ํˆฌ ์Šคํ”„๋ง๋ถ€ํŠธ - WikiDocs

pahkey/sbb3 - 2-07

์ด๋ฒˆ ์žฅ์—์„œ๋Š” SBB์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ธ ์งˆ๋ฌธ ๋ชฉ๋ก์„ ๊ตฌํ˜„ํ•  ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿผ ์‹œ์ž‘ํ•ด ๋ณด์ž.

์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์งˆ๋ฌธ ๋ชฉ๋ก์€ ๋‹ค์Œ ์ฃผ์†Œ์— ์ ‘์†ํ•  ๋•Œ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค. ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:8080/question/list์— ์ ‘์†ํ•ด ๋ณด์ž.

์•„๋งˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ 404 ์˜ค๋ฅ˜ํŽ˜์ด์ง€๊ฐ€ ๋‚˜ํƒ€๋‚  ๊ฒƒ์ด๋‹ค.
์•„๋งˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ 404 ์˜ค๋ฅ˜ํŽ˜์ด์ง€๊ฐ€ ๋‚˜ํƒ€๋‚  ๊ฒƒ์ด๋‹ค.

404 ์˜ค๋ฅ˜ ํ•ด๊ฒฐํ•˜๊ธฐ

404 ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด /question/list URL์— ๋Œ€ํ•œ ๋งคํ•‘์ด ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. QuestionController.java ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹ ๊ทœ ์ž‘์„ฑํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/questionQuestionController.java

package com.mysite.sbb.question;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class QuestionController {

    @GetMapping("/question/list")
    @ResponseBody
    public String list() {
        return "question list";
    }
}

์ด๋ ‡๊ฒŒ ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ http://localhost:8080/question/list์— ์ ‘์†ํ•˜๋ฉด ํ™”๋ฉด์— "question list" ๋ฌธ์ž์—ด์ด ์ถœ๋ ฅ๋  ๊ฒƒ์ด๋‹ค.


ํ…œํ”Œ๋ฆฟ ์„ค์ •ํ•˜๊ธฐ

ํ•˜์ง€๋งŒ ๋ณดํ†ต ๋ธŒ๋ผ์šฐ์ €์— ์‘๋‹ตํ•˜๋Š” ๋ฌธ์ž์—ด์€ ์œ„์˜ ์˜ˆ์ฒ˜๋Ÿผ ์ž๋ฐ” ์ฝ”๋“œ์—์„œ ์ง์ ‘ ๋งŒ๋“ค์ง€๋Š” ์•Š๋Š”๋‹ค.

์œ„์—์„œ๋Š” "question list" ๋ผ๋Š” ๋ฌธ์ž์—ด์„ ์ง์ ‘ ์ž๋ฐ” ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์— ๋ฆฌํ„ดํ–ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ํ…œํ”Œ๋ฆฟ ๋ฐฉ์‹์ด๋‹ค. ํ…œํ”Œ๋ฆฟ์€ ์ž๋ฐ” ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” HTML ํ˜•์‹์˜ ํŒŒ์ผ์ด๋‹ค.

ํ…œํ”Œ๋ฆฟ์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์ž. ์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„์—๋Š” Thymeleaf, Mustache, Groovy, Freemarker, Velocity ๋“ฑ์ด ์žˆ๋‹ค. ์ด ์ฑ…์—์„œ๋Š” ์Šคํ”„๋ง ์ง„์˜์—์„œ ์ถ”์ฒœํ•˜๋Š” ํƒ€์ž„๋ฆฌํ”„(Thymleaf) ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

ํƒ€์ž„๋ฆฌํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์„ค์น˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด 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"]๋กœ ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜์ž.

ํƒ€์ž„๋ฆฌํ”„ ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋กœ์ปฌ์„œ๋ฒ„ ์žฌ์‹œ์ž‘์ด ํ•„์š”ํ•˜๋‹ค. ๋กœ์ปฌ์„œ๋ฒ„๋ฅผ ๋ฐ˜๋“œ์‹œ ์žฌ์‹œ์ž‘ํ•˜๊ณ  ์ดํ›„ ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜์ž.


ํ…œํ”Œ๋ฆฟ ์‚ฌ์šฉํ•˜๊ธฐ

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ์˜ ๊ฒฝ๋กœ์— <FontIcon icon="fa-brands fa-html5"/> ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์„ ์‹ ๊ทœ๋กœ ์ž‘์„ฑํ•˜์ž.
๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ์˜ ๊ฒฝ๋กœ์— question_list.html ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์„ ์‹ ๊ทœ๋กœ ์ž‘์„ฑํ•˜์ž.

.question_list.html ํŒŒ์ผ์˜ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/resources/templates/question_list.html

<h2>Hello Template</h2>
question_list.html ํŒŒ์ผ์ด ๋ฐ”๋กœ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  QuestionController๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž.

package com.mysite.sbb.question;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class QuestionController {

    @GetMapping("/question/list")
    // @ResponseBody
    public String list() {
        return "question_list";
    }
}

๋ฆฌํ„ด ๋ฌธ์ž์—ด์€ "question list"๊ฐ€ ์•„๋‹Œ "question_list" ์ž„์— ์ฃผ์˜ํ•˜์ž.

ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด์— ์‚ฌ์šฉํ–ˆ๋˜ @ResponseBody ์• ๋„ˆํ…Œ์ด์…˜์€ ํ•„์š”์—†์œผ๋ฏ€๋กœ ์‚ญ์ œํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  list ๋ฉ”์„œ๋“œ์—์„œ question_list.html ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์˜ ์ด๋ฆ„์ธ "question_list"๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ http://localhost:8080/question/list์— ์ ‘์†ํ•ด ๋ณด์ž.

์šฐ๋ฆฌ๊ฐ€ <FontIcon icon="fa-brands fa-html5"/> ํŒŒ์ผ์— ์ž‘์„ฑํ•œ  ๋‚ด์šฉ์ด ๋ธŒ๋ผ์šฐ์ €์— ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
์šฐ๋ฆฌ๊ฐ€ question_list.html ํŒŒ์ผ์— ์ž‘์„ฑํ•œ <h2>Hello Template</h2> ๋‚ด์šฉ์ด ๋ธŒ๋ผ์šฐ์ €์— ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜์—ฌ ํ…œํ”Œ๋ฆฟ์— ์ „๋‹ฌํ•˜๊ธฐ

ํ…œํ”Œ๋ฆฟ์˜ ๋‚ด์šฉ์„ ํ™”๋ฉด์— ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์„ฑ๊ณตํ–ˆ๋‹ค. ์ด์ œ ํ…œํ”Œ๋ฆฟ์— ์งˆ๋ฌธ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜์—ฌ ์ถœ๋ ฅํ•ด ๋ณด์ž. ์งˆ๋ฌธ ๋ชฉ๋ก์„ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋กœ ์กฐํšŒํ•œ ์งˆ๋ฌธ ๋ชฉ๋ก์€ Model ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…œํ”Œ๋ฆฟ์— ์ „๋‹ฌํ• ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด์ž.

๋‹ค์Œ๊ณผ ๊ฐ™์ด QuestionController๋ฅผ ์ˆ˜์ •ํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/question/QuestionController.java

package com.mysite.sbb.question;

import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Controller
public class QuestionController {

    private final QuestionRepository questionRepository;

    @GetMapping("/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 ์• ๋„ˆํ…Œ์ด์…˜ ์ ์šฉ์ด ํ•„์š”ํ•˜๋‹ค.)

ํ…Œ์ŠคํŠธ์ฝ”๋“œ(SbbApplicationTests.java)์—์„œ๋Š” ์†์„ฑ์— @Autowired ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Question ๋ฆฌํฌ์ง€ํ„ฐ์˜ findAll ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์งˆ๋ฌธ ๋ชฉ๋ก ๋ฐ์ดํ„ฐ์ธ questionList๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Model ๊ฐ์ฒด์— "questionList" ๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ๊ฐ’์„ ์ €์žฅํ–ˆ๋‹ค. Model ๊ฐ์ฒด๋Š” ์ž๋ฐ” ํด๋ž˜์Šค์™€ ํ…œํ”Œ๋ฆฟ ๊ฐ„์˜ ์—ฐ๊ฒฐ๊ณ ๋ฆฌ ์—ญํ• ์„ ํ•œ๋‹ค. Model ๊ฐ์ฒด์— ๊ฐ’์„ ๋‹ด์•„๋‘๋ฉด ํ…œํ”Œ๋ฆฟ์—์„œ ๊ทธ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Model ๊ฐ์ฒด๋Š” ๋”ฐ๋กœ ์ƒ์„ฑํ•  ํ•„์š”์—†์ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ง€์ •ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ์ž๋™์œผ๋กœ Model ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.


ํ…œํ”Œ๋ฆฟ์—์„œ ์ „๋‹ฌ๋ฐ›์€ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉํ•˜๊ธฐ

Model ๊ฐ์ฒด์— ์ €์žฅํ•œ ๊ฐ’์„ ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์„๊นŒ? ๋‹ค์Œ๊ณผ ๊ฐ™์ด question_list.html ํ…œํ”Œ๋ฆฟ์„ ์ˆ˜์ •ํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/resources/templates/question_list.html

<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>

์งˆ๋ฌธ ๋ชฉ๋ก์„ 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์— ์ ‘์†ํ•ด ๋ณด์ž.

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค.
๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค.

์ด์ „์— ํ…Œ์ŠคํŠธ๋กœ ๋“ฑ๋กํ•œ ์งˆ๋ฌธ 1๊ฑด์ด ์กฐํšŒ๋œ ๋ชจ์Šต์ด๋‹ค. ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ์‹œ Question ๋ฐ์ดํ„ฐ๋ฅผ ๋” ์ถ”๊ฐ€ํ–ˆ๋‹ค๋ฉด ๋” ๋งŽ์€ ์งˆ๋ฌธ์ด ํ‘œ์‹œ๋  ๊ฒƒ์ด๋‹ค.

์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž„๋ฆฌํ”„์˜ ์†์„ฑ

ํƒ€์ž„๋ฆฌํ”„์˜ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ์—๋Š” ๋‹ค์Œ 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>

์ด ์ฑ…์—์„œ๋Š” ์ƒˆ๋กœ์šด ํƒ€์ž„๋ฆฌํ”„ ๋ฌธ๋ฒ•์ด ๋‚˜์˜ฌ ๋•Œ๋งˆ๋‹ค ์ž์„ธํžˆ ์„ค๋ช…ํ•  ๊ฒƒ์ด๋ฏ€๋กœ ์ง€๊ธˆ ๋‹น์žฅ ๋ชจ๋“  ํƒ€์ž„๋ฆฌํ”„์˜ ์†์„ฑ์— ๋Œ€ํ•ด ์•Œ์•„ ๋‘˜ ํ•„์š”๋Š” ์—†๋‹ค.

์ด์ƒ๊ณผ ๊ฐ™์ด ์งˆ๋ฌธ ๋ชฉ๋ก์„ ๋งŒ๋“ค์—ˆ๋‹ค.

์ถ•ํ•˜ํ•œ๋‹ค!!