[React] 컴포넌트 디자인

hosik kim·2021년 11월 22일
0

With CodeStates

목록 보기
19/45
post-thumbnail

💡Component Driven Development


: Component-Driven Development(CDD, 컴포넌트 주도개발)은 컴포넌트를 모듈 단위로 개발하여 사용자 인터페이스(UI)를
구축하는 개발 및 설계 방법론이다.컴포넌트를 먼저 생성하고 결합(조립)하여 페이지를 만드는 상향식(bottom-up) 방법이다.

모듈 단위로 UI 컴포넌트를 제작하는 것은 많은 장점을 가진다.

CDD는 복잡한 UI를 가진 페이지를 간단한 컴포넌트로 단순화시킨다. 컴포넌트의 재사용성을 극대화하고, 페이지 단위의 개발에서는
불가능한 방식의 작업을 팀원들과 공유하여 생산성을 높일 수 있다. 또한 컴포넌트 단위로 테스트를 하여 발생 가능한
버그를 쉽게 파악하고 해결할 수 있다.

Tool: Component Explorer

: Component Explorer(컴포넌트 탐색기)는 CDD를 지원하는 도구로 사용되는 UI 라이브러리이다.
가장 유명한 컴포넌트 탐색기는 Storybook이 있다.

🔸Storybook


: Storybook은 독립적인 환경에서 실행되는 컴포넌트 탐색기이다. 사용되는 각 컴포넌트를 따로 볼 수 있게 구성해주어
한 번에 하나의 컴포넌트를 집중적으로 개발할 수 있으며, 애플리케이션을 탐색하지 않아도 전체 UI를 한 눈에 보고 파악할 수 있다.

Storybook은 재사용성을 높이기 위해 컴포넌트를 문서화하여 회사의 UI 라이브러리로 사용될 수 있다. 때문에 외부에 공개하기 위한
디자인 시스템(Design System)을 개발하기 위한 기본 플랫폼으로도 사용한다.

또한 자동으로 컴포넌트를 시각화하므로 관련된 상태에 대한 모든 경우를 시뮬레이션 해볼 수 있다.
Storybook은 테스트 및 개발 속도를 향상시키며 의존성을 걱정하지 않고 애플리케이션을 빌드할 수 있게 한다.

storybook 주요 기능

  • UI 컴포넌트 카탈로그 화
  • Stories에 컴포넌트 변화 저장
  • Hot module reloading과 같은 개발 툴 경험 제공
  • 리액트를 포함한 다양한 뷰 레이어 지원

Storybook 가이드

💡CSS in JS


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


: 사용자들이 다양한 환경(디바이스)에서 인터넷을 사용함에 따라 개발자의 CSS 작성 방식도 꾸준히 발전해왔다.

구조화된 CSS 등장 이유

  • 협업에서 CSS를 작성하는 일관된 패턴 부재
  • 다양한 디바이스의 디스플레이를 커버하며 복잡해진 CSS

CSS 구조화를 위한 시도

: 위와 같은 이유로 CSS 작업을 효율적으로 하기 위한 구조화의 필요성이 대두되었고 다양한 방법이 등장했다.

  1. CSS 전처리기
    : CSS 전처리기(CSS Preprocessor)는 CSS 문서를 작성할 때 생기는 문제점인 반복 및 번거로운 작업, 어려운 유지 보수등을
    프로그래밍 개념(변수, 함수, 상속 등)을 활용하여 해결했다. CSS 전처리기 자체만으로는 웹 서버가 인지하지 못하므로, 각 전처리기에
    맞는 컴파일러로 컴파일을 하면 실제 사용하는 CSS 문서로 변환이 된다. 이를 통해 CSS 파일들을 구조화하여 작은 파일로 분리할 수 있게 되었다.

    CSS 전처리기 중에서 가장 유명한 Sass(Syntactically Awesome Style Sheets)는 CSS를 만들어주는 스크립팅 언어이다.
    자바스크립트처럼 특정 속성의 값을 $ 기호를 사용해 변수롤 선언하여 필요한 곳에 선언된 변수를 적용하거나 재사용 할 수 있다.
    Sass는 SCSS코드를 읽어서 전처리한 다음 컴파일해서 전역 CSS 번들 파일을 만들어준다. 하지만 전처리기는 스타일이 겹치는 문제를 해결해주지만
    추가적인 컴파일러가 필요하고, 디버깅이 어려우며, 컴파일된 CSS 용량이 커지는 문제를 낳았다.

  2. CSS 방법론
    : CSS 전처리기의 문제를 보완하기 위해 BEM, OOCSS, SMACSS 같은 CSS 방법론이 대두되었다.
    방법론들의 공통적인 지향점은 코드의 재사용, 간결화(유지보수 용이), 확장성, 예측성(클래스 명으로 의미 예측)이다.

    대표적인 CSS 방법론은 BEM이다. BEM은 Block, Element, Modifier로 구분하여 클래스명을 작성하는 방법이다.
    클래스 이름을 Block__Element와--Modifier처럼 규격화하여 일관된 코딩 구조를 만들어 준다.

    하지만 전처리기와 방법론 모두 언어 로직 상 캡슐화의 개념이 없었으므로, 개발자들은 스타일을 정의하기 위해 유일한 클래스명을
    선택하는 것에 의존할 수 밖에 없었다.

  3. CSS in JS
    : CSS의 캡슐화를 위해, 컴포넌트 기반으로 CSS를 작성하기 위해 Css in JS가 탄생했다. CSS in JS는 하나의 기능을 하는 컴포넌트 내부에
    HTML/CSS/JS가 모두 존재하는 형태이다. Styled-Component, Emotion, Styled-jsx 등이 있다.

🔸Styled-Component


: CSS i JS 관련 React 라이브러리에서 가장 인기 있는 Styled-Component는 컴포넌트 기반으로 CSS를 작성할 수 잇게 하는 라이브러리이다.
기존 CSS 문법으로 스타일 속성이 추가된 React 컴포넌트를 만들 수 있고, 컴포넌트 안에 CSS가 캡슐화되었으므로 외부 CSS 파일과 연결을 위한
네이밍이나 최적화를 신경쓸 필요가 없다. 또한 CSS 파일간에 의존성을 신경쓰지 않아도 된다.

Styled-Component 특징

  • Automatic critical CSS
    화면에 어떤 컴포넌트가 렌더링 되었는지 추적해서 해당 컴포넌트에 대한 스타일을 자동으로 삽입한다.

  • No class name bugs
    각 태그마다 스스로 유일성을 가진 hash값 같은 클래스 이름을 생성한다.

  • Easier deletion of CSS
    모든 스타일 속성이 특정 컴포넌트와 연결돼있으므로 컴포넌트와 스타일을 한 번에 삭제할 수 있다.

  • Simple dynamic styling
    React의 props와 전여 속성을 기반으로 간단하게 컴포넌트에 동적으로 스타일 속성을 부여한다.

  • Painless maintenance
    컴포넌트에 스타일을 상속하는 속성을 찾아 다른 CSS를 이용하여 스타일 속성을 정의하면 된다. 이외의 것들은 Styled-Component가
    알아서 처리해준다.

Styild-Component 단점

  • Slow interaction
    CSS in JS는 상태가 변경되면 다시 JS의 CSS 코드를 읽어와서 파싱하므로 퍼포먼스 저하를 유발한다.

하지만 개발 속도를 빠르게 해주며 휴먼 리소스를 줄일 수 있다는 점 때문에 CSS in JS를 사용한다.

Tagged template literals

Styled-Component는 ES6 문법인 Tagged template literals를 사용하여 컴포넌트에 스타일을 추가한다. 만드려는 컴포넌트의 이름과
사용하려는 tag를 styled.tag로 정의하고 back-tick(`) 내부에 스타일 속성을 정의한다.

특정 tag를 상속 받을 때는 styled(tag) 처럼 소괄호로 감싼 뒤 재정의하고 싶은 속성니ㅏ 다른 속성을 새로 정의하면 된다.
또한 props를 통해 스타일 속성을 전달하는 것이 가능하며, 함수를 사용하여 전달받은 속성을 사용하거나 default 속성을 사용할 수도 있다.

import styled from "styled-components"

const Input = styled.input`
  padding: 0.5em;
  margin: 0.5em;
  color: "black";
  background: ${(props) => (props.primary ? "papayawhip" : "yellow")};
  border: none;
  border-radius: 3px;
`;

const Input2 = styled(Input)`
  color: ${(props) => props.inputColor || "black"};
`;

export default function App() {
  return(
   <div>
     <Input defaultValue="김코딩" type="text" prmary/>
     <Input2 defaultValue="박해커" type="text" inputColor="red" />
    </div>
  )
}

Styled-Component 추가 사용법

: Styled Component 정의는 render 메소드 밖에 정의해야한다. 만약 리터문 안에서 정의되면 컴포넌트가 리렌더링 될 때마다 스타일 속성을
지닌 컴포넌트가 매번 새로 정의되고, 이는 렌더링 속도 저하에 큰 영향을 끼친다.

const ModalContainer = styled.div`
 /* ... */
`;

const Modal = () => {
  /* ... */
};

  return (
    <ModalContainer>
      /* ... */
    </ModalContainer>
   );

중첩 스타일링을 위해서 aspersand(&)를 사용한다. 모든 컴포넌트를 Styled Component로 정의하지 않고
CSS와 혼용하는 경우 충돌을 피하는 데에 사용할 수 있다. &를 사용하지 않으면 평범한 후손 셀렉터처럼 동작한다.

const Fruit = styled.div`
 &:hover {
  background-color: red;
}

&.apple {
  color: red;
}
`
.peach & {
  color: pink;
}

.blueberry {
 color: blue; 
}
`;

function App() {
 return (
  <React.Fragment>
   <Fruit>기본 Fruit Styled-Component</Fruit>
   <Fruit className="apple">
    Fruit에 apple 클래스를 적용 (&.apple)
   </Fruit>
   <div className="peach">
    <Fruit>
     peach 클래스를 가진 엘리먼트를 부모로 가진 Fruit (.peach &)
    </Fruit>
   </div>
   <Fruit>
    <span className="blueberry">ampersand(&)를 사용하지 않음</span>
   </Fruit>
  </React.Fragment>
);
}

💡Hook:useRef


: React에서는 DOM 엘리먼트에 직접 접근하여 메소드를 이용할 방법이 제한적이다. 다음과 같이 DOM 엘리먼트의 주소값을 활용해야하는 경우
useRef Hook을 사용한다.

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

useRef는 DOM 노드, 엘리먼트, 그리고 리액트 컴포넌트의 주소값을 참조하여 DOM 엘리먼트의 특정 메소드를 활용할 수 있다.

const 주소값_변수 = useRef(참조자료형);

return (
 <div>
    <input ref={주소값_변수} type="text" />
      {/* ref 속성에 주소값_변수를 값으로 할당하면 */}
      {/* 주소값_변수에는 input DOM 엘리먼트의 주소가 담김 */}
      {/* 이후 다른 컴포넌트에서 input DOM 엘리먼트 활용 가능 */}
 </div>
);

useRefcurrent 속성을 가지고 있는 객체를 반환하고 인자로 넘어온 초기값을 current에 할당한다.
매번 렌더링을 할 때 useRef는 동일한 객체를 제공한다. current 속성의 값은 state와 다르게 변경되어도
컴포넌트를 다시 렌더링되지 않으며, 컴포넌트가 리렌더링되어도 값을 유지한다. 이런 특성을 가지는 변경될 수 있는 값을 다룰 때 useRef
사용할 수 있다.

focus 예제

import React, { useRef } from "react";

const Focus = () => {
 const firstRef = useRef(null);
 const secondRef = useRef(null);
  
 const handleInput = (event) => {
  console.log(event.target); // <input></input>
   if(event.key === "Enter") {
     secondRef.current.focus();
     event.target.value = "";
   } else if (event.target === secondRef.current) {
     firstRef.current.focus();
     event.target.value = "";
   } else {
     return;
   }
  }
};

return (
 <div>
  <div>
   <label>hello</label>
   <input ref={firstRef} onKeyUp{handleInput} />
  </div>
  <div>
    <label>world</label>
    <input ref={secondRef} onKeyUp{handleInput} />
  </div>
 </div>
 );
};

export default Focus;
profile
안되면 될 때까지👌

0개의 댓글