Props를 컴포넌트 인터페이스로 설계하기
Props 복사/가공, 네이밍, 객체/전달 패턴 등 API 관점에서 보는 설계 기준
Props는 인터페이스다
— 컴포넌트 오염은 대부분 Props에서 시작된다
서론: Props를 어떻게 보느냐가 컴포넌트 수명을 결정한다
Props를 “부모에서 자식으로 내려주는 값”으로만 이해하면,
컴포넌트는 금방 지저분해지고, 바꾸기 어려워지고, 테스트가 힘들어진다.
Props의 본질은 컴포넌트의 공개 API다.
외부에서 이 컴포넌트를 어떻게 쓰는지, 무엇을 기대할 수 있는지,
그리고 무엇을 절대 건드리면 안 되는지가 Props 설계에 드러난다.
이 글의 목표는 Props를
가독성 / 변경 비용 / 테스트 용이성 관점에서 다시 보게 만드는 것이다.
1) 불필요한 Props 복사·연산
왜 중요한가
Props를 state로 복사하거나 가공하기 시작하면, 데이터 흐름이 즉시 불투명해진다.
흔한 잘못된 사고
- “props 그대로 쓰기 불편하니까 state로 복사”
- “여기서 한 번 계산해서 내려주자”
- “나중에 바뀔 수도 있으니 일단 state”
왜 나중에 문제 되는가
- props와 state가 동기화 대상이 된다
- 어느 시점의 값이 진짜인지 추적이 어려워진다
- 리팩터링 시 변경 지점이 늘어난다
더 나은 설계 방향
- props는 그대로 사용한다
- 계산은 “가장 가까운 곳”에서 한다
- “처음 값만 필요”하면 의도를 코드로 드러낸다
1
2
3
4
5
// ❌ props 복사
const [value, setValue] = useState(props.value);
// ✅ 그대로 사용
const value = props.value;
실무 체크리스트
- props를 state로 복사할 명확한 이유가 있는가
- 계산 결과를 굳이 props로 내려보내고 있지 않은가
- 데이터 흐름을 화살표로 그렸을 때 단방향인가
- “최신 값”의 기준을 한 문장으로 말할 수 있는가
- props 변경 시 state 동기화 로직이 필요한 구조인가
2) JSX 문법이 가독성에 미치는 영향 (Curly Braces / Shorthand)
왜 중요한가 JSX는 HTML처럼 보이지만, 가독성은 문법 선택에 크게 좌우된다.
흔한 잘못된 사고
- “짧으면 좋은 코드”
- “JSX는 어차피 다 비슷”
- “컨벤션은 취향 문제”
왜 나중에 문제 되는가
- 표현식이 섞일수록 읽는 순서가 깨진다
- 조건/계산이 JSX에 박히면 로직 탐색 비용이 증가한다
- 리뷰 시 의도를 파악하는 시간이 늘어난다
더 나은 설계 방향
- JSX에서는 값 전달만, 계산은 위에서
- Shorthand는 의미가 명확할 때만
- Curly Braces 남용 금지
1
2
3
4
5
6
// ❌ JSX 안에서 계산
<Component value={items.filter(i => i.active).length} />
// ✅ 위에서 계산
const activeCount = items.filter(i => i.active).length;
<Component value={activeCount} />
실무 체크리스트
- JSX를 위에서 아래로 읽기만 해도 의미가 전달되는가
- JSX 안에 로직이 숨어 있지 않은가
- 표현식이 길어질수록 위로 끌어올렸는가
- Shorthand가 의미를 숨기고 있지 않은가
- JSX를 “선언부”로만 유지하고 있는가
3) Props 네이밍이 사고를 바꾼다
왜 중요한가 Props 이름은 사용자를 올바른 방향으로 유도하거나, 실수를 유발한다.
흔한 잘못된 사고
data,value,info같은 포괄적 이름onClick,onChange남발- boolean에
is,has를 안 붙임
왜 나중에 문제 되는가
- Props 사용 의도가 드러나지 않는다
- 잘못된 값/함수가 전달돼도 눈치채기 어렵다
- 테스트 코드 가독성이 떨어진다
더 나은 설계 방향
- “무엇인지”보다 “역할/의미”를 드러내는 이름
- 이벤트는 의미 중심으로 네이밍
- boolean은 질문 형태로 읽히게
1
2
3
4
5
// ❌ 의도 불명확
<Button value={true} onClick={handleClick} />
// ✅ 의도 명확
<Button disabled={isSubmitting} onSubmit={handleSubmit} />
실무 체크리스트
- Props 이름만 보고 역할이 추측되는가
- boolean을 문장처럼 읽을 수 있는가
- 이벤트명이 “UI 동작”이 아니라 “도메인 행동”인가
- 테스트 코드에서 의미가 살아 있는가
- 같은 의미의 props가 여러 이름으로 존재하지 않는가
4) 인라인 스타일 / CSS-in-JS 인라인의 문제
왜 중요한가 스타일을 props로 직접 전달하면, 컴포넌트의 책임 경계가 무너진다.
흔한 잘못된 사고
- “조금만 스타일 바꾸니까 inline”
- “props로 style 내려주면 재사용 가능”
- “CSS 분리 귀찮다”
왜 나중에 문제 되는가
- 스타일 변경이 로직 변경처럼 취급된다
- 재사용 컴포넌트가 특정 화면에 종속된다
- 테스트 시 snapshot 변화가 잦아진다
더 나은 설계 방향
- 스타일은 역할 단위로 캡슐화
- 조건부 스타일은 variant / boolean props로 표현
- style 객체를 API로 만들지 않는다
1
2
3
4
5
// ❌ 스타일을 API로 노출
<Card style={{ marginTop: 12 }} />
// ✅ 의미 있는 props
<Card spaced />
실무 체크리스트
- 스타일이 컴포넌트 API로 노출돼 있는가
- 같은 스타일 로직이 여러 곳에 반복되는가
- 스타일 변경이 로직 변경처럼 리뷰되고 있는가
- 테스트에서 스타일 변화가 불필요한 실패를 만드는가
- “이 스타일은 누가 책임지는가”가 명확한가
5) 객체 Props의 단점
왜 중요한가 객체 props는 편해 보이지만, 변경 추적과 테스트를 어렵게 만든다.
흔한 잘못된 사고
- “묶어서 내려주면 편하잖아”
- “어차피 타입으로 막으니까”
- “props 개수 줄이려고”
왜 나중에 문제 되는가
- 참조 변경으로 불필요한 리렌더 발생
- 어떤 필드가 필요한지 컴포넌트 밖에서 알기 어렵다
- 테스트에서 필요한 최소 입력을 정의하기 힘들다
더 나은 설계 방향
- 필요한 값만 명시적으로 분리
- 객체는 도메인 단위일 때만 허용
- 컴포넌트의 요구사항을 props 시그니처로 드러낸다
1
2
3
4
5
// ❌ 객체 props
<UserCard user={user} />
// ✅ 명시적 props
<UserCard name={user.name} age={user.age} />
실무 체크리스트
- 이 컴포넌트가 객체의 모든 필드를 사용하는가
- 어떤 값이 필요한지 시그니처만 보고 알 수 있는가
- 객체 참조 변경으로 리렌더가 발생하지 않는가
- 테스트에서 최소 입력을 쉽게 만들 수 있는가
- 객체 props가 편의성 때문에 선택된 건 아닌가
6) ...props 사용 시의 위험
왜 중요한가 ...props는 컴포넌트 API를 암묵적으로 확장한다.
흔한 잘못된 사고
- “HTML 속성 다 받으려고”
- “확장성 좋아 보이잖아”
- “나중에 뭐 올지 모르니까”
왜 나중에 문제 되는가
- 어떤 props가 허용되는지 알 수 없다
- 내부 구현 변경이 외부 API에 영향을 준다
- 잘못된 props 전달을 컴파일 타임에 막기 어렵다
더 나은 설계 방향
- 필요한 props만 명시적으로 열어둔다
- 공통 props는 타입으로 제한한다
- 무분별한 패스스루 금지
실무 체크리스트
- 이 컴포넌트의 공개 API를 한 줄로 설명할 수 있는가
- 내부 구현 변경이 외부에 새 props를 노출하지 않는가
- 의도하지 않은 HTML 속성이 전달될 여지가 있는가
- 타입으로 허용 범위를 제한했는가
...props가 편의성 때문에 남아 있지 않은가
7) Props가 많아질 때의 구조적 대응
왜 중요한가 Props 수 증가는 곧 컴포넌트 책임 증가의 신호다.
흔한 잘못된 사고
- “어쩔 수 없이 많아”
- “일단 추가하고 보자”
- “나중에 리팩터링”
왜 나중에 문제 되는가
- 사용 방법을 외우기 힘들다
- 일부 props만 필요한 케이스가 늘어난다
- 테스트 케이스 조합이 폭발한다
더 나은 설계 방향
- 역할별로 컴포넌트 분리
- 공통/선택 props를 구분
- Compound Component / children 패턴 고려
실무 체크리스트
- Props가 6~7개를 넘는 이유가 명확한가
- 하나의 컴포넌트가 여러 역할을 맡고 있지 않은가
- 사용 시 항상 전달하는 props와 선택 props가 섞여 있지 않은가
- 컴포넌트 사용 예시를 3가지 이상 쉽게 만들 수 있는가
- 분리했을 때 API가 더 명확해지는가
8) 단순한 Props의 장점
왜 중요한가 단순한 Props는 읽기 쉽고, 바꾸기 쉽고, 테스트하기 쉽다.
핵심 정리
- props는 명시적일수록 좋다
- 적을수록 좋다
- 의미가 드러날수록 좋다
실무 체크리스트
- Props 시그니처만 보고 사용법을 알 수 있는가
- 테스트에서 최소 입력만으로 렌더링 가능한가
- 변경 시 영향 범위를 쉽게 예측할 수 있는가
- “이 컴포넌트는 이런 용도다”가 코드에 드러나는가
- Props를 줄이면 책임도 함께 줄어드는가
결론
Props는 단순 전달값이 아니다. 컴포넌트의 계약서다.
이 계약이 흐릿해질수록:
- 가독성은 떨어지고
- 변경 비용은 커지고
- 테스트는 어려워진다
Props를 설계하는 순간, 이미 그 컴포넌트의 미래는 결정된다. —
- 참고: 클린코드 리액트(React)
