intro
- 혼자 보기 아까워.. 는 핑계고.. 기록도 할 겸, 복기도 할 겸, 겸사 겸사 정리를 해봅니다.
- 스터디 주제로 나온 아티클로 발표를 준비하다보니 정리하게 되었지만 예시까지 들어가며 내용 정리가 아주 잘 되어있으니 시간이 된다면 정독을 추천합니다!
- 토스에서 좋은 프론트엔드 코드의 기준에 대해서 소개하는 Frontend Fundamentals가 오픈 소스로 공개되었습니다.
- 저희 같은 감자들에겐 아주 감지덕지죠!
그래서 무슨 내용?
- 내용은 크게 4가지 기준을 설명하는 것으로 일축됩니다.
- 큰 주제는 변경하기 쉬운 코드!
- 좋은 프론트엔드 코드 = 변경하기 쉬운 코드
- 즉, 유지보수에 유리한 코드가 좋은 코드다 정도로 이해 할 수 있을 것 같습니다.
좋은 코드에 대한 4개 원칙
- 그래서 좋은 코드를 위해서 어떤 것들이 중요한가?
- 가독성(Readability)
- 예측 가능성(Predictability)
- 응집도(Cohesion)
- 결합도(Coupling)
- 어찌보면 당연한 이야기라고 할 수도 있지만 자세히 알아보면 달라질 겁니다!
가독성(Readability)
: 코드가 읽기 쉬운 정도.
- 가독성을 높이는 전략
- 맥락 줄이기
- 같이 실행되지 않는 코드 분리하기
- 하나의 컴포넌트 안에서 두가지 경우를 나눠 줘야한다면, 총 3개의 컴포넌트로 나눠 부모 컴포넌트에서 두가지 경우의 컴포넌트로 분기 해주는 식으로 분리 ⇒ 맥락 감소! 가독성 증가!
- 구현 상세 추상화하기.
- a→b→c 의 로직 일 때, a,b,c 의 로직을 모두 노출시키는 것보다 각각의 로직 별로 추상화! ⇒ → 맥락 감소! 가독성 증가!
- 옵션으로는 1.Wrapper 컴포넌트 사용하거나 2.HOC(Higher-Order Component) 사용하는 방법 존재!
- 로직 종류에 따라 합쳐진 함수 쪼개기.
- API 호출에서 여러 쿼리 파라미터를 호출한다고 할 때, 한번에 모든 쿼리 파라미터를 호출하지 않고 기능 별로 나누어 관리! ⇒ 맥락 감소! 가독성 증가!
- 같이 실행되지 않는 코드 분리하기
- 이름 붙이기
- 복잡한 조건에 이름 붙이기.
- 조건이 복잡하거나, 재사용 가능성이 있거나, 단위 테스트가 필요하다면 함수를 분리해 조건에다 이름을 붙여 사용 ⇒ 가독성 증가!
- 단, 로직이 간단하거나 한번만 사용된다면 익명 함수로 한번에 직접 처리하는 것이 더 직관적 일 수 있음!
- 매직 넘버에 이름 붙이기.
- 매직 넘버(Magic Number) : 정확한 뜻을 밝히지 않고 소스 코드 안에 직접 숫자 값을 넣는 것!
- ex) Not Found → 404 , 하루 → 86400초.
- 이러한 매직 넘버 변수 대신 변수명을 지어 붙이기 ⇒ 가독성 증가!
- 복잡한 조건에 이름 붙이기.
- 위에서 아래로 읽히게 하기
- 시점 이동 줄이기.
- 시점 이동 : 코드를 읽을 때 코드의 위아래를 왔다갔다 하면서 읽거나, 여러 파일이나 함수, 변수를 넘나들면서 읽는 것.
- 조건을 요구사항 그대로 펼쳐서 드러내거나, 조건을 객체로 관리하는 방법 존재. ⇒ 위에서 아래로 시점이동없이 읽기 쉬움 ⇒ 가독성 증가!
- 삼항 연산자 단순하게 하기.
- 삼항 연산자가 길어지면 오히려 가독성을 떨어뜨릴 수 있음.
- 조건이 길어질 경우 if문으로 풀어서 가독성 증가!
- 시점 이동 줄이기.
예측 가능성(Predictability)
: 함께 협업하는 동료들이 함수나 컴포넌트의 동작 예측 가능 정도
- 예측 가능성을 높이는 전략
- 이름 겹치지 않게 관리하기
- 같은 이름이지만 같은 맥락이면서 공교롭게 약간의 차이가 있는 함수가 존재한다면 버그를 유발 가능. ⇒ 더 자세히 예측 할 수 있도록 이름을 변경하여 예측 가능성 증가!
import { http as httpLibrary } from "@some-library/http";
- 같은 이름이지만 같은 맥락이면서 공교롭게 약간의 차이가 있는 함수가 존재한다면 버그를 유발 가능. ⇒ 더 자세히 예측 할 수 있도록 이름을 변경하여 예측 가능성 증가!
- 같은 종류의 함수는 반환 타입 통일하기
- 어디선 data를, 어디선 Query 객체를 반환한다면 일관성이 떨어져 불편함 발생! ⇒ 통일시켜 예측 가능성 증가!
- 유효성 검사 함수의 반환 타입을 Discriminated Union으로 정의하면 타입을 통일하면서 동시에 컴파일러의 불필요한 접근 예방 가능!
const isAgeValid: ValidationCheckReturnType = checkIsAgeValid(1); if (isAgeValid.ok) { isAgeValid.reason; // Error: Property 'reason' does not exist on type '{ ok: true; }'. } else { isAgeValid.reason; }
type ValidationCheckReturnType = { ok: true; } | { ok: false; reason: string };
- 숨은 로직 드러내기
- 아무리 간단한 로직이라도 a로직 안에 b로직이 들어있다면 b로직에 오류가 생김으로써 a로직까지 망가져 a로직의 문제로 보일수 있음!
- 이러한 숨은 로직을 별도로 분리하여 예측 가능성 증가!
응집도(Cohesion)
: 수정되어야 할 코드가 항상 같이 수정되는 정도.
- 응집도가 높은 코드는 코드의 한 부분을 수정해도 의도치 않게 다른 부분에서 오류가 발생 X.
- 함께 수정되어야 할 부분이 반드시 함께 수정되도록 구조적으로 뒷받침되기 때문.
- 가독성과 응집도는 서로 상충 가능!
- 일반적으로 응집도를 높이기 위해서는 변수나 함수를 추상화하는 등 가독성을 떨어뜨리는 결정 필요.
- 함께 수정되지 않으면 오류가 발생할 수 있는 경우에는, 응집도를 우선해서 코드를 공통화, 추상화 지양!
- 위험성이 높지 않은 경우에는, 가독성을 우선하여 코드 중복을 허용 가능!
- 응집도를 높이는 전략
- 함께 수정되는 파일을 같은 디렉토리에 두기
- 하나의 디렉토리 안에 모아둔다면 의존관계를 파악하기도 쉽고, 특정 기능을 삭제한다고 하면 디렉토리 전체를 삭제함으로써 깔끔하게 처리 가능.
└─ src │ // 전체 프로젝트에서 사용되는 코드 ├─ components ├─ containers ├─ hooks ├─ utils ├─ ... │ └─ domains │ // Domain1에서만 사용되는 코드 ├─ Domain1 │ ├─ components │ ├─ containers │ ├─ hooks │ ├─ utils │ └─ ... │ │ // Domain2에서만 사용되는 코드 └─ Domain2 ├─ components ├─ containers ├─ hooks ├─ utils └─ ...
- 매직 넘버 없애기
- 가독성에서 언급했던 것처럼 직접 숫자를 넣는 매직 넘버 대신 변수명으로 사용 ⇒ 응집도 증가!
- 변수 변경 상황 : 매직 넘버 → 직접 숫자 변경 → 응집도 낮음.
- 폼의 응집도 생각하기
- 필드 단위 vs 폼 전체 단위
- 필드 단위 응집도
- 개별 입력 요소를 독립적으로 관리.
- 각 필드별로 고유의 검증 로직 보유.
- 재사용성 및 독립성 증가로 유지보수 장점.
- 독립적인 검증 : 필드 별로 검증 로직이나 비동기 검증이 필요할 때.
- 재사용 필요 : 공통 입력 필드를 독립적으로 관리, 재사용 할 때.
- 폼 전체 단위 응집도
- 모든 필드의 검증 로직이 폼에 종속.
- 중앙 집중식. 전체 흐름 유지 및 파악 장점.
- 단일 기능 : 모든 필드가 밀접하게 연관되어 하나의 기능 일 때.
- 단계 별 입력: 설문조사처럼 이전 단계의 입력 값이 다음 단계에 영향을 줄 때.
- 필드 간 의존성 : 비밀번호 확인이나 총액 계산 같이 필드 간 상호 작용이 필요할 때.
결합도(Coupling)
: 코드를 수정했을 때의 영향범위.
- 결합도를 낮추는 전략
-
- 책임을 하나씩 관리하기
- 쿼리 파라미터, 상태, API 호출과 같은 종류 로직은 하나로 관리하는 경우가 많은데, 함수나 컴포넌트, Hook을 로직별이 아닌 쿼리 파라미터 별로 한번 더 나누어 관리 ⇒ 결합도 감소!
- 중복 코드 허용하기
- 중복 코드를 하나의 Hook이나 컴포넌트로 공통화 ⇒ 응집도 증가!
- but! 불필요한 결합도 생성! ⇒ 유지보수에 어려움!
- 따라서 결과를 생각해보고 비교하여, 공통화 하지 않고! 중복 코드를 허용하는 것이 긍정적일수도 있다!
- Props Drilling 지우기
- Props Drilling = 부모 컴포넌트와 자식 컴포넌트 사이에 결합도가 생겼다는 의미!
- A. 조합(Composition) 패턴 활용
- 부모 컴포넌트에서 자식 컴포넌트를 함수화 하여 간단하게 사용 가능.
- but! 트리 구조가 깊어지면 조합 패턴만으로는 한계.
- B. ContextAPI 활용
- 컨텍스트API를 사용해 전역 변수로 관리 ⇒ Props Drilling 해결! ⇒ 결합도 감소!
- 책임을 하나씩 관리하기
정리
- 함수나 변수가 항상 같이 수정되기 위해서 공통화 및 추상화하면, 응집도가 증가. but 가독성 감소.
- 중복 코드를 허용하면, 코드의 영향범위를 줄일 수 있어서, 결합도 감소. but 하나 수정, 다른거 누락? ⇒ 응집도 감소.
- 상황에 맞게 고민하고 어떤 가치를 우선할지 고민이 필요!
- 가끔씩 꺼내 보면서 프론트엔드 개발자로서 코드 품질을 높이고자 할 때 방향을 찾는 나침반처럼 활용 추천!
- ps. 뒷부분에 보면 커뮤니티 카테고리에 디스커션 섹션과 A vs B 섹션, 좋은 토론 모아보기 섹션이 있는데 좋은 토론 모아보기 부분은 유익하면서도 재밌으니 추천드립니다!!!!