Skip to main content

02K. ๋‹ต๋ณ€ ๋“ฑ๋ก

About 2 minJavaSpringAWScrashcoursejavajdkjdk8streamspringspringframeworkspringbootawsaws-ec2

02K. ๋‹ต๋ณ€ ๋“ฑ๋ก ๊ด€๋ จ


2-11. ๋‹ต๋ณ€ ๋“ฑ๋ก

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

pahkey/sbb3 - 2-11open in new window

์•ž ์ ˆ์—์„œ ์šฐ๋ฆฌ๋Š” ์งˆ๋ฌธ์„ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” ์งˆ๋ฌธ์— ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•˜๊ณ  ๋ณด์—ฌ ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด ๋ณด์ž. ์งˆ๋ฌธ ์ƒ์„ธ ํ™”๋ฉด์— ๋‹ต๋ณ€์„ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•œ ํ…์ŠคํŠธ ์ฐฝ(textarea)๊ณผ <๋‹ต๋ณ€๋“ฑ๋ก> ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•˜๊ณ , ์ด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋‹ต๋ณ€์ด ์ €์žฅ๋˜๋„๋ก ๊ตฌํ˜„ํ•ด ๋ณด์ž.


๋‹ต๋ณ€ ๋“ฑ๋ก ๋ฒ„ํŠผ ๋งŒ๋“ค๊ธฐ

์งˆ๋ฌธ ์ƒ์„ธ ํ…œํ”Œ๋ฆฟ์— ๋‹ต๋ณ€ ์ €์žฅ์„ ์œ„ํ•œ form, textarea, input ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•˜์ž.

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

<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>

<form th:action="@{|/answer/create/${question.id}|}" method="post">
    <textarea name="content" id="content" rows="15"></textarea>
    <input type="submit" value="๋‹ต๋ณ€๋“ฑ๋ก">
</form>



ย 
ย 
ย 
ย 

<๋‹ต๋ณ€๋“ฑ๋ก> ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ „์†ก๋˜๋Š” form์˜ action์€ ํƒ€์ž„๋ฆฌํ”„์˜ "th:action" ์†์„ฑ์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค. ์œ„์™€ ๊ฐ™์ด ์ถ”๊ฐ€ํ•˜๊ณ  ์งˆ๋ฌธ ์ƒ์„ธ ํŽ˜์ด์ง€์— ์ ‘์†ํ•ด ๋ณด์ž.

๋‹ต๋ณ€์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ํ…์ŠคํŠธ์ฐฝ๊ณผ  ๋ฒ„ํŠผ์ด ์ƒ์„ฑ๋˜์—ˆ๋‹ค.
๋‹ต๋ณ€์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” ํ…์ŠคํŠธ์ฐฝ๊ณผ <๋‹ต๋ณ€๋“ฑ๋ก> ๋ฒ„ํŠผ์ด ์ƒ์„ฑ๋˜์—ˆ๋‹ค.

์ด์ œ <๋‹ต๋ณ€๋“ฑ๋ก> ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด POST ๋ฐฉ์‹์œผ๋กœ /answer/create/<์งˆ๋ฌธid> URL์ด ํ˜ธ์ถœ(submit)๋  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ ์•„์ง  URL์— ๋Œ€ํ•œ ๋งคํ•‘์ด ์—†์œผ๋ฏ€๋กœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ 404 ํŽ˜์ด์ง€๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.
ํ•˜์ง€๋งŒ ์•„์ง /answer/create/<์งˆ๋ฌธid> URL์— ๋Œ€ํ•œ ๋งคํ•‘์ด ์—†์œผ๋ฏ€๋กœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ 404 ํŽ˜์ด์ง€๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.

์ด ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๋‹ต๋ณ€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค๊ณ  ํ•ด๋‹น URL์— ๋Œ€ํ•œ ๋งคํ•‘์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.


๋‹ต๋ณ€ ์ปจํŠธ๋กค๋Ÿฌ ๋งŒ๋“ค๊ธฐ

์•ž์—์„œ ์งˆ๋ฌธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์—ˆ๋“ฏ์ด ๋‹ต๋ณ€ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด์ž.

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

package com.mysite.sbb.answer;

import com.mysite.sbb.question.Question;
import com.mysite.sbb.question.QuestionService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RequestMapping("/answer")
@RequiredArgsConstructor
@Controller
public class AnswerController {

    private final QuestionService questionService;

    @PostMapping("/create/{id}")
    public String createAnswer(Model model, @PathVariable("id") Integer id, @RequestParam String content) {
        Question question = this.questionService.getQuestion(id);
        // TODO: ๋‹ต๋ณ€์„ ์ €์žฅํ•œ๋‹ค. 
        return String.format("redirect:/question/detail/%s", id);
    }
}

๋‹ต๋ณ€ ์ปจํŠธ๋กค๋Ÿฌ์˜ URL ํ”„๋ฆฌํ”ฝ์Šค๋„ /answer๋กœ ๊ณ ์ •ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  /answer/create/{id}์™€ ๊ฐ™์€ URL ์š”์ฒญ์‹œ createAnswer ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋„๋ก @PostMapping์œผ๋กœ ๋งคํ•‘ํ–ˆ๋‹ค. @PostMapping์€ @GetMapping๊ณผ ๋™์ผํ•˜๊ฒŒ ๋งคํ•‘์„ ๋‹ด๋‹นํ•˜๋Š” ์—ญํ• ์„ ํ•˜์ง€๋งŒ POST์š”์ฒญ๋งŒ ๋ฐ›์•„๋“ค์ผ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•œ๋‹ค. ๋งŒ์•ฝ ์œ„ URL์„ GET๋ฐฉ์‹์œผ๋กœ ์š”์ฒญํ•  ๊ฒฝ์šฐ์—๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

@PostMapping(value="/create/{id}") ๋Œ€์‹  @PostMapping("/create/{id}") ์ฒ˜๋Ÿผ value๋Š” ์ƒ๋žตํ•ด๋„ ๋œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  createAnswer ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” @RequestParam String content ํ•ญ๋ชฉ์ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ์ด ํ•ญ๋ชฉ์€ ํ…œํ”Œ๋ฆฟ์—์„œ ๋‹ต๋ณ€์œผ๋กœ ์ž…๋ ฅํ•œ ๋‚ด์šฉ(content)์„ ์–ป๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ํ…œํ”Œ๋ฆฟ์˜ ๋‹ต๋ณ€ ๋‚ด์šฉ์— ํ•ด๋‹นํ•˜๋Š” textarea์˜ name ์†์„ฑ๋ช…์ด content์ด๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์„œ๋„ ๋ณ€์ˆ˜๋ช…์„ content๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ๋งŒ์•ฝ content ๋Œ€์‹  ๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค.

createAnswer ๋ฉ”์„œ๋“œ์˜ URL ๋งคํ•‘ /create/{id}์—์„œ {id}๋Š” ์งˆ๋ฌธ์˜ id ์ด๋ฏ€๋กœ ์ด id ๊ฐ’์œผ๋กœ ์งˆ๋ฌธ์„ ์กฐํšŒํ•˜๊ณ  ์—†์„ ๊ฒฝ์šฐ์—๋Š” 404 ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ์•„์ง ๋‹ต๋ณ€์„ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  ์ผ๋‹จ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฃผ์„์œผ๋กœ ๋‹ต๋ณ€์„ ์ €์žฅํ•ด์•ผ ํ•จ์„ ๋‚˜ํƒ€๋‚ด์—ˆ๋‹ค.

// TODO: ๋‹ต๋ณ€์„ ์ €์žฅํ•œ๋‹ค. 

๋‹ต๋ณ€ ์ €์žฅํ•˜๊ธฐ

์ด์ œ ๋‹ต๋ณ€์„ ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์ž. ๋‹ต๋ณ€์„ ์ €์žฅํ•˜๋ ค๋ฉด ๋‹ต๋ณ€ ์„œ๋น„์Šค๊ฐ€ ํ•„์š”ํ•˜๋‹ค. AnswerService๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์ž.

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

package com.mysite.sbb.answer;

import com.mysite.sbb.question.Question;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@RequiredArgsConstructor
@Service
public class AnswerService {

    private final AnswerRepository answerRepository;

    public void create(Question question, String content) {
        Answer answer = new Answer();
        answer.setContent(content);
        answer.setCreateDate(LocalDateTime.now());
        answer.setQuestion(question);
        this.answerRepository.save(answer);
    }
}

AnswerService์—๋Š” ๋‹ต๋ณ€ ์ƒ์„ฑ์„ ์œ„ํ•ด create ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. create ๋ฉ”์„œ๋“œ๋Š” ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์€ question๊ณผ content๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Answer ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ €์žฅํ–ˆ๋‹ค.

์ด์ œ ์ž‘์„ฑํ•œ create ๋ฉ”์„œ๋“œ๋ฅผ AnswerController์—์„œ ์‚ฌ์šฉํ•ด ๋ณด์ž.

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

(... ์ƒ๋žต ...)
public class AnswerController {

    private final QuestionService questionService;
    private final AnswerService answerService;

    @PostMapping("/create/{id}")
    public String createAnswer(Model model, @PathVariable("id") Integer id, @RequestParam String content) {
        Question question = this.questionService.getQuestion(id);
        this.answerService.create(question, content);
        return String.format("redirect:/question/detail/%s", id);
    }
}




ย 




ย 



์ฃผ์„๋ฌธ์„ ์‚ญ์ œํ•˜๊ณ  AnswerService์˜ create ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋‹ต๋ณ€์„ ์ €์žฅํ• ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ๋‹ค.

์ด์ œ ๋‹ค์‹œ ์งˆ๋ฌธ ์ƒ์„ธ ํŽ˜์ด์ง€์— ์ ‘์†ํ•˜์—ฌ ํ…์ŠคํŠธ ์ฐฝ์— ์•„๋ฌด ๊ฐ’์ด๋‚˜ ์ž…๋ ฅํ•˜๊ณ  <๋‹ต๋ณ€๋“ฑ๋ก>์„ ๋ˆŒ๋Ÿฌ ๋ณด์ž. ๋‹ต๋ณ€์€ ์ž˜ ์ €์žฅ๋˜์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ํ™”๋ฉด์—๋Š” ์•„๋ฌด๋Ÿฐ ๋ณ€ํ™”๊ฐ€ ์—†๋‹ค. ์™œ ๊ทธ๋Ÿด๊นŒ?

์™œ๋ƒํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์•„์ง ๋“ฑ๋กํ•œ ๋‹ต๋ณ€์„ ํ…œํ”Œ๋ฆฟ์— ํ‘œ์‹œํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์งˆ๋ฌธ ์ƒ์„ธ ํŽ˜์ด์ง€์— ๋‹ต๋ณ€ ํ‘œ์‹œํ•˜๊ธฐ

์งˆ๋ฌธ์— ๋“ฑ๋ก๋œ ๋‹ต๋ณ€์„ ์ƒ์„ธ ํ™”๋ฉด์— ํ‘œ์‹œํ•ด ๋ณด์ž. ๋‹ต๋ณ€์€ ๋“ฑ๋ก๋œ ์งˆ๋ฌธ ๋ฐ‘์— ๋ณด์—ฌ์•ผ ํ•˜๋ฏ€๋กœ ์งˆ๋ฌธ ์ƒ์„ธ ํ…œํ”Œ๋ฆฟ์— ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์ž.

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

<h1 th:text="${question.subject}"></h1>
<div th:text="${question.content}"></div>
<h5 th:text="|${#lists.size(question.answerList)}๊ฐœ์˜ ๋‹ต๋ณ€์ด ์žˆ์Šต๋‹ˆ๋‹ค.|"></h5>
<div>
    <ul>
        <li th:each="answer : ${question.answerList}" th:text="${answer.content}"></li>
    </ul>
</div>
<form th:action="@{|/answer/create/${question.id}|}" method="post">
    <textarea name="content" id="content" rows="15"></textarea>
    <input type="submit" value="๋‹ต๋ณ€๋“ฑ๋ก">
</form>

๊ธฐ์กด ์ฝ”๋“œ์—์„œ ๋‹ต๋ณ€์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์˜์—ญ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค. #lists.size(question.answerList)}๋Š” ๋‹ต๋ณ€ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. #lists.size(์ดํ„ฐ๋Ÿฌ๋ธ”๊ฐ์ฒด)๋Š” ํƒ€์ž„๋ฆฌํ”„๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๊ฐ์ฒด์˜ ๊ธธ์ด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋‹ต๋ณ€์€ question ๊ฐ์ฒด์˜ answerList๋ฅผ ์ˆœํšŒํ•˜์—ฌ <li> ์—˜๋ฆฌ๋จผํŠธ๋กœ ํ‘œ์‹œํ–ˆ๋‹ค.

์ด์ œ ์งˆ๋ฌธ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ๊ณ ์นจํ•˜๋ฉด ๋ฐฉ๊ธˆ ๋“ฑ๋กํ•œ ๋‹ต๋ณ€๋“ค์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค.
์ด์ œ ์งˆ๋ฌธ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ๊ณ ์นจํ•˜๋ฉด ๋ฐฉ๊ธˆ ๋“ฑ๋กํ•œ ๋‹ต๋ณ€๋“ค์ด ๋ณด์ผ ๊ฒƒ์ด๋‹ค.

์ถ•ํ•˜ํ•œ๋‹ค! ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ ๋‹ต๋ณ€์„ ์ €์žฅํ•˜๊ณ  ํ™•์ธํ• ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.


์ด์ฐฌํฌ (MarkiiimarK)
Never Stop Learning.