넘블 챌린지 [다른 색깔 찾기 게임]
넘블 챌린지 [다른 색깔 찾기 게임]
상태관리 라이브러리 사용하지 않고 구현 하기
GitHub Repository
https://github.com/jaewoong2/no-state-management-challenge/tree/master
프로젝트 바로가기
https://no-state-management-challenge.vercel.app/

스타일 없는 프로젝트 바로가기
https://no-state-management-challenge.vercel.app/simple

상태 관리 없이?
- App Component에서- Props로 내려주는- state들을 어떻게 최적화 하여 보내줄지 고민하게 되었다
시도 방법
- 
React.memo(Component, PropsAreEqual)사용하기(실패)- 
Header Component에서 매초 남은 시간을 카운트 다운 해줄 떄마다Boards Component가 무의미하게 리렌더링 되었다.
- 
PropsAreaEqaul을 사용해서Boards Component의 무의미한 리렌더링을 방지 하고자 하였다.- React.memo(Board, (prevProps, nextProps) => prevProps.stage === nextProps.stage)- Boards Component가- stage가 바뀔 떄만 리렌더링 되게 하였다.
 
- 
하지만, 리렌더링이 계속해서 되었다. 이유를 찾아보니 상위 컴포넌트의 리렌더링을 통한 자식 컴포넌트의 리렌더링은 막기 어렵다는 것이다. (막는 방법이 있을 것 같긴 한데... 못찾음 )
 
- 
- 
useReducer사용- 
사실 reducer보다useState를 통한 상태 관리가 익숙하고 편리한데,reduecr를 사용 한 이유는,Redux 와 같은 전역 상태 관리 라이브러리 를 사용하지 않기가 이번 프로젝트의 목표 이기 떄문에Redux와 비슷한 패턴을 갖는useReducer를 사용 하면 상태 관리가 조금더 쉽지 않을까? 라는 생각에서 사용 하였다.
- 
컴포넌트들이 리렌더링 되는 것을 비교해보았는데, useState를 통한 상태관리와 별 차이는 없었던 것 같다.
- 
장점: 이번 프로젝트는 App Component에서 모든 상태를 내려주어야 하는데useState를 통한 상태관리는App.tsx의 코드를 무의미하게 길어지게 만들었다.
- 
단점: reduecr파일을 따로 만들어야 하고, 조금 더 편하게 관리 하기 위해서reducer의 코드가 무의미하게 길어지는 경향이 있다.
 
- 
리팩토링 하며 알게된 부분
- 
React.Fragment <></>사용 VS 기존 Html Element 사용<main></main>- 
React.Fragment 를 사용 하여 각각 컴포넌트를 감쌌을 때, 
  남은시간이 매초 변경 됨에 따라서 각각 컴포넌트가 모두 리랜더링 된다. 
- 
<main></main>컴포넌트로 감싸 줬을 때,
  남은시간이 매초 변경 됨에 따라서 각각 컴포넌트가 리랜더링 되는 대신에, 
 <main></main>컴포넌트만 리랜더링 된다.- <Header />컴포넌트는 시간에 따라 바뀌어야 하기 때문에 정상적인 리렌더링
 
 
 결론: main 컴포넌트 하나만 리렌더링이 되는게 좋은 것인지, 모든 하위 컴포넌트가 리렌더링 되는 것이 좋은 것인지 모르겠으나, 각 컴포넌트가 비용이 많이 든다면, React Fragment를 사용하지 않는 것이 좋은 것 같다.
- 
주요로직
색상 관련 코드
// @utils/index.ts
export const getColor = ({
  r,
  g,
  b,
  a,
  weight,
}: {
  r: number;
  g: number;
  b: number;
  a: number;
  weight: number;
}) => {
  return `rgba(${r + weight}, ${g + weight}, ${b + weight}, ${a})`;
};
export const getRandomColor = (num: number) =>
  num > 1 ? Math.floor(Math.random() * num) : Math.random() * num;
export const getRgba = () => ({
  r: getRandomColor(255),
  g: getRandomColor(255),
  b: getRandomColor(255),
  a: getRandomColor(0.7) + 0.3,
});- getColor는- rgba()값으로 변경시켜주는 코드
- getRandomColor는- 0 ~ 255의 무작위 값을 반환 시켜주는 코드
- getRgba는- rgba값을 객채화 시켜주는 코드- <Boards />의- color State에 사용 위함
 
// @components/Boards/Boards.tsx
useEffect(() => {
  setColor({ ...getRgba(), weight: 100 - stage ** 0.7 * 6 });
}, [stage]);- stage가 변경될 때마다 랜덤- rgba값을 얻는다.
- stage가높아질 수록,- weight가줄어들면서 스테이지가 높을 수록 더욱더 찾기 어렵게 된다.
// @utils/index.ts
export const getNumbers = (stage: number) => {
  const col = Math.round((stage + 0.5) / 2) + 1;
  const area = Math.pow(col, 2);
  const answer = Math.floor(Math.random() * area);
  return {
    col,
    area,
    answer,
  };
};
// @components/Boards/Boards.tsx
const { col, answer, area } = useMemo(() => getNumbers(stage), [stage]);- stage에 따른- col: 가로 사각형 갯수,- area: 전체 사각형 갯수,- answer: 정답 index를 얻는다.
수정전
// @components/Boards/Boards.tsx
const boardStyle = useCallback(
  (isAnswer: boolean): React.CSSProperties => ({
    backgroundColor: isAnswer
      ? `${getColor(color)}`
      : `${getColor({ ...color, weight: 0 })}`,
  }),
  [color]
);
const boardWrapperStyle = useMemo(
  (): React.CSSProperties => ({
    gridTemplateColumns: `repeat(${col}, 1fr)`,
  }),
  [col]
);- stage에 따른- style를 반환 해준다.
수정후
// @component/Boards/Boards.tsx
<Board
  key={`Board-${i}-${v}`}
  onClick={() => onClickBoard(i === answer)}
  backgroundColor={
    i === answer ? getColor(color) : getColor({ ...color, weight: 0 })
  }
/>// @components/Board/Board.style.tsx
import styled from "@emotion/styled";
import { BoardProps } from "./Board.type";
export const Li = styled.li<BoardProps>`
  background-color: ${({ backgroundColor }) => backgroundColor};
  cursor: pointer;
`;// @components/Boards/Boards.style.tsx
import styled from "@emotion/styled";
export const Ul = styled.ul<{ col: number }>`
  grid-template-columns: ${({ col }) => `repeat(${col}, 1fr)`};
`;- tsx 컴포넌트 내에 간결한 코드를 위해서 수정 하였다.
코드 내에서 고려한 특정 유저 행동과 그에 대한 대처
weight: 100 - stage ** 0.7 * 6,처음에는 weight 값을 100 - stage 로 했었는데, stage의 변화에 따라서 난이도가 눈에 띄게 변화하지 않았다.
stage 의 변화에 따라서 난이도를 올리 되, 정답을 잘 맞추는 유저의 경우 weight 값이 음수가 되는 것을 막기 위해서 100 - stage ** 0.7 * 6 값으로 하여 초반에 깰 때는 난이도를 올리다가, 어느정도 되면 난이도가 쉽게 바뀌지 않을 정도로 하였다.
활용한 라이브러리와 그 이유
- 활용한 라이브러리는 없으나 useReducer의 훅을 사용 하였다. 이유는 앞서 작성한 것과 같다.
프로젝트를 진행할 때 어려웠던 점/고민했던 부분과 해결방법
- 
프로젝트를 진핼 할 때, Boards Component의 불필요한 리렌더링을 어떻게 막을지가 가장 고민하였던 부분이다.
- 
앞서 작성 하였듯, 해결하지는 못했지만 많은 방법등을 사용해보았던 점에서 많은 경험을 하게 되었고 관심 컴포넌트 분리화,전역 상태 관리 라이브러리등이 왜 필요하게 되었는지 깨닫게 되었다.- 
관심 컴포넌트 분리화: 상위 컴포넌트에서 리렌더링 하면 하위 컴포넌트도 영향을 받기 때문에 최대한 분리화를 시켜서 불필요한 리렌더링을 막아야 한다.
- 
전역 상태 관리 라이브러리 사용: 상위컴포넌트에서props를 내리는 횟수를 줄이고, 유동적으로 상태를 관리를 함으로써 리렌더링을 막기 위함
 
- 
Author And Source
이 문제에 관하여(넘블 챌린지 [다른 색깔 찾기 게임]), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jwisgenius/넘블-챌린지-다른-색깔-찾기-게임저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)