코딩과 결혼합니다

230902 - (코드리팩토링) api 6개를 3개로 합치기!! 본문

코딩과 매일매일♥/Seoulvival

230902 - (코드리팩토링) api 6개를 3개로 합치기!!

코딩러버 2023. 9. 2. 11:31
728x90

기존 TagController코드 -> 수정 후 코드


TagController

@RestController
@RequiredArgsConstructor
@RequestMapping("/tags")
public class TagController {

    private final TagService tagService;

    //전체 태그 인기순위
    @GetMapping("/All")
    public List<String> allRankNumber(){
        return tagService.allRankNumber();
    }

    //카테고리별 태그 인기순위
    @GetMapping("/category")
    public List<String> categoryRankNumer(@RequestParam String category){
        return tagService.categoryRankNumer(category);
    }

    //전체 인기 순위 태그 post 조회
    @GetMapping("/post/All")
    public PostListResponse hashtagPostResponseDtos(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String hashtagName,
            @RequestParam String type,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.hashtagPostResponseDtos(size, page-1, hashtagName, type, null);
        }
        User user = userDetails.getUser();
        return tagService.hashtagPostResponseDtos(size, page-1, hashtagName, type, user);
    }


    //전체 인기 순위 태그 post 조회 - 위치
    @GetMapping("/post/location/All")
    public PostListResponse postLocationResponseDtos(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String gu,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.postLocationResponseDtos(size, page-1, gu, null);
        }
        User user = userDetails.getUser();
        return tagService.postLocationResponseDtos(size, page-1,gu, user);
    }

    //카테고리별 인기 순위 태그 post 조회
    @GetMapping("/post/category")
    public PostListResponse categoryHashtagPostResponseDtos(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String hashtagName,
            @RequestParam String category,
            @RequestParam String type,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.categoryHashtagPostResponseDtos(size, page-1, hashtagName, category , type, null);
        }
        User user = userDetails.getUser();
        return tagService.categoryHashtagPostResponseDtos(size, page-1, hashtagName, category,type,user);
    }

    //카테고리별 인기 순위 태그 post 조회 + 위치
    @GetMapping("/post/location/category")
    public PostListResponse categoryLocationPostResponseDtos(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String gu,
            @RequestParam String category,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.categoryLocationPostResponseDtos(size, page-1, gu, category , null);
        }
        User user = userDetails.getUser();
        return tagService.categoryLocationPostResponseDtos(size, page-1, gu, category, user);
    }
}

수정 TagController

@RestController
@RequiredArgsConstructor
@RequestMapping("/tags")
public class TagController {

    private final TagService tagService;


    //태그 인기순위
    @GetMapping("/rank")
    public List<String> rankNumber(@RequestParam String category){
        return tagService.rankNumber(category);
    }

    //태그별 포스트
    @GetMapping("/posts")
    public PostListResponse tagsPosts(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String hashtagName,
            @RequestParam String type,
            @RequestParam String category,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.tagsPosts(size, page-1, hashtagName, type, category, null);
        }
        User user = userDetails.getUser();
        return tagService.tagsPosts(size, page-1, hashtagName, type,category, user);
    }

    //태그별 post + 위치
    @GetMapping("/posts/location")
    public PostListResponse postLocation(
            @RequestParam int size,
            @RequestParam int page,
            @RequestParam String gu,
            @RequestParam String category,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ){
        if(Objects.isNull(userDetails)){
            return tagService.postLocation(size, page-1, gu, category, null);
        }
        User user = userDetails.getUser();
        return tagService.postLocation(size, page-1,gu, category, user);
    }
}

기존의 코드는 회원일 때 보이는  api와 비회원일 때 보이는 api 이렇게 각각 2개씩 있었는데,

유저의 정보(토큰)가 null 인지 아닌지에 따라서 return 하는 값을 다르게 하여 하나의 api로 합칠 수 있었다.


기존 TagService코드 -> 수정 후 코드


TagService

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TagService {

    private final PostRepository postRepository;
    private final PostLikeRepository postLikeRepository;
    private final PostScrapRepository postScrapRepository;
    private final ReportRepository reportRepository;

    //전체 - 태그 순위
    public List<String> allRankNumber() {
        List<Post> postList = postRepository.findAll();
        List<String> rankedIds = getHashTagsFromPosts(postList);

        return getTopRankedTags(rankedIds, 6);
    }

    //카테고리 - 태그 순위
    public List<String> categoryRankNumer (String category) {
        List<Post> postList = postRepository.findAllBycategory(category);
        List<String> rankedIds = getHashTagsFromPosts(postList);

        return getTopRankedTags(rankedIds, 6);
    }

    //전체 - 태그별 post
    public PostListResponse hashtagPostResponseDtos (int size, int page, String hashtagName, String type, User user
    ) {
        Pageable pageable = PageRequest.of(page, size);

        //인기순/최신순
        Page<Post> postPage = type.equals("popular")
                ? postRepository.findAllByHashtagContainingOrderByPostViewCountDesc(hashtagName, pageable)
                : postRepository.findAllByHashtagContainingOrderByCreatedAtDesc(hashtagName, pageable);

        if (!postPage.hasContent()) {
            throw new IllegalArgumentException("존재하지 않는 태그입니다.");
        }

        List<PostResultDto> postResultDtos =
                postPage.getContent().stream()
                        .map(post -> mapToPostResultDto(post,user))
                        .collect(Collectors.toList());
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //전체 - 태그별 post - +위치
    public PostListResponse postLocationResponseDtos(int size, int page, String gu, User user) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        Page<Post> postPage = postRepository.findAllByGu(gu, pageable);

        List<PostResultDto> postResultDtos =
                postPage.getContent().stream()
                        .map(post -> mapToPostResultDto(post,user))
                        .collect(Collectors.toList());
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //카테고리 - 태그별 포스트
    public PostListResponse categoryHashtagPostResponseDtos (int size, int page, String hashtagName, String category, String type, User user) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Post> postPage = type.equals("popular")
                ? postRepository.findAllByCategoryAndHashtagContainingOrderByPostViewCountDesc(category, hashtagName, pageable)
                : postRepository.findAllByCategoryAndHashtagContainingOrderByCreatedAtDesc(category, hashtagName, pageable);

        if (!postPage.hasContent()) {
            throw new IllegalArgumentException("존재하지 않는 태그 혹은 존재하지 않는 카테고리 입니다.");
        }

        List<PostResultDto> postResultDtos = postPage.stream()
                .map(post -> mapToPostResultDto(post,user))
                .collect(Collectors.toList());
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //카테고리 - 태그별 포스트 - +위치
    public PostListResponse categoryLocationPostResponseDtos(int size, int page, String gu, String category, User user) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        Page<Post> postPage = postRepository.findAllByGuAndCategory(gu,category, pageable);

        List<PostResultDto> postResultDtos =
                postPage.getContent().stream()
                        .map(post -> mapToPostResultDto(post,user))
                        .collect(Collectors.toList());
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //주어진 리스트에서 상위 N개의 요소를 선택하는 메서드
    private List<String> getTopRankedTags(List<String> rankedIds, int limit) {
        return rankedIds.stream().limit(limit).collect(Collectors.toList());
    }

    //PostResultDto 객체로 변환하는 메서드
    private PostResultDto mapToPostResultDto(Post post, User user) {
        UserResponseDto userResponseDto = new UserResponseDto(post.getUser());
        PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(post);
        LocationResponseDto locationResponseDto = new LocationResponseDto(post.getLname(), post.getAddress(), post.getLat(), post.getLng(), post.getGu());
        if (Objects.isNull(user)){
            return new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto, false,false,false);
        }
        boolean hasLikedPost = postLikeRepository.existsLikeByPostAndUser(post, user);
        boolean hasScrapped = postScrapRepository.existsScrapByPostAndUser(post, user);
        boolean hasReported = reportRepository.existsReportByPostAndUser(post,user);
        return new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto,hasLikedPost,hasScrapped,hasReported);
    }

    //postList에서 해시태그를 추출하고 인기순으로 정렬된 태그 목록을 반환
    private List<String> getHashTagsFromPosts(List<Post> postList) {
        Map<String, Integer> idFrequencyMap = new HashMap<>();

        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (!tagName.isEmpty()) {
                    idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
                }
            }
        }

        return idFrequencyMap.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
}

중복된 코드가 굉장히 많고 비슷한 api가 반복되어 가독성이 떨어진다.

수정 후 TagService

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TagService {

    private final PostRepository postRepository;
    private final PostLikeRepository postLikeRepository;
    private final PostScrapRepository postScrapRepository;
    private final ReportRepository reportRepository;

    // 태그 인기순위
    public List<String> rankNumber(String category) {
        List<Post> postList;

        if (category == null || category.isEmpty()) {
            postList = postRepository.findAll();
        } else {
            postList = postRepository.findAllBycategory(category);
        }

        List<String> rankedIds = getHashTagsFromPosts(postList);
        return getTopRankedTags(rankedIds, 6);
    }

    // 태그별 포스트
    public PostListResponse tagsPosts(int size, int page, String hashtagName, String type, String category, User user) {
        Pageable pageable = PageRequest.of(page, size);
        Page<Post> postPage;

        if (category == null || category.isEmpty()) {
            postPage = type.equals("popular")
                    ? postRepository.findAllByHashtagContainingOrderByPostViewCountDesc(hashtagName, pageable)
                    : postRepository.findAllByHashtagContainingOrderByCreatedAtDesc(hashtagName, pageable);
        } else {
            postPage = type.equals("popular")
                    ? postRepository.findAllByCategoryAndHashtagContainingOrderByPostViewCountDesc(category, hashtagName, pageable)
                    : postRepository.findAllByCategoryAndHashtagContainingOrderByCreatedAtDesc(category, hashtagName, pageable);
        }

        if (!postPage.hasContent()) {
            throw new IllegalArgumentException("존재하지 않는 태그 혹은 존재하지 않는 카테고리입니다.");
        }

        List<PostResultDto> postResultDtos = postPage.stream()
                .map(post -> mapToPostResultDto(post, user))
                .collect(Collectors.toList());
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    // 태그별 포스트 - +위치
    public PostListResponse postLocation(int size, int page, String gu, String category, User user) {
        System.out.println("gu " + gu);
        System.out.println("category " + category);
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        Page<Post> postPage;

        if (category == null || category.isEmpty()) {
            postPage = postRepository.findAllByGu(gu, pageable);
            System.out.println("카테고리 없을 때");
        } else {
            postPage = postRepository.findAllByGuAndCategory(gu, category, pageable);
            System.out.println("카테고리 있을 때");
        }

        List<PostResultDto> postResultDtos =
                postPage.getContent().stream()
                        .map(post -> mapToPostResultDto(post, user))
                        .collect(Collectors.toList());

        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //주어진 리스트에서 상위 N개의 요소를 선택하는 메서드
    private List<String> getTopRankedTags(List<String> rankedIds, int limit) {
        return rankedIds.stream().limit(limit).collect(Collectors.toList());
    }

    //PostResultDto 객체로 변환하는 메서드
    private PostResultDto mapToPostResultDto(Post post, User user) {
        UserResponseDto userResponseDto = new UserResponseDto(post.getUser());
        PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(post);
        LocationResponseDto locationResponseDto = new LocationResponseDto(post.getLname(), post.getAddress(), post.getLat(), post.getLng(), post.getGu());
        if (Objects.isNull(user)){
            return new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto, false,false,false);
        }
        boolean hasLikedPost = postLikeRepository.existsLikeByPostAndUser(post, user);
        boolean hasScrapped = postScrapRepository.existsScrapByPostAndUser(post, user);
        boolean hasReported = reportRepository.existsReportByPostAndUser(post,user);
        return new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto,hasLikedPost,hasScrapped,hasReported);
    }

    //postList에서 해시태그를 추출하고 인기순으로 정렬된 태그 목록을 반환
    private List<String> getHashTagsFromPosts(List<Post> postList) {
        Map<String, Integer> idFrequencyMap = new HashMap<>();

        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (!tagName.isEmpty()) {
                    idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
                }
            }
        }

        return idFrequencyMap.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }
}

코드의 길이는 크게 차이가 나지 않지만 api가 3개로 줄어서 가독성이 좋아졌다. if - else 문으로 user가 있을 때와 없을때 repository에서 post들을  다르게 가져오도록 하였다.