React, TypeScript 및 Storybook을 사용하여 구성 요소 디자인 시스템 만들기

디자인 시스템을 만드는 것은 프로젝트 작업을 할 때 매우 유용합니다. 디자이너라면 이미 개념에 익숙할 것입니다. 브랜드 색상, 글꼴 크기 및 콘텐츠 형식 등이 포함된 디자인 파일이 있는 경우... 개발 측면에서도 동일하게 적용됩니다. 프런트엔드가 디자인 파일과 일치하는지 확인해야 하기 때문입니다.

Tailwindcss 또는 Bootstrap과 같은 CSS 프레임워크를 사용해 본 사람들에게는 이러한 프레임워크가 자체 디자인 시스템을 고수하기 때문에 디자인 시스템으로 작업한다는 아이디어가 새로운 것이 아닙니다. Storybook은 기본적으로 개발자가 프런트엔드에서 자신만의 구성 요소 디자인 시스템을 만들 수 있는 방법을 제공합니다. 구성 요소를 개별적으로 볼 수 있고 모든 구성 요소에 대한 문서 및 테스트를 생성할 수 있기 때문에 이것은 매우 좋습니다.

예를 들어 고객에게 Storybook의 디자인 시스템을 보여주면 모든 구성 요소가 어떻게 보이는지 볼 수 있습니다. 다른 버전을 볼 수 있도록 배경색 및 글꼴 크기와 같은 일부 값을 변경할 수도 있습니다. 디자이너의 경우 코드에서 자신의 디자인이 어떻게 보이는지 볼 수 있고 애니메이션이 있으면 그것도 볼 수 있습니다.

프로젝트 설정



Storybook으로 보일러플레이트 React 프로젝트를 설정하는 것으로 시작하겠습니다. 디렉터리로 이동하여 명령줄 도구를 엽니다. 아래 코드를 복사하여 명령줄에 붙여넣어 프로젝트를 설정합니다.

npx create-react-app my-app --template typescript
cd my-app
npx sb init


이제 두 개의 실행 스크립트가 있어야 합니다. 아래 두 스크립트를 모두 실행하여 서버를 시작하십시오.

# Starts the React Application
npm run start



# Starts the Storybook component design system
npm run storybook


Storybook 구성 요소 만들기



웹 브라우저에서 열린 Storybook 개발 페이지로 이동하면 작동 방식에 대한 느낌을 얻을 수 있도록 몇 가지 예제 구성 요소가 제공되어야 합니다. 이제 Storybook을 사용하여 구성 요소를 만드는 것이 어떤 것인지 확인할 수 있도록 UI 구성 요소를 만들어 보겠습니다.

먼저 Hero라는 폴더를 만들고 src 안에 있는 stories 폴더 안에 넣습니다. 이제 3개의 파일을 만들어 Hero 폴더 안에 넣습니다. Hero.css , Hero.stories.tsxHero.tsx 파일을 생성합니다.

아래 코드를 복사하여 해당 파일에 붙여넣습니다.
src/stories/Hero/Hero.css
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@400;500;700&display=swap');

.hero {
    background: rgb(236, 236, 236);

    border: 1rem solid rgb(220, 220, 220);

    max-width: 50rem;

    width: 100%;

    border-radius: 1rem;
}

.hero-content {
    width: 100%;

    display: flex;

    flex-flow: column nowrap;
}

.hero-content h1 {
    font-family: 'Quicksand', sans-serif;

    color: #000000;

    text-transform: uppercase;

    text-align: center;
}

.hero-content img {
    max-width: 50rem;

    width: 100%;
}

.hero-content p {
    font-family: 'Quicksand', sans-serif;

    color: #000000;

    padding: 0.5rem;
}

#preloader {
    width: 50rem;

    height: 50rem;
}

#loader {
    display: block;

    position: relative;

    left: 50%;

    top: 50%;

    width: 150px;

    height: 150px;

    margin: -75px 0 0 -75px;

    border-radius: 50%;

    border: 3px solid transparent;

    border-top-color: #9370db;

    -webkit-animation: spin 2s linear infinite;

    animation: spin 2s linear infinite;
}

#loader:before {
    content: '';

    position: absolute;

    top: 5px;

    left: 5px;

    right: 5px;

    bottom: 5px;

    border-radius: 50%;

    border: 3px solid transparent;

    border-top-color: #ba55d3;

    -webkit-animation: spin 3s linear infinite;

    animation: spin 3s linear infinite;
}

#loader:after {
    content: '';

    position: absolute;

    top: 15px;

    left: 15px;

    right: 15px;

    bottom: 15px;

    border-radius: 50%;

    border: 3px solid transparent;

    border-top-color: #ff00ff;

    -webkit-animation: spin 1.5s linear infinite;

    animation: spin 1.5s linear infinite;
}

@-webkit-keyframes spin {
    0% {
        -webkit-transform: rotate(0deg);

        -ms-transform: rotate(0deg);

        transform: rotate(0deg);
    }

    100% {
        -webkit-transform: rotate(360deg);

        -ms-transform: rotate(360deg);

        transform: rotate(360deg);
    }
}

@keyframes spin {
    0% {
        -webkit-transform: rotate(0deg);

        -ms-transform: rotate(0deg);

        transform: rotate(0deg);
    }

    100% {
        -webkit-transform: rotate(360deg);

        -ms-transform: rotate(360deg);

        transform: rotate(360deg);
    }
}

src/stories/Hero/Hero.stories.tsx
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Hero } from './Hero';

export default {
    title: 'Hero/Hero',

    component: Hero,
} as ComponentMeta<typeof Hero>;

const Template: ComponentStory<typeof Hero> = (args) => <Hero {...args} />;

export const Primary = Template.bind({});

Primary.args = {
    id: 1,

    title: 'Mountain',

    img: 'https://images.unsplash.com/photo-1464278533981-50106e6176b1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2274&q=80',

    content:
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis tellus et libero feugiat, vitae dignissim augue pulvinar.',
};

src/stories/Hero/Hero.tsx
import './Hero.css';

interface Heroprops {
    id: number;

    title: string;

    img: string;

    content: string;

    loading: boolean;
}

export const Hero = ({ id, title, img, content, loading, ...props }: Heroprops) => {
    return (
        <>
            <div className="hero">
                {loading ? (
                    <div id="preloader">
                        <div id="loader"></div>
                    </div>
                ) : (
                    <div className="hero-content">
                        <h1>{title}</h1>

                        <img src={img} alt={title} />

                        <p>{content}</p>
                    </div>
                )}
            </div>
        </>
    );
};

export default Hero;

App.css
파일 내부의 모든 코드를 아래 코드로 바꿉니다.

*,
*::before,
*::after {
    padding: 0;

    margin: 0;

    box-sizing: border-box;
}

html {
    font-size: 16px;
}

body {
    font-size: 1rem;

    font-family: 'Quicksand', sans-serif;

    color: #000000;

    background-color: #222;
}

.container {
    margin: 0 auto;

    width: 100%;

    max-width: 50rem;
}

App.tsx
파일 내부의 모든 코드를 아래 코드로 바꿉니다.

import Hero from '../src/stories/Hero/Hero';

import './App.css';

const App = () => {
    return (
        <>
            <div className="container">
                <Hero
                    id={1}
                    title={'Mountain'}
                    img={
                        'https://images.unsplash.com/photo-1464278533981-50106e6176b1?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2274&q=80'
                    }
                    content={
                        'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis mollis tellus et libero feugiat, vitae dignissim augue pulvinar.'
                    }
                    // Change this value to true to see the loading animation

                    loading={false}
                />
            </div>
        </>
    );
};

export default App;


React 앱과 Storybook 서버용 서버를 다시 로드하면 이제 이미지가 있는 Hero 구성 요소가 표시됩니다. 어떤 이유로든 이미지가 깨지면 이미지 URL을 다른 것으로 변경할 수 있습니다. App.tsxHero.stories.tsx 파일에서 이 작업을 수행합니다.

Hero 구성 요소에는 Storybook에서 몇 가지 사용자 지정 가능한 옵션이 있습니다. ID, 제목, img, 콘텐츠 및 로딩 상태까지 변경할 수 있습니다.

마지막 생각들



Storybook 사용에 대한 간략한 소개입니다. 자세한 내용은 메인Storybook website을 참조하십시오.

좋은 웹페이지 즐겨찾기