2025. 11. 28. 15:48ㆍjavascript
참조 Reference
객체 데이터 타입이 실제로 저장되는 메모리 주소를 가르킨다. 복사하지않고 공유하는것
원시타입
원시타입은 각각 값자체가 메모리에 저장되어 독립적이다.

참조타입
참조타입 같은 경우 메모리 주소에 저장을 하지만
실제값은 힙(Heep)에 저장되고 변수는 메모리 주소만 가지고 있다.
let obj1 = {name: 'cy'}
let obj2 = obj1
obj2.name = 'cy lee'
console.log(obj1) // {name: 'cy lee'} obj1도 같이 변경
obj2 변수가 obj1을 참조하고 있을때
obj2의 name값을 바꾸면 obj1의 name값도 바뀐다.
obj2가 obj1을 참조하고 있기때문에 두개의 변수가 서로 다른 메모리주소를 갖고있지만 같은 참조값을 공유하고 있다는 증거이다.


즉 서로 다른 메모리 주소를 가지고 있지만 같은 객체의 참조값(주소)를 공유하고 있는것.
얕은 복사(shallow copy)
1단계 최상위 레벨만 독립적인 메모리를 할당한다. 중첩된 객체는 참조를 그대로 복사한다.
즉 새객체를 생성하지만 내부 객체는 참조 상태인것.
// 메모리주소: 00112
const original = {
name: 'kim' // 값 자체 저장
score: [23,12] // 참조주소: 010x
}
// 메모리주소: 00113
const copy1 = {...original}
copy1.name = 'kim2'
original.name = 'kim' // 변경 x
copy.score.push(30)
console.log(original.score) // [23,12,30]
console.log(copy1.score) // [23,12,30]
console.log(original.score === copy1.score) // [23,12,30]
original객체를 얕은복사를 한게 copy1이다.
copy1객체의 name을 바꾸면 original의 name도 바뀔까?
당연히 바뀔거라고 생각하겠지만 바뀌지않는다.
얕은 복사를 했기때문에 original과 copy1은 이미 독립적인 메모리주소 (새로만든 객체) 를 각각 가지고 있다.
서로 다른 객체이고 (original === copy 은 false) name은 원시타입이기 때문에 값이 바뀌지 않는다.
score는 배열이다. 객체이기 때문에 두객체가 같은 참조를 하고 있다
그래서 값을 바꾸면 copy1, original 둘다 score의 값이 변경된다.

얕은복사는 새로운 객체를 만들어 내기 때문에 original과 copy1객체가 서로 다른 메모리 주소를 가지고 있지만 그안의 객체는 참조주소를 서로 공유하고 있다. 그래서 score값이 바뀌면 두개의 객체의 score값이 둘다 바뀐다.
깊은 복사(Deep copy)
얕은복사와 반대로 완전히 독립된 객체로 복사한다. 중첩된 객체도 독립된 객체로 분리한다.
const original = {
name: 'mina',
favorite: ['food','travle']
}
const copy = {...original}
const deep = JSON.parse(JSON.stringify(original))
deep.favorite.push('study')
copy.favorite // ['food', 'travle']
deep.favorite // ['food', 'travle', 'study'] <- deep만 변경
리액트에서 얕은비교(shallow comparison)
리액트는 state가 변경될때 내부적으로 Object.is의 알고리즘을 사용해서 이전 state와 새 state의 참조만 비교한다. 내부값이 바뀌어도 참조가 바뀌지 않으면 변경감지 못한다. (리렌더링 x) 그래서 불변성을 지켜줘야 한다.
function updateState(newstate) {
if(Object.is(prevState,newState)){
return
}
}
prevState와 newState를 비교해서 서로 참조가 같다면 리턴처리 하고 리렌더링을 하지 않고
서로 참조가 다르다면 리렌더링 처리 한다.
const [user,setUser] = useState({name: 'cy'})
user.name = 'my' // name 속성만 변경
setUser(user) // user객체 그대로
setUser에서 user로 상태를 바꾸면 어떻게 될까.
리액트는 참조가 바뀌지 않는다고 판단하고 리렌더링 하지 않는다. user객체는 그대로고 내부 속성만 변경했으니 참조가 바뀌지 않았다.
const [user,setUser] = useState({name: 'cy'}) // 메모리 주소: 001x2
user.name = 'my' // name 속성만 변경
setUser(user) // 001x2를 다시 전달
메모리 주소로 보면 이렇다.
Object.is(001x2, 001x2) === true라 판단하고 리렌더링하지 않는다.
// 새로운 객체 생성 (참조 변경)
setUser({...user, name: 'my'})
Object.is(001x2, 001x3) === false라 판단하고 리렌더링 처리한다.
props의 얕은비교
참조에서 props는 어떤 반응일까?
const Parent = () => {
const [user, setUser] = useState({
profile: {name: 'cy', age: '20'}
})
const updateProfile() => {
const newUser = {...user}
newUser.profile.name = 'cy lee'
setUser(newUser)
}
return <Child user={user.profile}/>
}
const Child = ({profile}) => {
return <div>{profile.name}</div>
}
upateProfile함수를 호출하면 Parent컴포넌트는 리렌더링 된다.
새로운 객체로 상태 업데이트를 하면 참조가 다르다고 판단하기 때문. (newUser === user -> false)
props를 내려주고 있는 Child컴포넌트를 봐보자.
Child컴포넌트에 user.profile을 props로 내려주고 있다. 이 데이터는 현재 newUser로 업데이트를 했더라도 profile은 user와 같은 참조이기 때문에 값이 같이 바뀐다. (user.profile === newUser.profile). 리렌더링 유무는 참조와 상관없다.
부모가 리렌더링 되기때문에 동시에 자식 컴포넌트인 Child도 같이 리렌더링 된다.
React.memo
그렇다면 매번 부모가 리렌더링되면 자동으로 리렌더링되는 자식컴포넌트를 막을 순 없을까? memo를 쓰면 막을 수 있다.
memo는 props를 얕은 비교 한다.
const child = React.memo(({profile}) => {
return <div>{profile.name}</div>
}
const Parent = () => {
const [user, setUser] = useState({
profile: {name: 'cy', age: '20'},
score: [0,1]
})
const updateProfile() => {
setUser((prev) => {
...prev,
score: [0,1,2]
})
}
return <Child user={user.profile}/>
}
const Child = React.memo(({profile}) => {
return <div>{profile.name}</div>
})
user의 profile 객체를 child로 props전달하고 memo로 감싸주었다.
updateProfile함수를 호출하면?
profile의 참조는 건들지 않고 score의 값만 변경했다.
어떻게 될까?
Child컴포넌트는 리렌더링 되지 않는다.
새 객체는 만들어 졌으나 profile의 참조는 유지되므로 memo는 참조가 같다고 판단해 리렌더링을 하지 않는다.
memo인 상태에서 Child도 리렌더링 되게 하려면?
profile의 참조값을 바꿔주면 된다.
'javascript' 카테고리의 다른 글
| Promise와 setTimeout은 왜 실행 순서가 다를까? (0) | 2025.12.24 |
|---|---|
| requestIdleCallback으로 LCP 개선 (0) | 2025.04.28 |
| 이벤트루프 드디어 정.리.해.보.았.다. (0) | 2024.02.22 |
| 논리연산자(논리합,옵셔널 체이닝,null 병합연산자)로 코드 간결하게 하기, if문 줄이기 (0) | 2022.06.28 |
| [javascript] 이벤트 버블링과 캡처링! 이벤트 흐름,전달 알아보기 (0) | 2022.05.28 |