티스토리 뷰

카테고리 없음

2024.09.03(화) TIL

HOJJANG 2024. 9. 3. 22:44

Storybook

storybook 공식에 나와 있는 예제를 따라하면 오류가 난다고 해서… 기본 예제를 그냥 복붙해서 가져다 쓰는 게 마음이 편하다.

Desciprtion

  1. ButtonProps에 적힌 내용이 Description 열에 들어간다. 근데 응집도를 더 높이는 방법을 추천한다.

     export interface ButtonProps {
       /**
        * Is this the principal call to action on the page?
        */
       primary?: boolean;
       backgroundColor?: string;
       size?: 'small' | 'medium' | 'large';
       label: string;
       onClick?: () => void;
     }
    

image.png

argTypes 내부에 description 속성을 사용해서 Description을 만들 수 있다.

argTypes: {
  backgroundColor: { 
        control: 'color',
    description: 'What background color to use',
},

폴더 구조

강사분은 components와 stories를 따로 관리하는 걸 추천한다. 나중에 디자인 시스템을 배포할 때 스토리에 관한 정보는 별도로 배포할 필요가 없기 때문에, 패키지 용량만 커진다.

import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { Button } from './Button';

// 버튼 컴포넌트의 메타 정보를 나타낸다 
const meta = {
  title: 'Example/Button', // 경로
  component: Button,
  parameters: { 
      // 스토리북에서 컴포넌트를 보여주는 위치를 가운데로 설정한다
      // 'left'로 설정하면 왼쪽 정렬
    layout: 'centered',
  },
  tags: ['autodocs'], // 이게 있어야 docs가 만들어진다
  argTypes: {
    backgroundColor: { 
        control: 'color',
        description: 'What background color to use',
    },
  },
  args: { onClick: fn() },
} satisfies Meta<typeof Button>; // 메타 정보는 버튼이 받는 프로퍼티를 정의한다

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
  args: {
    primary: true,
    label: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    label: 'Button',
  },
};

export const Large: Story = {
  args: {
    size: 'large',
    label: 'Button',
  },
};

export const Small: Story = {
  args: {
    size: 'small',
    label: 'Button',
  },
};

폰트 적용

preview.tsindex-preview.html 을 만들어서 경로를 import 해줘야 폰트가 올바르게 적용된다.

// .storybook/preview.ts
import '@fontsource-variable/noto-sans';
import '../src/index.css';

확장성에 대한 고민

Label을 만들었는데 타입을 아래와 같이 둬야 하는 것 아닌가?

type Label = 'email' | 'password';

하지만 이럴 경우 디자인 시스템을 배포해서 사용할 때 확장성이 매우 떨어질 수 있다. 이 경우 한 단계 위에서 감싸서 사용하는 방법을 생각해볼 수 있을 것 같다!

IconButton을 만들 때 children에 대해서

IconButton을 만들때 children에 들어갈 것은 svg 등의 이미지 파일이다. 따라서 button 사이에 img 태그가 들어가게 된다.

<button>
    <img />
</button>

이렇게 컴포넌트를 만들게 될 경우 children 보다는 srcPath 등 이미지 경로를 넣어주는 방식도 사용할 수 있다.

import { MouseEvent } from 'react';

export interface IconButtonProps {
  iconPath: string;
  onClick: (event?: MouseEvent<HTMLButtonElement>) => void;
}

export const IconButton = ({ iconPath, onClick }: IconButtonProps) => {
  return (
    <button onClick={onClick}>
      <img src={iconPath} />
    </button>
  );
};

이 경우 아이콘 이미지가 있는 경로를 잘 파악해야 한다.

  1. public 디렉토리 (src 폴더 외부에 있는 경우)

     iconPath = '/icon-name.svg'
  2. assets (src 폴더 내부에 있는경우)

    React 컴포넌트처럼 써야한다.

    강사님은 import 문제 때문에 assets 등 src 내부 폴더에 넣는 걸 선호하지는 않는다.

     import icon from '../assets/icon.svg';
    
     iconPath = icon // 이렇게 해줘야 출력된다
  3. 클라우드에서 권한을 public으로 놓고 export 해서 사용하는 방법

    바로 url을 복사해서 넣으면 된다.

     iconPath = 'https://via.placeholder.com/24'

클릭 이벤트의 경우 argTypes

onClick: { 
    action: 'clicked', 
    description: '버튼 클릭 시 호출될 함수'
}

React 최적화

Image CDN

이미지 최적화의 경우 이미지 CDN을 사용해서 변환을 거친 뒤 사용자에게 보여주는 방식으로 최적화를 할 수 있다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함