---
name: fe-tradeoffs
description: Toss Frontend Fundamentals 4원칙(가독성·예측 가능성·응집도·결합도)이 서로 부딪힐 때의 판단 기준과 원칙을 깨도 되는 경우. 공통화·추상화·중복 제거·Context 도입·폼 구조를 정할 때, 또는 리뷰에서 개선 제안을 낼지 판단할 때 사용
---

# 4원칙 트레이드오프와 예외

좋은 프론트엔드 코드는 **변경하기 쉬운 코드**다. 4원칙은 이걸 재는 도구이고, 넷을 동시에 다 만족시키기는 어렵다. 원칙 하나만 보고 제안하지 말고, 그 제안 때문에 다른 원칙이 나빠지지 않는지 함께 본다.

## 원칙끼리 부딪히는 지점

| 충돌 | 무엇이 부딪히나 |
|---|---|
| 응집도 ↔ 가독성 | 함께 바뀌는 코드를 묶으려고 추상화하면 한 번 더 건너가서 읽어야 해서 읽기 어려워진다 |
| 결합도 ↔ 응집도 | 중복을 허용하면 수정 영향 범위는 줄지만, 한쪽만 고치고 다른 쪽을 빠뜨릴 수 있다 |
| 공통화 ↔ 결합도 | 공통 훅·컴포넌트를 고치면 영향받는 곳이 넓어져서 오히려 수정이 어려워진다 |
| 응집도 ↔ 재사용성 | 폼 전체를 한 덩어리로 묶으면 필드 하나를 따로 재사용하기 어려워진다 |

## 판단 기준 (위에서부터 적용)

1. **위험성**: 함께 바뀌지 않으면 버그가 나는 코드면 공통화한다(응집도 우선). 그런 위험이 없으면 중복을 허용한다(가독성 우선).
2. **앞으로의 변경 방향**: 지금 동작이 같고 **앞으로도 같을 예정일 때만** 공통화한다. 코드만 봐서는 알 수 없으니, 확신이 없으면 "동작이 앞으로도 같은지 확인 필요"라고 적는다.
3. **변경 단위**: 필드 하나씩 바뀌는지, 폼 전체가 같이 바뀌는지에 따라 묶는 단위를 정한다.
4. **문제의 복잡도**: 정책 객체·추상 계층은 문제가 복잡할 때만 이득이다. 단순한 문제에 쓰면 읽기만 어려워진다.
5. **남는 판단**: 지금 상황에서 장기적으로 수정하기 쉬운 쪽을 택하고, 왜 그쪽을 택했는지 한 줄로 남긴다.

## 원칙을 깨도 되는 경우 (지적하지 않는다)

| # | 경우 | 출처 |
|---|---|---|
| 1 | 조건이 아주 단순하면 이름을 붙이지 않아도 된다 | condition-name |
| 2 | 한 번만 쓰이고 단순한 로직은 익명 함수 안에서 바로 처리하는 편이 더 직관적일 수 있다 | condition-name |
| 3 | 권한·정책이 단순할 때 정책 객체 같은 추상화는 오히려 읽기를 방해한다 | user-policy |
| 4 | 반복돼 보여도 중복을 허용하는 편이 나을 수 있다 | use-bottom-sheet |
| 5 | 페이지마다 동작이 달라질 여지가 있으면 공통화하지 않는다 | use-bottom-sheet |
| 6 | Props Drilling이라고 전부 Context로 옮길 필요는 없다. 컴포넌트의 역할과 의도를 담은 props는 문제가 아니다 | item-edit-modal |
| 7 | Context는 조합 패턴(children)으로 풀리지 않을 때 쓰는 최후 수단이다 | item-edit-modal |
| 8 | 폼은 상황에 따라 묶는다. 필드별 독립 검증·재사용이면 필드 단위, 단일 기능·여러 단계 폼·필드 간 의존이면 폼 단위 | form-fields |

## 하지 말아야 할 제안

리뷰의 수정 방법이 아래에 해당하면 다른 방법을 찾거나 이슈를 내린다.

- 읽기 어려워지는 과한 추상화, 한 번만 쓰는 코드를 위한 계층
- 의도를 가리는 섣부른 중복 제거(DRY)
- "나중에 바뀔 수도 있으니"를 근거로 한 계층·옵션 추가
- 기존 API 계약(이름, 반환 형태)을 깨는 변경
- 과한 타입 곡예
- 상태를 전부 전역 스토어로 옮기기
- 깊은 폴더 계층 만들기

## 함께 볼 기준 (커뮤니티 토론)

- **조건부 렌더링 추상화**: `&&`나 삼항 대신 `<If>` 같은 컴포넌트로 감싸면 TypeScript 타입 좁히기와 단락 평가가 깨질 수 있다. 추상화가 언어가 주던 보장을 잃게 하는지 본다.
- **암묵적 불리언 변환**: `if (!value)`는 빈 문자열, `0`, `null`, `undefined`를 한꺼번에 거른다. `0`이나 빈 문자열이 유효한 값이면 버그다. 의도한 값과 명시적으로 비교한다.
- **스타일 논쟁은 린터로**: 한 줄 `if`에 중괄호를 쓸지 같은 스타일은 리뷰에서 다투지 말고 린트 규칙으로 통일한다.

## 추상화를 볼 때 세 가지 질문

1. **이름이 약속하는 범위인가?** 호출부만 보고 동작을 예측할 수 있어야 한다. 구현 안에 이름이 약속하지 않은 일(로깅, 토큰 조회, 저장)이 있으면 "감춘" 게 아니라 "약속을 어긴" 것이다.
2. **공통화가 "앞으로 같이 바뀐다"는 예측에 맞는가?** 좋은 공통화는 줄어든 줄 수가 아니라, 요구사항이 갈라질 때 파라미터와 분기가 쌓이는지로 판단한다.
3. **한 함수 안의 추상화 수준이 맞는가?** 글처럼 같은 층위의 문장만 있어야 한다. 페이지 컴포넌트에 HTTP 세부사항과 화면 흐름이 섞여 있으면 수준이 어긋난 것이다.

## 리뷰에 적용하는 법

- 제안 전에 위 충돌 표에서 반대편 원칙이 나빠지는지 확인한다.
- "원칙을 깨도 되는 경우"에 해당하면 이슈로 올리지 않는다. 필요하면 이유와 함께 참고로만 적는다.
- 원칙끼리 부딪힌 걸 판단했으면 "A를 택함, 근거: 판단 기준 N" 형식으로 남긴다.

## 출처

- [좋은 코드를 위한 4가지 기준](https://frontend-fundamentals.com/code-quality/code/)
- [중복 코드 허용하기](https://frontend-fundamentals.com/code-quality/code/examples/use-bottom-sheet.html)
- [Props Drilling 지우기](https://frontend-fundamentals.com/code-quality/code/examples/item-edit-modal.html)
- [폼의 응집도 생각하기](https://frontend-fundamentals.com/code-quality/code/examples/form-fields.html)
- [복잡한 조건에 이름 붙이기](https://frontend-fundamentals.com/code-quality/code/examples/condition-name.html)
- [시점 이동 줄이기](https://frontend-fundamentals.com/code-quality/code/examples/user-policy.html)
- [명예의 전당 (커뮤니티 토론)](https://frontend-fundamentals.com/code-quality/code/community/good-discussions.html)
