티스토리 뷰

미션 PR - https://github.com/woowacourse/react-payments/pull/27

 

1. React.memo

 React.memo는 고차 컴포넌트(Higher Order Component) 이다. 말그대로 memo의 인자로 컴포넌트를 넘겨 사용할 수 있다. memo를 사용하면 메모이징을 이용하여 기존과 props가 다른 경우에만 리렌더링이 일어나도록 할 수 있다. 보통 성능최적화를 위해 많이 사용된다.

 

 페이먼츠 미션 중 카드정보를 입력 받는 페이지는 카드 번호, 만료일 등 다양한 인풋을 처리하기 위해 그만큼의 상태를 가져야했다. 때문에 하나의 인풋만 변경되어도 페이지의 모든 요소들이 리렌더링 됐다. 당시에는 이를 불필요한 리렌더링이라고 판단하여 memo로 컴포넌트를 래핑하여 리렌더링이 일어나지 않도록 하였다. 하지만 리뷰어님께서 이는 성급한 최적화라고 말씀해주셨다.

크리스님의 리뷰

리액트 공식문서에서도 다음과 같이 설명한다.

React.memo (리액트 공식문서)

 이전 미션에서 성급한 최적화에 대해 알게 됐다. 메모이제이션과 props의 얕은 비교도 결국 비용이기 때문에 단순히 리렌더링 횟수를 줄이기 위해 사용해서는 안된다는 것이 주요했다. 사용자가 체감할 수 있을 정도의 속도 변화 혹은 직접 테스트를 하여 유의미한 성능 차이를 확인할 때 도입해도 늦지 않다. 

 

 

2. 스토리북

Storybook.js.org/docs

 스토리북은 React, Vue, Angular 등의 환경에서 컴포넌트를 빠르고 간편하게 확인할 수 있도록 도와주는 툴이다.

 

 리액트의 공식문서에서도 나와있듯이 컴포넌트를 만들 때 중요하게 고려해야하는 점은 바로 재사용성이다. 컴포넌트를 재사용성이 높게 하기 위해서는 필연적으로 컴포넌트가 하는 일이 명확하고 단순해야한다. 때문에 보통 재사용성이 높은 컴포넌트는 크기가 작다. 이러한 크기가 작은 컴포넌트를 제대로 만들었는지 그리고 기능이 제대로 동작하는지 확인하려면 우리는 이러한 컴포넌트를 일일이 import 하여 확인해줘야 한다. 이는 굉장히 귀찮은 일이 아닐 수 없다. 예를 들어 결제 정보를 입력받는 인풋을 재사용 컴포넌트로 만들어 확인하기 위해서는 매번 결제 정보를 입력 받는 페이지로 이동해야 한다. 코드를 변경하고 다시 저장을 한다면 결과를 확인하기 위해 다시 맨 처음 페이지에서 결제 페이지로 이동해야한다... (귀찮쓰;;)

 

 스토리북을 사용한다면 위와 같은 불편함을 해소할 수 있다. 컴포넌트를 전체 앱에서 동작하게 하는 것이 아니라 컴포넌트 단위로 독립적으로 동작하게 할 수 있다. 이제 결제 정보를 입력받는 인풋을 결제 정보 페이지가 아니라 스토리북에서 수정과 확인을 동시에 진행할 수 있다. 뿐만 아니라 스토리북 자체를 배포하여 디자이너, 기획자에게 보여줄 수 있어 업무 생산성을 높일 수 있다.

 

 이번 페이먼츠 미션에서는 코드를 짜기 전에 재사용성이 높은 컴포넌트와 그렇지 않은 컴포넌트를 분리하는 시간을 가졌다. 이후 재사용성이 높은 컴포넌트를 스토리북을 활용하여 먼저 작성하였다. 다른 컴포넌트와 페이지들은 미리 만들어 놓은 재사용성이 높은 컴포넌트를 레고처럼 끼우는 식으로 진행하니 전체적으로 생산성이 높아졌다. 무엇보다도 컴포넌트를 하나하나 쌓아가며 성을 만들어가는 과정이 너무나도 재밌고 즐거웠다. 스토리북도 테스트 코드처럼 작성할 때는 귀찮지만 나중에는 꼭 필요해지는 순간이 온다고 생각한다.

 

 

3. box-sizing

box-sizing 속성은 요소의 너비와 높이를 계산하는 방법을 지정한다.

MDN 문서에도 나와있듯이 box-sizing: border-box를 사용하는 편이 크기를 계산하기 편하다. CSS를 normalize 할 때, 모든 요소에 box-sizing: border-box를 주면 더 편하게 스타일을 만들 수 있다.

 

 

4. Styled Component

 Styled Component는 HTML tag에 스타일링을 입힌 컴포넌트이다. Styled Component를 사용하면 스타일링을 위해 별도의 id나 className을 줄 필요가 없다. 무엇보다도 CSS에서 JS 문법을 사용할 수 있어 상태를 넘기거나 조건부로 CSS 속성을 변경할 수 있어 편리하다는 장점이 있다. (CSS-in-JS) 하지만 네이밍에 구분을 두지 않으면 일반 컴포넌트와 구분이 되지 않을 수 있다. 때문에 이번 미션에서는 스타일 컴포넌트는 'Styled' prefix를 사용하여 일반 컴포넌트와 네이밍을 구분하였다. 

import styled from 'styled-components';

const Styled = {
  Title: styled.div`
    margin-top: 105px;
    margin-bottom: 80px;
    font-size: 24px;
    text-align: center;
  `,
  InputContainer: styled.div`
    margin: 0 auto;
    margin-top: 30px;
    width: 240px;
    border-bottom: 1px solid black;
  `,
  ButtonContainer: styled.div`
    position: absolute;
    bottom: 25px;
    right: 25px;
  `,
};

export default Styled;

 

 특히 Styled Component는 JS 문법을 이용하여 상태를 넘겨줄 수 있기 때문에 재사용성이 높은 컴포넌트의 스타일을 사용하는 쪽에서 변경할 수 있도록 할 수 있다. 아래의 Button Component는 styles 객체를 받아 스타일을 변경할 수 있다.

// Button.js
export const Button = ({ children, styles, ...props }) => {
  return (
    <Styled.Button {...props} styles={styles}>
      {children}
    </Styled.Button>
  );
};

// Button.style.js
const Styled = {
  Button: styled.button(({ styles }) => ({
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    color: '#000',
    fontWeight: '700',
    fontSize: '14px',
    cursor: 'pointer',
    ...styles,
  })),
};

// 버튼을 사용하는 곳
<Button styles={{ color: COLOR.MINT }}>확인</Button>

 만약 개발자가 Button Styled Component의 styles 객체에 유효하지 않은 CSS 속성을 넘기면 어떻게 될까? 현재 코드에서는 이를 막을 방법이 없고 별도의 예외처리도 보이지 않는다. 하지만 비정상적인 속성이 styles로 넘어오더라도 코드는 정상적으로 실행되는데 이는 브라우저가 유효하지 않은 CSS 속성을 무시하기 때문이다. 코드가 정상적으로 실행되더라도 이는 바람직하지 않다. 타입스크립트를 사용하면 styles 객체에 CSS 속성만 들어오게 할 수 있다고 한다. 다음 미션에 타입스크립트를 사용하며 도전해봐야겠다.

 

 

5. isValidSomeState를 상태가 아닌 함수, 변수로 사용하기

Form을 제어 컴포넌트로 관리하기 위해서는 input 값을 state로 관리해야한다. 이 때, input 값이 올바른지 검증하는 validation을 별도의 상태로 관리하는 것은 바람직하지 않다. validation은 별도의 함수나 변수로 관리하는 것이 좋다. 불필요하게 state를 늘리면 렌더링 횟수가 늘어나게 된다. 즉, 상태로 관리하지 않아도 되는 것들은 최대한 함수와 변수를 사용하도록 하자.