TSX로 React 다시 배우기: Props 타입 정의 패턴
리액트(React)를 사용해 컴포넌트를 만들 때, 컴포넌트 간에 데이터를 전달하는 'Props'는 핵심적인 역할을 합니다. 과거 자바스크립트 환경에서는 이 Props에 어떤 값이 들어오는지 명확히 알기 어려워 런타임 에러를 마주하는 일이 흔했습니다. 하지만 React TypeScript 환경인 TSX를 도입하면서 이러한 불안감은 완전히 사라졌습니다.
TSX Props 타입 정의는 컴포넌트가 작동하기 위해 필요한 데이터의 '설명서'를 작성하는 과정과 같습니다. 올바른 타입을 미리 지정해 두면 코드를 작성하는 도중에 에러를 잡아낼 수 있어 개발 생산성이 획기적으로 향상됩니다. 처음에는 조금 번거롭게 느껴질 수 있는 TSX Props 타입의 기본부터 실무 패턴까지 차근차근 살펴보겠습니다.
1. Interface와 Type Alias로 시작하는 Props 기본 정의
TSX에서 Props 타입을 정의할 때 가장 먼저 고민하는 부분은 'interface'와 'type' 중 무엇을 쓸 것인가입니다. 결론부터 말하자면, 객체의 확장성을 고려해야 하는 대규모 프로젝트나 라이브러리 개발에는 interface를 선호하고, 단순한 결합이나 고정된 타입을 정의할 때는 type을 주로 사용합니다. 최근 트렌드에서는 두 문법의 기능적 차이가 많이 좁혀져 팀의 컨벤션에 맞춰 일관성 있게 사용하는 것이 더 중요합니다.
예를 들어 사용자 정보를 보여주는 Profile 컴포넌트의 Props를 정의해 보겠습니다. interface를 사용하면 'interface ProfileProps { name: string; age: number; }'와 같이 직관적으로 선언할 수 있습니다. 이렇게 정의된 타입을 컴포넌트 함수의 매개변수 부분에 지정해주면, 호출하는 쪽에서 필수 데이터를 빠뜨렸을 때 코드 에디터가 즉시 경고를 띄워줍니다.
2. 선택적 프로퍼티와 유니온 타입으로 유연성 더하기
모든 Props가 항상 필수적인 것은 아닙니다. 컴포넌트의 상황에 따라 전달받을 수도 있고 생략할 수도 있는 옵션 항목은 프로퍼티 이름 뒤에 물음표(?)를 붙여 '선택적 프로퍼티(Optional Property)'로 정의합니다. 예를 들어 프로필 이미지 URL이 없을 수도 있다면 'imageUrl?: string;'과 같이 선언하여 컴포넌트의 유연성을 높일 수 있습니다.
여기에 더해 지정된 몇 가지 값 중 하나만 허용하고 싶을 때는 유니온(Union) 타입을 활용합니다. 버튼의 색상 타입을 'theme: "primary" | "secondary" | "danger"'와 같이 구체적인 문자열의 합으로 제한하는 방식입니다. 이렇게 하면 개발자가 엉뚱한 값을 입력하는 실수를 완벽히 방지할 수 있으며 자동 완성 기능의 도움도 받을 수 있어 매우 유용합니다.
3. 컴포넌트 자식과 이벤트 타입 정복하기
리액트 컴포넌트를 설계할 때 자주 사용되는 두 가지 특수 타입이 바로 '자식 컴포넌트(children)'와 '이벤트 핸들러'입니다. 다른 컴포넌트나 태그를 내부에 감싸서 전달하는 'children' 속성은 주로 'React.ReactNode' 타입을 사용합니다. React.ReactNode는 문자열, 숫자, JSX 엘리먼트 등 화면에 렌더링될 수 있는 거의 모든 요소를 포괄하는 가장 안전한 타입입니다.
또한 버튼 클릭이나 입력창 변경 같은 사용자의 상호작용을 처리할 때는 정확한 이벤트 타입을 지정해야 합니다. 예를 들어 버튼의 onClick 핸들러는 'onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;'와 같이 작성합니다. React TypeScript 환경이 제공하는 이러한 구체적인 이벤트 타입을 활용하면, 이벤트 객체 내부의 속성들에 안전하게 접근하고 에러를 방지할 수 있습니다.
4. 제네릭을 통한 고차원 Props 패턴 구현
컴포넌트가 다루는 데이터의 형태가 고정되어 있지 않고 동적으로 변해야 할 때가 있습니다. 예를 들어 다양한 종류의 목록을 보여주는 리스트 컴포넌트나 드롭다운 메뉴가 대표적입니다. 이럴 때는 특정 타입을 미리 박아두는 대신, 호출하는 시점에 타입을 결정하는 '제네릭(Generics)'을 Props 정의에 도입할 수 있습니다.
제네릭 컴포넌트를 만들면 'ListProps<T>'처럼 타입 매개변수 T를 정의하여, 어떤 형태의 객체 배열이 들어오든 타입 안정성을 완벽하게 유지할 수 있습니다. 데이터의 내부 구조에 상관없이 재사용할 수 있는 범용 컴포넌트를 설계할 때 제네릭 패턴은 TSX의 진가를 발휘하게 만들어 줍니다.
결론: 안전하고 즐거운 리액트 개발의 시작
TSX 환경에서 Props 타입을 정의하는 것은 단순한 문법적 제약을 넘어, 동료 개발자 및 미래의 나와 나누는 신뢰성 있는 소통입니다. 올바르게 정의된 타입은 컴포넌트 자체를 살아있는 문서로 만들어 주며, 복잡한 리팩토링 과정에서도 든든한 버팀목이 됩니다.
처음에는 빨간 줄로 가득 찬 화면을 보며 좌절할 수도 있지만, 컴포넌트의 구조를 명확히 설계하는 습관을 들이면 점차 버그가 현저히 줄어드는 것을 체감하게 됩니다. 오늘 다룬 기본 패턴과 이벤트 타입 활용법을 바탕으로 더 안전하고 확장성 높은 리액트 애플리케이션을 만들어 보시기 바랍니다.