[Dev] 프론트엔드 클린 코드

Gyuwon Lee·2023년 9월 17일
3
post-thumbnail

클린 코드 책을 읽을 시간이 없는데 리팩토링 작업을 진행해야 해서, 토스 진유림님의 발표를 참고하고 정리했습니다.

클린 코드 !== 짧은 코드

  • 실무에서 클린 코드의 의의는, 유지보수 시간(코드 파악, 디버깅, ...)의 단축이다.

  • 따라서 진짜 클린 코드란 원하는 로직을 빠르게 찾을 수 있는 코드다.

  • 처음 기능을 짤 때와 다르게, 기능을 추가 하는 경우 기존 로직이 더럽혀질 위험이 커진다. 처음에는 기능에 맞추어 구조를 설계하지만, 추가할 땐 기존 구조에 맞추어 기능을 구현하기 때문이다.

    • 원래 한 가지 일만 하던 함수 (ex. click 핸들러) 가 여러 가지 일을 하게 되거나
    • 코드의 세부 구현 단계가 제각각이거나 (ex. 똑같은 click 이벤트에 대해 A 핸들러는 한 가지 일, B 핸들러는 여러 가지 일을 하는 경우)
    • 하나의 목적인 코드가 여러 로직에 걸쳐 흩뿌려지는 문제 등등!

깔끔하게 기능 추가하기

  • 응집도: 하나의 목적을 가진 코드라면 뭉쳐 두기
  • 단일 책임: 하나의 코드가 하나의 일을 수행하도록 하기
  • 추상화: 핵심 개념필요한 만큼만 노출하기

응집도

  • 커스텀 훅으로 무조건 뭉친다고 클린 코드가 되는 것이 아니다.
  • 무엇을 뭉쳐야 하는가?: 당장 몰라도 되는 디테일
  • 무엇을 남겨야 하는가?: 코드 파악에 필수적인 핵심 정보

action

1. 핵심 데이터와 세부 구현을 분리한다.

  • ex. 팝업 컴포넌트
  • 핵심 데이터: 팝업 버튼 클릭 시 액션, 팝업의 제목, 팝업의 내용
  • 세부 구현: 팝업의 open-close 상태, 세부 컴포넌트 마크업, 액션 바인딩 로직

2. 핵심 데이터는 밖에서 전달하고, 세부 구현은 내부적으로 뭉쳐 둔다. (커스텀 훅 등)

  • 선언적 프로그래밍
    • "무엇을 해야할지만 알려줘, 세부 구현은 미리 해놨거든"
    • 핵심 데이터만 전달받고, 세부 구현은 뭉쳐서 숨겨 두는 개발 스타일
    • 내부적으로는 명령형으로 작성(될 수밖에 없음)
    • '무엇'을 바꿔서 쉽게 재사용할 수 있음

단일 책임

  • 1. 하나의 일을 하는 뚜렷한 이름의 함수를 작성하기

    • 하나의 함수가 여러 일을 하고 있다면, 각 기능을 별도의 함수로 분리하고 해당 기능이 필요한 시점에 각 함수를 호출하기
  • 2. 기능성 컴포넌트로 분리하기

    • ex. 버튼을 클릭하면 서버에 로그를 찍는 코드
    • 버튼은 한 개의 onClick 핸들러만 받을 수 있음 → 한 개의 핸들러 함수에 1) 로깅 2) API콜 두 개의 로직이 섞여 있음
    • LogClick 등의 컴포넌트를 만들어, 버튼을 클릭하면 자동으로 로깅되도록 감쌀 수 있음
    // onClick 핸들러에서는 API콜만 신경쓸 수 있게 된다.
    <LogClick>
      <button onClick={() => {API}} />
    <LogClick />

추상화

  • 중요한 개념만 남기고 감추는 것

  • 로직 또는 인터페이스가 너무 많이 노출되어 있으면, 구현의 자유도는 올라가지만 버그 위험성과 유지보수 난이도가 높아진다.

  • 반대로 성급하게 추상화하면, 유연성이 낮아져 이후 추가 기능을 구현할 때 어려움을 겪을 수 있다.

  • 추상화는 로직의 중복을 없애기 위해서도 사용되는데, 이 때 중복의 개념을 잘 생각해야 한다.

    • 단지 겉보기에 같다고 중복이 아니다.
    • 점차 다른 방향으로 발전해 나간다면, 즉 코드의 목적이 다르다면 중복이 아니다.
    • 하나의 로직을 수정했을 때, 나머지 중복에도 동일한 수정을 해야 한다면 진짜 중복이다.
  • 한 레벨의 코드 안에 여러 추상화 수준이 섞여 있으면 코드를 파악하기 어려워진다.

    • 구체적으로(명령형) 작성되어 있는 로직을 읽고, 다른 로직도 충분히 구체적으로 작성되어 있는 것이라고 착각할 수 있다. 그러다 높게 추상화되어 있는 로직을 만나면 앞선 착각으로 인해 간단한 코드일 것이라고 생각했다가 내부적으로 복잡한 코드를 만나 생각의 널뛰기로 이어진다.

문서로 적어 보기

  • 향후 어떤 점에서 위험할 수 있는지
  • 어떻게 개선할 수 있는지
  • 원칙을 수립해볼 것

현재 팀에서는 '기능 구현'을 최우선으로 두고 개발하고 있어서, 요구되는 기능이 잘 동작한다면 코드의 구조적인 깔끔함은 후순위로 두고 우선 배포하는 편이다. 따라서 그동안 자잘한 기능들과 버그를 쳐내면서 코드의 이곳저곳에 침투해 새 로직을 찔러넣는 작업이 대부분이었다.

이 과정에서 때때로 로직이 꼬이면서 간단한 기능이라고 생각했던 작업이 시간을 잡아먹는 문제를 마주쳤는데, 클린 코드의 관점에서 생각해 보니 함수와 컴포넌트의 역할을 명확히 정의하지 않고 냅다 구현하려 드는 습관이 문제의 원인이었다는 생각이 든다.

내가 지금 수정하려는 로직이 기존에 어떤 역할을 수행하고 있었는지 파악한 다음 기존 구조를 해치지 않으면서 적절한 방식으로 새 기능을 집어넣어야 하는데, 이러한 큰 그림을 파악하지 않고 바로 세부 구현에 들어가 말 그대로 코드를 '찔러넣는' 작업 방식을 버려야겠다.

profile
하루가 모여 역사가 된다

0개의 댓글