우아한테크캠프 PRO/우아한테크캠프 회고록

[우아한테크캠프 Pro] 7주차 미션 후기 (레거시 코드 리팩터링)

PI.314 2023. 1. 2. 22:19

우아한테크캠프 Pro 7주차 레거시 코드 리팩터링을 진행한 내용과 후기를 정리해보았습니다.

 

미션 저장소

https://github.com/Gyeom/jwp-refactoring

 

GitHub - Gyeom/jwp-refactoring

Contribute to Gyeom/jwp-refactoring development by creating an account on GitHub.

github.com

 

 

학습 내용

  • 많은 기업들이 "서비스를 안정적으로 운영하면서 레거시 코드를 리팩터링할 수 있는 역량을 갖춘 개발자"를 요구한다.
    • 레거시 프로젝트를 리팩터링하는 경험을 통해 서비스를 안정적으로 운영하면서 레거시 코드를 리팩터링할 수 있는 역량을 키운다.
    • 프로젝트를 만드는 단계에서 끝나는 것이 아니라 프로젝트를 완료한 후 일정 기간 유지보수를 함으로써 레거시 코드를 리팩터링하는 경험을 쌓는다.

단계별 요구사항 및 PR 리뷰

🚀 1단계 - 테스트를 통한 코드 보호

  • kitchenpos 패키지의 코드를 보고 키친포스의 요구 사항을 README.md에 작성한다. 미션을 진행함에 있어 아래 문서를 적극 활용한다.
  • 정리한 키친포스의 요구 사항을 토대로 테스트 코드를 작성한다. 모든 Business Object에 대한 테스트 코드를 작성한다. @SpringBootTest를 이용한 통합 테스트 코드 또는 @ExtendWith(MockitoExtension.class)를 이용한 단위 테스트 코드를 작성한다.
  • 인수 테스트 코드 작성은 권장하지만 필수는 아니다. 미션을 진행함에 있어 아래 문서를 적극 활용한다.

https://github.com/next-step/jwp-refactoring/pull/651

 

[김대겸] Step1 PR by Gyeom · Pull Request #651 · next-step/jwp-refactoring

안녕하세요 리뷰어님, 1단계 - 테스트를 통한 코드 보호 리뷰 요청 드립니다. 잘 부탁드립니다 :)

github.com

  • 재사용이 빈번한 객체는 fixture 올려놓자.

🚀 2단계 - 서비스 리팩터링

 

 

  • 단위 테스트하기 어려운 코드와 단위 테스트 가능한 코드를 분리해 단위 테스트 가능한 코드에 대해 단위 테스트를 구현한다.

https://github.com/next-step/jwp-refactoring/pull/681

 

[김대겸] Step2 PR by Gyeom · Pull Request #681 · next-step/jwp-refactoring

안녕하세요 리뷰어님, 정말 힘들었던 미션이었습니다 ㅠㅠㅠㅠ 도메인 생성 관련 테스트 코드 작성 서비스 레이어에 있던 비즈니스 로직을 각 도메인에 책임 부여 dao 정상 동작을 위해, dao호출

github.com

 

  • 어떻게하면 inquery로 개선할 수 있을 지 고민하다가 복잡도가 N이 초과하지 않는 방법으로 map을 활용했다.
    private List<MenuProduct> findAllMenuProductsByProductId(List<MenuProductRequest> menuProductRequests) {
        List<Product> products = toProduct(menuProductRequests);
        validateProducts(products, menuProductRequests);
        Map<Long, Product> productIdToProduct = new HashMap<>();
        
        for (Product product : products) {
            productIdToProduct.put(product.getId(), product);
        }
        
        return menuProductRequests.stream()
        		.map(menuProductRequest -> menuProductRequest.toMenuProduct(productIdToProduct.get(menuProductRequest.getProductId())))
  		        .collect(Collectors.toList());
}

  • 단순히 change보다 해당 명령을 표현할 수 있는 네이밍을 고려하자.

  • enum 사용 시, 역할을 보다 쉽고 명확하게 알아볼 수 있도록 description을 만들어 관리하자.

  • OneToMany Lazy에서는 Hibernate Batch Size 옵션의 사용을 고려하자.

  • repository를 통해 조회하는 것은 findXXX 규칙을 사용하자. (다른 CRUD 도 마찬가지)

🚀 3단계 - 의존성 리팩터링

 

이전 단계에서 객체 지향 설계를 의식하였다면 아래의 문제가 존재한다. 의존성 관점에서 설계를 검토해 본다.

  • 메뉴의 이름과 가격이 변경되면 주문 항목도 함께 변경된다. 메뉴 정보가 변경되더라도 주문 항목이 변경되지 않게 구현한다.
  • 클래스 간의 방향도 중요하고 패키지 간의 방향도 중요하다. 클래스 사이, 패키지 사이의 의존 관계는 단방향이 되도록 해야 한다.
  • 데이터베이스 스키마 변경 및 마이그레이션이 필요하다면 아래 문서를 적극 활용한다.

https://github.com/next-step/jwp-refactoring/pull/735

 

[김대겸] Step3 PR by Gyeom · Pull Request #735 · next-step/jwp-refactoring

안녕하세요 리뷰어님, 3단계 의존성 리팩터링 리뷰요청드립니다. 패키지 분리 (Lifecycle이 같은 도메인들은 같은 Aggregate) 서로 다른 Aggregate의 엔티티간의 단방향 참조를 할 수 있도록 간접참조 활

github.com

  • 조영호님의 우아한 객체지향 강의를 듣고, 꼭 절차지향이 안 좋은게 아니다라는 것을 깨달았다.
  • 비즈니스 로직을 보다 명확하고 쉽게 관리할 수 있도록 Validator를 활용해 절차지향을 적용했다.
  • SRP, OCP를 준수해 validator들을 분리하고, validator들을 관리하는 별도의 객체를 생성해서 관리하면 보다 확장에 용이할 것 같다.

  • 양방향 의존관계를 끊기 위해서, 인터페이스 및 구현체를 활용하여 DIP 원칙을 적용했다.

미션 회고

이번 미션은 생각보다 정말 손이 많이갔다. 우선 처음에는 서비스 레이어에 있는 비즈니스 로직을 도메인에 책임을 부여하도록 리팩토링을 진행했는데, DTO를 개선하면서 구조가 바뀌다보니 테스트 코드에서 계속 에러가 발생했다. 그래서 한번에 모든 것을 변경하려고 하기보다 하나씩 점진적으로 리팩토링을 해나갔다. 어느 정도 코드 리팩토링이 된 후에는, 서비스 의존성 관점에서 설계를 검토하여 리팩토링을 진행했다. 미션을 진행하면서 평소에 어렵게만 느껴졌던 DDD 개념들이 하나씩 이해가 되기 시작했고, 프로젝트의 완성도가 점점 갖춰지는 것이 눈에 띄게 보여 성취감을 크게 느꼈다. 특히, 조영호님의 우아한 객체지향 강의를 통해 배운 'Validator를 활용한 절차지향 적용'과 '양방향 의존관계를 끊기 위한 DIP원칙 적용'은 평소에 내가 생각하던 고정관념을 완전히 깨뜨려 버렸다.

 

우아한 테크 캠프 PRO 과정에 도전하길 참 잘했다는 생각이 든다.