[๐Ÿ’ป ์ฝ”๋“œ์Šคํ…Œ์ด์ธ  FE 44๊ธฐ]๋””์ž์ธ ์‹œ์Šคํ…œ ๊ตฌ์ถ•ํ•˜๊ธฐ ๊ณผ์ œ - 1

JiEunยท2023๋…„ 5์›” 25์ผ
0
post-thumbnail

โœ”๏ธ ์‹œ์ž‘

๊ธฐ์กด์— ๊ตฌํ˜„ํ•œ ์†”๋กœ ํ”„๋กœ์ ํŠธ์— Storybook์„ ์‚ฌ์šฉํ•ด ํ”„๋กœ์ ํŠธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋””์ž์ธ ์‹œ์Šคํ…œ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์„ ์ง„ํ–‰ํ–ˆ๋‹ค.


๐Ÿ“ ์ง„ํ–‰ ํ˜„ํ™ฉ

1. UI ์ธ๋ฒคํ† ๋ฆฌ ์ž‘์„ฑ ๋ฐ ๋ถ„์„

Storybook ๋จผ์ € ์„ค์น˜ํ•ด์•ผํ•˜์ง€๋งŒ ๊ทธ ๋ถ€๋ถ„์€ ์ด์ „ ํ•™์Šต์—์„œ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰๋˜์–ด ๊ตณ์ด ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค.

๊ธฐ์กด์˜ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ ์ค‘์ธ ๋ชจ๋“  UI ์š”์†Œ๋“ค์„ ์ •๋ฆฌํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค.
ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ๋œ ๋ชจ๋“  UI๋ฅผ ์บก์ฒ˜ํ•˜๊ณ  ์ด๋ฅผ ํ•œ ๊ณณ์— ๋ชจ์•„๋‘๋ฉด ๋œ๋‹ค.

์•ˆ๋‚ด ์‚ฌํ•ญ์—์„œ๋Š” Google Slides๋˜๋Š” Keynote ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๋‚˜์™€ ์žˆ์–ด์„œ Google Slides๋ฅผ ํƒํ–ˆ๋‹ค.
๊ทผ๋ฐ ์ด๋ ‡๊ฒŒ ํ•˜๋Š”๊ฒŒ ๋งž๋Š”์ง€ ์˜๋ฌธ์ด ๋“ ๋‹ค...


์ด๋Ÿฐ ์‹์œผ๋กœ ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ž‘์€ ์˜์—ญ๊นŒ์ง€ ์บก์ฒ˜ํ•ด ๋ฐฐ์น˜ํ•ด ๋‘์—ˆ๋‹ค.

Header, CardItem, Bookmark, Madal, ProductTablist ์ •๋„๋กœ ๋ถ„๋ฆฌํ•ด ๋‘์—ˆ๋‹ค.

2. Storybook์„ ์‚ฌ์šฉํ•ด ์ปดํฌ๋„ŒํŠธ UI ๋ฌธ์„œํ™”

์‚ฌ์‹ค ํ”ผ๊ทธ๋งˆ ์ž‘์—…๊ณผ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ์„ ํ•ด์•ผํ•˜์ง€๋งŒ...
์ด๋ฏธ ์†”๋กœ ํ”„๋กœ์ ํŠธ์—์„œ ์ง„ํ–‰ํ–ˆ๋˜ ๋ถ€๋ถ„์ด๊ธฐ๋„ ํ•˜๊ณ  ๊ธ€๋กœ๋งŒ ๋‚˜์™€ ์žˆ์–ด.. ์ดํ•ด๊ฐ€ ๊ฐ€์งˆ ์•Š๋Š”๋‹ค..

๋‚˜ ์ฒ˜๋Ÿผ ํ—ท๊ฐˆ๋ฆฌ๋Š” ๋ถ„์ด ๋ฌธ์˜ํ•˜์…จ๋Š”๋ฐ
์ƒˆ๋กœ์šด ๋””์ž์ธ, ์Šคํ† ๋ฆฌ๋ฅผ common ํด๋”์— ๋„ฃ์–ด ๊ด€๋ฆฌํ•˜๊ณ  ์ดํ›„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค๊ณ  ํ•˜์…จ๋‹ค.

๋””์ž์ธ ์‹œ์Šคํ…œ์„ ์ž‘์—…ํ•  ๋•Œ ์ž‘์€ ๋‹จ์œ„์—์„œ ํฐ ๋‹จ์œ„๋กœ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

๋‚œ ๋ญฃ๋„ ๋ชจ๋ฅด๊ณ  ๋ถ๋งˆํฌ ๋ฒ„ํŠผ ๋ถ€ํ„ฐ ์ง„ํ–‰ ํ–ˆ๋˜ ๊ฑฐ ๊ฐ™๋‹ค.

primary false/true๋ฅผ ์ค˜์„œ ํด๋ฆญ, ํด๋ฆญ ํ›„ ์˜ ์Šคํƒ€์ผ์„ ๊ฐ๊ฐ ๋ถ„๋ฆฌ์‹œ์ผฐ๋‹ค. (์ด๊ฒŒ ๋งž๋‚˜..?ใ…Ž..)

๐Ÿ’ป BookmarkBtn.js

import { AiFillStar } from "react-icons/ai";
import styled from "styled-components"

export const BookmarkBtnComponent = styled.button`
  border: 0;
  background-color: transparent;
  font-size: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;

  &:active .AiFillStar, &:focus .AiFillStar {
    color: #FFD361;
  }

  >.AiFillStar {
    filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3));
    color: ${(props) => (props.primary ? "#FFD361" : "#dfdfdf")};
  }
      
`
function BookMarkBtn({ primary, handleBookmarkToggle, isBookMark }) {
  return (
    <BookmarkBtnComponent primary={primary} onClick={handleBookmarkToggle}>
      <AiFillStar className="AiFillStar" />
    </BookmarkBtnComponent>
  );
}

export default BookMarkBtn;

color: ${(props) => (props.primary ? "#FFD361" : "#dfdfdf")};์—์„œ
props.primary ์œ ๋ฌด์— ๋”ฐ๋ผ์„œ ์ƒ‰์ƒ์„ ๋‹ค๋ฅด๊ฒŒ ์ง€์ •ํ•ด ์ฃผ์—ˆ๋‹ค.

Primary, Secondary์„ ๋”ฐ๋กœ ์ง€์ •ํ•ด๋„ ๋˜๊ธดํ•˜์ง€๋งŒ props๋ฅผ ์ด์šฉํ•˜๋ฉด ์ข€ ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž‘์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ป BookmarkBtn.stories.js

import BookmarkBtn from './BookmarkBtn';
export default {
  title: 'Atoms/BookmarkBtn', // ์Šคํ† ๋ฆฌ ๋ถ„๋ฅ˜ ๋ฐ ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„
  component: BookmarkBtn, // ํ…Œ์ŠคํŠธํ•  ์ปดํฌ๋„ŒํŠธ(Button)
}

export const Primary = {
  args: {
    primary: true,
  },
};

export const Secondary = {
  args: {
    primary: false,
  },
};

์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š”๋ฐ ๋‹ต์€ ์—†๋‹ค๊ณ  ๋ณธ๋‹ค.

title: 'Atoms/BookmarkBtn'๋Š”

์Šคํ† ๋ฆฌ ๋ถ์—์„œ ์Šคํ† ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ถ„๋ฆฌํ•  ๊ฑด์ง€, ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์€ ๋ฌด์—‡์œผ๋กœ ์ง€์„์ง€ ์ž‘์„ฑํ•œ๋‹ค.

ํ•˜๋‹จ์— Primary, Secondary์— ๋Œ€ํ•œ ์†์„ฑ์„ ์ž‘์„ฑํ•ด ์ฃผ๋ฉด ์ž๋™์œผ๋กœ 2๊ฐ€์ง€ ๋ฒ„์ „์˜ ์Šคํƒ€์ผ์ด ์ƒ๊ธด๋‹ค.

์ด๋ ‡๊ฒŒ ์ž‘์—…ํ•˜๊ณ  ๋“ค์—ˆ๋˜ ์ƒ๊ฐ์ด ์ž‘์€ ๋‹จ์œ„๋กœ ์ž‘์—…์ด ์ด๋ฃจ์–ด์ ธ์•ผ ํ•œ๋‹ค๋ฉด
์•„์ด์ฝ˜, ๋ฒ„ํŠผ, ํ…์ŠคํŠธ ๋“ฑ ๋ถ€ํ„ฐ ์ง„ํ–‰๋˜์–ด์•ผ ํ•˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๊ณ 

๋ ˆํผ๋Ÿฐ์Šค ์Šคํ† ๋ฆฌ๋ถ๋„ ๊ทธ๋ ‡๊ฒŒ ๋‚˜์™€ ์žˆ์—ˆ๋‹ค.

๋ถ€๋žด๋ถ€๋žด ์•„์ด์ฝ˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ๋‹ค.
์ฝ”๋“œ๋Š” ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ•ด ์ง„ํ–‰ํ•ด ์ฃผ์—ˆ๋‹ค.

header์˜ nav๋ถ€๋ถ„๊ณผ ๋ถ๋งˆํฌ ๋ฒ„ํŠผ์„ ํ‘œ์‹œํ–ˆ๋‹ค. ์ƒ๊ฐ๋ณด๋‹ค ์•„์ด์ฝ˜์ด ์•„์ง?์€ ๋งŽ์ง€ ์•Š์€๊ฑฐ ๊ฐ™๋‹ค.

๐Ÿ’ป Icon.js

import * as Icons from "react-icons/ai";
import { styled } from 'styled-components';

export const iconNames = [
  'AiOutlineMenu',
  'AiOutlineGift',
  'AiOutlineStar',
  'AiFillStar',
];

export const Icon = ({ icon, color, size, ...rest }) => {
  const FeatherIcon = styled(Icons[icon])`
    color: ${(props) => props.color || "#000"};
    font-size: ${(props) => props.size || "2rem" };
  `;
  return <FeatherIcon size={size} color={color} {...rest} />;
};

import * as Icons from "react-icons/ai" ์ด๋ถ€๋ถ„์ด
import * as Icons from 'react-feather'์œผ๋กœ ๋˜์–ด ์žˆ์—ˆ๋‹ค.

react-feather ๊ตฌ๊ธ€๋ง ํ•ด๋ณด๋‹ˆ ์•„์ด์ฝ˜์ด์˜€๋‹ค.
๊ทธ๋Ÿผ ์ด๋ถ€๋ถ„์€ ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์•„์ด์ฝ˜์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  iconNamse์˜ ๋ฆฌ์ŠคํŠธ๋„ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜๋ฉฐ ๋˜์ง€ ์•Š์„๊นŒ ํ–ˆ๋‹ค. ๋‹คํ–‰ํžˆ ์ž˜ ๋…ธ์ถœ ๋˜์—ˆ๋‹ค.

๐Ÿ’ป Icon.stories.js ์ค‘์—์„œ..

.
.
.
export const Item = (args) => <Icon {...args} />; 
Item.argTypes = {
  icon: {
    options: iconNames, // ์•„์ด์ฝ˜ ์ด๋ฆ„ ์˜ต์…˜ ์„ค์ •
    control: { type: 'select' }, // ์ปจํŠธ๋กค ํƒ€์ž…์„ select๋กœ ์„ค์ •
  },

์Šคํ† ๋ฆฌ ํŒŒ์ผ ๊ฐ™์€ ๊ฒฝ์šฐ ๊ธฐ์กด ๋ ˆํผ๋Ÿฐ์Šค ์ฝ”๋“œ๋ฅผ ๊ฑฐ์˜ ๋ณต์‚ฌ/๋ถ™์ด๊ธฐ ํ•œ ์ˆ˜์ค€์ด๋ผ...
๋Œ€์‹  iconNamse๋ฆฌ์ŠคํŠธ๋Š” ์ด๋Ÿฐ์‹์œผ๋กœ options: iconNames์œผ๋กœ ์ง€์ •ํ•ด ์ฃผ๊ณ  ํƒ€์ž…์„ select๋กœ ํ•ด์ฃผ๋ฉด ๋˜๋Š”๊ฑฐ ๊ฐ™๋‹ค.


โœ๏ธ ๋งˆ์น˜๋ฉฐ

storybook์— ๋Œ€ํ•ด ์•„์ง ๋ฏธ์ˆ™ํ•ด ๊ตฌ๊ธ€๋ง์„ ํ•ด๋„ ์ƒ๊ฐ ๋ณด๋‹ค ์ž˜ ๋‚˜์˜ค์ง€ ์•Š์•˜๋‹ค.
๊ทธ๋ž˜์„œ ์ดˆ๋ฐ˜์— ์„ธํŒ…ํ•˜๋Š”๋ฐ๋งŒ 2์‹œ๊ฐ„? ์ •๋„ ๊ฑธ๋ ธ๋‹ค.

๋˜ํ•œ ๋‚ด๊ฐ€ ํ•˜๊ณ ์žˆ๋Š” ์ˆœ์„œ๊ฐ€ ๋งž๋Š”์ง€, ์ด๋Ÿฐ์‹์œผ๋กœ ํ•˜๋Š”๊ฒŒ ๋งž๋Š”์ง€? ์ด๋Ÿฐ๊ฑธ ์•Œ ์ˆ˜ ์—†์–ด
๊ณ ๋ฏผํ•˜๋Š”๋ฐ ์‹œ๊ฐ„ ๋‚ญ๋น„๋ฅผ ํ•˜๊ธฐ๋„ ํ–ˆ๋‹ค.

๋‹คํ–‰ํžˆ ์ดํ‹€ ๋™์•ˆ ์ง„ํ–‰ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.
์Šคํ† ๋ฆฌ๋ถ์— ๋งŒ๋“ค์–ด์ง„ ์ปคํฌ๋„ŒํŠธ๋กœ ์†”๋กœํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด ๋ณด๋ผ๊ณ  ํ–ˆ์ง€๋งŒ...

๊ทธ๊ฑด ๋˜ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”๊ฑฐ์ง€...? ์•„์ง ๊ฐˆ ๊ธธ์ด ๋ฉ€๋‹ค...

profile
๐Ÿ’ป ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๋ชฉํ‘œ๋กœ ์„ฑ์žฅ ์ค‘! (์•Œ์•„๋ดค๋˜ ๋‚ด์šฉ ๋“ฑ์„ ์ •๋ฆฌํ•˜๊ธฐ)

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