개발을 하다보면 한 번 이상은 반드시 마주치게 되는 케이스가 있다.
그 중 대표적인 것이 반복문으로 생성한 컴포넌트의 상태를 관리하는 경우이다.
예를들어, 아래와 같은 ui를 생각해보자.
데이터에 대한 정보를 가진 컴포넌트가 리스트 형태로 보여지고 있다.
단순하게 생각해서 ['김철수', '이영희', '박민수', '최순희', '홍길동'] 이라는 배열 값을 반복문을 이용해 컴포넌트를 생성하고 렌더링한 것이라 가정해보자.
이 때 사용자가 집에 있기 버튼을 클릭하면 해당 컴포넌트만 집에 있기 상태가 되어야 하고 버튼이 클릭되지 않은 나머지 컴포넌트들은 집에 있기 상태가 아닌것이다.
그렇기 때문에 집에 있기 버튼에 onChange 이벤트가 state를 업데이트 하는 동작을 해줘야 할 것이다.
const [isStayHome, setIsStayHome] = useState(false)
하지만 위의 코드처럼 상태 값을 단 한개의 boolean으로 관리하게 되면 모든 반복 생성된 컴포넌트가 단 하나의 상태값을 공유하게 되므로 김철수 의 집에 있기 버튼을 클릭하더라도 나머지 4개의 컴포넌트들도 집에 있기 버튼을 클릭한 것 처럼 상태가 변경될 것이다.
그렇기 때문에 이러한 경우에는 각각의 상태를 개별적으로 관리해야 한다는 생각을 할 수 있다!
하지만,
const [isStayHome철수, setIsStayHome철수] = useState(false)
const [isStayHome영희, setIsStayHome영희] = useState(false)
const [isStayHome민수, setIsStayHome민수] = useState(false)
const [isStayHome순희, setIsStayHome순희] = useState(false)
const [isStayHome길동, setIsStayHome길동] = useState(false)
위 코드처럼 모든 상태값을 전부 선언해서 사용할 수는 없다. 왜냐하면, 지금은 예시를 들기 위해 5개의 데이터만 준비했지만 실제로 수많은 데이터들을 받아야 하는 상황이라면 아마 수백줄은 useState를 선언하는 데 써야 할 것이다. 😅
그렇다면, 더 좋은 방법이 없을까?🤔
여러개로 선언하는게 번거롭다면, 한 개의 state를 배열로 선언하고 관리해보자!
이제 각각의 컴포넌트는 자신의 인덱스 순서에서 state를 업데이트할 수 있게 되었다. 이게 무슨 소리냐면,
const [isStayHome, setIsStayHome] = useState<boolean>(Array(5).fill(false)); // [false,false,false,false,false]
const dataArray = ['김철수', '이영희', '박민수', '최순희', '홍길동'];
return (
<ul>
{dataArray.map((data: string, index: number)=>{
<li key={data}>
<p>{data}</p>
<button onChange={()=>{
setIsStayHome((prev)=>{
const updatedState = [...prev];
updatedState[index] = !updatedState[index];
return updatedState;
})
}}>
{isStayHome[index] ? '집콕중' : '집에 있기'}
</button>
</li>
}
}
</ul>
)
스타일은 생략하고 간략하게 컴포넌트 구조와 이벤트, state를 위주로 코드를 작성해보면 위와 같을 것이다.
우선, state를 선언할 때 기존처럼 한 개의 boolean 값을 여러 개 선언하는 게 아니라 boolean 값이 5개 들어있는 배열로 초기화 및 선언을 해준다.
만약 이영희 값이 있는 컴포넌트에서 집에 있기 버튼을 클릭하려 한다면 updatedState에 이전 state 값이 복사되고,
updatedState의 index 번째 값은 기존에 들어있던 값의 not(boolean이므로 반대 값)인 값이 들어간다.
그리고 updatedState를 리턴함으로써 해당 값으로 isStayHome 상태 업데이트가 되도록 한다.
그러면 상태 값의 변화 때문에 이영희 의 옆에 있는 버튼은 집콕중 으로 변화하고 나머지 컴포넌트의 버튼은 그대로일 것이다.
여기까지가 useState를 배열로 선언함으로써 여러 개의 컴포넌트를 각각 개별적으로 state 관리할 수 있게 만든 가장 기초적인 방법이다.
그렇다면, 고작 5개가 아니라 수백 수천 개의 데이터를 반복해서 컴포넌트로 렌더링하는 경우를 생각해보자.
또한, 앞선 예제와 다르게 이번에는 각 컴포넌트에 input 태그를 포함해서 클릭한 행에 대해서만 해당 input이 disabled=false이고 다른 모든 행은 disabled=true가 된다고 가정하자.
이 상황도 사실 앞선 예제와 근본적으로는 다르지 않다. disabled는 boolean 값으로 컨트롤 할 수 있기 때문이다.
지금 예제에서는 행 개수가 대략 2천 개정도 된다고 하자.
const [isFocusing, setIsFocusing] = useState<boolean>(Array(2000).fill(false));
const dataArray = ['김철수', '이영희', '박민수', '최순희', '홍길동', ...]; // length가 2000인 배열로 선언한다.
return (
<ul>
{dataArray.map((data: string, index: number)=>{
<li key={data}>
<p>{data}</p>
<button onChange={()=>{
setIsFocusing((prev)=>{
const updatedState = [...prev];
updatedState[index] = !updatedState[index];
return updatedState;
})
}}>
<input type="text" disabled={isFocusing[index]} />
</button>
</li>
}
}
</ul>
)
위의 코드와 거의 흡사하게 작성할 수 있다.
이 코드는 단순히 이름이 담긴 배열만을 반복문으로 컴포넌트 생성하고 있지만,
실제로는 다양한 정보를 포함하게 될 가능성이 매우 높고 button을 클릭하기보단 input에 바로 포커싱이 가는 것이 자연스러운 user-flow가 될 것이다.
그렇기에 문제가 발생한다.
수천개씩 되는 데이터를 불러옴과 동시에 해당 데이터들이 각각의 input을 가지고 있다면, 한번 포커싱 할 때마다 발생하는 렉이 엄청날 것이다.
더군다나 포커싱으로 상태 값이 변화하게 된다면 그야말로 렉때문에 스크롤도 못 움직일 지경일 수 있다.
이는 실제로 경험한 것을 바탕으로 예시를 든 것이다.
수천개의 데이터를 불러옴과 동시에 해당 데이터들을 각각 수정해야 할 때 이러한 난관에 봉착하게 되는 것이다.
그렇다면 어떻게 처리하는 것이 효과적일까?
예를 들어 10번째부터 30번째 항목에 값을 입력하고 다시 160번째부터 210번째까지 항목에 값을 입력하는 작업을 해야 한다면,
스크롤을 일일이 오르내리며 직접 찾아가며 수정해야 하며, 어떠한 잠금장치도 없기 때문에 잘못 수정될 가능성도 매우 높다.
그렇기에 한 번에 한 페이지 내의 모든 데이터를 동시에 수정하는 방법보다 모달을 사용해서 수정하는 방법을 적용하는 것이 더 효율적일 수 있다.
위와 같이 각 데이터 항목의 input은 기본적으로 입력된 데이터만을 출력하는 용도로 쓰이되, 실제 수정 작업은 버튼을 클릭하면 나타나는 모달에서 진행하는 것으로 한다.
모달 창에서 수정 작업을 하게 되면 헷갈릴 염려가 적어지고 저장된 값을 모달에서만 수정이 가능하므로 잘못 입력할 가능성이 매우 낮아지게 된다.
이처럼 모달을 생성하여 데이터를 수정하고 관리하게 될 때 수천 개의 데이터를 불러오더라도 빠른 작업이 가능하게 된다.
하지만 모달을 한 번도 만들어본 적이 없거나 모달 만들기에 자신이 없는데 마감 기한이 얼마 남지 않은 작업을 수행해야 한다면
위 방법은 부담스러울 수 있다.
조금 더 쉽게 해결할 수 있는 방법을 알아보자.
수정하기 버튼이 존재하는 페이지 도메인이 "/" 일 때, 수정하기 버튼을 클릭하면 "/detail?id=1" 페이지로 이동하는 방식이다.
위 방식을 사용하면 단순히 페이지를 하나 더 만들어서 데이터별 수정 페이지로 이동하게 되어 작업할 수 있게 된다.
모달보다 사용자 경험은 저하되지만 짧은 시간 내에 만들기 편리하고 모달을 만들어본 경험이 없어도 개발할 수 있다는 이점이 있다.
수많은 데이터를 다루는 페이지 내에서 동시에 수정 작업까지 한다는 것은 성능에 안 좋을 수 있다.
그렇기 때문에 위와 같은 방법을 사용하여 해결할 수 있다.
하지만, 위의 방법이 반드시 정답은 아니므로 더 나은 방법이 있다면 시도해보고 공유해보는 것도 좋을 듯하다.
그리고 모달 화면은 꼭 만들어 보는 것이 좋은 경험이 될 것이다.
-끝