🎁[속보] 인프런 내 깜짝 선물 출현 중🎁

워밍업 클럽 3기 BE 클린코드&테스트 - 1주차 발자국

워밍업 클럽 3기 BE 클린코드&테스트 - 1주차 발자국

강의 수강

Readable Code: 읽기 좋은 코드를 작성하는 사고법

 

학습 내용 요약

섹션2. 추상

  • 추상화: 구체에서 정보를 제거하고 함축하여 중요한 것만 남기는 과정

  • 구체화: 추상을 보고 유추를 통해 생략된 정보를 재현해내서 이해하는 과정

  • 추상화 레벨: 얼마나 추상화했는지를 나타내는 단계

    • 고수준: 추상화 레벨이 높다 (추상적)

    • 저수준: 추상화 레벨이 낮다 (구체적)

  • 적절한 추상화: 해당 도메인의 문맥 안에서 핵심 개념만 남겨서 표현하는 것

    • -> 적절한 추상화는 복잡한 데이터/로직을 단순화하여 코드를 이해하기 쉽게 한다.

  • 이름 짓기, 메서드로 추출 + 메서드 선언부, 매직 넘버/매직 스트링 상수로 추출

섹션3. 논리, 사고의 흐름

  • 뇌 메모리 적게 쓰기 (인지적 경제성)

    • 코드를 작성할 때 읽는 사람의 뇌 메모리를 최대한 적게 사용하도록 작성할 것

  • Early return

    • early return: 메서드를 분리해 끝낼 수 있는 케이스들은 빨리 return 해버려 아래쪽 케이스를 읽을 때 위쪽 케이스를 신경쓰지 않아도 되도록 하는 것

       

  • 사고의 depth 줄이기

    • 코드를 읽는 사람의 사고가 적당한 수준으로(추상화된 정도로) 이해할 수 있도록 메서드 분리

  • 공백 라인으로 의미 단위 표현

  • 부정 연산자 제거

  • 예외 처리

    • 개발자가 의도한 예외

    • 개발자가 의도하지 않은 예외

섹션4. 객체 지향 패러다임

  •  관심사의 분리: 관심사에 따라 기능과 책임을 나누어 객체 생성

    • 높은 응집도

    • 낮은 결합도

  • SOLID

    • SRP(단일 책임 원칙)

      • 하나의 클래스는 하나의 책임(관심사)만을 가져야 한다.

      • SRP를 지킴으로써 각 클래스의 응집도를 높이고, 클래스 간의 결합도를 낮출 수 있다.

    • OCP(개방-폐쇄 원칙)

      • 확장에는 열려 있고, 변경에는 닫혀 있어야 한다.

      • 기존 코드의 변경 없이도 시스템의 기능을 확장할 수 있어야 한다.

      • 추상화와 다형성을 활용해서 OCP를 지킬 수 있다.

    • LSP(리스코프 치환 원칙)

      • 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환해도 정상 작동해야 한다.

      • 상속 구조에서 자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.

    • ISP(인터페이스 분리 원칙)

      • 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.

      • 인터페이스를 필요한 기능 단위로 잘게 쪼개서 사용해야 한다.

    • DIP(의존성 역전 원칙)

      • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.

        • 둘 모두 추상화에 의존해야 한다.

      • 구체적인 구현 클래스가 아닌 추상화(인터페이스, 추상 클래스)에 의존해야 한다.

섹션5. 객체 지향 적용하기

 

 

회고

평소에 코드를 짤 때 좋은 코드 작성에 대해서 깊게 생각하지 않았다. 일단 코드가 제기능을 하는지가 가장 큰 관심사였고, 개인 프로젝트를 주로 해왔기 때문에 내가 이해할 수 있으면 가독성에 대해 크게 신경쓰지 않았기 때문이다. 그래서 읽는 사람보다는 코드를 작성하는 나의 입장에서 편한 방향으로 코드를 작성해왔다.

강의를 왜 좋은 코드를 작성해야 하는지 명확하게 이해하고, 지금껏 생각하지 못한 관점을 배울 수 있었다. 또한 강사님을 따라 코드를 리팩토링하며 앞으로 코드를 짤 때 배운 내용을 적용하여 읽는 이가 이해하기 쉬운 코드를 짤 수 있도록 노력해야 겠다는 다짐을 하게 되었다.

  • 칭찬하고 싶은점: 바쁜 한주였지만 매일 학습 진도표에 맞춰서 미루지 않고 강의를 들었다.

  • 아쉬웠던 점: 강의 내용을 정리하며 학습하는 것은 조금 밀렸고, 미션 수행만으로는 학습한 내용을 내가 온전히 이해하고 있는지 확인하기 어려웠다.

  • 보완할 점: 이론적인 내용을 정리하는 것 외에도 배운 내용을 실제 코드에 적용해보는 연습이 더 필요할 것 같다.

  • 다음 주 목표: 강의와 미션 외에도 기존에 내가 짠 코드를 리팩토링 해봐야겠다.

 

미션

코드 리팩토링

리팩토링 전

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;
}

 

리팩토링 후

 public static final String EMPTY_ORDER_MESSAGE = "주문 항목이 없습니다.";
 public static final String INVALID_TOTAL_PRICE_MESSAGE = "올바르지 않은 총 가격입니다.";
 public static final String NO_USER_INFO_MESSAGE = "사용자 정보가 없습니다.";

 public boolean validateOrder(Order order) {
     if (order.hasNoItem()) {
         log.info(EMPTY_ORDER_MESSAGE);
         return false;
     }

     if (order.hasInvalidTotalPrice()) {
         log.info(INVALID_TOTAL_PRICE_MESSAGE);
         return false;
     }

     if (order.hasNoCustomerInfo()) {
         log.info(NO_USER_INFO_MESSAGE);
         return false;
     }

     return true;
 }

미션 해결 과정

코드를 살펴보며 강의에서 배운 내용을 하나씩 적용해보았다.

  • 중첩되어 복잡한 분기문 -> Early return을 적용해 불필요한 else와 중첩된 분기 제거

  • if문의 조건이 복잡함 -> if문의 조건을 메서드로 추출 분기 조건이 한번에 이해되도록 함 (사고의 depth 줄임)

    • 또한 order의 정보를 객체 외부에서 getter로 빼와 조작하는 대신 order가 처리하게 하여 캡슐화를

  • if문 조건에 부정 연산자 존재 -> 별도의 메서드(hasNoItem())를 만들어 if문 조건의 부정 연산자(!) 제거

  • 매직 스트링이었던 로그 메시지를 상수로 추출해 가독성을 높이고 유지보수를 용이하게 함

  • 읽는 이가 의미 단위로 이해할 수 있도록 공백 라인 추가

회고

  • if문의 조건을 order의 메서드로 추출하는 과정에서 메서드의 이름을 짓는 것이 생각보다 고민되었다. 좀 더 많은 코드를 보며 이런 기능의 메서드의 이름을 관용적으로 어떻게 짓는지 공부해볼 필요가 있어 보인다.

  • 다시 보니 log.info()로 처리되고 있는 예외 처리 부분을 thrwotry-catch로 관리하는 것이 유지보수에 좋았을 것 같다는 생각이 든다.

댓글을 작성해보세요.


채널톡 아이콘