토스FE의 좋은 코드를 위한 4가지 기준을 읽고나서.
먼저 토스의 4가지 기준에 대한 목차는 아래와 같다. 천천히 읽어가며 정리 후, 각 항목마다
내 생각을 덧붙이려한다.
읽기 귀찮으면 바로 5번의 정의로 가도 된다
목차
화살표 누르면 메뉴 펼쳐져요!
1. 가독성
- 맥락 줄이기
- A. 같이 실행되지 않는 코드 분리하기
- B. 구현 상세 추상화하기
- C. 로직 종류에 따라 합쳐진 함수 쪼개기
- 이름 붙이기
- A. 복잡한 조건에 이름 붙이기
- B. 매직 넘버에 이름 붙이기
- 위에서 아래로 읽히게 하기
- A. 시점 이동 줄이기
- B. 삼항 연산자 단순하게 하기
2. 예측 가능성
- A. 이름 겹치지 않게 관리하기
- B. 같은 종류의 함수는 반환 타입 통일하기
- C. 숨은 로직 드러내기
3. 응집도
- A. 함께 수정되는 파일을 같은 디렉토리에 두기
- B. 매직 넘버 없애기
- C. 폼의 응집도 생각하기
4. 결합도
- A. 책임을 하나씩 관리하기
- B. 중복 코드 허용하기
- C. Props Drilling 지우기
5. 정리
결론: 변경하기 쉬운 코드가 좋은 FE 코드이다
1. 가독성
코드가 읽기 쉬우면 변경도 쉽다
1-1. 맥락 줄이기
같이 실행되지 않는 코드 분리하기
- 하나의 함수/컴포넌트는 한 가지 기능만!
- 코드가 길어져도 가독성이 올라감
- 단점: 분리로 인한 휴먼에러 가능성
- 보완: 테스트 코드로 방지
구현 상세 추상화하기
- 불필요한 세부사항 감추고, 핵심 로직만 드러내기
- 관련 코드끼리 가까이 두면 응집도↑, 실수↓
- “왼쪽으로 10걸음”처럼 의도를 드러내는 간결한 표현이 중요
항목 | Wrapper 컴포넌트 (A) | HOC (B) |
---|---|---|
적용 방식 | JSX에서 직접 감싸기 | 함수로 감싸서 새 컴포넌트 반환 |
가독성 | 명시적, 구조 파악 쉬움 | 암묵적, 구조 파악 어려울 수 있음 |
재사용성 | 중간 | 높음 |
사용 용도 | UI 구조 조정에 적합 | 공통 로직 적용에 적합 |
url | 코드예시 |
로직 종류에 따라 합쳐진 함수 쪼개기
- 종류별로 함수/컴포넌트/Hook을 만들지 말자
- 너무 많아지면 이해가 힘듦
1-2. 이름 붙이기
복잡한 조건에 이름 붙이기
- filter 등에서 여러 조건이 있으면 변수로 이름 붙여서 가독성↑
- 무조건 이름 붙이지 말고, 다음 경우에만:
- 복잡한 로직을 다룰 때 (간단하면 굳이 X)
- 재사용성이 필요할 때 (한 번만 쓰면 굳이 X)
- 단위테스트가 필요할 때
매직 넘버에 이름 붙이기
- 코드에 직접 숫자(404, 86400 등) 넣지 말고 의미 있는 변수로!
- 예:
ONE_DAY = 86400
1-3. 위에서 아래로 읽히게 하기
시점 이동 줄이기
- 함수/파일에서 위→아래로 읽히게 작성하면 동작 파악이 빠름
- 관련 예시
삼항 연산자 단순하게 하기
- 삼항 연산자 복잡하게 쓰지 말고, 조건 구조를 명확하게!
2. 예측 가능성
이름, 함수, 반환 타입 등은 예측 가능해야 한다
2-1. 이름 겹치지 않게 관리하기
- 같은 이름의 함수/변수는 동일한 동작을 해야 함
- 이름은 확실하게, 혼동 없게!
2-2. 같은 종류의 함수는 반환 타입 통일하기
- 같은 역할의 함수가 서로 다른 반환 타입이면 헷갈림
- 협업 시 실수/버그 위험↑
- 반드시 같은 타입으로 맞추자!
// ❌ 나쁜 예: 같은 역할인데 반환 타입이 다름
function getPizzaOrder() {
const response = {
status: 'success',
data: { name: 'Pepperoni', size: 'Large' }
};
return response; // 전체 response 반환
}
function getBurgerOrder() {
const response = {
status: 'success',
data: { name: 'Cheeseburger', size: 'Medium' }
};
return response.data; // data만 반환
}
// ✅ 좋은 예: 반환 타입 통일
function getPizzaOrder() {
const response = {
status: 'success',
data: { name: 'Pepperoni', size: 'Large' }
};
return response;
}
function getBurgerOrder() {
const response = {
status: 'success',
data: { name: 'Cheeseburger', size: 'Medium' }
};
return response; // 같은 타입으로 통일
}
2-3. 숨은 로직 드러내기
fetchUser()
함수에 로깅이 숨어있으면 이름/타입만 봐선 알 수 없음- 로깅시스템 에러 시 유저데이터 가져오는 곳도 에러 발생
- 함수는 순수하게, 로깅은 따로!
3. 응집도
관련 기능은 함께 모여 있어야 한다
3-1. 함께 수정되는 파일을 같은 디렉토리에 두기
- 도메인별 디렉터리 구조 분리 (FSD 아키텍처 Slice 개념)
- FSD란?
- 관심사 분리의 핵심
3-2. 폼의 응집도 생각하기
Form을 관리할 때는 2가지 방법으로 응집도를 관리:
필드 단위 응집도
- 개별 입력 요소를 독립적으로 관리
- 각 필드가 고유의 검증 로직 보유
- 유지보수 쉬움, 다른 필드에 영향↓
폼 전체 단위 응집도
- 모든 필드의 검증 로직이 폼에 종속
- 폼 전체 흐름을 고려하여 설계
- 변경 단위가 폼 단위로 발생할 때 고려
- 폼 전체의 검증이 한 곳에서 관리되어 로직이 간결
- 상태가 중앙 집중식으로 관리되므로 폼 전체 흐름 이해 쉬움
- 재사용성은 떨어짐
4. 결합도
컴포넌트 간 의존성의 정도. 너무 높으면 수정이 어렵고, 너무 낮으면 중복이 생김
4-1. 중복 코드 허용하기
공통화의 딜레마
- 여러 페이지/컴포넌트에 걸친 중복 코드를 하나의 Hook/컴포넌트로 공통화
- 장점: 응집도↑, 함께 수정되는 코드 한꺼번에 관리
- 단점: 공통 컴포넌트/Hook 수정 시 영향 범위가 넓어질 수 있음
- 페이지마다 특이 요구사항이 생기면 복잡해질 수 있음 😭
결론
중복 코드 허용이 좋은 방향일 수 있음. 잘 판단해야 함!
4-2. Props Drilling 지우기
조합(Composition) 패턴
children
을 활용해서 컴포넌트를 조립하듯 구성- 부모가 자식에게 필요한 UI를 직접 전달 → 유연한 구조
- 단점: 깊은 컴포넌트 트리에서는 여전히 props 여러 단계 전달 문제 발생
Context API
- 전역 상태처럼 데이터 공유
- 중간 컴포넌트에 props 전달 없이 Props Drilling 완전 제거 가능
- 복잡한 컴포넌트 구조에서도 데이터 흐름이 깔끔
5. 정리
네 가지 기준이 서로 연결되어 있다는 것을 알 수 있었다.
🎯 각 기준의 역할
- 가독성: 출발점
- 코드가 읽기 쉬워야 다른 기준도 작동
- 예측 가능성도 결국 읽고 이해할 수 있어야 생김
- 예측 가능성: 안정성
- 협업/유지보수에서 실수 줄임
- 반환 타입 통일, 숨은 로직 제거 등은 안정성↑
- 응집도: 구조의 품질
- 함께 수정되는 코드가 함께 있어야 변경 쉬움
- 실수↓, 생산성↑
- 결합도: 유연성의 척도
- 너무 높으면 수정 어려움
- 너무 낮으면 중복↑
- 적절한 결합도가 좋은 설계의 핵심
🔄 네 가지 기준의 상호작용
기준 | 핵심 개념 | 서로 연결되는 방식 |
---|---|---|
가독성 | 코드를 쉽게 읽고 이해함 | 예측 가능성↑ → 가독성↑ 응집도↑ → 관련 코드 가까이 있어 읽기 쉬움 |
예측 가능성 | 코드 동작을 예상할 수 있음 | 가독성↑ → 코드 흐름 파악 쉬움 결합도↓ → 변경 시 영향 범위 예측 쉬움 |
응집도 | 관련 기능이 함께 모여 있음 | 가독성↑, 예측 가능성↑ 결합도↓에 도움 |
결합도 | 컴포넌트 간 의존성의 정도 | 결합도↓ → 예측 가능성↑, 유지보수성↑ 응집도와 균형 필요 (너무 낮으면 중복↑) |