프로젝트 2일차

개발 진행 내역

  • DynamicIcon 작업
    -> 1차적으로 완성

문제 해결

useEffect 두 번 렌더링 되는 문제

프로젝트에 필요한 redux 및 API 불러오기 등에 대해 공부하기 위해 코드를 작성해 보던 중, 특정 컴포넌트 내에서 API 호출을 하기 위해 작성한 useEffect 코드가 두 번씩 호출되어 불필요한 재렌더링이 발생했다.

'useEffect renders twice'라는 검색어로 구글링을 해보니, 이것이 React.Strictmode 에 의해 의도된 동작이라는 것을 알게 되었다. CRA로 리액트 프로젝트를 생성했을 때, index.js 내에서 App 컴포넌트를 불러오는 코드가 React.Strictmode 컴포넌트로 감싸져 있다는 정도 밖에 모르고 있었는데, 이번 기회에 React.Stricmode 에 대해서 공부해보았다.

(공식문서에는 당연히 관련된 부분이 명시되어 있었고, 다시 한 번 공식문서를 잘 보는것이 중요하다는 것을 배웠다.)

Strictmode 컴포넌트는 자식컴포넌트를 검사해서 권장되지 않는 코드나 예기치 못한 부작용 등을 에러메시지로 보여주는 역할을 한다.

Strictmode는 development mode 에서만 활성화 되고, production build 에는 어떤 영향도 주지 않는다.

또한, Strictmode가 적용된 컴포넌트들은 잠재적인 문제점이 있는 지 체크되는 과정에서 두 번씩 렌더링 된다.
-> 그래서 useEffect를 이용한 API 호출이 두 번씩 이루어지는 거였다.

공부 필요한 부분

propTypes

부모로부터 전달받은 props의 타입에 대한 검사를 실행하는 기능이다.

자식 컴포넌트에 전달된 prop 이 명시해둔 타입과 맞지 않을 경우에 콘솔에 에러를 표시해준다.

이는 성능상의 이유로 development mode 에서만 확인이 가능하다.

별다른 명시를 하지 않을 경우에 특정 prop은 필수적으로 전달받아야 하는 값이 아니라, 부모컴포넌트에서 전달되지 않아도 괜찮은 값이다. 하지만 isRequired 를 설정하게 되면 해당 prop 이 전달되지 않았을 때 오류가 뜨게 된다.

storybook

설치

npx sb init

위 명령어로 설치를 실행하면 .storybook 폴더와 stories 폴더가 생성된다.
그리고 package.json 의 script 부분에 'storybook'이라는 명령어가 추가된다.

.storybook 안에는 main.js 파일과 preview.js 파일이 생성되어 있다.

main.js 파일은 일종의 config 파일이다.
preview.js 또한 글로벌한 설정을 가능케 해주는 일종의 config 파일이라고 볼 수 있다.

stories 폴더는 우리가 실제로 스토리북을 활용하기 위해서 필요한 코드를 작성하게 되는 디렉토리이다.

실제 코드 작성해보기

stories 폴더 내에 파일이름.stories.js 형태, 즉 파일이름 가운데 stories라는 문구가 추가된 형태의 파일은 실제 스토리 파일이 아닌 하나의 페이지로 인식된다.

다음 코드는 Button 이라는 이름을 가진 스토리북 페이지를 만들고, 몇 가지 스토리를 만들어보기 위한 코드이다.

다음 3가지 단계를 통해 코드를 작성해주면 된다.

1) default export로 해당 페이지에 대한 기본정보를 가진 객체를 내보내준다.
-> 제목과, 어떤 컴포넌트가 이 페이지에 해당되는지 명시해준다.(해당 컴포넌트는 import 해주어야한다.)

2) 각각 story 라고 부르는, 해당 컴포넌트의 특정 형태를 내보내준다.
-> 해당 컴포넌트를 return 하는 함수를 export하는 형태로 코드를 작성하면 됨

3) 스토리북을 사용하는 가장 큰 이유 중 하나인, storybook 페이지에서 정해진 조건 내에서 동적으로 컴포넌트를 조작해볼 수 있도록 코드를 작성
-> 3-1) Template 함수 선언
-> 3-2) 특정 스토리에 해당 함수 복제(bind())
-> 3-3) 전달할 args 객체 생성

// src/stories/Button.stories.js

import Button from "../components/Button";

// 1) default export로 해당 페이지에 대한 기본정보를 가진 객체를 내보내준다.
export default {
  title: 'Button',
  component: Button
}

// 3-1) Template 함수 선언
const Template = (args) => <Button {...args} />;

// 2) 스토리를 만들어서 export!
export const Red = Template.bind({});//3-2) bind 로 함수 복제

// 3-3) 전달할 인수 선언
Red.args = {
  backgroundColor: "red",
  label: "Press me",
  size: "md",
};

기타

  • PropTypes.oneOf(['sm', 'md', 'lg'])

참고자료

https://ko.reactjs.org/docs/typechecking-with-proptypes.html

좋은 웹페이지 즐겨찾기