Programming/Refactoring 10

단위 테스트 대상 분리하기!

아래는 최범균님의 유튜브를 보고 느낀 점을 나름대로 정리한 것이다. https://www.youtube.com/watch?v=qZ1R0C_iiV4 테스트 불가능한 문제 아래의 코드가 테스트가 불가능한 문제가 있었다고 한다. ResultBuilder builder = ...; InputPeriod realPeriod = mapper.selectPeriod(param); // 1. DB에서 읽음 if (realPeriod == null) { // 2. 없으면 다른 값 읽음 InputPeriod expectedPeriod = mapper.selectExpectedPeriod(otherParam); builder.period(expectedPeriod).type(EXPECTED); } else if (realP..

필요한 파라미터만 사용하기! (DTO를 남용하지 말자.)

Controller - Service - Repository 패턴 아래는 전통적인 계층형 아키텍처(Layered Architecture)이다. DTO 형태로 요청을 받고 Controller, Service, Repository 등이 메시지를 주고받을 때도 DTO를 사용한다. 그러면 되는 걸까?! 아키텍처 Layered Architecture 관점으로 봤을 때 Controller는 Service를 알아야 하는 것이 맞다. 그렇지만 호출되는 Service가 Controller를 알아야 할 필요는 없다. 각 계층은 단지 하위의 있는 계층을 사용하면 된다. Controller에서 DTO를 자신의 Needs로 사용한다면 Service가 DTO를 통해 간접적으로 웹 계층에 의존하게 된다. 개념적 의존이 역류하게 ..

실무에서 SRP, Decorator 패턴 적용하기 (refactoring. with 메일 서비스)

회사에서 동료들과 메일 서비스의 레거시 개편을 수행하고 있다. From. PHP CodeIgniter, To. Java SpringBoot 그런데 새롭게 오픈하는 API임에도 불구하고 레거시적인 측면이 있었다. 가장 복잡한 로직인 발송 로직이 가장 지저분하다. 그래서 구조를 정리해봤다! 메일 발송(+ 저장)에서는 복잡한 비즈니스 로직을 Processor 라고 부르는 클래스가 처리를 담당하고 있다. MailService에서는 SaveProcessor를 사용해서 메일을 저장한 후 SendProcessor를 통해 메일을 발송한다. SendProcessor는 MailTransfer를 호출해서 SMTP 서버로 메일을 발송한다. 절차적인 코드 해당 구조가 지저분하게 보이는 이유는 발송 프로세스 쪽에 있다. 로직 파..

패키지 의존성 사이클 검사 및 개선하기! (feat. IntelliJ)

우아한 테크 세미나에서 발표한 우아한 객체지향 by 우아한형제들 개발실장 조영호님 강연을 무척 재밌게 봤다. Link: https://www.youtube.com/watch?v=dJ5C4qRqAgA&t=5217s 해당 영상에는 패키지 의존성 관련 내용이 주를 이룬다. 추가로 얼마 전 백기선님께 공개 코드 리뷰를 받으면서도 패키지 의존성 관련해서 대화를 많이 나눴다. Link: https://jaehoney.tistory.com/276 그래서 최근 진행하던 사이드 프로젝트의 패키지 의존성 사이클이 있는 지 검사해보기로 하였다. Analyze Cyclic Dependencies(feat. IntelliJ) ArchUnit을 사용해서 의존성 검사를 자동화할 수도 있다. Custom한 Layered archit..

DTO를 Inner static class로 간결하게 관리하기! (+ domain 분리)

DTO 관리 API가 클라이언트(Client)가 보낸 Request Payload를 받을 때 RequestDto를 사용하고, 결과 값을 내려줄 때 ResponseDto를 사용합니다. 문제는 API 스펙을 고도화할수록 Dto가 너무 많아진다는 것입니다. End-point 요청에 따라 Request Body가 다르고 Response Body도 다릅니다. 그래서 API 스펙을 많이 뽑을 수록 Dto 패키지는 아래 처럼 지저분해집니다. (이미지 길이 보니까 더 길어 보이네요..!) Domain 분리 프로젝트가 어느정도 크기가 커지면 로직 안에서 동작하는 클래스들은 domain별로 관리하는 게 좋습니다. domain별로 class를 정리합니다. 아까보다는 좋긴 하지만, 더 간결해질 방법이 있습니다. Inner s..

리팩토링 - Switch문을 다형성으로 바꾸기

조건문을 다형성으로 바꾸기 (Replace conditional with polymorphism) 모든 조건문을 다형성으로 바꿔야 좋은 것은 아닙니다. 여러 타입에 따라 각기 다른 로직으로 처리해야 할 경우에 조건문을 다형성으로 만들 수 있습니다. 공통으로 사용되는 로직은 상위클래스에 두고, 코드 변경의 여지가 있는 달라지는 부분만 하위 클래스에 둠으로써 가독성과 유지보수성을 얻는 것입니다 ! 예제 아래의 코드를 보면, printerMode라는 멤버 변수의 값에 따라 switch문에서 각기 다른 로직을 적용하게 됩니다. 각 조건마다 적지 않은 코드가 실행되고, 메서드 호출도 있고, 각각의 하위 메서드도 하나의 클래스에 전부 있는 상태입니다. public class Printer { private int ..

리팩토링 - 반복문 분리 (split loop)

반복문 분리 레거시 코드를 보면 하나의 반복문에서 여러 다른 작업을 수행하는 코드를 쉽게 찾을 수 있습니다. 하지만, 그렇게 되면 반복문 안의 하나의 작업을 수정하는 데도 다른 작업까지 고려해야 하는 상황이 발생합니다. 반복문을 작업별로 분리하면 쉽게 이해하고 유지보수할 수 있습니다. 예시 아래의 예제는 두 가지 작업을 하고 있습니다. 그럼에도 이렇게 짜는 이유는 우리는 반복문의 범위가 동일하다면 굳이 분리하지 않기 때문입니다. Date firstCreatedAt = null; Participant first = null; for (Comment comment: comments) { Participant participant = findParticipant(comment.getUserName(), par..

리팩토링 - 메소드 올리기 (Pull up method)

메소드 올리기 (Pull up method) 메소드 올리기는 하위 클래스들의 중복 메소드를 상위 클래스로 빼는 리팩토링 기술입니다. 아래와 같은 Dashboard 클래스가 있고, 이를 상속하는 두 클래스가 있다고 가정하겠습니다. [부모 클래스] public class Dashboard { public static void main(String[] args) throws IOException { ReviewerDashboard reviewerDashboard = new ReviewerDashboard(); reviewerDashboard.printReviewers(); ParticipantDashboard participantDashboard = new ParticipantDashboard(); parti..

리팩토링 - 함수 추출 (Extract function)

함수 추출 함수 추출은 함수를 정의할 때 각 기능들이 어느정도 코드가 길고 읽기가 어렵다면 그 기능을 함수로 추출하는 방법입니다. 예시는 아래와 같습니다. (읽지 않으시는 걸 추천드립니다..) 두 함수가 읽는 것에도 꽤 시간이 들어가고, 공동으로 사용하는 코드도 많은 것 같습니다. 해당 부분들을 각 함수로 추출해보겠습니다. public class StudyDashboard { private void printParticipants(int eventId) throws IOException { GitHub gitHub = GitHub.connect(); GHRepository repository = gitHub.getRepository("whiteship/live-study"); GHIssue issue =..

조건문 Refactoring 하는 방법! - 1편

조건문 Refactoring if ~ else문은 꼭 필요하면서도, 프로젝트를 복잡하게 만드는 요소입니다. 잘못 사용한다면, 열심히 개발한 프로젝트가 최악의 프로젝트가 될 수도 있죠. 조건문을 작성하면서 많은 개발자 분들이 실수하고 있는 부분들을 담을테니 참고해주세요! 1. return true; / return false;를 사용하지 마라! 조건 절이 Boolean 반환 값을 가지는데, 굳이 if ~ else문을 사용하면 코드가 복잡해집니다. [Bad😢] public boolean isAdmin(User user) { if(user.role == UserRole.ADMIN) { return true; } else { return false; } } [Good😍] public boolean isAdmin..