function App() {
const [mode, setMode] = useState("WELCOME");
const [id, setId] = useState(null);
const [nextId, setNextId] = useState(4);
const [topics, setTopics] = useState([
{ id: 1, title: "html", body: "html is ..." },
{ id: 2, title: "css", body: "css is ..." },
{ id: 3, title: "javascript", body: "javascript is ..." },
]);
let content = null;
if (mode === "WELCOME") {
content = <Article title="Welcome" body="Hello, Web"></Article>;
} else if (mode === "READ") {
let title,
body = null;
for (let i = 0; i < topics.length; i++) {
console.log(topics[i].id, id);
if (topics[i].id === id) {
title = topics[i].title;
body = topics[i].body;
}
}
content = <Article title={title} body={body}></Article>;
} else if (mode === "CREATE") {
content = (
<Create
onCreate={(_title, _body) => {
const newTopic = { id: nextId, title: _title, body: _body };
const newTopics = [...topics];
newTopics.push(newTopic);
//alert(newTopics[0].title);
setTopics(newTopics);
setMode("READ");
setId(nextId);
setNextId(nextId + 1);
}}
></Create>
);
}
return (
<div className="App">
<Header
title="WEB"
onChangeMode={() => {
setMode("WELCOME");
}}
></Header>
<Nav
topics={topics}
onChangeMode={(_id) => {
setMode("READ");
setId(_id);
}}
></Nav>
{content}
<a
href="/create"
onClick={(event) => {
event.preventDefault();
setMode("CREATE");
}}
>
Create
</a>
</div>
);
}
const newTopics = [...topics];
[...topics] 이렇게 사용하는 이유가 무엇일까??
const newTopics = [...topics];에서 [...]는 **전개 연산자 (spread operator)**입니다.
이걸 사용하는 이유는 기존 상태인 topics 배열을 직접 수정하지 않고, 복사본을 만들어서 수정하기 위해서입니다.
왜 직접 수정하면 안 될까?
React에서는 상태(state)를 직접 변경하면 컴포넌트가 제대로 리렌더링되지 않을 수 있어. 예를 들어:
topics.push(newTopic); // ❌ 직접 상태 변경
setTopics(topics); // 이건 React가 변경을 감지 못할 수도 있음
위처럼 원본을 직접 바꾸면, React는 “상태가 변하지 않았네?” 하고 넘어갈 수 있어.
그래서 복사해서 쓰는 방식이 필요한 거야
const newTopics = [...topics]; // ✅ 새로운 배열을 복사해서 만들고
newTopics.push(newTopic); // 새 항목 추가
setTopics(newTopics); // setTopics로 새로운 배열을 상태로 설정
이렇게 하면 React가 “아, 상태가 진짜 바뀌었구나!” 하고 인식하고 리렌더링해줘.
요약
- [...]는 배열을 **얕은 복사(shallow copy)**해서 기존 상태를 보존하면서 새로운 배열을 만드는 방법
- React에서 상태는 절대 직접 수정하지 말고, 항상 복사 → 수정 → setState 패턴으로 처리해야 해
그렇다면 왜 리액트는 상태를 직접변경하면 리렌더링이 되지 않을 수 있는걸까??
React가 상태를 직접 변경하면 리렌더링되지 않을 수 있는 이유는 React의 렌더링 방식과 불변성(immutability) 원칙에 있어.
🔄 1. React는 상태 변경을 비교해서 감지해
React는 컴포넌트가 setState() (혹은 useState의 setter)로 상태가 변경되었는지를 판단할 때, 단순히 **"참조가 바뀌었는지"**를 기준으로 판단해.
const [topics, setTopics] = useState([...]);
// ❌ 이 경우 React는 topics가 '같다'고 판단
topics.push(newTopic);
setTopics(topics); // 원래 참조 그대로니까 바뀐 줄 모름
JS 객체(배열 포함)는 메모리 주소(참조)를 비교해서 "같은 객체인지" 판단해.
✅ 2. 불변성을 지키면 참조가 달라져서 React가 "아! 바뀌었구나!" 하고 알아차림
const newTopics = [...topics]; // 참조가 바뀜
newTopics.push(newTopic);
setTopics(newTopics); // ✅ React가 바뀐 걸 감지함!
이렇게 새로운 객체를 만들어 넘기면, React는 setState 이후 상태가 "달라졌다"고 인식하고 해당 컴포넌트를 리렌더링해줘.
📌 요약
| 참조 동일 | ✅ | ❌ |
| React 리렌더링 | ❌ 안 될 수 있음 | ✅ 제대로 동작함 |
🔁 React는 왜 이렇게 작동할까?
- React는 성능을 위해 **얕은 비교(shallow comparison)**를 사용함
- 깊은 비교(deep equality)는 느림 → 모든 값을 비교하면 앱이 느려져
- 그래서 객체 참조가 바뀌었는지만 보고 변경 여부를 판단함
🔧 참고: useState 내부에서 무슨 일이 일어날까?
if (Object.is(oldState, newState)) {
// 상태가 같다고 보고 리렌더링 생략
}
topics와 newTopics의 메모리 주소가 서로 다르다는걸 그림을 사용해서 살펴보아요.
topics와 newTopics는 **서로 다른 메모리 주소를 가지는 객체(배열)**입니디ㅏ.
그걸 이해하기 위해 메모리 구조를 간단한 그림으로 표현해봅시다.
📦 상황: 상태를 직접 수정하는 경우
const [topics, setTopics] = useState([{ id: 1, title: "html" }]);
topics.push({ id: 2, title: "css" }); // ❌ 직접 변경
setTopics(topics); // 같은 참조를 다시 넣음
메모리 구조 (직접 수정)
topics ─────┐
▼
[ { id: 1, title: "html" },
{ id: 2, title: "css" } ] ← 메모리 주소: 0x1234
- topics는 주소 0x1234를 가리킴
- setTopics(topics)도 여전히 같은 주소 0x1234
- React는 주소가 같으니까 "아~ 상태가 안 바뀌었네" 하고 리렌더링 안 함
✅ 상황: 복사해서 새로운 상태로 설정하는 경우
topics ─────┐ newTopics ─────┐
▼ ▼
[ { id: 1, title: "html" } ] [ { id: 1, title: "html" },
↑ { id: 2, title: "css" } ]
주소: 0x1234 주소: 0x5678
- topics: 주소 0x1234
- newTopics: 주소 0x5678 (새 배열)
- setTopics(newTopics) → React는 주소가 바뀌었으니 "상태 변경됨!"이라고 판단하고 리렌더링함
🧠 정리
| setTopics(topics) | ✅ 같음 | ❌ 안 함 |
| setTopics([...topics]) | ❌ 다름 | ✅ 함 |
그림처럼 주소가 달라야 React가 "변했네!" 하고 감지 할 수 있습니다.
이게 바로 **불변성(immutability)**을 유지해야 하는 핵심 이유입니다.
출처: 생활코딩 리액트프로그래밍, chatgpt
'더조은컴퓨터아카데미 리액트 주말반' 카테고리의 다른 글
| 리액트 주말반 네번째 수업 (React Virtual DOM에 대하여) (2) | 2025.07.20 |
|---|---|
| 리액트 주말반 두번째 수업 (4) | 2025.07.13 |
| 리액트 주말반 첫번째 수업 (4) | 2025.07.12 |