Jeden Tag, aufrichtig und lustig.

4. 리액트 라이프사이클과 컴포넌트 Key값의 상호작용 이슈 본문

React

4. 리액트 라이프사이클과 컴포넌트 Key값의 상호작용 이슈

a-nanas 2023. 9. 19. 15:18

1. 발생 이슈

새로운 블록이 추가되는 기능을 구현하던 도중 컴포넌트의 key값과 관련하여 문제가 발생했다.
블록 추가 버튼을 눌렀을 때 기대했던 것은 한개의 블록이 추가되는 것이었는데,

실제로는 동일한 idx값을 가진 두 개, 세 개, 때로는 네 개의 다수의 블록이 동시에 추가되는 현상이 발생했다.

빠르게 버튼을 연타하지도 않았기 때문에
블록의 상태 관리와 추가되는 새 블록의 상태관리 로직을 살펴보고 콘솔로도 확인을 해보며 디버깅을 했다.

 

2. 문제 이유

블록이 렌더링 될 때마다 key값이 새로 할당 되는 로직이 문제였다.

리액트에서는 key라는 속성을 활용해서 자식 컴포넌트들을 유니크하게 식별한다.

그렇기 때문에 렌더링할 때마다 모든 블록 컴포넌트의 key 값이 변경 되면 리액트가 올바르게 컴포넌트를 식별할 수 없게 된다.

그러므로 리액트가 각 컴포넌트를 효율적으로 추적하고 렌더링하는 데 방해가 되어 이와 같은 이슈가 발생한 것이었다.

 

3. 해결 방법

해결책은 간단했다.

Block 컴포넌트의 key 값으로 block.id를 사용하고, 이 id는 블록이 처음 생성될 때 한 번만 설정하도록 코드를 변경했다.

각 블록의 key값이 렌더링에 의해 변동되지 않고 마운트단계에서 고유한 값을 갖도록 수정했다.

그 결과, 블록의 key는 블록이 리스트에서 사라질 때까지 일관성을 유지하게 되었다.

리액트는 각 컴포넌트를 정확하게 식별하고 렌더링 할 수 있게 되어 블록추가 기능이 정상적으로 작동하게 되었다.

 

* 리액트 컴포넌트의 생명주기는 크게 세 단계로 나눌 수 있다.

 

마운트 (Mounting): 컴포넌트가 생성되고 화면에 나타나는 단계

업데이트 (Updating): 컴포넌트의 상태나 속성(props)이 변경되어 다시 렌더링되는 단계

언마운트 (Unmounting): 컴포넌트가 화면에서 사라지는 단계

 

4. 정리

컴포넌트에 key값을 할당할 때는 고유한 값을 할당하는 것이 필요하다.

컴포넌트를 효율적으로 렌더링하는 데 영향을 미치기 때문이다.

 

아래는 문제 해결에 과정에서 수정한 코드이다.

 

수정 전

  const addBlock = (index) => {
    const newBlock = { id: Math.random(), design: 'default' }; // add unique id
    setBlocks((prevBlocks) => {
      const newBlocks = [...prevBlocks];
      newBlocks.splice(index + 1, 0, newBlock);
      return newBlocks;
    });
  };

  console.log(blocks, 'blocks');
 console.log(newBlocks, 'newBlocks');

  return (
    <>
      <Nav isLoading={isLoading} setIsLoading={setIsLoading} type='편집' />
      {blocks?.map((block) => (
        <Block key={`${page_idx}_${new Date().getTime()}`} idx={`${page_idx}_${new Date().getTime()}`} design={block.design} isOpen={isOpen} setIsOpen={setIsOpen} addBlock={addBlock} />
      ))}
      <EditorModal isOpen={isOpen} setIsOpen={setIsOpen} />
    </>
  );
};

 

수정 후 

  const addBlock = (index) => {
    const newBlock = { id: `${page_idx}_${new Date().getTime()}`, design: 'default' };
    setBlocks((prevBlocks) => {
      const newBlocks = [...prevBlocks];
      newBlocks.splice(index + 1, 0, newBlock);
      return newBlocks;
    });
  };


  return (
    <>
      <Nav isLoading={isLoading} setIsLoading={setIsLoading} type='편집' />
      {blocks?.map((block,idx) => (
        <Block key={block.id} idx={idx} design={block.design} isOpen={isOpen} setIsOpen={setIsOpen} addBlock={addBlock} />
      ))}
      <EditorModal isOpen={isOpen} setIsOpen={setIsOpen} />
    </>
  );
};

export default Editor;

'React' 카테고리의 다른 글

6. custom hook  (0) 2023.10.20
5. Form  (0) 2023.10.01
3. PropTypes  (0) 2023.08.18
2. state  (0) 2023.03.16
1. jsx 문법  (0) 2023.03.16