객체의 변경과 사이드 이펙트
배열과 마찬가지로 객체도 필드를 추가하거나 설정할 때 **사이드 이펙트(Side Effect)**가 발생할 수 있다. 특히, 기존 데이터를 유지하면서 새로운 필드를 추가하거나 기본값으로 채우려면 신중한 처리가 필요하다.
예를 들어, 아래와 같은 기본값 객체와 새로 추가할 데이터를 가진 객체를 보자.
const defaults = {
author: '',
title: '',
year: 2017,
rating: null
};
const book = {
author: 'Joe Morgan',
title: 'Simplifying JavaScript'
};
기존 방식: Object.keys와 반복문 활용
defaults와 book을 병합하려면 아래와 같은 방식으로 각 필드를 처리할 수 있다.
function addBookDefault(book, defaults) {
const fields = Object.keys(defaults);
const updated = {};
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
updated[field] = book[field] || defaults[field];
}
return updated;
}
const updatedBook = addBookDefault(book, defaults);
console.log(updatedBook);
// { author: 'Joe Morgan', title: 'Simplifying JavaScript', year: 2017, rating: null }
위 코드는 정확히 작동하지만, 코드가 다소 복잡하고 반복문을 사용해 명시적으로 처리해야 한다는 단점이 있다.
Object.assign을 사용한 간단한 병합
Object.assign 메서드를 사용하면 객체 병합 작업을 훨씬 간단하게 처리할 수 있다.
const updatedBook = Object.assign({}, defaults, book);
console.log(updatedBook);
// { author: 'Joe Morgan', title: 'Simplifying JavaScript', year: 2017, rating: null }
Object.assign은 첫 번째 매개변수로 대상 객체를 받고, 나머지 매개변수로 주어진 객체의 모든 속성을 대상 객체에 복사한다. 이 방법은 코드의 간결성을 크게 높여준다.
원본 객체 변경 문제
다만, Object.assign은 원본 객체를 변경할 수 있다는 문제가 있다. 예를 들어, 아래 코드는 defaults 객체 자체를 변경한다.
Object.assign(defaults, book);
console.log(defaults);
// { author: 'Joe Morgan', title: 'Simplifying JavaScript', year: 2017, rating: null }
원본 유지: 빈 객체를 사용한 병합
위 문제를 해결하기 위해 첫 번째 매개변수로 빈 객체를 전달해 원본 객체를 보호할 수 있다.
const updatedBook = Object.assign({}, defaults, book);
console.log(updatedBook);
// { author: 'Joe Morgan', title: 'Simplifying JavaScript', year: 2017, rating: null }
console.log(defaults);
// { author: '', title: '', year: 2017, rating: null }
깊은 복사와 Object.assign의 한계
Object.assign은 **깊은 복사(Deep Copy)**를 지원하지 않는다. 중첩된 객체를 복사하면 객체 참조만 복사되어, 원본 객체의 내부 값이 변경되면 복사된 객체에도 영향을 미친다.
중첩 객체 복사 예시
const defaultEmployee = {
name: {
first: '',
last: ''
},
year: 0
};
const employee = Object.assign({}, defaultEmployee);
employee.name.first = 'John';
console.log(defaultEmployee.name.first); // 'John' (원본 객체도 변경됨)
깊은 복사를 위한 해결 방법
중첩된 객체를 복사하려면 각 속성에 대해 별도로 Object.assign을 호출해야 한다.
const employee2 = Object.assign({}, defaultEmployee, {
name: Object.assign({}, defaultEmployee.name)
});
employee2.name.first = 'Jane';
console.log(defaultEmployee.name.first); // ''
console.log(employee2.name.first); // 'Jane'
대안: JSON 기반 깊은 복사
간단한 깊은 복사가 필요하다면 JSON.parse와 JSON.stringify를 사용할 수도 있다.
const employee3 = JSON.parse(JSON.stringify(defaultEmployee));
employee3.name.first = 'Alice';
console.log(defaultEmployee.name.first);
// '' console.log(employee3.name.first); // 'Alice'
이 방법은 객체 내 메서드를 포함하지 않거나, 순환 참조가 없는 경우에만 사용할 수 있다.
결론
- 얕은 복사가 필요한 경우 Object.assign을 사용하면 간결하고 직관적이다.
- 깊은 복사가 필요한 경우 중첩 객체를 직접 복사하거나, 재귀 함수를 작성하거나, lodash 같은 라이브러리를 활용할 수 있다.
- 객체 병합과 복사의 방식은 상황에 맞게 선택하되, 원본 객체가 손상되지 않도록 주의해야 한다.
'프로그래밍언어 > JavaScript' 카테고리의 다른 글
[JavaScript] Set을 활용한 고유값 관리 (1) | 2024.12.02 |
---|---|
[JavaScript] 객체 펼침 연산자(Spread Operator) 활용하기 (0) | 2024.11.27 |
[JavaScript] 정적인 키-값 탐색 (1) | 2024.11.26 |
[JavaScript] 펼침 연산자 (...), 활용과 예제 (0) | 2024.11.25 |
[JavaScript] 배열에서 includes() 메서드 사용하기 (0) | 2024.11.24 |