tuter77

게시판 만들기(10) - Review CRUD 구현 본문

GroupStudy

게시판 만들기(10) - Review CRUD 구현

tuter77 2023. 1. 29. 20:48

● Review CRUD 구현.

▷ 구현하기 전 정보

review는 앞서 엔티티를 구현할 때 각 게시글(healthInfo)의 id값을 참조로 가져 해당 id값을 게시글의 리뷰형태로 구현해야한다.

이 때문에 데이터를 view에 보여주는 것부터 고민이었는데, 일단 상세페이지에서 healthinfo id 값을 통해 리뷰를 불러오는 형태로 가기로 했다.

id값은 review 엔티티에 healthinfo 객체에 저장되는데, db에서 이름이 health_id로 지정이 되어있다.

앞서 review 엔티티를 구현할 때 healthinfo 엔티티에 리뷰를 추가하는 메서드를 구현해 놓았는데 이를 이용할 수 있을지.. 해봐야한다.

 

복습해보면, review 엔티티에서 manytoone의 관계를 healthinfo 엔티티와 가지고, 반대로 healthInfo에서는 onetomany의 관계를 가진다. 이는 양방향 관계로 JoinColomn과 mappedBy 로 관계의 주인이 참조값을 저장하는  review엔티티임을 명시했었다.n+1의 문제를 lazy로딩방식으로 대처했고, 엔티티의 상태변화가 cascade로 관계된 엔티티로 전달되게 했다.review 엔티티의 멤버 변수들은 review의 id, title, contents, health_id(참조값)이다.

 

먼저 필요한 클래스와 파일들을 나열하자면,  클라이언트의 요청을 받을 reviewController, 컨트롤러의 요청을 처리할 reviewService, 값이 저장되는 엔티티와 db와 데이터를 주고받을 repository는 이미 만들어두었고, 그 값을 감싸주는 DTO클래스는 미숙해서 당장은 생성하지 않을 예정이다.또한 review를 보여줄 view.html파일과 각 저장, 수정 기능이 구현될 save, modify html 파일도 필요할것이다.당장은 이렇게만 구성되어있지만, MVC패턴을 제대로 이용하려면 DTO와 객체생성을 맡은 Factory도 필요할 것이다.

 

데이터를 주고 받는 기능자체에서 이해도가 많이 떨어지기 때문에 차근차근 공부해보면서 진행할 것이다.

 

▷Review 컨트롤러

package com.example.demo.controller;

import com.example.demo.domain.HealthInfo;
import com.example.demo.domain.Review;
import com.example.demo.repository.ReviewRepository;
import com.example.demo.service.ReviewService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequiredArgsConstructor
@Slf4j
public class ReviewController {
    public final ReviewService reviewService;


    @GetMapping("/review")
    public String healthView(Model model,@RequestParam Long id){
        model.addAttribute("review", reviewService.findReview(id));
        return "review";
    }
    @GetMapping("/review/write") // 저장 페이지.
    public String writePage(@RequestParam Long id){
        return "reviewWrite";
    }

    @PostMapping("/review/writer") //저장 기능 구현.
    public String reviewSave(HealthInfo healthInfo, Review review){
        reviewService.reviewWrite(healthInfo, review);

        return "redirect:/review"; //prg패턴.
    }

    @GetMapping("/review/delete")
    public String reviewDeleter(@RequestParam Long id){
        reviewService.reviewDelete(id);
        return "redirect:/review";
    }


}

 

기존에 만들었던 형태와 유사하게 만들었다. 

homeContoller에서 repository와 직통으로 연결되는 의존의 형태를 최대한 service를 이용해 완화할 생각이다.

일단 리뷰 페이지는 healthInfo.id를 매개변수로 받아 해당 값을 가진 리뷰들이 모두 찾아지도록 만들 예정이다. 

저장기능을 대충이나마 구현했는데, healthInfo.id는 자동으로 저장되게끔 해야하는데 객체자체를 저장해야할지, Long id로 저장해야할지 모르겠다.

엔티티에는

@ManyToOne
@JoinColumn(name = "health_id")
private HealthInfo healthInfo;

healthInfo라는 HealthInfo타입의 변수가 선언되어있기는하지만, id값만 딱 저장되는건지는 모르겠다.

 

▷ReviewService

먼저 인터페이스를 보면 필요한 기능들을 명시해놓았다.

package com.example.demo.service;

import com.example.demo.domain.HealthInfo;
import com.example.demo.domain.Review;

import java.util.Optional;

public interface ReviewService {

    public void reviewWrite(HealthInfo healthInfo, Review review);
    public Review findReview(Long id) ;
    public Optional<Review> reviewList(HealthInfo healthInfo);
    //수정기능 값이 저장이 안됨.
    void reviewModify(Long id);

    public boolean reviewDelete(Long id);

}

저장, 삭제, 값찾아오기, 수정 등을 정의하고 구현체로 가보겠다.

 

package com.example.demo.service.impl;

import com.example.demo.domain.HealthInfo;
import com.example.demo.domain.Review;
import com.example.demo.repository.ReviewRepository;
import com.example.demo.service.ReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Optional;

@Service
@RequiredArgsConstructor
public class ReviewServiceImpl implements ReviewService {

    private final ReviewRepository reviewRepository;
    
    @Override
    public Review findReview(Long id) {
        return reviewRepository.findById(id).orElseThrow(IllegalArgumentException::new);
    }

    @Override
    public Optional<Review> reviewList(HealthInfo healthInfo) {
        Long health_id = healthInfo.getId();
        Optional<Review> list = reviewRepository.findById(health_id);
        return list;
    }


    @Override
    public void reviewWrite(HealthInfo healthInfo, Review review) {
        healthInfo.getId();
        healthInfo.addReview(review);
    }

    @Override
    public void reviewModify(Long id) {

    }

    @Override
    public boolean reviewDelete(@RequestParam Long id) {
        reviewRepository.deleteById(id);
        return true;
    }
}

삭제기능은 동일하게 id값으로 지우면되는데, 저장과 데이터 찾아오기에서 고민이다.

 

▷html파일들

- review

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>헬스장 리뷰 페이지</title>
</head>

<body>
<table>
    <thead>
    <tr>
        <h1>ReviewPage</h1>
    </tr>
    <a th:href="@{/review/write(id=${healthInfo.id})}">리뷰 쓰기</button>
    </thead>
    <tbody>
    <tr th:each = "review:${list}">
        <td th:text="${review.id}"></td>
        <td>
            <a th:text="${review.title}"></a>
            <a th:text="${review.contents}"></a>
            <a type="hidden" th:text="${review.healthInfo}"></a>
            <a th:href="@{/review/delete(id=${review.id})}">삭제</a>
        </td>
    </tr>
    </tbody>
</table>
</body>
</html>

 

 

 

- review 저장 view

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>헬스장 정보 저장 페이지</title>
</head>

<body>
<table>
    <form th:action="@{/review/writer}" method="post">
        <p>아래에 자료를 입력하세요.</p>
        <tr>
            <td><input type="text" name="title"></td>
        </tr>
        <tr>
            <td><input type="text" name="contents"></td>
        </tr>
        <a name="health_id" type="hidden" th:href="@{healthInfo.id}></a>
        <tr>
        <td><input type="reset" value="취소"></td>
        <td><button type="submit">저장</button></td>
        </tr>
    </form>
</table>
</body>
</html>

 

위 내용은 2023.01.19에 공부한 내용입니다.

링크 : https://dudwls3278.tistory.com/45