본문 바로가기
Project/Community

[커뮤니티 #13] 기존 프로젝트 리팩토링 계획 및 진행하기

by 제이._ 2022. 11. 23.

https://blog.naver.com/sosow0212/222854060468

 

스프링부트 커뮤니티 API 서버 만들기 #12 JUnit5로 Service 단위 테스트 만들어보기

스프링부트 커뮤니티 API 서버 만들기 #12 JUnit5로 Service 단위 테스트 만들어보기 https://github...

blog.naver.com

#13번 이전 글은 기존 네이버 블로그에 업로드되어 있습니다 :)


 

오랜만에 열어보는 커뮤니티 프로젝트

올해 7월에 당근마켓 떨어지고 "API 서버 개발을 집중적으로 해야겠다"라고 생각했습니다.

그때 만든 커뮤니티 프로젝트입니다.

 

단순 게시판 API 서버가 아닌 Redis나 Querydsl 같은 기술적인 것을 적용도 해보고, 직접 해결도 해보고 싶은 마음에 만들었습니다.

 

한 8월까지 만들다가, 해커톤 및 개강하고 학교에서 진행한 SW 개발대회 참가하느라 건들 수 없이 저 멀리 잊혀진 프로젝트였는데, 계속 마음속으로는 언젠간 살려야겠다는 생각을 했습니다.

 

다른 건 몰라도 이 프로젝트만큼은 애정을 가지고 더욱 확장시켜보겠습니다👊

 

 

상황 분석 및 리팩토링, 개발 계획

"당시에는 이 정도면 훌륭하지" 라는 근자감이 있었는데 우테코 프리코스를 마치고 온 지금 보니 코드가 개판이었습니다.

나만 볼 수 있는 더티 코드에다가 당시에는 잘 못다뤘던 테스트코드를 적용할 생각에 벌써부터 재밌는데 생각보다 너무 할 게 많았습니다.

 

먼저 이번 1차 리팩토링의 키워드는 [클린코드, 테스트 작성, 오류 해결] 입니다.

포괄적으로 해야 할 것들을 정리하면 다음과 같습니다.

 

0. 도커 세팅

1. Controller 레이어 메서드명 변경 및 공통적으로 사용하는 인증&인가 로직 메서드로 분리하기 (추후에 필터로 또 리팩토링 할 계획)

2. Service 레이어 클린코드 적용 및 테스트 코드 리팩토링, for문 Stream으로 변경, 그 외에 API별 여러 가지 이슈 해결

3. Domain 레이어 도메인 내부 메서드 만들어서 외부 레이어에서 불필요한 Getter, Setter 줄이기 // pk id 타입 Long으로 변경하기

4. Repository 레이어 반환 타입을 예외처리를 위해서 Optional<>로 바꾸기 

5. Domain 단위테스트 작성

6. 통합테스트 작성

7. Redis 적용 확장 (공지사항, 조회수, 댓글, 랭킹 등등 고민 중에 있습니다.)

8. CI/CD 적용 및 AWS 배포하기 

 

 

나 어떤 거부터 해야 할까..?

할게 너무 많다 보니 일단은 Docker를 이용해 개발 환경 구축부터 하기로 했습니다.

그리고 가장 많이 연관되어 있는 Board API를 살짝 리팩토링 진행해보겠습니다.

 

 

리팩토링 시작

1. 일단 추후에 배포를 편하게 하기 위해서 도커로 개발 환경 설정부터 해주었습니다.

MySQL, Redis를 컨테이너에 띄워주고, 기존 application.yml을 간단하게 바꿔주었습니다.

yml 부분은 추후에 시크릿 키와 더불어서 다르게 바꿔줄 예정입니다!

 

 

2. 다음으로 Board API를 1차 리팩토링 해보겠습니다.

먼저 Controller에서 공통으로 인증&인가 로직을 따로 메서드로 분리했습니다.

private User getPrincipal() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User user = userRepository.findByUsername(authentication.getName()).orElseThrow(MemberNotFoundException::new);
        return user;
    }

이 부분은 추후에 필터로 다시 리팩토링 진행할 예정입니다.

 

 

다음으로 기존 Board API에서 Service단에서 메서드, 변수 명도 읽기 힘들어서 일단 네이밍부터 진행하고, 상수 값도 만들어줬습니다.

메서드 명이 기존에 find() 이랬는데 findBoard()처럼 무엇을 어떻게 하는지 동사+명사로 명시해줬습니다. 

private final static String PROCESS_LIKE_BOARD = "좋아요 처리 완료";
private final static String PROCESS_UNLIKE_BOARD = "좋아요 취소 완료";
private final static String PROCESS_FAVORITE_BOARD = "즐겨찾기 처리 완료";
private final static String PROCESS_UNFAVORITE_BOARD = "즐겨찾기 취소 완료";

기존에 return "좋아요 처리 완료"; 같은 코드를 위에 상수로 바꿔서 상수 값만 바꾸면 한 번에 바뀌게 리팩토링 했습니다.

 

 

다음으로 메서드 분리를 진행했습니다.

	if (likeBoardRepository.findByBoardAndUser(board, user) == null) {
            // 좋아요를 누른적 없다면 LikeBoard 생성 후, 좋아요 처리
            board.setLiked(board.getLiked() + 1);
            LikeBoard likeBoard = new LikeBoard(board, user); // true 처리
            likeBoardRepository.save(likeBoard);
            return "좋아요 처리 완료";
        } else {
            // 좋아요를 누른적 있다면 취소 처리 후 테이블 삭제
            LikeBoard likeBoard = likeBoardRepository.findByBoardAndUser(board, user);
            likeBoard.unLikeBoard(board);
            likeBoardRepository.delete(likeBoard);
            return "좋아요 취소";
        }
        return true;

이랬던 코드를

 

    @Transactional
    public String updateLikeOfBoard(int id, User user) {
        Board board = boardRepository.findById(id).orElseThrow(BoardNotFoundException::new);
        if (!canUserClickLike(board, user)) {
            board.processUnLiked();
            return removeLikeBoard(board, user);
        }
        board.processLiked();
        return createLikeBoard(board, user);
    }

    public String createLikeBoard(Board board, User user) {
        LikeBoard likeBoard = new LikeBoard(board, user); // true 처리
        likeBoardRepository.save(likeBoard);
        return PROCESS_LIKE_BOARD;
    }

    public String removeLikeBoard(Board board, User user) {
        LikeBoard likeBoard = likeBoardRepository.findByBoardAndUser(board, user);
        likeBoardRepository.delete(likeBoard);
        return PROCESS_UNLIKE_BOARD;
    }

    public boolean canUserClickLike(Board board, User user) {
        if (likeBoardRepository.findByBoardAndUser(board, user) == null) {
            return true;
        }
        return false;
    }

이렇게 바꿨습니다.

 

기존 코드보다 무엇을 하는지 더 보기 좋고, 필요 없는 getter, setter를 외부 레이어가 아닌 도메인에서 처리하게 리팩토링 했습니다.

 

테스트 코드도 이에 따라 리팩토링을 진행해주었습니다.

 

아직은 갈 길이 많이 멀지만, 꾸준하게 해서 좋은 프로젝트로 환생시켜보겠습니다!

질문 및 피드백은 환영입니다🙂

 

소스코드는 아래 깃허브에서 보실 수 있습니다.

https://github.com/sosow0212/community

 

GitHub - sosow0212/community: Community API Server (with JUnit5, Redis ...)

Community API Server (with JUnit5, Redis ...). Contribute to sosow0212/community development by creating an account on GitHub.

github.com