Post

Props를 컴포넌트 인터페이스로 설계하기

Props 복사/가공, 네이밍, 객체/전달 패턴 등 API 관점에서 보는 설계 기준

Props를 컴포넌트 인터페이스로 설계하기

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를 설계하는 순간, 이미 그 컴포넌트의 미래는 결정된다. —

This post is licensed under CC BY 4.0 by the author.