:ledger: 자바스크립트 객체에 대해 알아보자

자바스크립트엔 여덞 가지 자료형이 있다. 이 중 일곱 개는 오직 하나의 데이터(문자열,숫자 등)만 담을 수 있어 ‘원시형’이라 부르지만 객체형은 원시형과 달리 다양한 데이터를 담을 수 있다. 키로 구분된 데이터 집합이나 복잡한 개체를 저장할 수 있으며 자바스크립트 거의 모든 면에 녹아있는 개념으로 무엇인지 알아보자.

:one: 객체란?

객체를 서랍장으로 생각해보면 이해하기가 쉽다. 서랍장 안 파일은 프로퍼티, 파일 각각에 붙어있는 이름표는 객체의 키라고 생각하면 된다. 복잡한 서랍장 안에서 이름표(key)를 보고 원하는 파일을 쉽게 찾을 수 있듯이, 객체에선 키를 이용해 프로퍼티를 쉽게 찾을 수 있다.

:pushpin: 1-1) 객체 생성 방법

빈 객체를 만드는 방법은 두가지가 있다. 주로 객체 리터럴 방법을 사용한다.

let user = new Object(); // '객체 생성자' 문법
let user = {}; // '객체 리터럴' 문법

:pushpin: 1-2) 객체의 주요 활동

  1. 데이터 구조화 및 관리 (관련 데이터를 그룹화한다. => const user = {name, age, email})
  2. API 통신 (JSON 형식의 데이터를 사용 => 자바스크립트 객체와 유사)
  3. 상태 관리 (react useState 등)

:pushpin: 1-3) 객체를 다룰 때 알아야하는 필수 기능

  1. 프로퍼티 접근
  2. 객체 디스트럭처링
  3. Object 메서드 (keys(), values(), entries())
const user = { name: "김민규", age: 30 };

// 1. 프로퍼티 접근
console.log(user.name);

// 2. 객체 디스트럭처링
const { name, age } = user;
console.log(name, age); // 김민규 30

// 3. Object 메서드
console.log(Object.keys(user)); // [name, age]
console.log(Object.values(user)); // ["김민규", 30]
console.log(Object.entires(user)); // [["name", "김민규"], ["age", 30]]

:pushpin: 1-4) 불변성 유지 및 필요한 키만 선택

  • 불변성 유지: 객체를 직접 수정하는 대신, 새로운 객체를 생성하여 변경된 값을 반영한다.
const user = { name: "김민규", age: 30 };
const updateUser = { ...user, age: 31 };
console.log(updateUser); // {name: "김민규", age: 31}
  • 필요한 키만 선택하기: 객체를 다룰 때, 필요한 프로퍼티만 선택하여 새로운 객체를 생성할 수 있다.
const user = { name: "김민규", age: 30, email: "kim@example.com" };
const { name, email } = user;
console.log({ name, email }); // { name: "김민규", email: "hong@example.com" }

:two: 리터럴과 프로퍼티

중괄호 {...} 안에는 키:값 쌍으로 구성된 프로퍼티가 들어간다.

let user = {
  // 객체
  name: "김민규", // 키: "name",  값: "김민규"
  age: 30, // 키: "age", 값: 30
};

:pushpin: 2-1) 프로퍼티 읽기

점 표기법(dot notation)을 이용해서 프로퍼티 값을 읽을 수 있다.

alert(user.name); // 김민규

:pushpin: 2-2) 프로퍼티 삭제

delete연산자를 사용하여 프로퍼티를 삭제할 수 있다.

delete user.age;

:pushpin: 2-3) 프로퍼티 복수의 단어도 사용이 가능

여러 단어를 조합해 프로퍼티 이름을 만든 경우에는 프로퍼티 이름을 따옴표로 묶어줘야 한다.

let user = {
  name: "김민규",
  age: 30,
  "likes birds": true, // 복수의 단어는 따옴표로 묶어준다.
};

:pushpin: 2-4) 프로퍼티 값은 모든 자료형이 올 수 있다.

프로퍼티 값엔 모든 자료형이 올 수 있다.

user.isAdmin = true;

:pushpin: 2-5) 상수 객체는 수정될 수 있다.

const로 선언된 객체는 수정이 될 수 있다.

const user = {
  name: "김민규",
};

user.name = "규민김"; // (*)

alert(user.name); // 규민김
  • (*) 표시한 라인에서 오류를 일으키는 것 처럼 보일 수 있지만 객체의 프로퍼티는 수정이 가능하고 const로 선언된 user를 전체적으로 설정하려 할 때만 오류가 발생한다.

:three: 대괄호 표기법

키가 유효한 변수 식별자가 아닌 경우엔 점 표기법 대신 대괄호 표기법(square bracket notation)이라 불리는 방법을 사용할 수 있다.

let user = {};

// set
user["likes birds"] = true;

// get
alert(user["likes birds"]); // true

// delete
delete user["likes birds"];

아래와 같이 변수를 키로 사용한 것 같은 문자열뿐만 아니라 모든 표현식의 평과 결과를 프로퍼티 키로 사용할 수도 있다.

let key = "likes birds";

// user["likes birds"] = true; 와 같음
user[key] = true;

변수 key는 런타임에 평가되기 때문에 사용자 입력값 변경 등에 따라 변경될 수 있다. 어떤 경우든, 평가가 끝난 이후의 결과가 프로퍼티 키로 사용된다. 이를 응용하면 코드를 유연하게 작성할 수 있다.

// 예시
let user = {
  name: "John",
  age: 30,
};

let key = prompt("사용자의 어떤 정보를 얻고 싶으신가요?", "name");

// 변수로 접근
alert(user[key]); // John (프롬프트 창에 "name"을 입력한 경우)

// 점표기법은 불가능함
alert(user.key); // 프롬프트 창은 열리지만 값은 undefined

:three: 계산된 프로퍼티

객체를 만들 때 객체 리터럴 안의 프로퍼티 키가 대괄호로 둘러싸여 있는 경우, 계산된 프로퍼티라고 한다.

  • [fruit]는 프로퍼티 이름을 변수 fruit에서 가져오겠다는 것을 의미한다.
  • 사용자가 프롬프트 대화상자에 apple을 입력했다면 bag엔 {apple: 5}가 할당 된다.
  • apple 이 아닌 다른 값을 입력하면 undefined 출력
// 방식 1
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");

let bag = {
  [fruit]: 5, // 변수 fruit에서 프로퍼티 이름을 동적으로 받아 온다.
};

alert(bag.apple); // fruit에 "apple"이 할당되었다면, 5가 출력된다. 다른 값을 넣으면 undefined

// 방식 2
let fruit = prompt("어떤 과일을 구매하시겠습니까?", "apple");
let bag = {};

// 변수 fruit을 사용해 프로퍼티 이름을 만들었습니다.
bag[fruit] = 5;

:pushpin: 3-1) 대괄호 표기법은 그래서 언제 사용해?

  • 대괄호 표기법은 프로퍼티 이름과 값의 제약을 없애주기 때문에 점 표기법보다 훨씬 강력하지만 작성하기 번거롭다는 단점이 있다.
  • 프로퍼티 이름이 확정된 상황이고, 단순한 이름이라면 처음엔 점 표기법을 사용하고 복잡한 상황일 경우 대괄호 표기법으로 바뀌는 경우가 많다.

:four: 단축 프로퍼티

실무에서 프로퍼티 값을 기존 변수에서 받아와 사용하는 경우가 종종 있다. 아래의 예시를 보자.

  • 예시에서 프로퍼티들은 이름과 값이 변수의 이름과 동일한데, 이런 경우 프로퍼티 값 단축 구문을 사용하여 코드를 짧게 줄일 수 있다.
// 변경 전
function makeUser(name, age) {
  return {
    name: name,
    age: age,
    // ...등등
  };
}

// 변경 후
function makeUser(name, age) {
  return {
    name,
    age,
    // ...등등
  };
}

let user = makeUser("John", 30);
alert(user.name); // John

:five: 프로퍼티 이름 제약사항

변수 이름(key)엔 예약어(for, let, return)가 사용되면 안된다. 하지만 객체 프로퍼티에서는 제약이 없다.

// 예약어를 키로 사용해도 ㄱㅊ
let obj = {
  for: 1,
  let: 2,
  return: 3,
};

alert(obj.for + obj.let + obj.return); // 6

:six: in 연산자로 프로퍼티 존재 여부 확인하기

자바스크립트 객체의 중요한 특징 중 하나는 다른 언어와는 달리, 존재하지 않는 프로퍼티에 접근하려 해도 에러가 발생하지 않고 undefined를 반환한다는 것이다.

let user = {};

alert(user.test === undefined); // true는 '프로퍼티가 존재하지 않음'을 의미한다.

위와 같이 비교하는 방법 이외에에도 연산자 in을 사용하면 프로퍼티 존재 여부를 쉽게 확인할 수 있다.

  • in 왼쪽엔 반드시 프로퍼티 이름이 와야한다.
  • 프로퍼티 이름은 보통 따옴표로 감싼 문자열이다.
// "key" in object

let user = { name: "김민규", age: 30 };
alert("age" in user); // user.age가 있으므로 true가 출력된다.
alert("KIN" in user); // user.KIN은 존재하지 않기 때문에 false가 출력된다

:pushpin: 6-1) undefined를 비교하지 않고 in 연산자를 사용하는 이유

일치 연산자( === )를 사용해서 프로퍼티의 존재 여부를 알아내는 방법도 잘 동작하지만 가끔씩 실패할 때도 있기에 in 연산자를 사용하면 프로퍼티 존재 여부를 제대로 판별할 수 있다. 아래는 실패할 때의 예시이다.

let obj = {
  test: undefined, // 이렇게 까지..?
};

alert(obj.test); // 값이 `undefined`이므로, 얼럿 창엔 undefined가 출력됩니다. 그런데 프로퍼티 test는 존재함.
alert("test" in obj); // `in`을 사용하면 프로퍼티 유무를 제대로 확인할 수 있음(true가 출력됨).

:seven: for..in 반복문

for..in 반복문을 사용하면 객체의 모든 키를 순회할 수 있다.

// 문법
for (key in object) {
  // 각 프로퍼티 키(key)를 이용하여 본문(body)을 실행함
}

// 예시
let user = {
  name: "John",
  age: 30,
  isAdmin: true,
};

for (let key in user) {
  // 키
  alert(key); // name, age, isAdmin
  // 키에 해당하는 값
  alert(user[key]); // John, 30, true
}

:eight: 객체 정렬 방식 (정수 프로퍼티)

객체의 정렬 방식은 특별한 방식으로 정렬

  • 정수 프로퍼티(integer property): 키가 정수로 해석 가능한 경우, 프로퍼티가 자동으로 오름차순 정렬된다.
  • 그 외의 프로퍼티: 객체에 추가된 순서대로 유지된다.

:pushpin: 8-1) 정수 프로퍼티란?

정수 프로퍼티는 “정수로 변환해도 변형 없이 동일한 값을 유지하는 문자열”을 의미한다.

// 정수 프로퍼티 예시
alert(String(Math.trunc(Number("49")))); // "49" → 정수 프로퍼티
alert(String(Math.trunc(Number("+49")))); // "49" → 정수 프로퍼티 아님
alert(String(Math.trunc(Number("1.2")))); // "1"  → 정수 프로퍼티 아님

:pushpin: 8-2) 정수프로퍼티 정렬 동작

예를 들어, 국제전화 나라 번호가 담긴 객체가 있다고 가정한다. 아래는 예시다.

let codes = {
  49: "독일",
  41: "스위스",
  44: "영국",
  1: "미국",
};

for (let code in codes) {
  alert(code); // 1, 41, 44, 49
}
  • 이 경우, 키가 정수로 해석 가능하므로 1, 41, 44, 49 순으로 출력된다.
  • 사용자가 독일(49)을 주로 사용한다고 가정했을 때, 이를 맨 앞에 출력하고 싶다면 어떻게 해야 할까?
    • 키를 정수로 취급하지 않게 하려면 속임수를 사용하면 된다. 예를 들어, 각 나라 번호 앞에 “+”를 붙이면 된다.
let codes = {
  "+49": "독일",
  "+41": "스위스",
  "+44": "영국",
  "+1": "미국",
};

for (let code in codes) {
  alert(+code); // 49, 41, 44, 1
}

:pushpin: 8-3) 정수 키 사용 시 주의할 점

  • 정수 키는 자동 정렬되므로, 순서가 중요한 경우 정수 대신 문자열로 처리하는 것이 좋다.
  • 프로퍼티 순서가 중요한 경우 배열을 사용하는 것이 더 적합할 수 있다.

:fire: 회고

자바스크립트 객체에 대해 익숙하다 생각했는데, 새롭게 알게 되거나 놓친 부분도 많았다. 정렬 규칙은 전혀 생각도 못한 내용이었는데, 알게되었고 대괄호 표기법에 대해서도 자세히 알 수 있어 좋앗다.

:pushpin: 알게된 점

  1. 객체는 프로퍼티 정렬 규칙을 따른다: 정수 프로퍼티는 자동으로 정렬되고, 나머지 프로퍼티는 작성된 순서를 유지한다는 점이 흥미로웠다. 특히 이 규칙이 어떻게 적용되는지를 구체적으로 알 수 있었다.
  2. 정수 프로퍼티 회피 방법: 정수 프로퍼티로 인해 의도한 순서가 깨지는 상황에서, “+”를 붙이는 방식으로 해결할 수 있다는 점은 실무에서도 유용하게 쓰일 것 같다.

:pushpin: 참고 문서

태그:

카테고리: ,

업데이트: