항해99

23.10.13 항해 99 16기 실전 프로젝트 9일차

김용글 2023. 10. 14. 00:13

오늘 공부한 것

* 댓글 대댓글 기능 Pagination 기능 변경 (무한 스크롤로)

* 마이페이지에 들어갈 댓글 및 대댓글 기능 Pagination 기능 변경 (무한 스크롤로)

* CORS 해결 방안 모색

 

어제는 Pagination 기능을 ResponseEntity<Page<CommentsResponseDto>> 와 @RequestParam("page") int page을

통해 구현했었다.

하지만 설정을 프론트엔드에서 변경할 수 있도록 @PageableDefault을 사용하는 코드로 변경했다

 

또한, Page 대신 Slice를 사용했다

Page 의 경우 데이터의 총 개수 및 전체 페이지를 알 수 있기 때문에 카운트 쿼리가 발생하는데 이것이 다른 쿼리보다

필요한 리스소가 크므로 데이터 양이 많아짐에 따라 성능 이슈가 생길 것을 우려하여 카운트 쿼리가 발생하지 않는

Slice를 사용했다

 

해당 구현은 구글 검색으로 slice 무한스크롤 을 검색했으며

아래 두개의 블로그를 참고해서 구현했다

https://earth-95.tistory.com/116

 

[Querydsl, pageable] slice를 이용한 무한 스크롤

들어가기 전에 프로젝트를 진행하면서, 빵집의 상세 페이지에서 메뉴 더보기 클릭 시, 해당 빵집에 존재하는 모든 메뉴를 순차적으로 보여주어야 했습니다. 이때, 아래로 쭉 내렸을 때 메뉴가

earth-95.tistory.com

https://zzerosouth.tistory.com/32

 

[QueryDSL] Page와 Slice

본격적인 포스팅에 앞서 예전에 했던 프로젝트에서 인피니티 스크롤을 어떻게 구현했었는지 찾아보았다. ※ 혐오 주의 ※ scrollsize = 10; int start = (pageNum * scrollsize); int end = start + scrollsize; List list;

zzerosouth.tistory.com

 

1. 댓글

 1) Controller

    아래와 같이 변경 하였다 Page -> Slice

    // 댓글 조회
    @GetMapping("/posts/{postId}/comments")
    public ResponseEntity<Slice<CommentsResponseDto>> commentsList(@PathVariable("postId") Long postId,
                                                                   @PageableDefault Pageable pageable) {
        return ResponseEntity.ok(commentsService.commentsList(postId, pageable));
    }
    
    // 마이페이지에서 내가 쓴 댓글 조회
    @GetMapping("/posts/{postId}/commentsme")
    public ResponseEntity<Slice<CommentsMeResponseDto>> commentsMeList(@PathVariable("postId") Long postId,
                                                                      @AuthenticationPrincipal UserDetailsImpl userDetails,
                                                                       @PageableDefault Pageable pageable) {
        return ResponseEntity.ok(commentsService.commentsMeList(postId, userDetails.getUsers(), pageable));
    }

 

2) Repository

// 추가
Slice<Comments> findByPosts_IdOrderByCreatedAtDesc(Long postId, Pageable pageable);
Slice<Comments> findByPosts_IdAndEmailOrderByCreatedAtDesc(Long postId, String email, Pageable pageable);


// 삭제
Page<Comments> findByPosts_Id(Long postId, Pageable pageable);
Page<Comments> findByPosts_IdAndEmail(Long postId, String email, Pageable pageable);

 

3) Service

    // 댓글 조회
    public Slice<CommentsResponseDto> commentsList(Long postId,
                                                  Pageable pageable) {

        Page<Comments> commentsList = commentsRepository.findByPosts_IdOrderByCreatedAtDesc(postId, pageable);
        if (commentsList.isEmpty()) {
            throw new CustomException(ErrorCode.POST_NOT_EXIST); // 존재하지 않는 게시글입니다
        }

        List<CommentsResponseDto> commentsResponseDtoList = new ArrayList<>();

        for (Comments comments : commentsList) {
            commentsResponseDtoList.add(new CommentsResponseDto(comments, comments.getNickname()));
        }

        return new SliceImpl<>(commentsResponseDtoList, pageable, commentsList.hasNext());
    }
    
    // 마이페이지에서 내가 쓴 댓글 조회
    public Slice<CommentsMeResponseDto> commentsMeList (Long postId,
                                                        Users users,
                                                        Pageable pageable) {

        Slice<Comments> commentsMeList = commentsRepository.findByPosts_IdAndEmailOrderByCreatedAtDesc(postId, users.getEmail(), pageable);

        if (commentsMeList.isEmpty()) {
            throw new CustomException(ErrorCode.POST_NOT_EXIST); // 존재하지 않는 게시글입니다
        }

        List<CommentsMeResponseDto> CommentsMeResponseDtoList = new ArrayList<>();

        for (Comments comments : commentsMeList) {
            CommentsMeResponseDtoList.add(new CommentsMeResponseDto(comments, comments.getPosts().getTitle(), comments.getNickname()));
        }

        return new SliceImpl<>(CommentsMeResponseDtoList, pageable, commentsMeList.hasNext());
    }

 

2. 대댓글

 1) Controller

     아래와 같이 변경 하였다 Page -> Slice

	// 대댓글 조회
    @GetMapping("/comments/{commentId}/replies")
    public ResponseEntity<Slice<RepliesResponseDto>> repliesList(@PathVariable("commentId") Long commentId,
                                                                 @PageableDefault Pageable pageable) {
        return ResponseEntity.ok(repliesService.repliesList(commentId, pageable));
    }

    // 마이페이지에서 내가 쓴 대댓글 조회
    @GetMapping("/comments/{commentId}/repliesme")
    public ResponseEntity<Slice<RepliesMeResponseDto>> repliesMeList(@PathVariable("commentId") Long commentId,
                                                                    @AuthenticationPrincipal UserDetailsImpl userDetails,
                                                                    @PageableDefault Pageable pageable) {
        return ResponseEntity.ok(repliesService.repliesMeList(commentId, userDetails.getUsers(), pageable));
    }

 

 2) Repository

	// 추가
	Slice<Replies> findByComments_IdOrderByCreatedAtDesc(Long commentId, Pageable pageable);
    Slice<Replies> findByComments_IdAndEmailOrderByCreatedAtDesc(Long commentId, String email, Pageable pageable);
    
	// 삭제
    Page<Replies> findByComments_Id(Long postId, Pageable pageable);
    Page<Replies> findByComments_IdAndEmail(Long commentId, String email, Pageable pageable);

 

3) Service

	// 대댓글 조회
    public Slice<RepliesResponseDto> repliesList(Long commentId,
                                                 Pageable pageable) {

        Slice<Replies> repliesList = repliesRepository.findByComments_IdOrderByCreatedAtDesc(commentId, pageable);

        if (repliesList.isEmpty()) {
            throw new CustomException(ErrorCode.COMMENTS_NOT_EXIST); // 존재하지 않는 댓글입니다
        }

        List<RepliesResponseDto> RepliesResponseDtoList = new ArrayList<>();

        for (Replies replies : repliesList) {
            RepliesResponseDtoList.add(new RepliesResponseDto(replies, replies.getNickname()));
        }

        return new SliceImpl<>(RepliesResponseDtoList, pageable, repliesList.hasNext());
    }

    // 마이페이지에서 내가 쓴 대댓글 조회
    public Slice<RepliesMeResponseDto> repliesMeList(Long commentId,
                                                     Users users,
                                                     Pageable pageable) {

        Slice<Replies> repliesMeList = repliesRepository.findByComments_IdAndEmailOrderByCreatedAtDesc(commentId, users.getEmail(), pageable);

        if (repliesMeList.isEmpty()) {
            throw new CustomException(ErrorCode.COMMENTS_NOT_EXIST); // 존재하지 않는 댓글입니다
        }

        List<RepliesMeResponseDto> RepliesMeResponseDtoList = new ArrayList<>();

        for (Replies replies : repliesMeList) {
            RepliesMeResponseDtoList.add(new RepliesMeResponseDto(replies, replies.getComments().getPosts().getTitle(), replies.getNickname()));
        }

        return new SliceImpl<>(RepliesMeResponseDtoList, pageable, repliesMeList.hasNext());
    }

 

이후에는 프론트엔드분께서 서버와 CORS 에러가 난다고해서 그걸 계속 살펴보았다.

 

1. WebSecurityConfig

    아래와 같이 코드를 추가했는데도 계속해서 문제가 발생하였다

    //Cors
    @Bean
    public CorsConfigurationSource configurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));

        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
        //configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
//        configuration.setExposedHeaders(Arrays.asList("Authorization"));
        configuration.setExposedHeaders(Arrays.asList("*"));
        configuration.setMaxAge(1800L);
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CORS 설정
        http.cors((cors) -> cors.configurationSource(configurationSource()));

 

2. UserController

    프론트엔드분이 확인하시고자 하는 기능이 회원가입 부분이어서

    @CrossOrigin(origins = "http://localhost:5173")으로 해결을 우선 해보려고 했지만 이것 또한 되지 않았다

@CrossOrigin(origins = "http://localhost:5173")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

 

결국 오늘은 CORS를 확인하지 못했다.. 내일은 꼭 해결할 수 있길 바란다