23.10.13 항해 99 16기 실전 프로젝트 9일차
오늘 공부한 것
* 댓글 대댓글 기능 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를 확인하지 못했다.. 내일은 꼭 해결할 수 있길 바란다