티스토리 뷰


day2 미션 제출입니다.

1. 코드 리팩토링
- 기존 코드

public boolean validateOrder(Order order) {
    if (order.getItems().size() == 0) {
        log.info("주문 항목이 없습니다.");
        return false;
    } else {
        if (order.getTotalPrice() > 0) {
            if (!order.hasCustomerInfo()) {
                log.info("사용자 정보가 없습니다.");
                return false;
            } else {
                return true;
            }
        } else if (!(order.getTotalPrice() > 0)) {
            log.info("올바르지 않은 총 가격입니다.");
            return false;
        }
    }
    return true;
}

 


- 리팩토링 코드
OrderSerivce.java (가상의 OrderService 클래스가 validateOrder() 메소드를 가지고 있다고 가정했습니다)

package cleancode.mission.day4;

public class OrderService {
    public boolean validateOrder(Order order) {
        try {
            if (checkIfOrderDoesNotHaveItem(order)) {
                throw new AppException("주문 항목이 없습니다.");
            }

            if (checkIfTotalPriceIsNotValidate(order)) {
                throw new AppException("올바르지 않은 총 가격입니다.");
            }

            if (checkIfOrderDoesNotHaveCustomerInfo(order)) {
                throw new AppException("사용자 정보가 없습니다.");
            }
        } catch (AppException exception) {
            System.out.println(exception.getMessage());
            return false;
        } catch (Exception exception) {
            System.out.println("시스템 에러입니다.");
            exception.printStackTrace();
            return false;
        }

        return true;
    }

    private boolean checkIfOrderDoesNotHaveItem(Order order) {
        return order.doesNotHaveItemIn();
    }

    private boolean checkIfTotalPriceIsNotValidate(Order order) {
        return order.isNotValidateTotalPrice();
    }

    private boolean checkIfOrderDoesNotHaveCustomerInfo(Order order) {
        return order.doesNotHaveCustomerInfo();
    }
}

 


Order.java (필드로 존재하는 가상의 Item 클래스는 과제 상 중요하지 않은 것 같아 코드는 생략합니다)

package cleancode.mission.day4;

import java.util.List;

public class Order {
    private List<Item> itemList;
    private boolean customerInfo;

    public int countOrderedItemsSize() {
        return this.itemList.size();
    }

    public int countTotalPrice() {
        return this.itemList.stream().mapToInt(Item::getPrice).sum();
    }

    public boolean hasCustomerInfo() {
        return this.customerInfo;
    }

    public boolean doesNotHaveCustomerInfo() {
        return !this.hasCustomerInfo();
    }

    public boolean doesHaveItemIn() {
        return this.countOrderedItemsSize() > 0;
    }

    public boolean doesNotHaveItemIn() {
        return !this.doesHaveItemIn();
    }

    public boolean isNotValidateTotalPrice() {
        return !this.isValidateTotalPrice();
    }

    public boolean isValidateTotalPrice() {
        return this.countTotalPrice() > 0;
    }
}

 


- 리팩토링 시 염두한 것

1) 불필요한 분기문을 제거해서 사고의 depth 줄이기(if, if-else의 반복)

2) 메소드 레벨에서 추상화가 가능한 부분은 추상화 레벨에 맞게 메소드로 추출(OrderService의 check~ 메소드)
3) Order 객체의 getter를 제거하고, Order 클래스 내에서 메소드 만들기(주문 항목 존재 여부, 사용자 정보 존재 여부, 총 가격이 올바른지)
4) 가독성을 위한 분기 조건 내의 부정어 ! 뒤집기 -> 메소드 생성(Order 클래스 내의 doesNotHave~, isNotValidate~ 등의 메소드)

5) 적절한 예외 처리문 추가(AppException 정의, 그 외 예외는 시스템 에러로 간주)
6) 적절한 코드 간 공백 간격 유지(하나의 단락의 의미를 나타내기 위해)


2. 내가 생각하는 SOLID 정리 

- S : SRP(Single-Responsibility-Principle) 단일책임원칙
 단일책임원칙이란, 하나의 클래스는 하나의 책임만 져야한다는 뜻이다. 더 구체적으로 말하면 책임이란 변경하려는 이유이다. 다른 시점에 다른 이유로 하나의 클래스가 변경되면, 클래스의 응집도가 떨어지게 된다. 
회사 업무 분장을 생각해봐도, 각자의 책임이 명확하면 명확할수록 업무의 의사결정 속도가 빨라지게 된다. 명시적으로 어떤 업무가 누구의 책임인지 알기 때문에, 여러 사람이 여러 역할을 맡아서 분담하는 것보다는 직관적으로도 나아보인다. 또한 업무가 변경될 때에도, 한 사람이 여러 책임을 떠안을 때에는 해당 책임 간 높은 결합도가 생겨있을 가능성이 크고, 문제가 발생할 수 있다.

 

- O: OCP(Open/Closed-Principle) 개방폐쇄원칙

개방폐쇄원칙은, 확장에는 열려있어야 하고 변경에는 닫혀있어야 한다는 원칙이다. 소프트웨어의 확장성을 고안한 개념 같다. 예를 들어, 어떤 소프트웨어의 저장소에 대해 핵심 도메인 로직은 저장소가 어떤 형태이든 최대한 영향을 받지 않도록 설계해야한다. 저장소가 메모리, RDB, Nosql 등 다양할 수 있지만 핵심 로직은 저장소의 변경에는 닫혀 있어야 하며(영향을 받지 않고), 저장소를 갈아끼울 수 있도록 설계하는 것이 중요하다.

- L: LSP(Liskov-Substitution-Principle) 리스코프치환원칙

리스코프치환원칙은, 자식 클래스는 부모 클래스로 대체될 수 있어야 한다는 원칙이다. 사실 이 원칙을 생각하면,  상속의 기능과 정면으로 배치되지는 않을까라는 의문이 든다. 그 이유는, 상속을 통해 자식 클래스에서 새로운 기능을 추가하고, 부모 클래스에서는 불가능한 기능을 할 수도 있기 때문이다. 내가 생각하기에, 도메인 로직을 큰 가지로 기반으로 하고 최대한 부모 클래스의 책임을 따르는게 좋을 것 같다.

- I: ISP(Interface-Segregation-Principle) 인터페이스분리원칙
인터페이스를 클라이언트의 역할에 맞게 분리해야한다는 원칙이다. 예를 들어 미국 이민 비자를 생각해보자. 비자에는 취업 이민 비자, 가족 초청 비자, 약혼자 비자 등 다양한 종류가 있다. 근데 대사관에서 이걸 뭉뚱그려가지고 '이민 비자' 하나로 퉁쳤다고 생각해보면, 이걸 보는 입장에서는 내가 약혼도 안했는데 약혼자 비자 조건을 읽고 있으니 당황스러울 수 밖에 없다. 

-D: DIP(Dependency-Inveresion-Princple) 의존성역전원칙

상위 모듈은 하위 모듈에 의존하면 안되고, 구체가 아닌 추상에 의존해야 한다는 원칙이다. 구체가 아닌 추상에 의존함으로써 해당 추상을 구체화하는 모듈이 변경되더라도 상위 모듈은 영향을 받지 않게 된다. 코드 레벨에서 생각해보면, 어떤 클래스에서 어떤 객체의 참조가 필요할 때, 인터페이스에 의존해야하지 그 구현체에 의존하면 안된다. 예를 들어, 자동차를 생각해보면 고객은 이게 전기차나 내연기관에 상관없이, 기본적인 운전 방법에 대해서는 동일한 행위를 기대하고 있다(액셀을 밟고, 핸들을 돌리고, 브레이크를 밟는 등). 

 


출처:

인프런 워밍업 클럽 2기(백엔드 클린코드, 테스트코드) - https://www.inflearn.com/course/offline/warmup-club-2-be-wb
강의 출처 - https://www.inflearn.com/course/readable-code-%EC%9D%BD%EA%B8%B0%EC%A2%8B%EC%9D%80%EC%BD%94%EB%93%9C-%EC%9E%91%EC%84%B1%EC%82%AC%EA%B3%A0%EB%B2%95


 

Readable Code: 읽기 좋은 코드를 작성하는 사고법 강의 | 박우빈 - 인프런

박우빈 | 이 강의를 통해 클린 코드 원칙에 대한 깊은 이해를 하고, 객체 지향 사고 방식에 입각한 깔끔한 코드를 작성할 수 있게 됩니다. 클린 코드와 객체 지향이 궁금한 분, 코드를 정말 잘 짜

www.inflearn.com

 

 

 

[지금 무료] 인프런 워밍업 클럽 스터디 2기 - 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot) | 인

인프런 | IT 입문자를 위한 인프런만의 특별한 커뮤니티, 인프런 워밍업 클럽! 지식공유자가 직접 운영하는 스터디에 참여하고, 실무에 쓰이는 지식과 인사이트를 얻어보세요 😎, 부트캠프에 참

www.inflearn.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함