Skip to main content

03L. ์•ต์ปค

About 2 minJavaSpringAWScrashcoursejavajdkjdk8streamspringspringframeworkspringbootawsaws-ec2

03L. ์•ต์ปค ๊ด€๋ จ


3-12. ์•ต์ปค

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

pahkey/sbb3 - 3-12open in new window

์ด์ œ SBB๊ฐ€ ์ ์  ์™„์„ฑ ๋˜์–ด๊ฐ€๊ณ  ์žˆ๋‹ค. ์ด๋ฒˆ์—๋Š” ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์— ๋ฐœ๊ฒฌ๋œ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ์ ์„ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

๋ฐœ๊ฒฌ๋œ ๋ฌธ์ œ์ ์€ ๋‹ต๊ธ€์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•œ ํ›„์— ํ•ญ์ƒ ํŽ˜์ด์ง€ ์ƒ๋‹จ์œผ๋กœ ์Šคํฌ๋กค์ด ์ด๋™๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณธ์ธ์ด ์ž‘์„ฑํ•œ ๋‹ต๋ณ€์„ ํ™•์ธํ•˜๋ ค๋ฉด ๋‹ค์‹œ ์Šคํฌ๋กค์„ ๋‚ด๋ ค์„œ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ด ๋ฌธ์ œ๋Š” ๋‹ต๋ณ€์„ ์ถ”์ฒœํ•œ ๊ฒฝ์šฐ์—๋„ ๋™์ผํ•˜๊ฒŒ ๋ฐœ์ƒํ•œ๋‹ค.

Ajax์™€ ๊ฐ™์€ ๋น„๋™๊ธฐ ํ†ต์‹  ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” ๋ณด๋‹ค ์‰ฌ์šด ๋ฐฉ๋ฒ•์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ๋ณด์ž. HTML์—๋Š” URL ํ˜ธ์ถœ์‹œ ์›ํ•˜๋Š” ์œ„์น˜๋กœ ์ด๋™์‹œ์ผœ ์ฃผ๋Š” ์•ต์ปค(anchor) ํƒœ๊ทธ๊ฐ€ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด HTML ์ค‘๊ฐ„์— <a id="test"></a> ๋ผ๋Š” ์•ต์ปค ํƒœ๊ทธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ด HTML์„ ํ˜ธ์ถœํ•˜๋Š” URL ๋’ค์— #test ๋ผ๊ณ  ๋ถ™์—ฌ์ฃผ๋ฉด ํ•ด๋‹น ํŽ˜์ด์ง€๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด์„œ ํ•ด๋‹น ์•ต์ปค๋กœ ์Šคํฌ๋กค์ด ์ด๋™๋œ๋‹ค.

๋‹ต๋ณ€๋“ฑ๋ก, ๋‹ต๋ณ€์ˆ˜์ •, ๋‹ต๋ณ€์ถ”์ฒœ ์‹œ ์•ต์ปค ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์œ„์น˜๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •ํ•ด ๋ณด์ž.


๋‹ต๋ณ€ ์•ต์ปค ์ถ”๊ฐ€

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

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

<!-- (... ์ƒ๋žต ...) -->
<!-- ๋‹ต๋ณ€์˜ ๊ฐฏ์ˆ˜ ํ‘œ์‹œ -->
<h5 class="border-bottom my-3 py-2" 
    th:text="|${#lists.size(question.answerList)}๊ฐœ์˜ ๋‹ต๋ณ€์ด ์žˆ์Šต๋‹ˆ๋‹ค.|"></h5>
<!-- ๋‹ต๋ณ€ ๋ฐ˜๋ณต ์‹œ์ž‘ -->
<div class="card my-3" th:each="answer : ${question.answerList}">
    <a th:id="|answer_${answer.id}|"></a>
    <div class="card-body">
<!-- (... ์ƒ๋žต ...) -->






ย 


๋‹ต๋ณ€์ด ๋ฐ˜๋ณต๋˜์–ด ํ‘œ์‹œ๋˜๋Š” th:each ๋ฐ”๋กœ ๋‹ค์Œ์— <a th:id="|answer_${answer.id}|"></a>์™€ ๊ฐ™์ด ์•ต์ปค ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ์•ต์ปค ํƒœ๊ทธ์˜ id ์†์„ฑ์€ ์œ ์ผํ•œ ๊ฐ’์ด์–ด์•ผ ํ•˜๋ฏ€๋กœ answer_{{ answer.id }}์™€ ๊ฐ™์ด ๋‹ต๋ณ€ id๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.


๋‹ต๋ณ€ redirect

์ด์ œ ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ๋•Œ ์œ„์—์„œ ์ง€์ •ํ•œ ์•ต์ปค ํƒœ๊ทธ๋กœ ์ด๋™ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ž. ๋‹ค์Œ์€ ๋‹ต๋ณ€ ๋“ฑ๋ก ๋˜๋Š” ๋‹ต๋ณ€ ์ˆ˜์ •์„ ํ•œ ๋’ค ์‚ฌ์šฉํ–ˆ๋˜ ๊ธฐ์กด ์ฝ”๋“œ์˜ ์ผ๋ถ€์ด๋‹ค.

return String.format("redirect:/question/detail/%s", answer.getQuestion().getId());

์—ฌ๊ธฐ์— ์•ต์ปค๋ฅผ ํฌํ•จํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

return String.format("redirect:/question/detail/%s#answer_%s", 
    answer.getQuestion().getId(), answer.getId());
ย 
ย 

๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜๋Š” ์งˆ๋ฌธ ์ƒ์„ธ URL์— #answer_2์™€ ๊ฐ™์€ ์•ต์ปค๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋‹ต๋ณ€ ์ž‘์„ฑ, ์ˆ˜์ •, ์ถ”์ฒœ์˜ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋ถ€๋ถ„์„ ๋ณ€๊ฒฝํ•˜์ž.


AnswerService

์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋‹ต๋ณ€์ด ๋“ฑ๋ก๋œ ์œ„์น˜๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ต๋ณ€ ๊ฐ์ฒด๊ฐ€ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด AnswerService๋ฅผ ๋จผ์ € ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

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

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

    // (... ์ƒ๋žต ...)

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

    // (... ์ƒ๋žต ...)
}





ย 






ย 





AnswerController

๊ทธ๋ฆฌ๊ณ  ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฝ”๋“œ๋“ค์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž.

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

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

    // (... ์ƒ๋žต ...)

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/create/{id}")
    public String createAnswer(Model model, @PathVariable("id") Integer id, 
            @Valid AnswerForm answerForm, BindingResult bindingResult, Principal principal) {
        Question question = this.questionService.getQuestion(id);
        SiteUser siteUser = this.userService.getUser(principal.getName());
        if (bindingResult.hasErrors()) {
            model.addAttribute("question", question);
            return "question_detail";
        }
        Answer answer = this.answerService.create(question, 
                answerForm.getContent(), siteUser);
        return String.format("redirect:/question/detail/%s#answer_%s", 
                answer.getQuestion().getId(), answer.getId());
    }

     // (... ์ƒ๋žต ...)

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/modify/{id}")
    public String answerModify(@Valid AnswerForm answerForm, @PathVariable("id") Integer id,
            BindingResult bindingResult, Principal principal) {
        if (bindingResult.hasErrors()) {
            return "answer_form";
        }
        Answer answer = this.answerService.getAnswer(id);
        if (!answer.getAuthor().getUsername().equals(principal.getName())) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "์ˆ˜์ •๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.");
        }
        this.answerService.modify(answer, answerForm.getContent());
        return String.format("redirect:/question/detail/%s#answer_%s", 
                answer.getQuestion().getId(), answer.getId());
    }

    // (... ์ƒ๋žต ...)

    @PreAuthorize("isAuthenticated()")
    @GetMapping("/vote/{id}")
    public String answerVote(Principal principal, @PathVariable("id") Integer id) {
        Answer answer = this.answerService.getAnswer(id);
        SiteUser siteUser = this.userService.getUser(principal.getName());
        this.answerService.vote(answer, siteUser);
        return String.format("redirect:/question/detail/%s#answer_%s", 
                answer.getQuestion().getId(), answer.getId());
    }
}















ย 
ย 
ย 
ย 
















ย 
ย 










ย 
ย 


๋‹ต๋ณ€์„ ์ž‘์„ฑ, ์ˆ˜์ •, ์ถ”์ฒœํ•œ ํ›„์— ํ•ด๋‹น ๋‹ต๋ณ€์œผ๋กœ ์ด๋™ํ•˜๋„๋ก ์•ต์ปค ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์—ˆ๋‹ค.


๋‹ต๋ณ€ ์•ต์ปค ํ™•์ธ

์ด์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•œ ํ›„ ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•  ๋•Œ ์Šคํฌ๋กค์ด ์ง€์ •ํ•œ ์•ต์ปค๋กœ ์ด๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.
์ด์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•œ ํ›„ ๋‹ต๋ณ€์„ ๋“ฑ๋กํ•  ๋•Œ ์Šคํฌ๋กค์ด ์ง€์ •ํ•œ ์•ต์ปค๋กœ ์ด๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด์ž.

ํ™”๋ฉด์— ๋„ค๋ชจ๋ฐ•์Šค๋กœ ํ‘œ์‹œํ•œ ๋ถ€๋ถ„์„ ๋ณด๋ฉด ์ƒ์„ธ ํ™”๋ฉด URL์— #answer_8์ด ์ถ”๊ฐ€๋˜์—ˆ๊ณ , ์Šคํฌ๋กค์ด ํ•ด๋‹น ์œ„์น˜๋กœ ์ด๋™ํ–ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.


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