Front End/React

Custom Component (Styled Components, Storybook)

Component Driven Development (CDD)

Component Driven Development는 디자인과 개발 단계에서부터 재사용할 수 있는 UI 컴포넌트를 부품단위로 만들어 나가는 개발방법을 의미한다. 페이지 전체에 적용될 수 있는 작은 요소들(버튼, 토글, 모달 등)을 사전에 먼저 컴포넌트화 하여 해당 요소가 필요할 때 재사용하고, 이런 컴포넌트들이 모여 페이지를 완성하는 상향식 개발이다.

Styled Components

Styled Components는 앞서 말했듯 CSS를 컴포넌트 영역으로 불러들이기 위한 CSS-in-JS 중 하나로, 컴포넌트부터 상향식으로 제작하는 CDD를 위한 라이브러리이기도 하다. CSS를 컴포넌트화 시킴으로써 기존 CSS가 가진 불편함(class, id 이름을 짓는 문제나 CSS 파일이 너무 길어지는 등)을 해결시켜준다. CSS-in-JS 라이브러리인 Styled Components를 사용하게 되면 CSS도 쉽게 Javascript 안에 넣어줄 수 있기 때문에 HTML + JS + CSS까지 묶어서 하나의 JS파일 안에서 컴포넌트 단위로 개발할 수 있게 된다.

Styled Components 설치

터미널에서 npm이나 yarn을 이용해 라이브러리를 설치한다.

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

# with yarn
$ yarn add styled-components

Styled Components는 package.json에 다음 코드를 추가하도록 권장하고 있다. 아래의 코드를 추가하면 여러 버전의 Styled Components가 설치되어 발생하는 문제를 줄여준다.

{
  "resolutions": {
    "styled-components": "^5"
  }
}

Styled Components 문법

컴포넌트 만들기

Styled Components는 ES6의 템플릿 리터럴 문법(``)을 사용한다. 컴포넌트를 선언한 후 styled.TAG를 할당하고, 백틱 안에 기존 CSS 작성 문법과 동일하게 스타일 속성을 작성한 뒤 React 컴포넌트를 사용하듯 리턴문 안에 작성해주면 스타일이 적용된 컴포넌트가 렌더된다.

만약 해당 컴포넌트에 의사선택자(:hover 등)을 사용하고 싶다면 &을 활용하여 작성할 수 있다. &은 해당 컴포넌트를 재참조한다는 뜻이다.

import styled from "styled-components";

// Styled Components로 컴포넌트를 만들고
const BlueButton = styled.button`
  background-color: blue;
  color: white;
	/* 의사선택자 사용 */
	&:hover{ 
		background-color: white;
	}
`;

export default function App() {
  // React 컴포넌트를 사용하듯이 사용
  return <BlueButton>Blue Button</BlueButton>;
}

컴포넌트 재활용

이미 만들어진 컴포넌트를 재활용해 새로운 컴포넌트를 만들 수도 있다. 컴포넌트를 선언하고 styled()에 재활용할 컴포넌트를 전달인자로 넣고 추가하고 싶은 스타일 속성을 백틱 안에 작성해주면 된다.

import styled from "styled-components";

const BlueButton = styled.button`
  background-color: blue;
  color: white;
`;

// 만들어진 컴포넌트를 재활용해 컴포넌트 생성
const BigBlueButton = styled(BlueButton)`
  padding: 10px;
  margin-top: 10px;
`;

// 재활용한 컴포넌트를 재활용도 가능
const BigRedButton = styled(BigBlueButton)`
  background-color: red;
`;

export default function App() {
  return (
    <>
      <BlueButton>Blue Button</BlueButton>
      <br />
      <BigBlueButton>Big Blue Button</BigBlueButton>
      <br />
      <BigRedButton>Big Red Button</BigRedButton>
    </>
  );
}

Props 활용

Styled Components로 만든 컴포넌트도 React 컴포넌트처럼 props를 내려줄 수 있다. 내려준 props 값에 따라 컴포넌트를 렌더링 하는 것도 가능하다. 백틱 내에 템플릿 리터럴 문법(${})을 사용하여 JavaScript 코드를 사용할 수 있고, props를 받아오려면 props를 인자로 받는 함수를 만들어 사용하면 된다.

Props로 조건부 렌더링하기

위 코드는 삼항연산자를 활용해 <Button> 컴포넌트에 skyblue 라는 props가 있는지 확인하고, 있으면 배경색으로 skyblue를, 없을 경우 white를 지정해주는 코드이다. 이 코드에 따라 렌더링 된 <button>컴포넌트는 아래 그림과 같다.

Button1의 경우 skyblue라는 props가 있어 배경색이 skyblue로 지정되었고, Button2는 props가 없기 때문에 배경색이 white로 지정된 것을 볼 수 있다.

import styled from "styled-components";
import GlobalStyle from "./GlobalStyle";
// 받아온 prop에 따라 조건부 렌더링 가능
const Button1 = styled.button`
  background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;

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

Props 값으로 렌더링하기

props의 값을 통째로 활용해 컴포넌트 렌더링에 활용할 수 있다.

삼항연산자를 사용하고, props.color가 없다면 white를, props.color가 있다면 그 값을 그대로 배경색으로 리턴하여 적용되게끔 하는 것을 볼 수 있다. props를 활용한 스타일 지정은 꼭 삼항연산자만 활용할 수 있는 것이 아닌, JavaScript 코드라면 무엇이든 사용할 수 있기 때문에 다양하게 활용 가능하다.

import styled from "styled-components";
import GlobalStyle from "./GlobalStyle";

// 받아온 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 (
    <>
      <GlobalStyle />
      <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>
    </>
  );
}

전역 스타일 설정

전역에 스타일을 설정을 할 수 있게 Styled Components는 이를 위한 컴포넌트도 준비해두었다. 우선 전역 스타일 설정을 위해 Styled Components에서 createGlobalStyle을 불러온 뒤, 해당 함수를 사용해 일반적인 컴포넌트와 같은 형식으로 작성해 생성한다. 생성된 전역 스타일 컴포넌트는 최상위 컴포넌트에서 사용해주면 전역 스타일 컴포넌트가 적용된다.

// createGlobalStyle 함수 불러오기
import { createGlobalStyle } from "styled-components";

// 전역 스타일을 위한 컴포넌트 생성
const GlobalStyle = createGlobalStyle`
	button {
		padding : 5px;
    margin : 2px;
    border-radius : 5px;
	}
`

function App() {
	return (
		<>
			{// 전역 스타일 컴포넌트 적용}
			<GlobalStyle />
			<Button>전역 스타일 적용하기</Button>
		</>
	);
}

Storybook

Storybook은 CDD를 지원하는 도구 중 하나인 Component Explorer(컴포넌트 탐색기)에 속하는 UI 개발 도구로 각각의 컴포넌트들을 따로 볼 수 있게 구성해주어 한 번에 하나의 컴포넌트에서 작업할 수 있다. 복잡한 개발 스택을 시작하거나, 특정 데이터를 데이터베이스로 강제 이동하거나, 어플리케이션을 탐색할 필요 없이 전체 UI를 한 눈에 보고 개발할 수 있다.

Storybook은 재사용성을 확대하기 위해 컴포넌트를 문서화하고, 자동으로 컴포넌트를 시각화하여 시뮬레이션 할 수 있는 다양한 테스트 상태를 확인할 수 있어 이를 통해 버그를 사전에 방지할 수 있게 도와준다. 테스트 및 개발 속도를 향상시키는 장점이 있으며, 어플리케이션 또한 의존성을 걱정하지 않고 빌드할 수 있다.

🤔
Storybook 과 같은 UI 개발 도구를 왜 사용할까? → Storybook은 기본적으로 독립적인 개발 환경에서 실행되기 때문에 개발자는 어플리케이션의 다양한 상황에 구애받지 않고 UI 컴포넌트를 집중적으로 개발할 수 있다. 회사의 내부 개발자들을 위해 문서화(documentation)를 하여 회사의 UI 라이브러리로써 사용하거나, 외부 공개용 디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로 사용할 수 있다.
Storybook에서 지원하는 주요 기능
  • UI 컴포넌트들을 카탈로그화하기
  • 컴포넌트 변화를 Stories로 저장하기
  • 핫 모듈 재 로딩과 같은 개발 툴 경험을 제공하기
  • 리액트를 포함한 다양한 뷰 레이어 지원하기

Storybook 설치

프론트엔드 라이브러리(React 등) 설치 후, 아래의 명령어를 입력한다. 이 명령어는 package.json을 참조하여 프론테엔드 라이브러리에 맞는 Storybook 사용 환경을 알아서 만들어주기 때문에, React가 아니더라도 다양한 프론트엔드 라이브러리에서 사용할 수 있다.

npx storybook init

Storybook 기본 사용법

Storybook에서 컴포넌트화된 UI 요소를 미리 확인하기 위해서 컴포넌트를 생성한다. 이 때, Styled Components를 활용해 props를 넣을 수 있다.

import react from 'react'
import styled from 'styled-components'

const StyledButton = styled.button`
  background: ${(props) => props.bgColor || "white"};
  color: ${(props) => props.color || "black"};
  width: ${(props) => props.size === "big" ? "200px" : "100px"};
  height: ${(props) => props.size === "big" ? "80px" : "40px"};
`
const Button = ({bgColor, color, size, text}) => <StyledButton bgColor={bgColor} color={color} size={size}>{text}</StyledButton>

export default Button

/.storybook안에 있는 Storybook 설정 파일에 의해 컴포넌트 파일과 똑같은 파일 이름에 .stories를 붙여 파일을 만들면 알아서 스토리로 인식한다. 같은 위치에 컴포넌트 파일명.stories.js파일을 생성하고 아래와 같이 작성한다.

// 작성한 컴포넌트를 불러온다
import Button from "./Button";

// 스토리의 기본 속성을 정의
export default {
	// 컴포넌트의 이름으로 '/'를 이용해 카테고리화 할 수 있다.
  title: "Practice/Button",
  // 어떤 컴포넌트를 가져와서 스토리를 만들지 명시한다.
  component: Button,
  // 컴포넌트 정의에 사용된 전달인자의 속성을 정한다.
  // Storybook에서 보게 될 속성의 타입이자 전달인자의 타입
  argTypes: {
    bgColor: {control: "color"},
    color: { control: "color" },
    size: { control: { type: "radio", options: ["big", "small"] } },
    text: { control: "text" },
  }
};

// 전달인자를 Storybook 상에서 직접 받는 스토리
// (컴포넌트의 기본적인 스타일로 이해할 수 있음)
// Storybook에서 확인하고 싶은 컴포넌트 작성 : export const
export const StorybookButton = (args) => {
  // 전달인자를 받아 구조 분해 할당으로 props를 내려준다.
  return <Button {...args}></Button>;
};

// 위의 컴포넌트를 토대로 사전 설정 역시 가능
// 스토리를 만들기 위한 템플릿
// Title 컴포넌트가 args를 전달받아 props로 내려준다
const Template = (args) => <Button {...args} />

// 템플릿을 사용하여 Storybook에 넣어줄 스토리를 하나 만든다
// Template.bind({}); 는 정해진 문법이라고 생각하고 사용
// (bind 하지 않으면 전달인자를 설정할 수 없음)
export const RedButton = Template.bind({});

// 만들어준 스토리의 전달인자를 작성
RedButton.args= {
    bgColor: "red",
    color: "white",
    size: "big",
    text: "Red Button"
}

stories.js 파일을 저장하고 나면 Storybook 상에서 아래와 작성한 컴포넌트를 확인할 수 있다.


Uploaded by N2T