coupling
Installation
SKILL.md
결합도
코드 변경의 영향 범위를 최소화한다. 한 기능 변경이 다른 기능을 깨뜨리면 안 된다.
핵심 패턴
Props Drilling 제거 (조합 패턴)
❌ props가 계층을 통과:
function ItemEditModal({ items, onConfirm, onClose }) {
return (
<Modal onClose={onClose}>
<ItemEditBody items={items} onConfirm={onConfirm} onClose={onClose} />
</Modal>
);
}
function ItemEditBody({ items, onConfirm, onClose }) {
return (
<>
<Button onClick={onClose}>닫기</Button>
<ItemEditList items={items} onConfirm={onConfirm} />
</>
);
}
✅ children으로 조합:
function ItemEditModal({ items, onConfirm, onClose }) {
return (
<Modal onClose={onClose}>
<ItemEditBody onClose={onClose}>
<ItemEditList items={items} onConfirm={onConfirm} />
</ItemEditBody>
</Modal>
);
}
function ItemEditBody({ children, onClose }) {
return <><Button onClick={onClose}>닫기</Button>{children}</>;
}
책임 분리
❌ 모든 것을 관리하는 Hook:
function usePageState() {
const [query] = useQueryParams({
cardId: NumberParam, dateFrom: DateParam, dateTo: DateParam, statusList: ArrayParam
});
}
✅ 단일 책임 Hook:
function useCardIdQueryParam() {
const [cardId, setCardId] = useQueryParam("cardId", NumberParam);
return [cardId ?? undefined, setCardId] as const;
}
빠른 참조
| 코드 냄새 | 개선 방법 |
|---|---|
| 3개 이상 계층 통과하는 props | 조합 패턴: children 사용 |
| 5개 이상 반환하는 Hook | 단일 책임 Hook으로 분리 |
| A 수정 시 관련 없는 B 깨짐 | 결합 지점 식별 후 인터페이스 도입 |
| 모든 곳에서 import하는 util | 사용처 가까이로 이동 |
Context가 적절한 경우
진정한 전역 관심사만: 테마, 로케일, 인증 상태, 10개 이상 컴포넌트가 필요한 데이터.
조합 패턴을 피하려고 Context 남용하지 말 것.
예외: 중복이 나은 경우
- 페이지마다 동작이 달라질 여지가 있을 때
- 공통 코드 수정 시 모든 의존 코드 테스트 부담이 클 때
참고: https://frontend-fundamentals.com/code-quality/loosely-coupled/
Related skills
More from toss/frontend-fundamentals
readability
Use when 삼항 연산자가 중첩되거나, 복잡한 조건식 `a && !b || c`가 이름 없이 사용되거나, 동시에 실행되지 않는 코드가 한 컴포넌트에 섞여 있을 때
16cohesion
Use when 한 기능 수정 시 여러 디렉토리를 건드리거나, 같은 숫자/상수가 여러 파일에 흩어져 있거나, import 경로가 `../../..`처럼 길어질 때
15predictability
Use when `getX()`나 `fetchX()`에 숨은 부수 효과가 있거나, 같은 종류의 함수들이 서로 다른 반환 타입을 가지거나, 함수 이름과 실제 동작이 다를 때
14