seed_logo
Published on

코드 어떻게 깨끗하게 할 수 있는건가요? (항해 플러스 프론트엔드 4주차)

Authors
  • avatar
    Name
    이주영
    Twitter

서론

이번 주는 깨끗한 코드를 작성하는 방법을 서로 공유하고 과제에 적용해 보는 시간을 가졌습니다. 한 가지 몸소 경험한 것은 냄새나는 코드를 리팩터링 하는 것은 무지 어렵다는 것... 아마 발제 과제의 의도 중 하나가 리팩터링은 쉽지 않다는 것을 알려주기 위함이 아니었을까 싶습니다.

클린 코드에 대해 간단히 정리해 보고 그리고 실무에 적용할 포인트를 정리해 보고 지속적으로 실무 코드에 반영하여 혼자 개발하는 상황이지만 클린 한 구조와 모양의 소프트웨어를 만들어보려고 합니다. (말은 쉽지만 행동은 정말 어려운 것 같습니다. 다시 한번 다짐해 봅니다. 파이팅입니다 :)

본론

1. 팀 컨벤션은 뚝딱 만들어지는 것이 아니구나...

이번 4주 차를 진행하기 전, 팀원들과 함께 동일한 코드를 리팩터링 하는 시간을 가졌습니다. 먼저 리팩터링을 진행한 후, 동료들에게 설명하고 이해하는 시간을 가졌습니다. 주로 함수에 대한 이야기로 뜨겁게 논쟁이 진행됐습니다.
  • 함수의 추상화 수준에 따라 파일 상단에서 내려오도록 작성해야 합니다.
  • 리액트에서 이벤트 핸들러 이름은 handle로 시작되고 Callback은 on으로 시작되도록 합니다.
  • 함수의 리턴 타입은 언제 붙일까? (명시적 vs 추론 vs 함수이름 자체)
    • 객체 형태 반환될 때는 반드시 타입을 붙여줍니다. 왜냐하면 자동 완성 기능을 적용할 수 있어 편하기 때문입니다.
    • 반환 값이 복잡한 경우는 타입을 붙여줍니다.
  • 함수의 매개변수는 2개에서 3개로 3개를 초과하지 않도록 합니다. 왜냐하면 하나의 역할을 하는 함수를 만들기 위해 safe 한 규칙을 설정하기 위함이었습니다.

혼자 개발하는 것은 개발 속도는 빠르지만 클린 코드 관점에서 놓치고 있다는 생각이 들었습니다. 나만의 코드 컨밴션을 만들어 나가야되겠다고 생각했습니다.

2. let을 const로만 바꿔도 많은 것들이 해결될 수 있다?

let을 사용하지말고 const를 사용하라.

위의 말은 익히 익숙했던 말이었습니다. 하지만 왜? 어떻게?라고 누군가 물어본다면 어떻게 답변할 수 있을까요? 함수를 더욱 예측할 수 있게 한다는 것의 의미는 생각보다 강력했습니다.

let은 재할당이 가능하여 특정 함수에서 해당 값을 변경할 수 있습니다. 그로 인해 예상하지 못하는 동작이 발생할 수 있는 것이죠.

let 식별자로 선언된 변수를 const로 변경하려면 이 변수의 위치를 고민하게 되는 것 같습니다. 무작정 최상위 스코프에 선언된 변수를 특정 함수에서 변경하는 것이 아니라 특정 함수 내부로 선언 위치를 이동시켜 함수를 예측 가능하게 만드는 것이죠. 결국 예측 가능성이라고 답할 수 있습니다. 이 사소한 개념이 말이죠.

리팩터링을 진행하면서 느꼈던 것은 리팩터링을 시작하는 단계에서 무턱대로 새롭게 무언가를 추가한다거나 변경하는 것은 다시 뒤엎는 지름길이라는 것을 알게 됐습니다. 이번 4주 차 과제의 핵심 키워드는 "점진적으로 리팩터링 하자."라고 생각합니다.

3. 정책 변수 정리 주석을 정리해보자

도메인 정책 변수를 따로 관리하면 좋다고 생각했는데 동료의 코드에서 Note 주석을 통해 각 프로퍼티가 어떤 의미인지 적혀 있는 것을 보고 도메인 관련 정책은 주석을 달아 놓으면 좋을 것 같다고 생각했습니다.

export const DISCOUNT_POLICY = {
  BULK_DISCOUNT_RATE: 0.25, // NOTE: 30개 이상 장바구니 할인
  BULK_PURCHASE_THRESHOLD: 30, // NOTE: 30개 이상 장바구니 구매 시
  LIGHTNING_SALE_PROBABILITY: 0.3, // NOTE: 번개세일 확률 30%
  LIGHTNING_SALE_RATE: 0.2, // NOTE: 번개세일 20% 할인
  MIN_QUANTITY_FOR_DISCOUNT: 10, // NOTE: 10개 이상 시 할인
  PRODUCT_DISCOUNT_RATES: {
    // NOTE: 상품 별 할인율
    p1: 0.1,
    p2: 0.15,
    p3: 0.2,
    p4: 0.05,
    p5: 0.25,
  },
  RECOMMENDATION_DISCOUNT_RATE: 0.05, // NOTE: 상품 추천 할인 5%
  WEEKLY_DISCOUNT_RATES: {
    tuesday: 0.1, // NOTE: 매주 화요일 10% 할인
  },
}

export const STOCK_POLICY = {
  STOCK_THRESHOLD: 5, // NOTE: 재고 부족 임계치
}

export const TIMER_POLICY = {
  LIGHTNING_SALE_INTERVAL: 30000, // NOTE: 번개세일 주기 (30초)
  PRODUCT_RECOMMENDATION_INTERVAL: 60000, // NOTE: 추천상품 주기 (60초)
}

실무에 적용 포인트 // TODO : 추후 업데이트 예정

과제를 진행하면서 작은 단위에서 큰 단위로 리팩터링 하는 것을 연습해 보았습니다. 내가 만든 코드여도 적용해 보는 시간을 가져보려고 합니다. 지금 당장은 할 수 없지만, 코치님이 주신 템플렛을 공유하며 회사 코드에 적용할 수 있는 항목들에 point를 붙였습니다.

  1. 의미 있는 이름 사용

  2. 함수를 작고 단일 책임을 가지도록 만들기

    • 각 함수는 한 가지 작업만 수행 point
    • 함수의 길이 20 - 30줄 이내로 유지 point
    • 함수의 매개변수 수를 3개 이하로 제한 point
    • 부수 효과를 최소화하고 만약 있다면 명확히 표현 point
      • 이부분 실무에 적용할게 있습니다. 조용히 혼자 반성합니다. HistoricalType 그 객체 당장 수정해보자.

    • 추상화 수준을 일관되게 유지 point
    • 큰 함수를 여러 개의 작은 함수로 분리
  3. 중복 코드 제거

    • DRY 원칙 적용
    • 반복되는 코드 패턴을 식별 -> 공통 함수나 모듈로 추출 point
      • 사내 코드 CRUD 페이지 그 페이지 단위를 인터페이스로 만들자.

    • 상속 컴포지션, 고차 함수등을 활용하여 재사용성 높이자.
    • 유틸리티 함수나 팰퍼 클래스를 만들어 공통 기능을 모듈화하자 point
      • 이거 실무에 적용할 수 있을까? account를 순회하면서 dailies 배열 내부 equity를 특정 날짜의 value로 더하는 것을 클래스로 할 수 있을까? 시도해보자

    • 유틸리티 함수나 헬퍼 클래스를 만들어 공통 기능을 모듈화합니다. 예: 여러 곳에서 사용되는 날짜 포맷팅 로직을 하나의 유틸리티 함수로 만듭니다. point
  4. 주석 대신 자체 설명적인 코드 작성

    • 복잡한 알고리즘이나 비즈니스 로직의 경우에만 주석을 사용합니다. point
      • AUM을 계산하고 필터하는 로직에 주석을 남겨보자

  5. 일관된 포맷

    • 파일, 클래스, 함수의 구조를 일관되게 유지합니다. 예: 모든 import 문을 파일 상단에 그룹화하고, 알파벳 순으로 정렬합니다. point

    • import sorting plugin을 즉시 도입하자

      .

  6. 복잡한 조건문 단순화

    • 얼리 리턴
    • 스위치 대신 객체 리터럴이나 Map을 사용하자
    • 다형성을 활용해서 조건부 로직을 대체하자
      • 예: if (isValidUser && hasPermission && !isLocked) 대신 canAccessResource() 함수 사용
  7. 적절한 추상화 수준 유지

    • 비즈니스 로직과 저수준 구현 세부 사항을 분리한다. point
      • 정확히 이해가지 않았지만 함수형 프로그래밍 관련 세션을 듣고, 이해할 수 있었습니다. 계산하는 로직을 분리한다는 것을 의미가 아닐까?

    • 인터페이스와 구현을 명확히 구분하자. point
    • 과도한 추상화를 피하고 필요한 만큼만 추상화하자
    • 동일한 수준의 추상화를 한 곳에서 다룬다

결론

개발의 묘미는 동일한 기능임에도 구현 방식이 다양할 수 있다는 것. 실은 모든 만물의 진리죠. 사람의 생각은 다르니 말입니다. 그렇기 때문에 끊임없이 고민해야 하는 것 같습니다. 결국 내가 작성한 코드는 레거시가 될 것이고 도메인에 대한 지식이 없는 사람이 본다면 이해하기 쉽지 않습니다.

새로운 개발자가 들어와 당혹스러운 감정을 느낄 시점을 앞당겨 현재 내가 많이 답답해놓아야 한다는 생각이 들었습니다. 구현해야 할 기능들과 해야 할 것들은 많지만 미리 고통 느껴보기로 오늘도 다짐해 봅니다.

이번주 과제를 하면서 어려웠던 것은 비즈니스 로직을 UI와 분리하는 게 그렇게 쉽지 않았다는 것.

이제 다음 주는 디자인 패턴과 함수형 프로그래밍에 대한 과제를 진행합니다. 1주일 안에 소화하기 어려운 내용이라고 생각 들지만 마음 편하게 실무에 적용할 것들을 줍줍 한다는 생각으로 진행해보려고 합니다. 사내에서 구현해야 하는 기능이 있는데 이를 구현하기 위해 계산 로직을 분리해야겠다는 인사이트를 시작부터 받게 됐습니다. 다들 다음 주도 파이팅입니다.