[React] Storybook

XIXIยท2022๋…„ 6์›” 27์ผ
0

๋Œ€๊ตฌAI์Šค์ฟจ

๋ชฉ๋ก ๋ณด๊ธฐ
43/57
post-thumbnail

๐ŸŒฑ React

โœ๏ธ๊ฐœ๋… ์žฅ์ 

  • ํŽ˜์ด์Šค ๊ฐœ๋ฐœ์— ์‚ฌ์šฉํ•œ ๊ธฐ์ˆ 

  • ๊ณต๊ฐœ ์†Œํ”„ํŠธ์›จ์–ด

  • ํ™”๋ฉด ์ถœ๋ ฅ์— ํŠนํ™”๋œ ํ”„๋ ˆ์ž„์›Œํฌ

  • ์ปดํฌ๋„ŒํŠธ๋ฅผ ์กฐ๋ฆฝํ•˜์—ฌ ํ™”๋ฉด ๊ตฌ์„ฑ

  • ๊ฒŒ์ž„ ์—”์ง„ ์›๋ฆฌ๋ฅผ ๋„์ž…ํ•˜์—ฌ ํ™”๋ฉด ์ถœ๋ ฅ ์†๋„๊ฐ€ ๋น ๋ฆ„(Virtual Dom)

โœ๏ธ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์ˆซ์ž ์ฆ๊ฐ€ํ•˜๊ธฐ

  • state ์‚ฌ์šฉ
  • ํŠน์„ฑ: ๋น„๋™๊ธฐ
function App() {
  let counter = 0;
  const increase = ()=>{
    counter = counter + 1;
  }
  return (
    <div>
      <Box1 name="ํ•œ๊ตญ"/>
      <Box1 name="๋ฏธ๊ตญ"/>
      <Box1 name="์ค‘๊ตญ"/>

      <div>{counter}</div>
      <button onClick={increase}>ํด๋ฆญ!</button>
    </div>
  );
}

๐ŸŒฑ ์—์–ด๋น„์•ค๋น„ ๋””์ž์ธ ์‹œ์Šคํ…œ ๋”ฐ๋ผํ•˜๊ธฐ

โœ๏ธ๊ฐœ๋…

  • ์Šคํ† ๋ฆฌ๋ถ(StoryBook): UI ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ ๋„๊ตฌ
  • ๋ฐ๋ชจ์šฉ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ ๋„์›€์„ ์ฃผ๊ณ  ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋  ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŒ€์›๋“ค๊ณผ ํŽธํ•˜๊ฒŒ ๊ณต์œ ํ•˜๋Š” ๋„๊ตฌ๋กœ ํ™œ์šฉ
  • ๊ตฌ์„ฑ ๋‹จ์œ„๋Š” ์Šคํ† ๋ฆฌ
  • ํ•˜๋‚˜์˜ UI ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณดํ†ต ํ•˜๋‚˜ ์ด์ƒ Story๋ฅผ ๊ฐ€์ง

โœ๏ธ์žฅ์ 

  • ๋ณต์žกํ•œ ๋กœ์ง ์—†์ด ๋…๋ฆฝ์ ์ธ ํ™˜๊ฒฝ์—์„œ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ ๊ฐ€๋Šฅ
  • ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•œ ์ปดํฌ๋„ŒํŠธ๋“ค์„ story์—์„œ ์กฐํ•ฉํ•ด ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Œ
  • ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋ฌธ์„œํ™” ํ•  ์ˆ˜๋„ ์žˆ๊ณ  ๋””์ž์ธ ์‹œ์Šคํ…œ์— ์ ์šฉํ•ด ํ”ผ๊ทธ๋งˆ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค๊ณผ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Œ

โœ๏ธ์‹ค์Šต ์ด๋ก 

โ€ข npx create-react-app ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ
โ€ข cd ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ
โ€ข npx โ€“p storybook sb init
โ€ข npm run storybook

ํด๋”๊ตฌ์กฐ

โ€ข main.js : stories๋ฅผ ์œ„ํ•œ config ์„ค์ •
โ€ข preview.js : ๋ชจ๋“  story๋“ค์— ๊ธ€๋กœ๋ฒŒํ•˜๊ฒŒ ์ ์šฉ๋  ํฌ๋งท ์„ธํŒ…

์Šคํ† ๋ฆฌ ๊ธฐ๋ณธ๊ตฌ์กฐ

Export default {
  Title : ์Šคํ† ๋ฆฌ๋ถ์— ์˜ฌ๋ฆด component ํด๋” ๊ณ„์ธต ๊ตฌ์กฐ,
  Component : ์Šคํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„
}

Export const ์Šคํ† ๋ฆฌ์ด๋ฆ„ = () => ํ•ด๋‹น์Šคํ† ๋ฆฌ์—์„œ ํ…Œ์ŠคํŠธํ•  ์ธ์ž๊ฐ€ ๋‹ด๊ธด ์ปดํฌ๋„ŒํŠธ

โœ๏ธ์‹ค์Šต

  1. componunt ํด๋” ์ƒ์„ฑ
  2. ํŒŒ์ผ์ƒ์„ฑ Text.jsx
import React, { Component } from 'react'
import PropTypes from "prop-types" ;

export function Text({children, color, italic, underline}) {
  const style = {
    color : color,
    fontStyle : italic? "italic" : "normal",
    textDecoration : underline? "underline" : "none"
  };
  return <span style={style}>{children}</span>
};

Text.propTypes = {
  children: PropTypes.string.isRequired,
  color : PropTypes.string,
  italic : PropTypes.bool,
  underline : PropTypes.bool,
};

Text.defaultProps = {
  color : "black",
  italic : false,
  underline : false,
}
  1. ํŒŒ์ผ์ƒ์„ฑ Text.stories.js
import React, {Component} from "react";
import {Text} from "./Text";

export default {
  title : "Text",
  component : Text,
};

const TEST_TEXT = "Story Text Test";
export const Default = ()=><Text>{TEST_TEXT}</Text>;
export const Red = ()=><Text color="red">{TEST_TEXT}</Text>;
export const Italic = ()=><Text italic>{TEST_TEXT}</Text>;
export const Underline = ()=><Text underline>{TEST_TEXT}</Text>;

Storybook์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

โœ๏ธ์‹ค์Šต2

  1. component input.jsx ํŒŒ์ผ ์ƒ์„ฑ
import React, { Component } from "react";
import PropTypes from "prop-types";

class Input extends Component {
  constructor(props) {
    super(props);
    this.setRef = this.setRef.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    const { name, onChange } = this.props;
    if (onChange) {
      onChange(name, e.target.value);
    }
  }

  componentDidMount() {
    if (this.props.autoFocus) {
      this.ref.focus();
    }
  }

  componentDidUpdate() {
    if (this.props.autoFocus) {
      this.ref.focus();
    }
  }

  setRef(ref) {
    this.ref = ref;
  }

  render() {
    const { errorMessage, label, name, value, type, onFocus } = this.props;
    return (
      <label>
        {label}
        <input
          id={"input_${name}"}
          ref={this.setRef}
          onChange={this.handleChange}
          onFocus={onFocus}
          value={value}
          type={type}
        />
        {errorMessage && <span className="error">{errorMessage}</span>}
      </label>
    );
  }
}

Input.propTypes = {
  type: PropTypes.oneOf(["text", "number", "price"]),
  name: PropTypes.string.isRequired,
  value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  errorMessage: PropTypes.string,
  label: PropTypes.string,
  onChange: PropTypes.func,
  autoFocus: PropTypes.bool,
};

Input.defaultProps = {
  onChange: () => {},
  onFocus: () => {},
  autoFocus: false,
  type: "text",
};

export default Input;
  1. component input.stories.js
import React, { Component } from 'react';
import Input from "./Input";

export default{
    title : "Input",
    component : Input,
};

export const label = ()=><Input name="name" label="์ด๋ฆ„ :"></Input>;

๐Ÿƒ ์–ด๋ ค์› ๋˜ ์  or ํ•ด๊ฒฐ๋ชปํ•œ ๊ฒƒ

์ƒ์†Œํ•œ ๋‚ด์šฉ์ด๋ผ์„œ ์–ด๋ ต๊ฒŒ ๋Š๊ปด์ง.

๐Ÿ€ ํ•ด๊ฒฐ๋ฐฉ๋ฒ• ์ž‘์„ฑ

โœ๏ธ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐ์„ ํ–ˆ๋Š”๊ฐ€?
โœ๏ธ ์ด๋ ‡๊ฒŒ ์ดํ•ด๋ฅผ ํ–ˆ๋‹ค
โœ๏ธ ์–ด๋””๊นŒ์ง€ ์ดํ•ดํ–ˆ์ง€?
โœ๏ธ ๋‹ค์Œ์— ์‹œ๋„ํ•ด๋ณผ ๋ฐฉ๋ฒ•
์ „์ฒด ๊ณผ์ •๊ณผ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๋ณด๊ณ  ์ดํ•ดํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ ธ์•ผ๊ฒ ๋‹ค

๐ŸŒท ํ•™์Šต ์†Œ๊ฐ

Storybook์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ๋ณต์Šตํ•˜๋Š” ์‹œ๊ฐ„์ด ํ•„์š”ํ•œ ๊ฒƒ ๊ฐ™๋‹ค

profile
Life is experience:)

0๊ฐœ์˜ ๋Œ“๊ธ€