1. 클릭 시 함수 호출 흐름
Function Call Flowon Click
JayTak
2. move는 좌표, state는 스위치: React 시간여행의 핵심 원리
2.1 move에 고유한 값을 준다
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}>
<button className="move-button" onClick={() => jumpTo(move)}>{desc}</button>
</li>
)
})
move=history의 index- 0, 1, 2, 3 … → 절대 겹치지 않음
- 에서 key로 사용 가능
<li key={move}>
// “이 버튼은 몇 번째 상태를 가리키는지 명확하다”
2.2 상태값이 바뀐다 (setStepNumber, setXIsNext)
- 버튼 클릭
jumpTo(move)실행- state 변경
const jumpTo = (step) => {
setStepNumber(step);
setXIsNext((step % 2) === 0);
}
-
📌 이 순간:
“현재 상태가 바뀌었다”고 React가 인식
3. React는 리스트를 업데이트할 때 key로 “누가 누구인지” 판단한다
3.1 key가 없을 때 (경고가 뜨는 이유)
<ul>
<li>haha</li>
<li>hoho</li>
</ul>
React의 문제
- 리스트 항목이 여러 개인데
- 구분할 이름표(key)가 없음
그래서 React는 경고함:
Each child in a list should have a unique "key" prop.
왜 위험하냐면
- 다음 렌더링에서
- 누가 유지되고, 누가 바뀌었는지 모름
3.2 index를 key로 쓰는 경우 (문제의 시작)
{list.map((item, index) =>
<li key={index}>{item}</li>
)}
처음상태:
index 0 → haha
index 1 → hoho
❗ 중간에 항목 추가 (kiki 추가 (맨 앞)
index 0 → kiki
index 1 → haha
index 2 → hoho
React의 착각
- React는 이렇게 생각함:
“index 0은 그대로네? 그럼 기존 DOM 재사용!”
3.3 index key가 괜찮은 경우 (틱택토)
<li key={move}>...</li>
왜 여기선 OK?
move는:- history index
- 절대 중간 삽입/삭제 없음
- 항상 0 → 1 → 2 순서
3.4 key 중복 (가장 위험)
<li key="haha">haha</li>
<li key="haha">haha</li>
React의 반응 (“이름표가 같은 애가 두 명인데요…?”)
Encountered two children with the same key
🧠 React의 사고방식 한 문장
“key가 같으면 같은 컴포넌트다”
그래서:
- key가 바뀌면 → 새 컴포넌트
- key가 같으면 → 기존 컴포넌트 재사용
4. stepNumber로 현재를 선택하고, history로 시간을 관리하는 시간여행 로직
4.1 전체 흐름 요약
1. 과거로 이동 (jumpTo)
2. stepNumber 기준으로 history 자르기
3. 현재 상태 복사
4. 새 수 두기
5. 새 history 만들기
6. stepNumber를 새 위치로 이동
이를 함수형 컴포넌트로 바꾸면 다음과 같습니다.
(1) 과거로 이동 (jumpTo)
const jumpTo = (step) => {
setStepNumber(step);
setXIsNext(step % 2 === 0);
};
(2) stepNumber 기준으로 history 자르기
const newHistory = history.slice(0, stepNumber + 1);
(3) 현재 상태 복사
const current = newHistory[newHistory.length - 1];
const newSquares = current.squares.slice();
(4) 새 수 두기
newSquares[i] = xIsNext ? 'X' : 'O';
(5) 새 history 만들기
setHistory([...newHistory, { squares: newSquares }]);
(6) stepNumber를 새 위치로 이동
setStepNumber(newHistory.length);