본문 바로가기

개발인생다반사/TIL(Today i learned)

TIL - 211027 [React] 컴포넌트 디자인

Achievement Goals

  • 컴포넌트 기반 Bottom-up 방식 개발이 무엇인지 이해한다.
  • 컴포넌트 UI 개발에 도움을 주는 라이브러리인 Storybook을 활용할 수 있다.
  • 구조적으로 CSS를 작성하는 방법의 발전과 이유에 대해서 이해한다.
  • 컴포넌트 기반 CSS 작성에 도움을 주는 라이브러리인 Styled-Component를 활용할 수 있다.
  • DOM Reference를 활용하기 위한 useRef Hook을 활용할 수 있다.

 

Chapter 1 - 컴포넌트 단위로 개발하기

(1)  Component Driven Developement

 

여러 팀 간에 같은 UI 컴포넌트를 공유.

디자인과 개발 단계에서 부터 재사용할 수 있는 UI 컴포넌트를 미리 디자인하고 개발(재사용할 수 있는 UI 컴포넌트의 필요성)

CCD - 부품 단위로 UI 컴포넌트를 만들어 나가는 개발 방식

(2) 컴포넌트 UI 개발을 위한 Storybook

Component Explorer(컴포넌트 탐색기) - CDD 지원 도구. 지원 도구 중 하나인 Storybook

 

Storybook은 컴포넌트의 재사용성을 확대하기 위해 컴포넌트를 문서화하고 자동으로 컴포넌트를 시각화하여 시뮬레이션할 수 있는 다양한 테스트 상태 확인 가능. 버그 사전 방지. 테스트 및 개발 속도 향상. 

 

회사의 내부 개발자들을 위한 문서화하여 회사 UI 라이브러리로 사용하거나  외부 공개용 디자인 시스템을 개발하기 위한 플랫폼으로 사용이 가능

 

Storybook 설치 및 세팅 방법

# Clone the template
npx degit chromaui/intro-storybook-react-template taskbox

cd taskbox

# Install dependencies
yarn
import React from "react";
import { Button } from "@storybook/react/demo";

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

export const Primary = () => (
  <Button>Hello Button</Button>
);

export const Secondary = () => (
 <Button>Bye Button</Button>
);

Button 컴포넌트가 있고 button 컴포넌트를 맞춤제작한 Primary와 Secondary 있음.

이 Primary와 Secondary function component를 export하면  strorybook에서 UI 목업이 확인가능

 

Chapter 2 - CSS in JS 방법론

(1) 구조적인 CSS 작성 방법의 발전

프로젝트 규모가 커지고 팀원 수도 많아지면서 CSS 작업의 효율화를 위해 구조화된 CSS 필요성 대두

CSS 전처리(CSS Preprocessor) 등장.

SASS 반복되는 CSS코드가 있다면 변수를 활용해서 재사용이 가능하게 함. 변수 선언시 $ 기호 활용.(단점 CSS용량이 과도하게 커짐)

CSS 전처리기의 문제 보완 위해 BEM, OOCSS, SMACSS 같은 CSS방법론 대두.

코드 재사용/코드 간결화/코드 확장성/코드 예측성

클래스명은 BEM방식의 이름을 여러 번 반복항 재사용할 수 있도록 해주고 HTML/CSS/SASS파일도 일관된 코딩 구조.

클래스명 선택자가 장황해짐, 긴 클래스명 때문에 마크업이 불필요.

캡슐화가 안되서 유일한 클래스명을 선택하는 것에 의존.

 

CSS in JS : styled component 등장

(2) 컴포넌트 기반 CSS 작성에 적합한 Styled-Component

Styled Component : CSS in JS 관련 Reat 라이브러리 중에서 가장 인기(유사 라이브러리 : emotion styled-jsx)

기존 CSS 문법으로도 스타일 속성이 추가된 React 컴포넌트 제작이 가능

Button 제작 위한 코드. JS에서 변수 선언하듯 Button을 만들고 tag의 속성을 정희하고(아래서는 a tag), 

back-ticks(``)안에 기존 CSS 문법을 이용하여 스타일 속성을 정의해 준다.

const Button = styled.a`
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
`;

Styld Component 특징

  • 유니크한 className 생성
  • 화면에 렌더링된 컴포넌트를 추적해서 스타일을 자동으로 삽입
  • 사용하지 않는 컴포넌트의 스타일 속성이 제거
  • props 기반의 컴포넌트 스타일 속성 부여
  • CSS 파일 일일이 검색할 필요없어 유지보수 용이
  • 개별 컴포넌트 마다 기존 CSS 이용하여 스타일 속성만 정의

installation

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

# with yarn 
$ yarn add styled-components

package.json에 아래 코드 추가. 여러 버전의 Styled Component가 설치되어 발생하는 문제를 줄여줌

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

 

Getting Started

ES6의 템플릿 리터럴을 사용하여 작업. CSS파일 없이 컴포넌트의 스타일 속성 정의.

var t1Closure = template`${0}${1}${0}!`;
t1Closure('Y', 'A');  // "YAY!"
var t2Closure = template`${0} ${'foo'}!`;
t2Closure('Hello', {foo: 'World'});  // "Hello World!"

 

Adapting based on props & Extending Styles

Styled Component는 스타일 속성을 지닌 컴포넌트를 정의할 때 함수를 전달하고 그 함수 안에서 props를 사용할 수 있다.

// Button component
...
  background: ${(props) => (props.primary ? "palevioletred" : "white")};
  color: ${(props) => (props.primary ? "white" : "palevioletred")};
...

// App component
...
  <Button>Normal</Button>
  <Button primary>Primary</Button>
...

같은 스타일 속성을 지닌 여러개의 컴포넌트들 중 몇 개의 컴포넌트에는 약간의 변화를 줄 수 있다. 이때에는 상속받고자 하는 스타일 속성을 지닌 컴포넌트를 styled( ) 로 감싼뒤 변경하고 싶은 속성만 새로 정의해주면 된다.(back-tick 사용)

// 기존의 Button 컴포넌트에 Tomato 컴포넌트만을 위한 새로운 속성 추가
const Tomato = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

Passed props

컴포넌트에 props로 스타일 속성이 전달되다면 해당 컴포넌트는 props로 전달된 속성을 우선 적용한다.

 

(3) DOM reference를 잘 활용할 수 있는 useRef

DOM 엘리먼트 주소값을 활용해야 하는 경우

  • focus
  • text selection
  • media playback
  • 에니메이션 적용
  • d3.js, greensock 등 DOM 기반 라이브러리 활용

Reactsms 위의 예외적인 상황을 useRef으로 DOM 노드, 엘리먼트, 리액트 컴포넌트 주소값을 참조할 수 있다.

const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다.
return (
    <div>
      <input ref={주소값을_담는_그릇} type="text" />
        {/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
        {/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */}
        {/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있습니다. */}
    </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를 남용하는 것은 부적절하고 리액트의 특징인 선언적 프로그래밍 원칙과 배치되기 때문에 사용을 조심