본문 바로가기

회고와 이슈/이슈와 정보

하나의 상태에 의존하는 다수의 컴포넌트에 벌어진 일

반응형

이게 무슨 일인가 싶었다.

NextJS 를 이용해 만든 명언 웹 사이트를 방문하여 TTS 를 동작시키니 다수의 카드가 동시에 실행되는 문제가 있었습니다. 문제를 인지하지 못하고 있었는데, 구글폼을 통해서 건의를 해주신 사용자분이 계셔서, 발견할 수 있었습니다.  정말 감사하다는 말을 여기서나마 다시 전해봅니다.

 

모든 카드가 동일한 텍스트를 렌더링하고 있습니다. 도플갱어가 따로업습니다.

 

문제의 발단이 되었던 사건과 코드 구조 분석

본론으로 와서 분명 해당 문제는 이전에 테스트했을 때는 발견되지 않았던 문제였습니다. 다만 최근에 통합테스트에 대해 학습하면서 이를 적용 해보면 좋을 것 같은 컴포넌트를 건드리긴 했습니다. 이 때, 최대한 prop 을 줄이고, 상태를 최상위에서 관리하기 위해 리팩터링 작업을 시도하였는데, 이 작업으로 괜히 잘 돌아가던 기능에 버그를 발생시킨 것으로 보입니다.

 

문제를 인지 후 tts 기능을 전역적으로 관리하기 위해 만들었던  useTTS 를 살펴보았습니다. 그리고 해당 훅을 import 하고 있는 Container 컴포넌트와 해당 컴포넌트의 자식 컴포넌트가 어떤 계층 구조를 이루고 있는지 확인해 보았습니다. 

 

확인 결과,  Container > List > Card 형태였고, 앞서 useTTS 훅에서 반환받은 함수와 상태는 자식 컴포넌트로 prop 을 통해 전달하고 있는 상황이었습니다.

'use client'
import useTTS from '@/custom/useTTS'

// 중략
export default function QuoteContainer({ items }: PropsType) {

  const { setText, readText, progress, isPlaying } = useTTS()
  const ttsInfos = { setText, readText, progress, isPlaying }
  
  return () 
     // 중략
  }

 

문제의 원인은 

단순하게 생각해보면, useTTS는 QuoteCard 내부에서만 사용됨에도 불구하고, Container 컴포넌트에 위치해 있습니다.

 

이 경우 useTTS 는 1개의 단일 상태를 관리하는 것과 동일합니다. 반대로, Card 컴포넌트의 경우에는 명언의 수 만큼 컴포넌트가 생성됩니다. 즉,  Card 컴포넌트에서 특정 요소의 상태를 변경시킨다면, 각 요소에 개별적으로 대응하는 상태 변수를 만들거나 아에 Card 컴포넌트 자체에 상태를 정의하여 사용해야 합니다.

 

예를 들어, Card 컴포넌트가 20개 라면, 각각의 Card 컴포넌트는 서로 다른 메모리 공간을 차지하여 식별되는 키를 가지고 있게 됩니다. 그러나 Container 내의 useTTS 훅 내부에 정의된 상태는 ttsInfos 라는 객체를 통해 전달된다고 하더라도, 동일한 참조를 가진 복제본이 Card 컴포넌트의 수 만큼 map 함수를 통해 생성되는 것이므로 모든 Card 컴포넌트가 동일한 상태를 공유하게 되는 것입니다.

 

문제를 개선하려면?

이 문제를 개선하는 방법은 단순합니다. 현재 동일한 상태가 20개 복제되는 것이 문제이므로 서로 다른 메모리 공간을 참조하는 상태를 생성하게 하면 됩니다. 다시말해, useTTS 훅 자체를 Card 컴포넌트 내부에서 import 하여 사용하면 카드 컴포넌트가 명언의 수 만큼 생성될 때 그에 상응하는 상태가 생성되므로 문제를 개선할 수 있습니다.

 

다만, 해당 개선책의 아쉬운 점은 최대한 Card 컴포넌트는 프레젠테이션 역할로 두려고 하였으나, 이를 지키지 못하는 상황이 발생하기 때문입니다. 애초에 Card 컴포넌트 내부에서는 GSAP 애니메이션을 처리하기 위해 별도의 이벤트를 처리하고 있어서 분리했던 것인데,  현재 보이는 방법으로서는 이게 최선으로 보였습니다. 하지만 더 좋은 방식이 있다면 리팩터링하는 방향으로 나아가 봐야 겠습니다. 

 

개선결과

24.07.08 기준으로 개선방향에 따라서 코드를 수정해보니 정상적으로 동작하고 있는 것을 볼 수 있었습니다.

해당 사이트가 궁금하시다면, https://wise-sayings.com/  를 방문해주세요. 회원가입을 하지 않아도 대부분의 서비스 이용이 가능합니다.  애초에 회원가입을 넣는 이유 자체도 사용자 편의성을 높이기 위함이었지 최대한 비로그인 상태에서 활용할 수 있도록 만드려던게 목표였거든요.

 

 

반응형