S3 Unit 3. [React] Custom Component

나현·2022년 10월 27일
0

학습일지

목록 보기
33/53
post-thumbnail

💡 이번에 배운 내용

  • Section3. 사용자 친화적이고 안전한 Web App을 만들 수 있다.
  • Unit3. [React] Custom Component: React로 프론트엔드를 구축할 때, 보다 구조적으로 코드를 작성하는 방법에 대해 학습한다.

느낀점

이번에는 리액트의 컴포넌트 사용법 중 styled-component에 대해 배웠다. 배워보니 CSS를 처리할 수 있는 효율적인 방법이라 그동안 HTML, CSS, JS를 분리해가며 사용해봤던 나에게는 신세계였다. 이제는 컴포넌트 단위로 HTML,JS에 이어 CSS까지 합쳐지게 되었다! 무엇이든 장단점이 있겠지만 나는 막상 실습해보니 정말 재미있었다. 앞으로 필요한 컴포넌트들은 이렇게 UI단위로 쪼개서 만들면 되지 않을까?
하지만 그것과 별개로 공부할 내용도 많은 유닛이었다. 오늘은 실습도 해가며 블로그를 정리하다보니 저녁까지 자동으로 공부하게 되었다.
어우 이걸 어떻게 해 하고 봤던 UI들을 직접 만들어보니 뿌듯하다.
부디 많이 까먹지 말기를! 👀


키워드

Component Driven Development(CDD), CSS 전처리기(CSS Preprocessor), SASS, CSS 방법론, BEM 방식, CSS-in-JS, styled-components, Storybook, useRef


학습내용

Ch1. CSS in JS

Component Driven Development(CDD)

Component Driven Development(CDD)이란 디자인과 개발 단계서부터 재사용할 수 있는 UI컴포넌트를 미리 디자인하고 개발하는 방법을 의미한다.
CDD방법으로 개발하면 부품처럼 UI 컴포넌트를 만들어 놓았기 때문에 이 컴포넌트들을 조립하는 방법으로 개발을 진행할 수 있다.
CDD는 밑에서 배울 CSS-in-JS와 관련이 있는 개발 방법이다.

프로젝트 규모, 복잡도가 커지고 다양한 디바이스가 등장하면서 CSS를 작성하는 방법과 관리도 더 중요하게 되었다. 특히 이런 개발 환경에서 CSS를 작성하는 패턴이 없다는 것은 문제가 되었다.
때문에 CSS작업을 효율적으로 하기 위한 여러 방법론과 툴(전처리기)이 등장하게 되었다.

CSS 전처리기

CSS 전처리기(CSS Preprocessor)란 CSS가 구조적으로 작성될 수 있게 도움을 주는 도구로 많은 CSS를 작성하여 관리할 때 용이하다.
전처리기는 CSS에서도 변수, 함수, 상속 등을 사용할 수 있게 해주며 유지관리에 좀 더 도움을 준다.
그러나 CSS 전처리기는 웹 서버가 인지하지 CSS문서로 변환을 해야하며 CSS의 용량이 커진다는 단점이 있다.
대표적인 CSS전처리기로 SASS(Syntactically Awesome Style Sheets)가 있다.

SASS

SASS는 CSS를 확장해 주는 스크립팅 언어로 앞서말한 대표적인 CSS전처리기이다.
SASS의 특징은 다음과 같다.

  • CSS를 자바스크립트처럼 특정 속성의 값(ex. #ffffff, 25rem, 100px 등)을 변수로 선언하고 사용할 수 있다.
    ex) color, margin, width 등의 값(#ff0000, 25rem, 100px)을 변수로 선언 가능
  • 반복되는 코드를 선언하여 재사용할 수 있다.
  • CSS 계층 구조를 만들 수 있다.

그러나 SASS를 사용하는 단점으로 언급했듯이 CSS의 용량이 커지고 컴파일을 해주어야 한다는 단점이 있다. 이 밖에도 CSS전처리기의 문제를 해결하기 위해
BEM, OOCSS, SMACSS 같은 CSS 방법론이 등장하게 되었다.

CSS 방법론

CSS방법론이란 좀 더 효율적으로 CSS를 사용할 수 있도록 CSS네이밍, 작성 방법 등에 규칙을 두어 지키는 것을 말한다.
대표적인 CSS 방법론으로는 BEM이 있다.

  • BEM은 CSS 방법론으로 이름처럼 Blcok, Element, Modifier를 사용하여 CSS 구조를 정하고, 웹 UI를 블럭 단위로 컴포넌트화하여 개발하는 방식이다.
  • BEM방식으로 CSS 선택자 명을 정할 때는 소문자, 숫자만을 이용해서 작명하며, 기본적으로 ID를 사용하지 않으며, class만을 사용한다.
  • 요소의 형태가 아닌 사용 목적에 따라 Blcok__Element--Modifier 방식으로 네이밍한다.
    • Block(블록): 문단 전체에 적용된 element나 element를 담고 있는 컨테이너. 기능적으로 독립적인 페이지 구성 요소. 블록은 재사용과 중첩이 가능해야 하므로 여백, 위치 등에 영향받지 않도록 해야 한다.
    • Element(요소): block 안에서 특정 기능을 수행하는 컴포넌트. 단독으로 사용할 수 없는 블록의 부분적인 구성요소로 블록처럼 중첩해서 사용할 수 있으나 블록에 의존적인 이름을 가진다.
    • Modifier(수식어): 사이즈, Boolean 값처럼 블록이나 요소로 알 수 없는 모양, 상태, 행동을 정의한다.
    • 각 자리의 이름을 연결할 때는 block-name과 같이 하이픈 하나만 써서 연결한다.

BEM 방식을 사용하면 이름만 봐도 해당 컴포넌트에 대해 보다 직관적으로 알 수 있으며 HTML/CSS/SASS 파일에서도 더 일관된 코딩 구조를 만들어 준다.

아래는 여러 CSS 방법론의 특징과 장단점이다.

특징장점단점
CSS기본적인 스타일링 방법-일관된 패턴 갖기 어려움,
!important의 남용
SASS
(preprocessor)
프로그래밍 방법론을 도입하여,
컴파일된 CSS를 만들어내는 전처리기
변수/함수/상속 개념을 활용하여
재사용 가능 CSS의 구조화
전처리 과정이 필요,
디버깅의 어려움이 있음
컴파일일한 CSS 파일이 거대해짐
BEMCSS클래스명 작성에
일관된 패턴을 강제하는 방법론
네이밍으로 문제 해결,
전처리 과정 불필요
선택자의 이름이 장황하고,
클래스 목록이 너무 많아짐
Styled-
Component
(CSS-in-JS)
컴포넌트 기반으로
CSS를 작성할 수 있게 도와주는
라이브러리
CSS를 컴포넌트 안으로 캡슐화,
네이밍이나 최적화를
신경 쓸 필요가 없음
빠른 페이지 로드에 불리함

위와 같이 CSS 방법론의 특징, 장단점을 살펴본 것처럼 CSS 방법론을 사용하는 자체에도 장단점이 있다.

  • 장점
    • 코드의 재사용
    • 코드의 간결화(유지 보수 용이)
    • 코드의 확장성
    • 코드의 예측성(클래스 명으로 의미 예측)
  • 단점
    • 길어지고 복잡해지는 클래스명
    • 긴 클래스명으로 인한 길어지는 마크업
    • 재사용할 때마다 모든 UI 컴포넌트를 명시적으로 확장
    • 진정한 캡슐화 개념의 부재
      캡슐화(encapsulation): 객체의 속성, 행위를 묶고, 실제 구현 내용 일부를 외부에 감추어 은닉하는 개념

CSS방법론의 이런 단점을 해결하기 위해 CSS도 컴포넌트 영역으로 불러들이는 CSS-in-JS가 탄생하게 되었다.
이 CSS-in-JS에는 대표적으로 Styled-Component가 있다.


Ch2. Styled-Component

Styled-Component는 하나의 컴포넌트에서 CSS를 독립적, 효과적으로 사용할 수 있는 기능을 제공하는 라이브러리다. 앞서 언급한대로 CSS-in-JS개념이 적용되었다.
Styled-Component를 사용하면 아래와 같은 문제를 해결할 수 있다.

  • CSS도 컴포넌트화하여 사용할 수 있다.
  • class, id 이름을 따로 짓지 않아도 된다.
  • 컴포넌트에서 독립적으로 사용이 가능하여 태그별로 원하는 부분을 쉽게 찾을 수 있다.
  • 중복된 스타일은 겹치치 않는 부분만 추가하여 재사용할 수 있다..
  • JS 구문을 활용할 수 있다. (props사용, 삼항연산자 등등)

Styled Components 설치하기

터미널에 npm install --save styled-components 라고 입력하면 Styled Components 라이브러리를 설치할 수 있다. styled가 아니라 style을 입력하면 안되니 주의하자.
(권장) 여러 버전의 Styled Components가 설치되어 생기는 문제를 방지하기 위해 아래의 코드를 package.json에 입력한다.

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

그리고 각 파일 상단에 import styled from "styled-components" 처럼 import구문을 작성하여 사용하면 된다.

Styled Components 문법

1. 기본 사용법 - 컴포넌트 사용
Styled Components는 ES6의 Templete Literals을 사용하므로 다음과 같이 백틱(`)안에 내용을 입력하여 사용한다. 백틱 안에는 기존 CSS문법이 들어간다.

const 컴포넌트이름=styled.태그`
  css속성:속성값,
  css속성:속성값,
  ...
`;

2. 컴포넌트 재활용

const 컴포넌트이름=styled(재활용할 컴포넌트명)`
  추가 CSS
`;

3. Props 활용
Styled Component로 만든 컴포넌트도 props사용이 가능하다.
아래처럼 템플릿 리터럴 문법을 활용해 ${} 안에 코드를 작성하고,
화살표 함수 형식으로 props를 전달인자로 받아와 활용할 수 있다.

const 컴포넌트이름=styled.태그`
  css속성:${(props)=>props를 활용한 코드},
  ...
`;

이 props를 활용하여 속성으로 지정한 컬러를 사용할 수도 있고,
삼항연산자나 or연산자(||)를 사용하여 특정 컬러를 기본값으로 사용할 수도 있다.
아래 코드는 흰색(#fff)를 기본색으로 사용하며, props값이 있다면 그 속성의 값으로 CSS를 적용한다.

const Paragraph=styled.p`
  color:${(props)=>props.color || '#fff'}, //만약 조건이 여러개라면 if문을 쓴다.
  ...
`;
<Paragraph color="#ff0000">글자색은?</Paragraph>
<Paragraph color="#00ff00">글자색은?</Paragraph>
<Paragraph color="#0000ff">글자색은?</Paragraph>

위 코드에서 조건부로 렌더링 하는 부분은
${(props)=>props.color?props.color:'#fff'}
처럼 삼항연산자를 사용해도 된다.

4. 전역 스타일 설정
전역 스타일은 Styled Components에서 제공하는 함수를 사용한다.
때문에 함수 createGlobalStyle를 import한 후 사용한다.

import { createGlobalStyle } from "styled-components";

import 한 후에 아래와 같이 최상위 컴포넌트에 사용한다.

const GlobalStyle = createGlobalStyle`
	전역 CSS 작성
`

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

Ch3. Storybook

Storybook은 CDD방법에 기반하여 각 UI컴포넌트들을 시각적으로 쉽게 볼 수 있는 개발도구이다.
만들어 둔 각 UI컴포넌트들을 카탈로그 처럼 시각적으로 볼 수 있고 각 props를 설정해보며 수정해볼 수도 있다.
예시: 🔗 UN 사이트의 스토리북 페이지

Storybook 특징

Storybook은 다음과 같은 특징이 있다.

  • Storybook은 애플리케이션 상황과 상관없이 독립적인 개발 환경에서 실행된다.
  • 컴포넌트를 문서화할 수 있다.(재사용성 용이)
  • 자동으로 컴포넌트를 시각화하고, 시뮬레이션할 수 있다.
  • 테스트를 하며 버그를 사전에 방지할 수 있도록 도와준다.
  • 이미 만들어진 UI컴포넌트가 있다면 개발 속도가 향상된다.

또한 Storybook을 만들어 놓으면, 외부 공개용 디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로 사용할 수 있습니다.

Storybook의 주요 기능

  • UI 컴포넌트들을 카탈로그화
  • 컴포넌트 변화를 Stories로 저장
  • 핫 모듈, 재 로딩과 같은 개발 툴 경험을 제공(수정 시 바로 적용된다.)
  • 리액트를 포함한 다양한 뷰 레이어 지원

Storybook 설치 후 실행

여기서는 리액트를 통해 사용하는 방법을 소개한다.
Create React App을 통해 리액트 프로젝트를 하나 생성하고,
생성된 폴더 안에서 터미널에 npx storybook init명령어를 입력한다.
Storybook은 package.json 을 보고 사용 중인 프론트엔드 라이브러리에 맞는 Storybook 사용 환경을 알아서 만들어주며 리액트 외의 다른 프론트엔드 라이브러리에서 사용 가능하다.

Storybook 설치가 완료되면, 아래의 폴더가 생성된다.

  • /.storybook 폴더 : Storybook 관련 설정 파일
  • /src/stories 폴더: Storybook 예시 파일

그리고 npm run storybook을 입력하면, localhost:6006으로 Storybook이 실행된다.

Storybook 작성

1. 하나의 컴포넌트로 여러 버전의 Storybook 만들기
스토리북을 만들기 위해 일단 기본이 될 컴포넌트가 필요하다.
아래와 같이 src폴더에 Button.js 파일을 만들어 기본 Button 컴포넌트를 만든다.

import React from "react";

const Button = ({title, color}) => (
<button style={{color: color}}>{title}</button>
);

export default Button;

위 Button.js에는 styled-component를 추가하고 props를 사용해도 스토리북을 사용할 수 있다.

import React from "react";
import styled from "styled-components";

const StyledButton=styled.button`
	background:${(props)=>props.color || white};
	width:${(props)=>{
      if(props.size==='big') return '280px';
      if(props.size==='normal') return '180px';
      if(props.size==='small') return '120px'
    }};
	height:${(props)=>{
      if(props.size==='big') return '56px';
      if(props.size==='normal') return '48px';
      if(props.size==='small') return '40px'
    }}
`;

const Button = ({title, color, size}) => (
  <StyledButton color={color} size={size}>{title}</StyledButton>
);

export default Button;

그리고 같은 위치인 src 폴더 안에 Button.stories.js 파일을 만든다.
/.storybook 안에 있는 Storybook 설정 파일에 의해서 컴포넌트 파일명.stories.js로 파일을 만들면 자동으로 스토리로 인식된다.
아래는 Button.stories.js의 내용이다.

import Title from "./Button";

export default {
    title: "UI Components/Button", // storybook 문서의 카테고리
    component: Button, //스토리로 만들 컴포넌트
  //컴포넌트에 필요한 전달인자의 종류와 타입
    argTypes: {
        title: { control: "text" },
        color: { control: "text"},
        size: { control: { type:'radio', options : ['big', 'normal', 'small'] }},
    }
}

//템플릿 생성: Button 컴포넌트가 args를 전달받아 props로 내려준다.
const Template = (args) => <Button {...args} />
//만약 스토리북에서 인자를 직접 받으려면 위의 Template를 그대로 export한다.

//아래부터 스토리를 생성하며, 생성한 스토리는 스토리북에서 볼 수 있다.

//스토리 생성: 스토리는 export const로 템플릿을 사용하여 추가한다.
export const RedButton = Template.bind({}); //뒤의 사항은 거의 정해진 문법이라고 생각하면 좋다.

// 만들어준 스토리에 인자를 전달한다.
RedButton.args= {
    title: "Red Button",
    color: "#ff0000"
}


export const StorybookButton = (args) =>{
    return <Button {...args} />
}

스토리북을 실행하면 만든 스토리를 해당 카테고리(위 예제에서는 UI Components)에서 확인할 수 있다.
인자를 직접 입력하는 스토리는 control패널에서 입력하거나 값을 설정할 수 있다.
control패널에서 입력하는 값을 설정할 때 위의 artTypes부분을 보면

    argTypes: {
        title: { control: "text" },
        color: { control: "text"},
        size: { control: { type:'radio', options : ['big', 'normal', 'small'] }},
    }

control 패널에서 직접 컨트롤 하는 부분을 설정해 놓았다.

  • { control: "text" }: control 패널에서 직접 텍스트를 입력할 수 있다.
  • { control: "color" }: control 패널에서 직접 컬러를 조정할 수 있다.
  • { control: { type:'radio', options : ['big', 'normal', 'small'] }}:
    컨트롤 패널에서 radio 타입 버튼으로 big, normal, small의 옵션을 선택할 수 있다.
    이 때 여기의 옵션은 props.size값이다. 스토리북에서 라디오 버튼을 선택하면
    Button.js에서 styled-components에 작성한 내용에 맞는 CSS가 적용된다.

Ch4. useRef

React 앱을 만들 때 DOM을 직접 조작하는 것은 지양해야 한다.
그러나 몇몇 예외로 리액트 DOM을 직접 선택하여 조작해야 하는 경우가 있다.
이럴 때 사용하는 것이 useRef이다.
useRef는 아래와 같이 사용한다.

const '주소값(DOM)을 할당할 임의의 변수 elInput'= useRef(참조자료형)
return (
    <div>
      <input ref={elInput} type="text" />
        {/* ref 속성에 변수를 할당하면, 그 변수에는 엘리먼트의 주소가 할당된다.*/}
        {/* 위의 elInput에는 input DOM 엘리먼트의 주소가 할당되어*/}
        {/* 다른 컴포넌트에서 이 elInput를 활용할 수 있다. */}
    </div>);

이 주소값은 컴포넌트가 다시 렌더링 되어도 바뀌지 않는다.

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를 사용해야 하는 상황은 주로 아래와 같다.

  • focus : 현재 input에서 엔터키를 누르면 다음 input을 focusing 시킬 때.
    -> 다음 input의 주소값이 필요하다.
  • media playback : 삽입된 동영상의 재생, 일시정지를 다른 버튼으로 제어하고자 할 때.
    -> 삽입된 동영상의 주소값이 필요하다.
  • 그 외 애니메이션 적용, text selection 등등 DOM의 주소가 필요할 때
  • d3.js, greensock 등 DOM 기반 라이브러리를 활용할 때 등등

이러한 예외 사항을 제외하고는 useRef를 남용하지 않도록 한다.
React의 특징인 선언형 프로그래밍이 원칙에 위배되기 때문이다.


추가학습

1. styled-components
styled-components의 공식페이지에 가면 다양한 활용법과 정보를 확인할 수 있다.
그 중에서도 아래 페이지는 리액트로 실습하면서 스타일 컴포넌트의 기본 적용법이 적혀있다.
🔗 styled-components : Basics

2. Storybook
공식문서에 가면 리액트로 스토리북을 활용하는 방법이 친절하게 한국어로 설명되어 있다.
🔗 Storybook Tutorials

profile
프론트엔드 개발자 NH입니다. 시리즈로 보시면 더 쉽게 여러 글들을 볼 수 있습니다!

0개의 댓글