TypeScript 를 사용하면 JavaScript의 불편함을 해결해줄 수 있고, IDE를 더욱 적극적으로 활용 할 수 있게 해줍니다. 특히, 리액트 컴포넌트를 TypeScript 로 작성하면, PropTypes 를 완전히 대체 할 수 있고 훨씬 유용하고 편합니다.

아직 TypeScript 를 사용해본적이 없다면, 타입스크립트 기초 연습 블로그 포스트를 한번 훑어보는 것을 권장드립니다. TypeScript를 완벽하게 숙지하고 있지 않아도, 이 강의를 진행하는 것에는 큰 지장은 없습니다. 한번 그냥 따라해보세요. 사실 TypeScript 별거 없습니다.

TypeScript 환경 설정

자, 우리가 만든 Storybook 프로젝트에 TypeScript 환경 설정을 해봅시다!

우선 다음 패키지들을 설치해주세요.

yarn add --dev babel-preset-react-app react-docgen-typescript-loader typescript
# 또는 npm install --save-dev babel-preset-react-app react-docgen-typescript-loader typescript

babel-preset-react-app는 create-react-app 에서 사용하는 babel preset 과 동일합니다. TypeScript를 적용 할 때 우리는 babel-loader 를 사용 할 것입니다. 참고로 babel-loader는 Storybook 프로젝트에 이미 설치가 되어있습니다.

react-docgen-typescript-loader는 컴포넌트의 props 에서 사용된 TypeScript 타입들을 추출하여 문서로 만들어주는 도구입니다. 우리가 이전에 PropTypes 를 작성하여 DocsPage 에서 보여줬었던 것 처럼 말이지요.

typescript는 프로젝트에서 TypeScript를 사용하기 위하여 필수적으로 설치해야하는 패키지입니다.

설치하고 나서 프로젝트 루트 디렉터리에 tsconfig.json 파일을 작성해주세요.

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react"
  },
  "include": ["src"]
}

tsconfig.json은 TypeScript 컴파일러의 환경설정을 정의하는 파일입니다. 위 설정은 CRA의 TypeScript 템플릿으로 새 프로젝트를 만들었을 때 사용하는 tsconfig.json 와 동일합니다.

이제 webpack 설정을 커스터마이징 해 주어야 합니다. .storybook 경로에 webpack.config.js 파일을 생성하면 Storybook의 기본 webpack 설정을 커스터마이징하여 사용 할 수 있습니다. (참고 링크)

.storybook 경로에 webpack.config.js 파일을 만들어서 다음과 같이 코드를 작성해주세요.

.storybook/webpack.config.js

module.exports = ({ config, mode }) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve('babel-loader'),
        options: {
          presets: [['react-app', { flow: false, typescript: true }]]
        }
      },
      require.resolve('react-docgen-typescript-loader')
    ]
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
};

ts 및 tsx 확장자에 대하여 babel-loader 와 react-docgen-typescript-loader 를 사용하도록 설정해주었습니다.

그 다음에는 .storybook/config.js 파일을 열어서 tsx 파일도 Storybook에서 처리하도록 코드를 수정해주세요.

.storybook/config.js

import { configure } from '@storybook/react';

configure(require.context('../src', true, /\.stories\.(js|mdx|tsx)$/), module);

마지막으로, src 디렉터리에 typings.d.ts 파일을 만들어서 다음과 같이 입력을 해주세요.

src/typings.d.ts

declare module '*.mdx';

이 파일의 용도는 우리가 추후 ts 파일에서 mdx 확장자로 이루어진 파일을 불러오게 될 때 모듈이 없다는 에러를 방지하는 것 입니다.

이제 TypeScript 환경 설정이 끝났습니다.

@storybook/preset-typescript 를 사용하면 이 작업을 더욱 간소화 할 수도 있습니다. 하지만, babel-loader를 사용하고자 한다면 preset 기능을 쓸 수는 없습니다. 우리는 나중에 아이콘을 리액트 컴포넌트 형태로 사용하기 위하여 babel을 사용 할 것이므로, 직접 TypeScript환경을 직접 설정하는 방식으로 진행하겠습니다.

TypeScript로 컴포넌트 작성하기

이제, 기존에 작성했던 Hello.js 컴포넌트의 파일이름을 Hello.tsx 로 변경해주세요. TypeScript를 사용하는 리액트 컴포넌트의 확장자는 .tsx 입니다.

그리고 나서 컴포넌트 파일에서 PropTypes 를 제거하고 TypeScript 로 props 의 타입을 명시해주겠습니다.

src/Hello/Hello.tsx

import React from 'react';

type HelloProps = {
  /** 보여주고 싶은 이름 */
  name: string;
  /** 이 값을 `true` 로 설정하면 h1 태그로 렌더링합니다. */
  big?: boolean;
  /** Hello 버튼 누를 때 호출 할 함수 */
  onHello?: () => void;
  /** Bye 버튼 누를 때 호출 할 함수 */
  onBye?: () => void;
};

/**
 * 안녕하세요 라고 보여주고 싶을 땐 `Hello` 컴포넌트를 사용하세요.
 *
 * - `big` 값을 `true`로 설정하면 **크게** 나타납니다.
 * - `onHello` 와 `onBye` props로 설정하여 버튼이 클릭했을 때 호출 할 함수를 지정 할 수 있습니다.
 */
const Hello = ({ name, big, onHello, onBye }: HelloProps) => {
  return (
    <div>
      {big ? <h1>안녕하세요, {name}!</h1> : <p>안녕하세요, {name}!</p>}
      <div>
        <button onClick={onHello}>Hello</button>
        <button onClick={onBye}>Bye</button>
      </div>
    </div>
  );
};

Hello.defaultProps = {
  big: false
};

export default Hello;

TypeScript를 사용하여 컴포넌트의 props 의 타입을 선언 할 때에는 Type Alias 또는 Interface 를 사용합니다. props 의 타입을 명시 할 때 둘 중 아무거나 써도 상관 없습니다. 여기서 ? 표시는 해당 props 는 생략을 해도 된다는 것을 의미합니다.

타입을 선언하고 나서는 props 가 해당 타입이란것을 명시하기 위하여 파라미터쪽에 다음과 같이 입력하면 됩니다.

const Hello = ({ name, big, onHello, onBye }: HelloProps) => {

그 다음엔, Hello.stories.js 파일의 이름을 Hello.stories.tsx 로 변경해주세요. 파일 내용은 바꿀 필요 없습니다.

이제, Storybook을 종료 후 다시 켜주세요.

Storybook 서버가 구동 될 때 오류가 나지 않았고, Storybook 페이지가 제대로 보여지는 것을 확인해보세요.

여기까지 잘 따라오셨으면, 이제 여러분들은 Storybook을 활용하는 기본적인 방법을 모두 숙지하신 겁니다. 이제 디자인 시스템을 만들어볼 준비가 끝났습니다. 다음 강의, 확인해주세요~