자바스크립트에서 객체는 가장 기본적인 데이터 구조입니다. Object의 정적 메서드들을 제대로 알고 있으면, 데이터 변환이나 불변성 관리에서 훨씬 깔끔한 코드를 작성할 수 있습니다.

Object.keys, values, entries

객체의 키, 값, 키-값 쌍을 배열로 추출하는 메서드입니다.

JS
const user = { name: "정훈", age: 25, job: "개발자" };

// 키 배열
Object.keys(user);    // ["name", "age", "job"]

// 값 배열
Object.values(user);  // ["정훈", 25, "개발자"]

// [키, 값] 쌍 배열
Object.entries(user); // [["name", "정훈"], ["age", 25], ["job", "개발자"]]

entries의 활용 — 객체 순회

JS
const prices = { apple: 1000, banana: 2000, cherry: 3000 };

// 구조 분해와 함께 사용
for (const [fruit, price] of Object.entries(prices)) {
  console.log(`${fruit}: ${price}원`);
}

// 객체 → 변환 → 다시 객체
const discounted = Object.fromEntries(
  Object.entries(prices).map(([key, value]) => [key, value * 0.9])
);
console.log(discounted); // { apple: 900, banana: 1800, cherry: 2700 }

Object.fromEntries()Object.entries()의 역변환입니다. 이 두 개를 조합하면 객체를 map처럼 변환할 수 있습니다.

Object.assign — 얕은 병합

하나 이상의 소스 객체의 프로퍼티를 타깃 객체에 복사합니다.

JS
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };

const result = Object.assign(target, source1, source2);
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(target); // { a: 1, b: 2, c: 3 } — target이 변경됨!

**주의 **: Object.assign은 타깃 객체를 ** 직접 수정 **합니다. 원본을 보존하려면 빈 객체를 타깃으로 사용합니다.

JS
// 원본을 보존하는 복사
const copy = Object.assign({}, original);

// 스프레드 연산자로 더 간결하게 (동일한 얕은 복사)
const copy2 = { ...original };

얕은 복사의 한계

JS
const original = {
  name: "정훈",
  address: { city: "서울" },
};

const copy = { ...original };
copy.address.city = "부산";
console.log(original.address.city); // "부산" — 중첩 객체는 공유됨

Object.freeze — 객체 동결

객체의 프로퍼티를 추가, 수정, 삭제할 수 없게 만듭니다.

JS
const config = Object.freeze({
  API_URL: "https://api.example.com",
  TIMEOUT: 5000,
});

config.API_URL = "https://hacked.com"; // 무시됨 (strict mode에서는 TypeError)
config.newProp = "test"; // 무시됨
delete config.TIMEOUT;   // 무시됨

console.log(config.API_URL); // "https://api.example.com"

얕은 동결의 한계

JS
const settings = Object.freeze({
  theme: "dark",
  nested: { fontSize: 14 },
});

settings.theme = "light";          // 무시됨
settings.nested.fontSize = 20;     // 변경됨! — 얕은 동결
console.log(settings.nested.fontSize); // 20

깊은 동결이 필요하면 재귀적으로 적용해야 합니다.

JS
function deepFreeze(obj) {
  Object.freeze(obj);
  Object.values(obj).forEach((value) => {
    if (typeof value === "object" && value !== null && !Object.isFrozen(value)) {
      deepFreeze(value);
    }
  });
  return obj;
}

Object.seal — 밀봉

기존 프로퍼티의 값은 변경할 수 있지만, 추가/삭제는 막습니다.

JS
const user = Object.seal({ name: "정훈", age: 25 });

user.name = "길동";    // OK — 값 변경은 가능
user.email = "a@b.c"; // 무시됨 — 추가 불가
delete user.age;       // 무시됨 — 삭제 불가

freeze vs seal vs preventExtensions 비교

기능freezesealpreventExtensions
프로퍼티 추가XXX
프로퍼티 삭제XXO
프로퍼티 값 변경XOO
프로퍼티 설정 변경XXO

Object.hasOwn — 안전한 프로퍼티 확인

JS
const obj = { name: "정훈" };

// 기존 방식 — prototype chain 문제 가능
obj.hasOwnProperty("name"); // true

// Object.create(null)로 만든 객체에서는 실패
const bare = Object.create(null);
bare.name = "test";
// bare.hasOwnProperty("name"); // TypeError!

// 안전한 방식 (ES2022)
Object.hasOwn(bare, "name"); // true

프로퍼티 디스크립터

JS
const user = {};

Object.defineProperty(user, "id", {
  value: 1,
  writable: false,     // 값 변경 불가
  enumerable: false,   // for...in에 나타나지 않음
  configurable: false, // 삭제/재정의 불가
});

user.id = 999; // 무시됨
console.log(user.id); // 1
console.log(Object.keys(user)); // [] — enumerable: false

실전 활용 패턴

JS
// 객체에서 특정 키만 추출 (pick)
function pick(obj, keys) {
  return Object.fromEntries(
    Object.entries(obj).filter(([key]) => keys.includes(key))
  );
}

const user = { name: "정훈", age: 25, password: "1234" };
const safe = pick(user, ["name", "age"]);
console.log(safe); // { name: "정훈", age: 25 }

// 객체에서 특정 키 제외 (omit)
function omit(obj, keys) {
  return Object.fromEntries(
    Object.entries(obj).filter(([key]) => !keys.includes(key))
  );
}

const public_ = omit(user, ["password"]);
console.log(public_); // { name: "정훈", age: 25 }

** 기억하기 **: Object.keys/values/entries로 객체를 배열처럼 다루고, Object.freeze로 불변성을 확보합니다. 다만 freeze는 얕은 동결이라는 점을 면접에서 꼭 언급해야 합니다.

댓글 로딩 중...