๊ด€๋ฆฌ ๋ฉ”๋‰ด

Daily Front_Minhhk

[React] Custom Component ๋ณธ๋ฌธ

Code๊ฐœ๋ฐœ์ผ์ง€

[React] Custom Component

Minhhk 2022. 12. 22. 16:23
CDD, styled-Component , storybook, useRef

 

 

 

๐Ÿ“Œ Component Driven Development (CDD)

 

๋””์ž์ธ๊ณผ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋ถ€ํ„ฐ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฏธ๋ฆฌ ๋””์ž์ธํ•˜๊ณ  ๊ฐœ๋ฐœ

๋ ˆ๊ณ ์ฒ˜๋Ÿผ ์กฐ๋ฆฝํ•ด ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋ถ€ํ’ˆ ๋‹จ์œ„๋กœ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋‚˜๊ฐ€๋Š” ๊ฐœ๋ฐœ ์„ ์ง„ํ–‰

(์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„๋กœ ๋งŒ๋“ค์–ด ํŽ˜์ด์ง€๋ฅผ ์กฐ๋ฆฝํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ์‹์ธ ์ƒํ–ฅ์‹ ๊ฐœ๋ฐœ)

 

๐Ÿ“Œ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ(CSS Preprocessor)

 

CSS๊ฐ€ ๊ตฌ์กฐ์ ์œผ๋กœ ์ž‘์„ฑ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋„์›€์„ ์ฃผ๋Š” ๋„๊ตฌ

CSS ์ „์ฒ˜๋ฆฌ๊ธฐ ๋กœ๋Š” ์›น ์„œ๋ฒ„๊ฐ€ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ CSS ์ „์ฒ˜๋ฆฌ๊ธฐ์— ๋งž๋Š” Compiler๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ  ์ปดํŒŒ์ผ์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” CSS ๋ฌธ์„œ๋กœ ๋ณ€ํ™˜

 

Sass(Syntactically Awesome Style Sheets)

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ํŠน์ • ์†์„ฑ์„ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•ด ์ ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฐ˜๋ณต๋˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ๋ฒˆ์˜ ์„ ์–ธ์œผ๋กœ ์—ฌ๋Ÿฌ๊ณณ์— ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜์žˆ๋‹ค.

๐Ÿ‘‰ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ

์Šคํƒ€์ผ์ด ๊ฒน์น˜๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ์œ„ํ•ด ๊ณ„์ธต ๊ตฌ์กฐ ๋งŒ๋“ค์–ด ์˜์ง€ → ์šฉ๋Ÿ‰์ด ๋„ˆ๋ฌด ์ปค์กŒ๋‹ค~

 

 

css์—์„œ๋„ ์บก์Šํ™”์˜ ์ค‘์š”์„ฑ ์ฆ๊ฐ€!!

CSS-in-JS

Styled-Component

๊ธฐ๋Šฅ์ ์ด๊ฑฐ๋‚˜ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ์ปดํฌ๋„ŒํŠธ๋“ค๋กœ๋ถ€ํ„ฐ UI๋ฅผ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

css๋ฅผ ์ปดํฌ๋„ŒํŠธ ์•ˆ์œผ๋กœ ์บก์Šํ™”,, ๋„ค์ด๋ฐ์ด๋‚˜ ์ตœ์ ํ™”๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค!

๋น ๋ฅธ ํŽ˜์ด์ง€ ๋กœ๋“œ์— ๋ถˆ๋ฆฌํ•˜๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Œ

 

 

๐Ÿ“Œ styled Components

 

1. styled Components  ์„ค์น˜

# with npm
$ npm install --save styled-components

# with yarn
$ yarn add styled-components

 

2. ์ปดํฌ๋„ŒํŠธ ํ™œ์šฉ

import styled from "styled-components";

const ์ปดํฌ๋„ŒํŠธ์ด๋ฆ„ = styled.ํƒœ๊ทธ์ข…๋ฅ˜`
    background: black;
    color: white;
`;

์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธํ•œ ํ›„ [ styled.ํƒœ๊ทธ ]๋ฅผ ํ• ๋‹นํ•˜๊ณ , ๋ฐฑํ‹ฑ ์•ˆ์— ๊ธฐ์กด์— CSS๋ฅผ ์ž‘์„ฑํ•˜๋˜ ๋ฌธ๋ฒ•๊ณผ ๋˜‘๊ฐ™์ด ์Šคํƒ€์ผ ์†์„ฑ์„ ์ž‘์„ฑ!

์ด๋ ‡๊ฒŒ ๋งŒ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ React ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋“ฏ ๋ฆฌํ„ด๋ฌธ ์•ˆ์— ์ž‘์„ฑํ•ด์ฃผ๋ฉด ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

2-1. ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌํ™œ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

import styled from "styled-components";


const button1 = styled.button`
    background: black;
    color: white;
`;

// const ์ปดํฌ๋„ŒํŠธ์ด๋ฆ„ = styled.(์žฌํ™œ์šฉํ•  ์ปดํฌ๋„ŒํŠธ)
const button2 = styled.button1`
    padding: 10px;
    margin-top: 10px;
`;

 

3. Props ํ™œ์šฉ

3-1. Props๋กœ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋งํ•˜๊ธฐ

import styled from "styled-components";

//๋ฐ›์•„์˜จ prop์— ๋”ฐ๋ผ ์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
const Button1 = styled.button`
  background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;

export default function App() {
  return (
    <>
      <Button1>Button1</Button1>
      <Button1 skyblue>Button1</Button1>
    </>
  );
}

๊ฒฐ๊ณผ๋Š”?!

 

 

3-2. Props ๊ฐ’์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ

import styled from "styled-components";

//๋ฐ›์•„์˜จ prop ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•ด ๋ Œ๋”๋งํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค
const Button1 = styled.button`
  background: ${(props) => (props.color ? props.color : "white")};
`;
//๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์œผ๋กœ๋„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
const Button2 = styled.button`
  background: ${(props) => props.color || "white"};
`;

export default function App() {
  return (
    <>
      <Button1>Button1</Button1>
      <Button1 color="orange">Button1</Button1>
      <Button1 color="tomato">Button1</Button1>
      <br />
      <Button2>Button2</Button2>
      <Button2 color="pink">Button2</Button2>
      <Button2 color="turquoise">Button2</Button2>
    </>
  );
}

๊ฒฐ๊ณผ๋Š” ์ด๋ ‡๊ฒŒ~~

 

 

4. ์ „์—ญ ์Šคํƒ€์ผ ์„ค์ •

Styled Components์—์„œ createGlobalStyle ํ•จ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

๊ทธ ๋‹ค์Œ ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด CSS ํŒŒ์ผ์—์„œ ์ž‘์„ฑํ•˜๋“ฏ ์„ค์ •ํ•ด์ฃผ๊ณ  ์‹ถ์€ ์Šคํƒ€์ผ์„ ์ž‘์„ฑ!

import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
	button {
		padding : 5px;
    		margin : 2px;
    		border-radius : 5px;
	}
`

๋‹ค์Œ >>

 

์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„ <GlobalStyle> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•ด์ฃผ๋ฉด

์ „์—ญ์— <GlobalStyle> ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์ด ์ ์šฉ!

function App() {
	return (
		<>
			<GlobalStyle />
			<Button>์ „์—ญ ์Šคํƒ€์ผ ์ ์šฉํ•˜๊ธฐ</Button>
		</>);
}

 

 


 

 

๐Ÿ“Œ Storybook

Storybook

npx create-react-app storybook-practice

ํด๋”๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด, ํด๋” ์•ˆ์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Storybook์„ ์„ค์น˜

npx storybook init

์ด์ œ ํ„ฐ๋ฏธ๋„ ์ฐฝ์— ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ Storybook์„ ์‹คํ–‰

npm run storybook

 

 

์ด์ œ ๊ฐ„๋‹จํ•œ ์Šคํ† ๋ฆฌ๋ฅผ ์ง์ ‘ ์ž‘์„ฑ์„ ์œ„ํ•ด

1.

src ํด๋” ์•ˆ์— Title.js ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ React ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด export ํ•ด์ค๋‹ˆ๋‹ค.

import React from "react";

// title์€ h1 ์š”์†Œ์˜ textContent, textColor์€ ๊ธ€์ž์ƒ‰์ด ๋˜๋Š” props์ž…๋‹ˆ๋‹ค.
const Title = ({title, textColor}) => (
<h1 style={{color: textColor}}>{title}</h1>);

export default Title;

 

2.

๊ฐ™์€ ์œ„์น˜์ธ src ํด๋” ์•ˆ์— Title.stories.js ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

.stories๋ฅผ ๋ถ™์—ฌ ํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ์•Œ์•„์„œ ์Šคํ† ๋ฆฌ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค.

Title.stories.js ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

// ์•ž์—์„œ ์ž‘์„ฑํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
import Title from "./Title";

// title : ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์œผ๋กœ, '/'๋ฅผ ๋„ฃ์–ด ์นดํ…Œ๊ณ ๋ฆฌํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
//         ์ดํ›„ ์˜ˆ์‹œ์—์„œ ์กฐ๊ธˆ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
// component : ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์Šคํ† ๋ฆฌ๋กœ ๋งŒ๋“ค ๊ฒƒ์ธ์ง€ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
// argTypes : ์ปดํฌ๋„ŒํŠธ์— ํ•„์š”ํ•œ ์ „๋‹ฌ์ธ์ž์˜ ์ข…๋ฅ˜์™€ ํƒ€์ž…์„ ์ •ํ•ด์ค๋‹ˆ๋‹ค.
//            ์ง€๊ธˆ์€ title, textColor์ด๋ผ๋Š” ์ „๋‹ฌ์ธ์ž์— text ํƒ€์ž…์ด ํ•„์š”ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
export default {
    title: "Practice/Title",
    component: Title,
    argTypes: {
        title: { control: "text" },
        textColor: {control: "text"}
    }
}

// ํ…œํ”Œ๋ฆฟ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. ์ด ํ…œํ”Œ๋ฆฟ์—์„œ๋Š”
// Title ์ปดํฌ๋„ŒํŠธ๊ฐ€ args๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ props๋กœ ๋‚ด๋ ค์ค๋‹ˆ๋‹ค.
const Template = (args) => <Title {...args} />// Storybook์—์„œ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ ์ปดํฌ๋„ŒํŠธ๋Š” export const๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
// ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜์—ฌ Storybook์— ๋„ฃ์–ด์ค„ ์Šคํ† ๋ฆฌ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
// Template.bins({}); ๋Š” ์ •ํ•ด์ง„ ๋ฌธ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
export const RedTitle = Template.bind({});

// ๋งŒ๋“ค์–ด์ค€ ์Šคํ† ๋ฆฌ์˜ ์ „๋‹ฌ์ธ์ž๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.
RedTitle.args= {
    title: "Red Title",
    textColor: "red"
}

// ์Šคํ† ๋ฆฌ๋ฅผ ํ•˜๋‚˜ ๋” ๋งŒ๋“ญ๋‹ˆ๋‹ค.
export const BlueTitle = Template.bind({});

// ์Šคํ† ๋ฆฌ์˜ ์ „๋‹ฌ์ธ์ž๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.
BlueTitle.args= {
    title: "Blue Title",
    textColor: "blue"
}

๊ฒฐ๊ณผ๋Š”??

๊ฒฐ๊ณผ ์ด๋ฏธ์ง€์™€ ๊ฐ™์ด ์•„๋ž˜ ์†์„ฑ์„ ๋„ฃ์–ด์ค€ title, textColor์— ๋‹ค๋ฅธ ๊ฐ’์„ ๋„ฃ์–ด ๋ณ€๊ฒฝ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 


 

 

๐Ÿ“Œ useRef( )

DOM reference๋ฅผ ์ž˜ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” useRef

React๋Š” ์ด๋Ÿฐ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์—์„œ useRef ๋กœ DOM ๋…ธ๋“œ, ์—˜๋ฆฌ๋จผํŠธ, ๊ทธ๋ฆฌ๊ณ  React ์ปดํฌ๋„ŒํŠธ ์ฃผ์†Œ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜์‹œ๋ฉด ์ฃผ์†Œ๊ฐ’์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ = useRef(์ฐธ์กฐ์ž๋ฃŒํ˜•)
// ์ด์ œ ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ ๋ณ€์ˆ˜์— ์–ด๋–ค ์ฃผ์†Œ๊ฐ’์ด๋“  ๋‹ด์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
return (
    <div>
      <input ref={์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡} type="text" />
        {/* React์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ref๋ผ๋Š” ์†์„ฑ์— ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡์„ ๊ฐ’์œผ๋กœ ํ• ๋‹นํ•˜๋ฉด*/}
        {/* ์ฃผ์†Œ๊ฐ’์„_๋‹ด๋Š”_๊ทธ๋ฆ‡ ๋ณ€์ˆ˜์—๋Š” input DOM ์—˜๋ฆฌ๋จผํŠธ์˜ ์ฃผ์†Œ๊ฐ€ ๋‹ด๊น๋‹ˆ๋‹ค. */}
        {/* ํ–ฅํ›„ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ input DOM ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. */}
    </div>);

์ด ์ฃผ์†Œ๊ฐ’์€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ re-render ๋˜๋”๋ผ๋„ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”

์ด ํŠน์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์•„๋ž˜์˜ ์ œํ•œ๋œ ์ƒํ™ฉ์—์„œ useRef ๋ฅผ ํ™œ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>);
}

useRef()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Ref ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ์ด ๊ฐ์ฒด๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ์„ ํƒํ•˜๊ณ  ์‹ถ์€ DOM ์— ref๊ฐ’์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด, Ref ๊ฐ์ฒด์˜ .current ๊ฐ’ ์€ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” DOM ์„ ๊ฐ€๋ฅดํ‚ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

'Code๊ฐœ๋ฐœ์ผ์ง€' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[React] Props Drilling + Cmarket Hooks  (0) 2022.12.27
[React] React Custom Component  (0) 2022.12.26
[Figma] ๋‹จ์ถ•ํ‚ค,, ๋‹น๊ทผ๋งˆ์ผ“ ์•ฑ lo-fi clone  (0) 2022.12.21
UI UX  (0) 2022.12.19
JSON.stringify() // JSON.parse() // Tree UI  (0) 2022.12.16