React - type 관리

정찬수·2024년 9월 23일

React

목록 보기
1/1

오늘은 나름 엄근진으로 씁니다.
이유는 타입스크립트 타입 관리 때문에 시간을 너무 버려서 살짝 부들부들하네요.

요즘 웹 시장에서 리액트를 경험하지 못하는 분들은 거의 없을겁니다.
보니까 대부분 JSX로 하시더라구요?.. 저도 처음에 자바스크립트 좋다좋다~ 하면서 썻는데
아니 이거 쓰다보니까 타입선언 안해서 오류가 나도 찾기가 너무 힘들어요

그래서 타입스크립트 사용하게 되었는데 하... 이거이거 이건 또 타입관리가 문제더라구요 ㅠㅠ..
그냥 타입들만 명시해서 관리하면 참 편할텐데 props로 상태관리까지 들어가니까 씁......

그래서 오늘은 어떻게 하면 타입관리 좀 편하게 있을까? 에 대해 적어볼려고 합니다.

우선
https://velog.io/@yoonth95/React%EC%97%90%EC%84%9C%EC%9D%98-TypeScript-%ED%83%80%EC%9E%85-%EC%A7%80%EC%A0%95 (yoonth95님 velog)
인데 타입지정 참 잘 나와 있어요. 본인이 타입스크립트 자체를 모른다하시면 일단 읽고 오세요.

자 그럼 이제 시작합니다.

리액트 개발 하다보면 주로 많이 쓰는 게 있죠. 전 4가지라고 생각하거든요?

Props, State, hooks, event
뭐 redux나 이런건 제외합니다.
그 중에 Props를 예시로 설명 드릴거에요.

1. Props

Props 상태 정의할 때 다들 어떻게 하시나요?

interface MyComponentProps {
  title: string;
  count: number;
}

const MyComponent = ({ title, count }: MyComponentProps) => {
  return <h1>{title} - {count}</h1>;
};

이런 느낌으로 상태 정의 하시죠? 와 이거 컴포넌트가 많아지니까 하나만 추가해도 너무 수정할게 많아요..;;

그래서 뭐 쓰죠? React.FC 쓰죠?

interface MyComponentProps {
  title: string; // title은 문자열 타입
  count: number; // count는 숫자 타입
}

const MyComponent: React.FC<MyComponentProps> = ({ title, count, children }) => {
  return (
    <div>
      <h1>{title} - {count}</h1> {/* title과 count는 외부에서 전달된 값 */}
      <div>{children}</div> {/* children은 React.FC에 의해 자동으로 포함된 값 */}
    </div>
  );
};

이런느낌 주석보시면 알시겠지만 React.FC는 children이 자동포함이에요.

자 React 정의 된거 실제로 들어가서 보면

type FC<P = {}> = FunctionComponent<P>;

interface FunctionComponent<P = {}> {
    (props: P, deprecatedLegacyContext?: any): ReactNode;
    propTypes?: WeakValidationMap<P> | undefined;
    contextTypes?: ValidationMap<any> | undefined;
    defaultProps?: Partial<P> | undefined;
    displayName?: string | undefined;
}

이렇게 되어있어요 보시면 아시겠죠?
정해진대로 자동으로 추론해서 해주니 따로 자식요소 명시적으로 안받아도 되서 좋아요.

근데 이제 또 문제가 생깁니다.
보면

interface MyComponentProps {
  title: string; // title은 문자열 타입
  count: number; // count는 숫자 타입
}

여기서 count가 number로 안 들어올 수도 있잖아요,
이 컴포넌트 여러군데서 재활용할건데
그럼 어떻게하냐?

interface MyComponentProps {
  title: string; // title은 문자열 타입
  count: React.ReactNode; // React.ReactNode는 렌더링 가능한 모든 값.![](https://velog.velcdn.com/images/chansoo/post/2a274377-44e6-4e0d-887f-1cbb75d4e14d/image.webp)

}

이러면 count에 골고루 받을수 있습니다.
ReactNode 설명 주석 달아두긴 했는데 이것도 직접한번 까보자고요

    type ReactNode =
        | ReactElement
        | string
        | number
        | Iterable<ReactNode>
        | ReactPortal
        | boolean
        | null
        | undefined
        | DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES[
            keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES
        ];

예, 다 넣을수 있어요~ 그럼 이제 매번 컴포넌트 마다 인터페이스 구현해서 저렇게 맨날 써줄꺼냐?
그럼 굉장히 굉장히 굉장히! 힘듭니다.
많아지면 감당이 안되요 감당이...

그래서 그냥 type만 모아둘 수 있게 디렉토리 하나 만드시고

export interface ChildrenType {
  children: React.ReactNode;
}

이런식으로 ts 파일 하나 만들고 다른곳에서 import해서 가져다가 쓰면 매우 편합니다.
예? 그럼 인터페이스마다 React.ReactNode 이거 넣어줘야되냐고요?

import {ChildrenType} from './common';

export interface LinkProps extends ChildrenType{
  to: string;
  className?: string;
}

자 이렇게 필요한 컴포넌트의 Props는 자체 정의하고 children은 이미 만들어둔 React.ReactNode, import해서 가져다 또 확장해서 쓰세요.

쉽죠? 예? Antd같은 UI 라이브러리 가져다 쓸건데 어쩌냐구요?

import { Button as AntdButton, ButtonProps as AntdButtonProps } from 'antd';

예 Antd는 Props가 또 따로 다 내장되어 있더라구요
한번 또 까서 봐야죠?

export interface BaseButtonProps {
    type?: ButtonType;
    icon?: React.ReactNode;
    iconPosition?: 'start' | 'end';
    shape?: ButtonShape;
    size?: SizeType;
    disabled?: boolean;
    loading?: boolean | {
        delay?: number;
    };
    prefixCls?: string;
    className?: string;
    rootClassName?: string;
    ghost?: boolean;
    danger?: boolean;
    block?: boolean;
    children?: React.ReactNode;
    [key: `data-${string}`]: string;
    classNames?: {
        icon: string;
    };
    styles?: {
        icon: React.CSSProperties;
    };
}
type MergedHTMLAttributes = Omit<React.HTMLAttributes<HTMLElement> & React.ButtonHTMLAttributes<HTMLElement> & React.AnchorHTMLAttributes<HTMLElement>, 'type'>;
export interface ButtonProps extends BaseButtonProps, MergedHTMLAttributes {
    href?: string;
    htmlType?: ButtonHTMLType;
    autoInsertSpace?: boolean;
}

children?: React.ReactNode; 아주 잘 정의되어 있네요.
네 그냥 왠만하면 antd처럼 지원하는 라이브러리 쓰세요.

처음에 할때 interface 하나씩 명시적으로 이것 저것 작성하고 재사용도 생각하면서 진짜 시간 많이 들었는데

네... 하다보니 답답해서 그냥 interface도 따로빼서 재사용하자 했는데 되네요

의미있는 결과가 되었으니 기분은 좋습니다.
어떻게 하면 꼼꼼히 할까? 라는 생각보다 어떻게 하면 좀 더 쉽게 할까? 라는 생각을 하는 순간 꼼꼼히 되는 것 같네요.

많은 도움이 되시길 바랍니다!

profile
Beyond the Best

0개의 댓글