26.[React] Custom Component

문도연·2022년 6월 30일
0
post-thumbnail

Chapter1. Component Driven Development
1-1. CDD
1-2. CSS-in-JS
Chapter2. CDD 개발 도구
2-1. Styled Components
2-2. Storybook
Chapter3. useRef


Chapter1. Component Driven Development

  • Component Driven Development(CDD)의 필요성과 이점에 대해서 이해한다.
  • 구조적으로 CSS를 작성하는 방법의 발전과 이유에 대해서 이해한다.
  • CSS 방법론들의 특징과 장단점을 이해한다.

1-1. CDD

  • 기획자가 이전 페이지에서 요청했던 버튼과 똑같은 버튼을 추가된 페이지에 사용하도록 요청하는 경우,
    -->디자인과 개발 단계에서부터 재사용할 수 있는 UI 컴포넌트를 미리 디자인했다면 이 문제는 손쉽게 해결가능.

이러한 종류의 고민을 해결하기 위해 등장한 개발 방법이 바로 Component Driven Development (CDD)

레고처럼 조립해 나갈 수 있는, 부품 단위로 UI 컴포넌트를 만들어 나가는 개발방법론

퀴즈
CDD를 활용한 개발 방법은 컴포넌트 단위로 만들어 페이지를 조립하는 개발 방식인 상향식 개발에 가깝습니다.


1-2. CSS-in-JS

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

구조화된 CSS가 필요하게 된 이유

인터넷이 만들어진 이후 기술의 발달과 함께 사용자들은 다양한 환경(디바이스)에서 인터넷을 사용하기 시작함
-> 이에 따라 개발자들의 CSS 작성 방식도 진화해옴

  • 점점 커지는 프로젝트의 규모와 복잡도, 팀원 수의 증가
  • 모바일이나 태블릿을 비롯한 다양한 디바이스의 등장
    • 웹사이트들이 다양한 디스플레이를 커버해야 하기 때문에 css는 더 복잡해짐

💡 CSS 작업을 효율적으로 하기 위해 구조화된 CSS의 필요성이 대두

css 구조화를 위한 다양한 시도

css 전처리기(CSS Preprocessor)의 등장
CSS가 구조적으로 작성될 수 있게 도움을 주는 도구

  • 하지만 이 CSS 전처리기(CSS Preprocessor) 자체만으로는 웹 서버가 인지하지 못하기 때문에 각 CSS 전처리기에 맞는 Compiler를 사용해야 하고 컴파일을 하게 되면 실제로 우리가 사용하는 CSS 문서로 변환됨
    • 이를 통해 CSS 파일들을 잘 구조화할 수 있게 되었고, 최소한 CSS 파일을 몇 개의 작은 파일로 분리할 수 있는 방법이 생김

SASS

CSS 전처리기 중에서 가장 유명한 SASS는 Syntactically Awesome Style Sheets의 약자로 CSS를 확장해 주는 스크립팅 언어입니다.

  • 즉, CSS를 만들어주는 언어로서 자바스크립트처럼 특정 속성(ex. color, margin, width 등)의 값(ex. #ffffff, 25rem, 100px 등)을 변수로 선언하여 필요한 곳에 선언된 변수를 적용할 수도 있음
  • 반복되는 코드를 한 번의 선언으로 여러 곳에서 재사용할 수 있도록 해주는 등의 기능을 가짐
  • SASS는 SCSS 코드를 읽어서 전처리한 다음 컴파일해서 전역 CSS 번들 파일을 만들어 주는 전처리기(preprocessor)의 역할을 함
    • 그러나 SASS가 ‘CSS의 구조화’를 해결해 주는 것의 장점보다 다른 문제들을 더 많이 만들어낸다는 것이 밝혀짐
      • 사람들은 전처리기(preprocessor)가 내부에서 어떤 작업을 하는지는 알지 못한 채, 스타일이 겹치는 문제를 해결하기 위해 단순히 계층 구조를 만들어 내는 것에 의지하게 됨
      • 그 결과 컴파일된 css 용량은 어마어마하게 커지게 됨

CSS 방법론의 대두

CSS 전처리기의 문제를 보완하기 위해 BEM, OOCSS, SMACSS 같은 CSS 방법론이 대두되었습니다. 각각의 장단점이 있으나 결국 세 방법론 모두 같은 지향점을 가지고 있음

방법론의 공통 지향점은 다음과 같음

  • 코드의 재사용
  • 코드의 간결화(유지 보수 용이)
  • 코드의 확장성
  • 코드의 예측성(클래스 명으로 의미 예측)

BEM
Block, Element, Modifier로 구분하여 클래스명을 작성하는 방법
Block, Element, Modifier 각각은 —와 __로 구분

  • 하지만 이러한 방법론들에서도 문제점이 발생하기 시작합니다. 클래스명 선택자가 장황해지고, 이런 긴 클래스명 때문에 마크업이 불필요하게 커지며, 재사용하려고 할 때마다 모든 UI 컴포넌트를 명시적으로 확장해야만 했음

    • 즉, 언어 로직 상에 진정한 캡슐화의 개념없이 개발자들은 유일한 클래스명을 만드는것에 의존할 수 밖에 없엇음

    💡 encapsulation : 객체의 속성과 행위를 하나로 묶고 실제 구현 내용 일부를 외부에 감추어 은닉하는 개념

CSS-in-JS의 등장 - Styled-Component
애플리케이션으로 개발 방향이 진화하면서 컴포넌트 단위의 개발은 캡슐화의 중요성을 불러왔고,
결국 CSS도 컴포넌트 영역으로 불러들이기 위해 CSS-in-JS가 탄생해서 이 문제를 정확하게 해결하게 됨

Styled-Component는 기능적(Functional) 혹은 상태를 가진 컴포넌트들로부터 UI를 완전히 분리해 사용할 수 있는 아주 단순한 패턴을 제공함

CSS 방법론들의 특징, 장단점


Chapter2. CDD 개발 도구

  • Styled Components의 기본 사용법을 익힌다.
  • HTML과 CSS로 작성된 요소를 Styled Components 컴포넌트로 바꿀 수 있다.
  • UI 개발 도구의 필요성을 이해한다.
  • Storybook의 기본 사용법을 익힌다.

2-1. Styled Components

Styled Components

React로 Component Driven Development를 할 수 있는 대표적인 도구
CSS를 컴포넌트화시키는, React 환경에서 사용 가능한 라이브러리

  • Styled Components는 앞서 배운 CSS in JS 라는 개념이 대두되면서 나온 라이브러리
    • 기존에 HTML, CSS, JS 파일로 쪼개서 개발하던 방법에서, React 등의 라이브러리의 등장으로 컴포넌트 단위 개발이 주류가 되었지만, CSS는 그렇지 못했다는 점에서 출발한 개념임
  • CSS in JS 라이브러리를 사용하면 CSS도 쉽게 Javascript 안에 넣어줄 수 있으므로, HTML + JS + CSS까지 묶어서 하나의 JS파일 안에서 컴포넌트 단위로 개발할 수 있게 됨
  • CSS in JS 라이브러리 중 현재 가장 인기 있는 라이브러리가 바로 Styled Components

Styled Components 설치

# 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를 불러와주면 사용 준비는 완료입니다!

import styled from "styled-components"

Styled Components 문법

  1. 컴포넌트 만들기
  2. 컴포넌트를 재활용해서 새로운 컴포넌트 만들기
  3. Props 활용하기
  4. 전역 스타일 설정하기

1. 컴포넌트 만들기


Styled Components는 ES6의 Templete Literals 문법을 사용합니다. 즉, 따옴표가 아닌 백틱을 사용한다.

이렇게 만든 컴포넌트를 React 컴포넌트를 사용하듯 리턴문 안에 작성해주면 스타일이 적용된 컴포넌트가 렌더된다.

import styled from "styled-components";

const PinkBtn = styled.button`
  background-color:pink;
  color:white;
  font-size:60px;
`;

export default function App() {
  return <PinkBtn>pinkpink</PinkBtn>
}

2. 컴포넌트를 재활용해서 새로운 컴포넌트 만들기

컴포넌트를 선언하고 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>
    </>
  );
}

3. Props 활용하기

Styled Component로 만든 컴포넌트도 React 컴포넌트처럼 props를 내려줄 수 있음

  • 내려준 props 값에 따라서 컴포넌트를 렌더링하는 것도 가능

Styled Components는 템플릿 리터럴 문법( ${ } )을 사용하여 JavaScript 코드를 사용할 수 있습니다. props를 받아오려면 props를 인자로 받는 함수를 만들어 사용하면 됨

1) Props로 조건부 렌더링하기

위 코드의 경우는 삼항연산자를 활용해 <Button> 컴포넌트에 skyblue 라는 props가 있는지 확인하고, 있으면 배경색으로 skyblue를, 없을 경우 white를 지정해주는 코드

import styled from "styled-components";
//받아온 prop에 따라 조건부 렌더링이 가능합니다.
const Btn = styled.button`
  background-color:${(props)=> (props.skyblue ? 'skyblue' : 'red')};
  color: white;
`
export default function App() {
  return (
    <>
      <Btn skyblue>나는 하늘색</Btn>
      <Btn>나는 정열의 빨강</Btn>
    </>
  )
}

2) Props 값으로 렌더링하기

3번과 비슷하게, props의 값을 통째로 활용해서 컴포넌트 렌더링에 활용할 수 있음

똑같이 삼항연산자를 사용하고 있지만, 이번에는 props.color 가 없다면 white를, props.color 가 있다면 props.color의 값을 그대로 가져와서 스타일 속성 값으로 리턴해주고 있는 것을 볼 수 있습니다. 그 결과 color 라는 이름으로 받은 props의 값으로 배경색이 지정된 것을 확인할 수 있음

꼭 삼항연산자만 사용해야하는 것은 아닙니다. JavaScript 코드라면 무엇이든 사용할 수 있으므로 원하는 값을 사용할 수 있도록 함수 코드를 만들어서 사용하면 됨

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 함수를 불러와야함

import { createGlobalStyle } from "styled-components";

그 다음 이 함수를 사용해 CSS 파일에서 작성하듯 설정해주고 싶은 스타일을 작성한다.

const GlobalStyle = createGlobalStyle`
	button { 	// 태그종류임
	padding : 5px;
    margin : 2px;
    border-radius : 5px;
	}
`

이렇게 만들어진 <GlobalStyle> 컴포넌트를 최상위 컴포넌트에서 사용해주면 전역에 <GlobalStyle> 컴포넌트의 스타일이 적용된다.

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

예제

import styled from "styled-components";
import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
  button {
    padding : 10px;
    margin : 20px;
    border-radius : 100%;
  }
`;

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

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

전역 스타일링 적용전

적용후


2-2. Storybook

컴포넌트들을 문서화하고 관리할 수 있는 UI 개발 도구

컴포넌트 UI 개발을 위한 Storybook

Component Driven Development 가 트렌드로 자리 잡게 되면서 이를 지원하는 도구 중 하나인 Component Explorer (컴포넌트 탐색기) 가 등장함. Component Explorer에는 많은 UI 개발 도구가 다양하게 있는데 그중 하나가 Storybook

Storybook이 무엇인가요?

Storybook은 UI 개발 즉, Component Driven Development를 하기 위한 도구입니다. - 각각의 컴포넌트들을 따로 볼 수 있게 구성해 주어 한 번에 하나의 컴포넌트에서 작업할 수 있음
- 전체 UI를 한눈에 보고 개발할 수 있습니다.

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

Storybook 과 같은 UI 개발 도구를 왜 사용할까요?

  • Storybook은 기본적으로 독립적인 개발 환경에서 실행됩니다. 개발자는 애플리케이션의 다양한 상황에 구애받지 않고 UI 컴포넌트를 집중적으로 개발할 수 있음
  • 회사의 내부 개발자들을 위해 문서화(documentation)를 하여 회사의 UI 라이브러리로써 사용하거나, 
외부 공개용 디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로 사용할 수 있음

Storybook에서 지원하는 주요 기능

  • UI 컴포넌트들을 카탈로그화하기
  • 컴포넌트 변화를 Stories로 저장하기
  • 핫 모듈 재 로딩과 같은 개발 툴 경험을 제공하기
  • 리액트를 포함한 다양한 뷰 레이어 지원하기

Chapter3. useRef

React 애플리케이션을 만들 때 DOM을 직접 조작하는 것은 지양해야한다고 배웠음. 하지만, 개발을 하다보면 DOM을 직접 건드려야하는 상황이 발생하기도 함. 이럴 때 사용할 수 있는 것이 바로 useRef 라는 Hook 함수임

  • useRef 의 필요성에대해 이해한다.
  • useRef 가 필요한 상황들을 기억한다.
  • useRef 의 사용법을 이해한다.

DOM reference를 잘 활용할 수 있는 useRef

React로 모든 개발 요구 사항을 충족할 수는 없습니다. 아래와 같이 DOM 엘리먼트의 주소값을 활용해야 하는 경우 특히 그렇습니다.

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

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 onBtnclick = () => {
    	inputEl.current.focus();
    }
  
  return (
  	<>
    	<input ref={inputEl} type="text"></input>
		<button onClick={onBtnClick}>Focus the input</button>
    </>
  )
  
}

제시된 상황 제외한 대부분의 경우 기본 React 문법을 벗어나 useRef 를 남용하는 것은 부적절하고, React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용해야 함

예제 : useRef 를 사용할 수 있는 대표적인 상황

예제 1 : focus

import React, { useRef } from "react";

const Focus = () => {
  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);

  const handleInput = (event) => {
    console.log(event.key, event); 
    if (event.key === "Enter") {  //onKeyup이벤트의 key가 Enter라면 
      if (event.target === firstRef.current) {
        console.log(event.target, firstRef.current); 
        console.log(firstRef); //
        secondRef.current.focus();
        event.target.value = "";
      } else if (event.target === secondRef.current) {
        thirdRef.current.focus();
        event.target.value = "";
      } else if (event.target === thirdRef.current) {
        firstRef.current.focus();
        event.target.value = "";
      } else {
        return;
      }
    }
  };

  return (
    <div>
      <h1>타자연습</h1>
      <h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
      <div>
        <label>별 헤는 밤</label>
        <input ref={firstRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>메밀꽃 필 무렵</label>
        <input ref={secondRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>즐거운 편지</label>
        <input ref={thirdRef} onKeyUp={handleInput} />
      </div>
    </div>
  );
};

첫번째 콘솔 console.log(event.key, event);

e.key
ㅂ ㅕ ㄹ Enter
event
onkeyup이벤트

두번째 콘솔 console.log(event.target, firstRef.current);

event.target
이벤트가 발생한 인풋
firstRef.current
첫번째 input엘리먼트의 주소가 담긴 변수 firstRef의 current 속성
=== 첫번째 input엘리먼트

세번째 콘솔 console.log(firstRef);

input 엘리먼트 주소가 담긴 객체 형태의 변수

예제 2 : media playback

import { useRef } from "react";

export default function App() {
  const videoRef = useRef(null);
  const playVideo = () => {
    videoRef.current.play();
    console.log(videoRef);
  };
  const pauseVideo = () => {
    videoRef.current.pause();
  };
  return (
    <div>
      <div>
        <button onClick={playVideo}>play</button>
        <button onClick={pauseVideo}>pause</button>
      </div>
      <video ref={videoRef}>
        <source
          type="video/mp4"
          src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
        />
      </video>
    </div>
  );
}

console.log(videoRef);


종합퀴즈

CDD

  • 부품단위로 UI 컴포넌트를 만들어 나가는 개발을 진행할 수 있다.
  • React 환경에서 CDD를 지원하기 위한 툴 중 하나는 Storybook이 있다.
  • 재사용할 수 있는 컴포넌트를 개발할 수 있다
    -CDD를 활용한 개발 방법은 컴포넌트 단위로 만들어 페이지를 조립하는 개발 방식인 상향식 개발에 가깝다.

CSS in JS

  • CSS-in-JS에는 대표적으로 Styled-Components가 있다.
  • CSS 작성을 위해 여러 라이브러리를 사용하게 되므로, 번들의 크기가 커진다
  • CSS-in-CSS는 상태에 맞는 스타일을 전부 미리 만들어두기 때문에, 상태가 변하면 만들어둔 스타일을 바로 적용할 수 있다.
    • 반면, CSS-in-JS를 사용하는 경우, 상태가 변경되면 자바스크립트의 CSS 코드를 읽어와 파싱하는 단계부터 시작한다.
    • 따라서 CSS-in-CSS에 비해 CSS 적용이 느릴 수 밖에 없다.
    • CSS-in-JS를 사용하면 class 명이 빌드 시 유니크한 해시값으로 변경되기 때문에 별도의 명명 규칙이 필요하지 않다.

Storybook

  • 컴포넌트 재사용성, 테스트, 개발 속도를 향상시킬 수 있다.
  • 컴포넌트를 문서화 및 시각화할 수 있다.
  • Storybook은 기본적으로 독립적인 개발 환경에서 실행됨. 개발자는 애플리케이션의 다양한 상황에 구애받지 않고 UI 컴포넌트를 집중적으로 개발할 수 있음.

useRef

  • DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있다.
  • useRef를 통해 DOM을 직접 조작시 리렌더링이 되지 않는다.
  • useRef를 통해 값을 저장시 리렌더링 후 값이 초기화가 되지 않는다. 따라서 렌더링 여부에 상관없이 값을 유지하고 싶을때 사용되기도 한다.
  • useRef 를 남용하는 것은 부적절하고, React의 선언형 프로그래밍 원칙과 배치된다.

실시간 세션

스토리북

  • 스토리북으로 배포할수있다. 앱 자체가 아니라 앱 디자인시스템을 어떻게 할 것인지
  • 디자인측면에서 이런식의 컴포넌트를 만들 수 있어요 ~ 포트폴리오로도 활용할 수 있음
  • 규모가 큰 회사들은 컴포넌트 종류가 넘 많아서 따로 관리함. 대부분 스토리북 사용한다.

useRef

  • 리렌더링 안됏으면 좋겟을때 사용하는 쥐구멍
profile
중요한건 꺾이지 않는 마음이 맞는 것 같습니다

0개의 댓글