React는 복잡한 시스템으로 예기치 못한 문제가 발생할 수 있으며, 특히 기본 메커니즘에 익숙하지 않은 사용자에게는 결함을 감지하기 어려울 수 있습니다. 이러한 문제는 해결하기 어려운 특성으로 인해 해결이 어려울 수 있습니다.

특정 프로퍼티를 기반으로 컴포넌트를 조건부로 렌더링할 때 “여러 소품을 사용한 조건부 렌더링”이라는 예기치 않은 문제가 발생할 수 있습니다. 이 문제를 철저히 이해하기 위해 컴포넌트에 키를 사용하는 React의 고유한 접근 방식을 사용하여 그 복잡성을 파헤치고 가능한 해결책을 모색해 보겠습니다.

React 컴포넌트는 항상 독립적이지 않다

React의 복잡하지 않은 구조는 이 자바스크립트 라이브러리에 대한 숙련도를 높이는 주요한 근거가 됩니다. 그러나 수많은 이점을 제공하지만 불완전성도 무시할 수 없습니다.

React 개발에서 발생할 수 있는 잠재적인 문제 중 하나는 컴포넌트가 다양한 프로퍼티를 사용하여 조건부로 렌더링되어 의도하지 않은 동작이나 오류가 발생하는 시나리오입니다.

React는 효율성과 성능을 염두에 두고 설계되었기 때문에 비슷한 시나리오에 직면했을 때 두 가지 컴포넌트 중 하나만 렌더링하여 최적화하는 방법을 선택합니다. 이를 통해 메모리 관리가 개선되고 사용자 경험이 더 부드러워집니다. 결과적으로 초기 컴포넌트 내에서 정의된 모든 상태는 이후 재렌더링에서도 그대로 유지되어 안정성과 연속성을 제공합니다.

주어진 예는 다음과 같습니다:

 import { useState, useEffect } from "react"

export function Counter({name}) {
  const [count, setCount] = useState(0)

  return(
    <div>
      <div>{name}</div>
      <button onClick={() => setCount(c => c - 1)}> - </button>
      <br />
      <button onClick={() => setCount(c => c + 1)}> + </button>
    </div>
  )
}

Counter 컴포넌트는 프로퍼티로 전달된 값에 접근하기 위한 React의 일반적인 관행인 디스트럭처링을 통해 부모의 props 객체에서 명명된 프로퍼티를 추출합니다. 추출된 값은 “You said”라는 텍스트가 포함된 div 엘리먼트를 렌더링하기 위한 매개변수 역할을 합니다. 또한 이 카운터에는 내부 상태에 저장된 현재 카운트를 수정하는 데 사용할 수 있는 두 개의 버튼이 있습니다. 한 버튼은 카운트를 줄일 수 있고 다른 버튼은 카운트를 늘릴 수 있습니다.

이 글도 확인해 보세요:  Windows Defender "서비스를 시작할 수 없습니다" 오류를 수정하는 방법

앞서 언급한 코드는 애플리케이션 아키텍처 내에서 사용되었으며, 기능적으로 문제가 없는 것으로 간주되며 내재적인 불일치나 오류가 없습니다. 그러나 후속 프로그래밍 부분, 특히 상태 변수 ‘카운터’를 통합하는 ‘앱’ 구성 요소의 구현에서 관찰된 이상 현상의 원인이 있는 것으로 보입니다.

 import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return(
    <div>
      { isKingsley ? <Counter name="Kingsley" /> : <Counter name="Sally" /> }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

앞서 언급한 코드는 기본적으로 “Kingsley”라는 레이블이 붙은 카운터를 표시합니다. 카운터를 5까지 증가시키고 “스왑” 버튼을 클릭하면 이후에는 “Sally”라고 표시된 카운터가 표시됩니다.

그러나 배터리를 교환할 때 카운터가 초기 상태인 0으로 돌아가지 않는 문제가 발생합니다.

두 컴포넌트가 특정 시퀀스에서 동일한 요소를 표시하기 때문에 문제가 발생합니다. 그러나 React는 ‘이름’ 속성이 유일한 차이점이기 때문에 ‘킹슬리’ 카운터가 ‘샐리’ 카운터와 다르다는 것을 인식하지 못합니다. 유감스럽게도 React는 요소를 구분하는 데 이 특성을 활용하지 않습니다.

이 문제를 해결하기 위한 한 가지 가능한 해결책은 두 카운터를 구분하기 위해 문서 객체 모델(DOM)의 구조를 변경하는 것입니다. 구체적으로, 초기 카운터는

태그 안에 포함시키고 후속 카운터는

태그 안에 포함시킬 수 있습니다. 하지만 이 접근 방식을 사용하려면 구문 분석 중인 HTML 또는 XML 문서를 트리 형태로 표현하는 역할을 하는 DOM에 대한 이해가 필요합니다.

 import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return (
    <div>
      { isKingsley ?
        (<div>
          <Counter name="Kingsley" />
        </div>)
        :
        (<section>
          <Counter name="Sally" />
        </section>)
      }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

“킹슬리” 카운터를 증가시킨 후 “스왑” 버튼을 클릭하면 두 DOM(문서 객체 모델) 트리의 구조가 서로 다르기 때문에 시스템 상태가 재설정되는 문제가 발생합니다.

애플리케이션의 동작은 킹슬리 변수의 진리 값에 따라 달라집니다. 참이면 HTML 구조는 세 개의 중첩된 요소로 구성됩니다. div 요소는 다른 두 개의 div 요소 안에 포함되고 마지막으로 카운터 구성 요소를 포함하는 또 다른 div 요소로 둘러싸여 있습니다. 그러나 사용자가 카운터 컴포넌트의 상태를 전환하는 버튼을 클릭하여 시스템과 상호 작용하면 결과 구조는 단일 div 요소, 섹션 요소, 카운터 컴포넌트로 변경됩니다. 이러한 구조 변경은 이전 구조와 현재 구조가 일치하지 않기 때문에 카운터 컴포넌트의 상태를 새로 고치는 작업을 트리거합니다.

이 글도 확인해 보세요:  우분투에서 OpenShot 충돌을 해결하는 4가지 방법

이러한 방식으로 마크업의 구성을 일관되게 수정하고 싶지 않을 수도 있습니다. 이 문제를 해결하기 위한 대안적인 접근 방식은 고유한 마크업에 대한 요구 사항을 제거합니다.

키를 사용하여 새로운 컴포넌트 렌더링하기

React는 컴포넌트를 렌더링할 때 컴포넌트를 구분하는 수단으로 키를 사용합니다. 동일한 컴포넌트가 여러 개 존재하는 경우, 고유한 키를 할당하면 React가 각 컴포넌트의 개성을 인식할 수 있습니다. 고유 키 속성은 각 컴포넌트 인스턴스에 특정한 식별자를 제공함으로써 이러한 목적을 수행합니다.

물론입니다! 요청을 우아하게 바꾸는 방법은 다음과 같습니다: ”’자바 지도 map = 새로운 해시맵<>(); map.put(“apple”, 3); map.put(“바나나”, 5); map.put(“orange”, 2); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + “=” + entry.getValue()); } “`

 import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return(
    <div>
      { isKingsley ?
        <Counter key="Kingsley" name="Kingsley" /> :
        <Counter key="Sally" name="Sally" />
      }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

“Kingsley” 카운터의 값을 늘리고 “Swap” 버튼을 클릭하면 React는 카운터의 새 인스턴스를 생성하고 내부 상태를 초기값인 0으로 복원합니다.

React를 사용하여 동질적인 엘리먼트 배열을 렌더링할 때는 배열 내의 각 컴포넌트 인스턴스에 고유한 키를 사용하는 것이 좋습니다. 이렇게 하면 React가 한 엘리먼트를 다른 엘리먼트와 효과적으로 구별할 수 있고 필요에 따라 컴포넌트를 효율적으로 가상화 및 재활용할 수 있습니다.

 export default function App() {
  const names = ["Kingsley", "John", "Ahmed"]

  return(
    <div>
      { names.map((name, index) => {
        return <Counter key={index} name={name} />
      })}
    </div>
  )
}

React는 할당된 각 키에 대해 별도의 개수를 유지함으로써 배열의 각 엘리먼트에 고유 식별자를 활용합니다. 이를 통해 React는 배열에 대한 모든 수정 사항을 효과적으로 추적하고 업데이트할 수 있습니다.

다른 고급 키 사용 사례

키-값 쌍을 활용해 서로 다른 두 컴포넌트 간의 관계를 설정할 수 있습니다. 특히 이러한 키를 사용하여 특정 상태 변수의 현재 상태에 따라 다양한 요소를 할당할 수 있습니다.

이 글도 확인해 보세요:  휴대폰은 50, 랜섬웨어 복호화 설명, ChatGPT가 팟캐스트를 제작합니다!

이 개념을 실제로 설명하기 위해 앱 컴포넌트 내에서 몇 가지 변경 사항을 구현하여 수정해 보겠습니다.

 import { useState } from "react"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return(
    <div>
      { isKingsley ? <div>Kingsley's Score</div> : <div>Sally's score</div> }
      <input key={ isKingsley? "Kingsley" : "Sally" } type="number"/>
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

사용자가 해당 버튼을 클릭하여 킹슬리와 샐리와 연관된

컴포넌트 사이를 전환할 때마다 입력 요소의 키 속성이 그에 따라 업데이트됩니다. 결과적으로 React는 키 입력이나 마우스 동작을 할 때마다 전체 입력 컴포넌트를 처음부터 다시 생성해야 합니다.

React 애플리케이션 최적화를 위한 추가 팁

웹과 모바일 애플리케이션 모두에서 최적의 사용자 경험을 제공하기 위해서는 코드 최적화가 중요하며, 개발자는 다양한 최적화 기법에 대한 지식이 있으면 React 애플리케이션을 최대한 활용할 수 있습니다.

이러한 최적화의 장점 중 하나는 다양한 React Native 애플리케이션에 적용 가능하다는 점입니다.

By 김민수

안드로이드, 서버 개발을 시작으로 여러 분야를 넘나들고 있는 풀스택(Full-stack) 개발자입니다. 오픈소스 기술과 혁신에 큰 관심을 가지고 있고, 보다 많은 사람이 기술을 통해 꿈꾸던 일을 실현하도록 돕기를 희망하고 있습니다.