티스토리 뷰
Storybook
storybook 공식에 나와 있는 예제를 따라하면 오류가 난다고 해서… 기본 예제를 그냥 복붙해서 가져다 쓰는 게 마음이 편하다.
Desciprtion
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; }
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.ts
나 index-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>
);
};
이 경우 아이콘 이미지가 있는 경로를 잘 파악해야 한다.
public 디렉토리 (src 폴더 외부에 있는 경우)
iconPath = '/icon-name.svg'
assets (src 폴더 내부에 있는경우)
React 컴포넌트처럼 써야한다.
강사님은 import 문제 때문에 assets 등 src 내부 폴더에 넣는 걸 선호하지는 않는다.
import icon from '../assets/icon.svg'; iconPath = icon // 이렇게 해줘야 출력된다
클라우드에서 권한을 public으로 놓고 export 해서 사용하는 방법
바로 url을 복사해서 넣으면 된다.
iconPath = 'https://via.placeholder.com/24'
클릭 이벤트의 경우 argTypes
onClick: {
action: 'clicked',
description: '버튼 클릭 시 호출될 함수'
}
React 최적화
Image CDN
이미지 최적화의 경우 이미지 CDN을 사용해서 변환을 거친 뒤 사용자에게 보여주는 방식으로 최적화를 할 수 있다.