자바스크립트 객체는 이름표가 달린 값들의 묶음이다
배열이 값을 순서로 묶는다면, 자바스크립트 객체는 값을 이름으로 묶는다. 모델링하려는 대상에 이름 붙일 수 있는 속성들이 있다면 — 이름과 나이를 가진 사용자, 메서드와 URL을 가진 요청처럼 — 이럴 땐 객체를 쓰는 게 정답이다.
가장 간단하게 객체를 만드는 방법은 객체 리터럴이다.
각 항목은 key: value 쌍으로 이루어집니다. 내부적으로 키는 항상 문자열이고(name처럼 유효한 식별자라면 따옴표는 생략해도 됩니다), 값에는 자바스크립트가 표현할 수 있는 건 뭐든 넣을 수 있어요 — 숫자, 문자열, 불리언, 배열, 함수, 심지어 다른 객체까지 가능합니다.
마지막 항목 뒤에 쉼표가 남아 있어도 괜찮습니다. 오히려 대부분의 팀은 일부러 남겨두는데, 나중에 속성을 추가할 때 diff가 깔끔하게 유지되기 때문이에요.
객체 속성 읽기와 쓰기
속성에 접근하는 방법은 두 가지입니다. 바로 점 표기법과 대괄호 표기법이죠.
점 표기법이 더 깔끔해서 기본적으로 가장 많이 쓰는 방식입니다. 대괄호 표기법은 키가 변수에 담겨 있거나, 키 이름이 식별자 규칙에 맞지 않을 때 진가를 발휘하죠:
존재하지 않는 속성을 읽으면 에러가 나지 않고 undefined가 반환됩니다:
그렇게 두면 오타를 조용히 덮어버리게 됩니다. 오류를 제대로 터뜨리고 싶다면, 직접 명시적으로 체크해야 합니다.
객체 속성 추가와 삭제
자바스크립트 객체는 언제든 열려 있습니다. 필요할 때마다 자유롭게 키를 추가할 수 있죠:
그리고 delete로 속성을 제거할 수 있습니다:
delete 연산자는 매일 쓸 일은 없지만, 키 자체를 완전히 제거해야 할 때 꼭 필요한 도구입니다. 단순히 undefined를 할당하는 것과는 다르죠. 예를 들어 user.email = undefined로 두면 키는 그대로 남아 있어서 "email" in user가 여전히 true를 반환합니다.
객체에 속성이 있는지 확인하기
속성 존재 여부를 확인하는 방법은 크게 세 가지인데, 각각 의미가 조금씩 다릅니다:
in연산자는 프로토타입 체인을 타고 상속된 키까지 포함해서 존재 여부를 확인합니다.Object.hasOwn(obj, key)는 객체 자신의 키만 검사합니다. 상속된 속성을 무시하고 싶을 때 쓰면 됩니다. 예전에 쓰던hasOwnProperty를 대체하는 최신 방식이에요.obj.key !== undefined는 대부분 잘 동작하지만, 값이 명시적으로undefined로 설정된 속성이 있으면 잘못된 결과를 줍니다.
뭘 써야 할지 애매할 땐 Object.hasOwn을 고르세요. 대부분 의도한 대로 동작합니다.
메서드: 객체에 속한 함수
값이 함수인 속성을 메서드 라고 부릅니다. 객체 리터럴 안에서 메서드를 정의할 때 쓰는 단축 문법도 있어요:
메서드 안에서 쓰인 this는 그 메서드를 호출한 객체를 가리킵니다. 이 예제에서는 user가 되겠죠. 그래서 greet가 어떤 이름을 써야 할지 알 수 있는 겁니다.
한 가지 주의할 점이 있습니다. 화살표 함수는 자기만의 this를 갖지 않기 때문에, 객체의 다른 속성을 참조해야 하는 메서드에는 적합하지 않습니다:
this를 쓰는 메서드는 축약형 문법(greet() { ... })으로 정의하세요. 화살표 함수는 콜백에는 좋지만, 객체 메서드로는 적합하지 않습니다.
중첩 객체
값 자리에 또 다른 객체가 올 수도 있습니다. 원하는 만큼 깊게 중첩할 수 있죠:
중첩된 속성에 접근할 때는 user.address.city처럼 점으로 이어서 쓰면 됩니다. 다만 한 가지 함정이 있는데요, 중간 경로 중 어느 하나라도 null이나 undefined면 TypeError가 발생합니다:
console.log(user.profile.city);
// TypeError: undefined 의 속성을 읽을 수 없음 ('city' 읽는 중)
옵셔널 체이닝(user.profile?.city)이 요즘 쓰는 해결책입니다. 중간 고리가 없을 때 에러를 던지는 대신 undefined를 돌려주죠. 이 주제는 이번 챕터 뒤쪽에서 따로 다룰 예정입니다.
객체 순회하기 (for...in, Object.keys)
객체의 모든 키를 훑어야 할 때는 Object.keys, Object.values, Object.entries 삼총사가 제격입니다:
Object.entries가 제일 쓸모 있습니다. 키와 값을 한 번에 꺼내 주니까 배열 구조 분해(destructuring)랑 궁합이 딱 맞죠.
for...in 반복문도 있긴 한데, 상속된 속성까지 훑어버리기 때문에 대부분의 경우 원하는 동작이 아닙니다:
for (const key in scores) {
console.log(key); // 동작하지만 상속된 키도 포함됨
}
상속된 속성까지 순회해야 하는 특별한 경우가 아니라면, Object.keys나 Object.entries를 쓰는 게 좋습니다.
꼭 알아둬야 할 객체 리터럴 단축 문법
실무 코드에서 정말 자주 마주치게 되는 문법 설탕(syntactic sugar) 두 가지를 소개합니다.
프로퍼티 축약은 변수명과 키가 같을 때 작동합니다. 계산된 키([expr] 형태)는 프로퍼티 이름을 동적으로 만들 때 쓰는데, 특정 필드를 이름으로 받아 업데이트하는 함수를 작성할 때 특히 유용합니다.
객체 동등 비교는 참조 기준
이건 거의 모든 사람이 한 번쯤 당하는 함정입니다:
객체에 ===를 쓰면 내용이 같은지가 아니라 메모리상 같은 객체를 참조하는지를 검사합니다. 즉, 속성이 똑같아 보이는 두 객체라도 실제로는 서로 다른 객체예요.
"두 객체의 모양이 같은가?"를 확인하고 싶다면 필드를 직접 하나씩 비교하거나, 깊은 비교(deep equality) 헬퍼를 사용해야 합니다. JSON.stringify(a) === JSON.stringify(b)는 평범한 데이터에는 쓸만한 빠른 편법이지만, 함수나 undefined가 들어 있거나 순환 참조가 있으면 바로 무너집니다.
const를 써도 객체는 바뀐다
const는 변수에 담긴 값 하나를 고정할 뿐, 그 값이 가리키는 객체까지 얼려주지는 않습니다:
정말로 불변성이 필요하다면 Object.freeze(user)로 이후 변경을 막을 수 있습니다(단, 얕게 동작하므로 중첩된 객체는 여전히 변경 가능합니다). 실무에서는 Object.freeze보다 관례에 기대는 경우가 더 많은데요, const로 선언한 객체는 "이 바인딩을 재할당하지 말자" 정도로 받아들이고, 불변성은 설계 차원에서 챙기는 편입니다.
다음 주제: 배열
객체와 배열은 나머지 모든 것을 쌓아 올리는 두 가지 기본 재료입니다. 객체는 이름표가 붙은 데이터를, 배열은 순서가 있는 데이터를 다루죠. 이어서 자바스크립트 배열이 어떻게 동작하는지, 그리고 대부분의 실제 작업을 책임지는 핵심 메서드들(push, slice, map 등)을 살펴보겠습니다.
자주 묻는 질문
자바스크립트에서 객체는 어떻게 만드나요?
가장 흔한 방법은 객체 리터럴입니다. const user = { name: 'Ada', age: 30 }처럼 중괄호 안에 키-값 쌍을 쉼표로 나열하면 됩니다. 키는 문자열이며(유효한 식별자라면 따옴표는 생략 가능), 값에는 숫자, 문자열, 배열, 함수, 다른 객체 등 뭐든 넣을 수 있습니다.
점 표기법(dot)과 대괄호 표기법(bracket)은 어떻게 다른가요?
키가 고정된 식별자일 때는 user.name과 user['name']이 똑같이 동작합니다. 대괄호는 키가 변수에 담겨 있을 때(user[key]), 공백이나 하이픈처럼 점 표기법으로 파싱할 수 없는 문자가 있을 때, 또는 숫자로 시작할 때 써야 합니다. 그 외에는 점 표기법이 더 깔끔합니다.
객체에 특정 속성이 있는지 어떻게 확인하나요?
'name' in user는 상속받은 속성까지 포함해서 키 존재 여부를 확인합니다. 객체 고유의 속성만 보고 싶다면 Object.hasOwn(user, 'name')을 쓰세요 — 기존 hasOwnProperty를 대체하는 최신 방식입니다. user.name !== undefined로 검사하는 방법도 있지만, 값이 명시적으로 undefined로 설정된 경우엔 잘못된 결과가 나올 수 있습니다.
객체의 키를 순회하려면 어떻게 해야 하나요?
for (const key in obj)는 상속받은 키까지 포함해서 순회합니다. 실무에서는 보통 Object.keys(obj), Object.values(obj), Object.entries(obj)를 for...of 루프나 .forEach()와 함께 씁니다. 특히 키와 값을 동시에 써야 할 때는 Object.entries가 가장 편합니다.