Rendered more hooks than during the previous render (React)

Steve·2023년 1월 13일
1

Description

티켓설명

  • 스토리카드에 서브헤드라인이 새롭게 추가되면서 서브헤드라인과, 헤드라인의 바텀 패딩이 유연하게 바껴야하는 상황 (8px <-> 12px)

티켓해결 위해 고려해야할 사항

  • 서브헤드라인의 위치, 서브헤드라인 show On/Off, Art 위치, 헤드라인의 위치, 반응형

이슈 발생 이유

  • 반응형에 대한 잘못된 설정

UAT 완료 당시 발견 못한 이유

  • UAT를 하는 동안은 시험중이던 페이지빌더 페이지에서 스토리 카드를 유지한 채 진행이 되는데 이번 이슈 발생 시점은 새롭게 스토리카드를 가지고 올 때였다. 하여 과거에 발견되지 않았음.

이슈 에러 문구

"Rendered more hooks than during the previous render"

살며 처음 본 에러다. 대충 hook을 잘못 썼다는 거긴한데.
stackoverflow 검색 결과 같은 문제를 겪은 사람들이 받은 답변들은 아래와 같다.
1. Move all hooks to the top level of the function component
2. Avoid using hooks within conditions to fix the problem.

막막하다. 코드에 hook이 적힌 곳이 너무도 많다.
문제가 발생한 곳의 커밋을 확인하기위해 무자비하게 커밋을해논 곳을 확인했고 여러번 checkout한 결과
아래 그림처럼 드래그한 부분을 기점으로 문제 발생함을 확인.
(이번을 계기로 저런 무식한 커밋들은 하면 안된다는것을 깨달음. 보자마자 '헉'하게됨.

git reset을 잘 활용하자

물론 의미있는 수행단위의 커밋은 필요함. 만약 모든 수행을 하나의 커밋으로 몰아 넣었더라면 이번 이슈해결이 더 오래걸렸을 것이다.)

6c9c623, b400221 사이를 기점으로 문제 확인.
차차 해결과정이 잡혀나가는 느낌.
저 둘 사이의 코드 차이를 어떻게 봐야하는지를 여기저기 자문을 구하다보니 아래와 같은 CLI로 찾을 수 있었다.

git diff 6c9c623 b400221 > my.patch
git apply my.patch
2개의 커밋간의 차이를 보는 방법 링크

수많은 파일들과 코드에서 4개의 파일로 좁혀졌고(훅에 의해 발생됐다라는 전제를 가지고 있었기 때문에 여기서부턴 가벼운 노가다라 관련부분을 지워보고렌더링, 지워보고 렌더링)
위의 과정 끝에 아래의 파일의 코드에 의해 에러발생 확인.

components/features/common/custom-fields/story-card.js

const browserSize = useBrowserSize();

위의 코드는 반응형 구분을 줄 때 사용하는 코드이다. 위에 적어 놓은 이슈 에러 문구 통해 해결방법데로라면,
1. '모든 훅들을 함수형 컴포넌트 최상단에다 올려라.
2. 조건문 안에 훅을 쓰지마라.

훅을 위에도 올려보고 조건문을 찾아보려해도 너무 복잡하고 해결이 안된다.
보다보니 문득 'storycard에서 왜 분기처리를 했지?' 조선닷컴의 가장 중요한 심장과도 같은 storycard에 부하를 주는 행위를 하고 있었다.(if문 분기처리를 Headline 컴포넌트에서 해도 되는것을 storycard 컴포넌트에서 하고 있었음)
Headline컴포넌트를 보니 이미 과거의 다른 개발자가 다른 방법으로 반응형을 그곳에서 사용하고 있었고 그것을 그대로 이용해서 반응형을 썼으면 됐었던 것이다.

조선닷컴 코드에서 반응형을 사용하는 방법이 아래와 같이 3가지 방식이 있다.

1번째 방법(모바일 사이즈인지 아닌지만 구분)

import { isMobileBP } from '../../../../utilities';
const [isMobile, checkMobile] = useState(isMobileBP());
useEffect(() => {
  window.addEventListener('resize', () => {
    checkMobile(isMobileBP());
  });
});
if (isMobile) {
  blabla~~~~~~~~~~~~
}
if (!isMobile) {
  blabla~~~~~~~~~~~~
}

2번째 방법(데스크탑, 태블릿, 모바일 사이즈를 세세하게 구분)

import { useBrowserSize } from '~/utilities/browser-size';
const browserSize = useBrowserSize(true);
if (browserSize === ‘lg') {
  blabla~~~~~~~
}
if (browserSize === ‘md') {
  blabla~~~~~~~
}
if (browserSize === ’sm') {
  blabla~~~~~~~
}

3번째 방법(아직 건드려 본적은 없지만 SSR과 관련된거같다)

import { browserSizes } from '~/utilities/browser-size';
if (size === browserSizes.SMALL) {
  blabla~~~~~~~
}
if (size === browserSizes.MEDIUM) {
  blabla~~~~~~~
}
if (size === browserSizes.LARGE) {if (size === browserSizes.SMALL) {
  blabla~~~~~~~
}

결론

리액트를 사용하는 이유 중 하나인 컴포넌트의 분리에 대한 고려를 했어야 했다.
story-card.js에 있을 필요없는 hook 및 함수를 아래 파일 headline.js에 옮기니 해결됨
Headline컴포넌트에 반응형 2번째 방법을 과거의 개발자가 사용하고 있었으니 그대로 사용.

/Users/chosunbiz/Chosun-PageBuilder-Fusion-Features/components/features/common/custom-fields/story-card.js

/Users/chosunbiz/Chosun-PageBuilder-Fusion-Features/components/features/story-card/_children/headline.jsx

이번 이슈를 겪으며 2가지 느낀바..

  1. Headline에서 필요한 분기처리를 StoryCard에서 굳이 해놨기에 발생한 문제. 컴포넌트 분리라는 장점을 헛되게 사용하면 안됨.
    또한 가장 많이 사용하는 Storycard에 해놓으면 과부하를 야기할 수도.
  2. 불필요한 커밋은 바로바로 제거. (git reset에 대해서 무지한 채 개발을 해와서 수많은 커밋들이 쌓임)
  3. 기능 구현의 커밋은 필요. (그 사이사이 잘게 쪼개 놓은 커밋 덕분에 문제점을 찾기가 수월했음)
profile
Front-Dev

0개의 댓글