Programming/Refactoring 9

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

아래는 최범균님의 유튜브를 보고 느낀 점을 나름대로 정리한 것이다. 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를 통해 간접적으로 웹 계층에 의존하게 된다. 개념적 의존이 역류하게 ..

패키지 의존성 사이클 검사 및 개선하기! (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..