문제 상황
아래 사진에서 삭제 버튼을 클릭했을 때 메모리에 있는 skillList(redux로 관리하고 있는 배열로, 객체들을 요소로 가지고 있다.)의 값은 변경되지만, 실제 화면에 보여지는 값은 변경되지 않는 문제점이 있었다.
시도한 해결방법과 문제점
skillList.map을 실행할 때 주는 key의 값을 index(숫자)가 아닌 랜덤한 값을 주는 방법이였다.
이 방법을 시도하게 된 이유는 스킬 추가 버튼을 클릭했을 때 콘솔창에 오류가 발생하는데 key prop이 문제라는 내용의 오류 문구였고, 이를 해결하면 문제 상황이 해결될 줄 알았다. 지금 생각해보면 그 오류는 그 오류고,(사실 오류도 아니였고 warning이였다😂) 값이 변경되지 않는 문제는 다른 문제였을텐데 아무튼 여기에 꽃혀서 빙빙 돌아갔다..
결론적으로 랜덤한 값을 key로 줄 수는 없기 때문에 index 값을 key로 주었다. 공식문서에 따르면 key는 반드시 변하지 않고, 예상 가능하며, 유일해야한다고 한다. 변하는 key를 사용하게 되면 많은 컴포넌트 인스턴스와 DOM 노드를 불필요하게 재생성하게 되어 성능이 나빠지거나 자식 컴포넌트의 state가 유실될 수 있기 때문이다.
하지만 index 값을 key로 사용하게 되면 상태가 뒤죽박죽 된다. 공식문서에도 나와있듯이 컴포넌트 인스턴스는 key를 기반으로 갱신되고 재사용되기 때문에 인덱스를 key로 사용하면, 항목의 순서가 바뀌었을 때 key 또한 바뀌기 때문이다.
다시말해, key가 변경되면
1. key가 다르니 html 코드 자체를 삭제 후 새로 생성
2. html 코드 자체가 재생성
그래서 2번 스킬을 삭제했음에도 불구하고, html 코드 자체가 재성되어 1, 2, 3, 4 번째의 스킬이 노출되게 되는 것이다.
최종 해결방법
하지만, 내가 원했던 결과화면은 2번 스킬을 삭제했을 때, 1, 3, 4, 5가 보여야 한다. index를 key로 사용하면서도 뒤죽박죽인 상태를 피할 수 있는 방법이 있다.
input에 value 옵션을 주면 된다. input에 value 옵션을 작성해주면 화면에 반영이 되기 때문이다.(이 점을 간과했다ㅠ 아차차)
마찬가지로 2번 째 스킬을 삭제해보면 정상적으로 스킬이 삭제되어 2번 째 스킬 자리가 3번 째 스킬의 값으로 변경된 것을 확인할 수 있다. 문제를 해결하느라 빙빙 돌았지만 너무 간단하게 해결했다. 핵심은 html 코드는 화면에 노출되는 값이고 실제 메모리랑 값이 별개라는 점이다.
다시말해,
1. key는 index 값으로 사용
2. react 입장에서 스킬이 5개에서 4개로 줄었으니 마지막 html만 삭제 (1 ~ 4번 스킬의 html은 냅두기)
3. 화면에 노출되는 html은 변동되지 않음
4. 3번 문제를 해결하기 위해 Input의 value를 이용해서 html 갱신
위와 같은 동작이 가능한 이유는 react는 virtual DOM이라는 특징을 가지고 있기 때문이다.
따로 정리해둔 글이 있으니깐 참고 하면 좋을 것 같다! ⬇️⬇️⬇️
최종 코드 및 결과 화면
// src/components/makeCharacter/Skill.tsx
return (
<>
{skillList.map((skill, index: number) => (
<div key={index} className="flex max-h-[72%] pl-[1%]">
<input
value={skill.title}
onChange={(e) => onChangeTitle(e, index)}
placeholder="스킬"
className="w-[40%] mr-[2%] text-center bg-deepGray"
/>
// ...
<input
value={skill.description}
onChange={(e) => onChangeDescription(e, index)}
placeholder="자세한 설명"
className="w-[100%] mb-[2%] pl-[1%] bg-deepGray"
/>
</div>
))}
</>
);
참고자료
- https://velog.io/@yeonbot/React에서-key의-역할-컴포넌트를-다시그리는-과정
- https://ko.reactjs.org/docs/reconciliation.html
- https://blog.woolta.com/categories/1/posts/210
질문이나 잘못된 점은 댓글로 남겨주세요 :)💖
'프로젝트' 카테고리의 다른 글
이미지와 데이터 서버로 같이 전송하는 법 - FormData 생성 및 사용법 (0) | 2023.08.12 |
---|---|
이벤트 버블링이 되지 않았던 문제 해결하기 (0) | 2023.03.26 |
마우스 포인터가 요소를 벗어나지 않았음에도 onMouseOut 이벤트가 실행되는 이유 + 해결방법 (0) | 2023.03.26 |
댓글