Rental Application (React & Spring boot Microservice) - 14 : post-service(3)

yellow_note·2021년 8월 27일
0

#1 post-service 에러 수정

  • git-local-repo에 있는 post-service.yml 파일이 잘못되어 다시 설정했습니다. user-service.yml파일도 spring: 을 추가해주셔야 합니다.
  • post.service.yml
spring:
    datasource:
        url: "jdbc:mariadb://localhost:3306/POSTSERVICE?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC"
        username: biuea
        password: '{cipher}AQA0kR480WB9lTIJ8iVxdL6PszDEQpyEgtiG2FJeAbM1xKj5dlPq0spDlPXIzp0UdB4KaGX2swtmnnMcU/LKfk+RPy/CeJ0wFs19l1qpFzu0/XVqi3jfvxZj38GTBZB9+vD9GABqckEf9oUcKMlxcbW7IY7+7xtRGxgNFj8MvVyno9TpKO64QonkulGw1zWpCW73HQWPxfCyl6xjF2PPZIFtUt1hZe2akwlPsSRwLwl+zS6i/o4g8jQZpkXYXrc9VGnc27+OZs58oe3CgpX9oWc3yxYTuD9F8nGmux3aAqkYUmZLsgMd99yeeiLWT5XuTEBkwmxv93GZk1hACXVCQLFlubeZ1c5elWwwDmzc3aSQeVhr27buHQNfdyALEw/8q6A='
        driver-class-name: org.mariadb.jdbc.Driver

gateway:
    ip: 127.0.0.1

#2 post-service test

두 가지의 문제를 해결하니 post-service가 정상작동을 하였습니다. 테스트하기 이전에 우선 apigateway-service의 application.yml파일에 post-service요청에 대한 경로를 설정해야합니다. 다음과 같이 설정하도록 하겠습니다.

  • application.yml
spring:
  application:
    name: apigateway-service

  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest

  cloud:
    gateway:
      routes:
      ...
        - id: post
          uri: lb://POST-SERVICE
          predicates:
            - Path=/post-service/**
          filters:
            - RemoveRequestHeader=Cookie
            - RewritePath=/post-service/(?<segment>.*), /$\{segment}

      ...

1) POST /post-service/write - 게시글 작성


빌려줄게요라는 포스트타입으로 게시글을 작성 요청을 했고, 데이터베이스에 잘 저장된 모습입니다. 빌려주세요라는 타입으로도 작성 요청을 해보겠습니다.


다음과 같은 에러메시지를 받았는데, 아마 컨트롤러 부분에서 NullPointerException에 관한 문제인 것 같습니다. 데이터를 dto로 변환할 때, 빌려줄게요에서는 date, rentalPrice등등 이런 값들을 받아오지만 빌려주세요에서는 이런 값들이 아예 없기 때문에 처음에 작성한 코드에서 NullPointerException 문제가 생기는 것 같습니다. 다음과 같이 코드를 바꿔보도록 하겠습니다.

  • PostController
package com.microservices.postservice.controller;

import com.microservices.postservice.dto.CommentDto;
import com.microservices.postservice.dto.PostDto;
import com.microservices.postservice.service.CommentService;
import com.microservices.postservice.service.PostService;
import com.microservices.postservice.vo.RequestCreateComment;
import com.microservices.postservice.vo.RequestWrite;
import com.microservices.postservice.vo.ResponsePost;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/")
@Slf4j
public class PostController {
    ...

    log.info("Post Service's Controller Layer :: Call write Method!");
        
        PostDto postDto = null;

        if(postVo.getPostType().equals("빌려줄게요")) {
            postDto = PostDto.builder()
                             .postType(postVo.getPostType())
                             .title(postVo.getTitle())
                             .content(postVo.getContent())
                             .startDate(postVo.getDate().get(0))
                             .endDate(postVo.getDate().get(1))
                             .rentalPrice(postVo.getRentalPrice())
                             .writer(postVo.getWriter())
                             .userId(postVo.getUserId())
//                                     .multipartFiles(postVo.getImages())
                             .build();
        } else {
            postDto = PostDto.builder()
                             .postType(postVo.getPostType())
                             .title(postVo.getTitle())
                             .content(postVo.getContent())
                             .startDate(null)
                             .endDate(null)
                             .rentalPrice(null)
                             .writer(postVo.getWriter())
                             .userId(postVo.getUserId())
            //                                     .multipartFiles(postVo.getImages())
                             .build();
        }

        PostDto post = postService.write(postDto);
        ResponsePost result = ResponsePost.builder()
                                          .id(post.getId())
                                          .postType(post.getPostType())
                                          .title(post.getTitle())
                                          .content(post.getContent())
                                          .rentalPrice(post.getRentalPrice())
                                          .startDate(post.getStartDate())
                                          .endDate(post.getEndDate())
                                          .createdAt(post.getCreatedAt())
                                          .writer(post.getWriter())
                                          .userId(post.getUserId())
//                                        .images(post.getImages())
                                          .comments(post.getComments())
                                          .status(post.getStatus())
                                          .build();

        return ResponseEntity.status(HttpStatus.CREATED).body(result);

    ...
}

빌려줄게요, 빌려주세요 케이스를 나누었습니다. 빌려줄게요는 대여에 대한 정보가 있어야 하므로 대여 정보를 포함한 게시글을 요청하였고, 빌려주세요는 대여에 대한 정보가 없으므로 대여 정보를 제외한 게시글을 요청했습니다.


  • 현재 모든 코드들은 index번호를 부여하는 코드지만 이전에 캡쳐해둔 결과 사진으로 random.UUID를 이용하여 postId를 부여하는 방식을 사용하였습니다. 하지만 지금은 코드를 수정하여 id값을 index 번호로 부여했습니다. 이 결과 사진을 쓰는 이유는 제가 겪었던 에러를 보여드리기 위해서 보여드리는 사진들입니다. 결과화면을 보는데에 큰 무리가 없을 것 같습니다. 2)부터는 인덱스 번호를 부여하는 현재 코드의 결과화면입니다.

결과도 잘 나오는 모습을 볼 수 있습니다.

2) GET /post-service/:postId/post - 게시글 불러오기

3) GET /post-service/:userId/posts - 유저가 작성한 게시글 불러오기

4) GET /post-service/ - 전체 게시글 불러오기

5) POST /post-service/:postId/delete - 삭제로 상태 변화

6) GET /post-service/:status - 상태 값에 따른 게시글 불러오기


7) POST /post-service/:postId/comments - 댓글 작성

8) GET /post-service/:postId/posts - 댓글 불러오기
이 부분부터 댓글과 게시글 사이에 양방향 매핑 문제 때문에 재귀호출이 되는 상황이 발생합니다.

  • 양방향 순환 참조 문제

에러 화면은 캡쳐를 하지 못해 보여드리진 못하지만 제가 겪은 문제는 jpa로 만든 PostEntity, CommentEntity에서의 순환 참조 문제였습니다. PostEntity에서는 Comment List가, CommentEntity에서는 PostEntity의 객체를 각각 가지고 있었습니다. 즉, 게시글을 호출하게 되면 다음과 같은 순환 참조 오류가 발생했습니다.

{
  "id": 1,
  ...
  "comment": {
    "id": 3,
    ...
    "post": {
      "id": 1,
      ...
      "comment": {
        "id": 3,
        ...
      }
    }
  }
}

위 json 데이터처럼 게시글을 호출하면 게시글 데이터안에는 댓글 데이터가 있고, 그 댓글 데이터 안에는 게시글 데이터가 있으므로 계속 서로를 참조하는 형태를 띄웠던 오류였습니다.

그래서 이 문제에 대한 여러 해답글들을 찾아봤지만 제 문제에는 해답이 되지 못했고, 그러다 내린 해결책이 comment데이터를 post에 담을 때 comment데이터에 있는 post데이터를 빼고 넣자라는 결론이었습니다. 이게 올바른 해결책인지는 모르겠지만 제 경우에는 잘 작동을 해서 제 나름의 해결책대로 사용하도록 하겠습니다. 수정한 PostService의 코드는 다음과 같습니다.

  • PostServiceImpl
@Service
@Slf4j
public class PostServiceImpl implements PostService {
    ...

    @Transactional
    @Override
    public PostDto readPostById(Long id) {
        log.info("Post Service's Service Layer :: Call readPostById Method!");

        PostEntity postEntity = postRepository.findPostById(id);
//        List<ImageEntity> images = new ArrayList<>();
        List<CommentEntity> comments = new ArrayList<>();

//        postEntity.getImages().forEach(i -> {
//            images.add(i);
//        });

        postEntity.getComments().forEach(i -> {
            comments.add(CommentEntity.builder()
                                      .id(i.getId())
                                      .comment(i.getComment())
                                      .writer(i.getWriter())
                                      .createdAt(i.getCreatedAt())
                                      .build());
        });

        return PostDto.builder()
                      .userId(postEntity.getUserId())
                      .postType(postEntity.getPostType())
                      .rentalPrice(postEntity.getRentalPrice())
                      .title(postEntity.getTitle())
                      .content(postEntity.getContent())
                      .startDate(postEntity.getStartDate())
                      .endDate(postEntity.getEndDate())
                      .createdAt(postEntity.getCreatedAt())
                      .writer(postEntity.getWriter())
//                      .images(images)
                      .comments(comments)
                      .status(postEntity.getStatus())
                      .build();
    }

    @Transactional
    @Override
    public List<PostDto> getAllPosts() {
        log.info("Post Service's Service Layer :: Call getAllPosts Method!");

        Iterable<PostEntity> posts = postRepository.findAll();
        List<PostDto> postList = new ArrayList<>();

        posts.forEach(v -> {
            List<CommentEntity> comments = new ArrayList<>();

            v.getComments().forEach(i -> {
                comments.add(CommentEntity.builder()
                                          .id(i.getId())
                                          .comment(i.getComment())
                                          .writer(i.getWriter())
                                          .createdAt(i.getCreatedAt())
                                          .build());
            });

            postList.add(PostDto.builder()
                                .userId(v.getUserId())
                                .postType(v.getPostType())
                                .rentalPrice(v.getRentalPrice())
                                .title(v.getTitle())
                                .content(v.getContent())
                                .startDate(v.getStartDate())
                                .endDate(v.getEndDate())
                                .createdAt(v.getCreatedAt())
                                .writer(v.getWriter())
//                                .images(v.getImages())
                                .comments(comments)
                                .status(v.getStatus())
                                .build());
        });

        return postList;
    }

    @Transactional
    @Override
    public Iterable<PostDto> getAllPostsByStatus(String status) {
        log.info("Post Service's Service Layer :: Call getAllPostsByStatus Method!");

        Iterable<PostEntity> posts = postRepository.findAllByStatus(status);
        List<PostDto> postList = new ArrayList<>();

        posts.forEach(v -> {
            List<CommentEntity> comments = new ArrayList<>();

            v.getComments().forEach(i -> {
                comments.add(CommentEntity.builder()
                                          .id(i.getId())
                                          .comment(i.getComment())
                                          .writer(i.getWriter())
                                          .createdAt(i.getCreatedAt())
                                          .build());
            });

            postList.add(PostDto.builder()
                    .userId(v.getUserId())
                    .postType(v.getPostType())
                    .rentalPrice(v.getRentalPrice())
                    .title(v.getTitle())
                    .content(v.getContent())
                    .startDate(v.getStartDate())
                    .endDate(v.getEndDate())
                    .createdAt(v.getCreatedAt())
                    .writer(v.getWriter())
    //                .images(v.getImages())
                    .comments(comments)
                    .status(v.getStatus())
                    .build());
        });

        return postList;
    }

    @Transactional
    @Override
    public List<PostDto> getPostsByUserId(String userId) {
        log.info("Post Service's Service Layer :: Call getPostsByUserId Method!");

        Iterable<PostEntity> posts = postRepository.findAllByUserId(userId);
        List<PostDto> postList = new ArrayList<>();

        posts.forEach(v -> {
            List<CommentEntity> comments = new ArrayList<>();

            v.getComments().forEach(i -> {
                comments.add(CommentEntity.builder()
                                          .id(i.getId())
                                          .comment(i.getComment())
                                          .writer(i.getWriter())
                                          .createdAt(i.getCreatedAt())
                                          .build());
            });

            postList.add(PostDto.builder()
                                .userId(v.getUserId())
                                .postType(v.getPostType())
                                .rentalPrice(v.getRentalPrice())
                                .title(v.getTitle())
                                .content(v.getContent())
                                .startDate(v.getStartDate())
                                .endDate(v.getEndDate())
                                .createdAt(v.getCreatedAt())
                                .writer(v.getWriter())
//                                .images(v.getImages())
                                .comments(comments)
                                .status(v.getStatus())
                                .build());
        });

        return postList;
    }

    ...
}

해당 코드의 결과값을 보여드리겠습니다.


결과화면처럼 순환 참조 문제가 해결된 모습을 볼 수 있습니다.

9) DELETE /post-service/:id/comments - 댓글 삭제

댓글 삭제 기능 또한 잘 되는 것을 볼 수 있습니다. 이상으로 post-service에 대한 포스트를 마치고, 다음 포스트는 rental-service에 대한 부분을 구현하도록 하겠습니다.

0개의 댓글

관련 채용 정보