자바스크립트 기초 #5 객체와 배열
#4 함수 에서 동작을 묶는 도구를 봤으니, 이제 데이터를 묶는 도구 차례입니다. 자바스크립트의 두 핵심 자료구조 — 객체와 배열.
객체 — 키-값 쌍의 모음 #
const user = {
id: 'u1',
name: '커티스',
age: 30,
isAdmin: false,
};
console.log(user.name); // 커티스
console.log(user['name']); // 커티스 — 같은 효과
객체는 키-값 쌍을 묶는 자료구조입니다. 키는 문자열(또는 심볼)이고, 값은 어떤 타입이든 됩니다.
속성 추가/수정/삭제 #
const user = { id: 'u1', name: '커티스' };
user.age = 30; // 추가
user.name = '다른 이름'; // 수정
delete user.id; // 삭제
console.log(user);
// { name: '다른 이름', age: 30 }
const로 선언했는데 객체 안의 값이 바뀌는 게 처음엔 헷갈려요. const는 변수가 가리키는 곳을 못 바꾼다는 뜻이지, 객체 안의 값이 못 바뀐다는 뜻이 아닙니다(#2 참조 vs 원시).
단축 표기 #
같은 이름의 변수를 객체 키로 쓸 때 단축할 수 있습니다.
const id = 'u1';
const name = '커티스';
// 풀 표기
const user = { id: id, name: name };
// 단축 — 변수 이름과 키 이름이 같으면 한 번만
const userShort = { id, name };ES2015 이후로 기본인 모양이라, 옛 코드만 풀 표기를 쓰는 정도입니다.
메서드 단축 #
값으로 함수를 둘 때도 단축이 있습니다.
const calc = {
add(a, b) { return a + b; }, // 단축
subtract: function(a, b) { return a - b; }, // 풀 표기
};
calc.add(2, 3); // 5
calc.subtract(5, 2); // 3
배열 — 순서 있는 값들의 묶음 #
const fruits = ['사과', '바나나', '포도'];
console.log(fruits[0]); // 사과 — 인덱스는 0부터
console.log(fruits.length); // 3
console.log(fruits[10]); // undefined — 없는 인덱스
배열도 객체의 한 종류입니다(typeof []가 'object'). 다만 인덱스로 순서가 있는 데이터를 다루기 좋게 특화돼 있습니다.
추가/제거 #
const fruits = ['사과'];
fruits.push('바나나'); // 끝에 추가
// ['사과', '바나나']
fruits.unshift('포도'); // 앞에 추가
// ['포도', '사과', '바나나']
fruits.pop(); // 끝에서 제거
// ['포도', '사과']
fruits.shift(); // 앞에서 제거
// ['사과']
이 네 메서드는 원본을 직접 바꿔요. 원본을 안 바꾸고 새 배열을 만들고 싶으면 spread(뒤에서)를 쓰는 게 안전합니다.
자주 쓰는 배열 메서드 — map, filter, reduce #
순회하면서 변환/거르기/누적하는 세 메서드. 모던 자바스크립트의 가장 자주 쓰는 도구입니다.
map — 변환
#
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map((n) => n * 2);
// [2, 4, 6, 8, 10]
const labels = nums.map((n) => `#${n}`);
// ['#1', '#2', '#3', '#4', '#5']
원본은 그대로 두고 새 배열을 반환합니다. 길이는 같고, 각 원소는 콜백의 반환값.
filter — 거르기
#
const nums = [1, 2, 3, 4, 5];
const evens = nums.filter((n) => n % 2 === 0);
// [2, 4]
const big = nums.filter((n) => n > 3);
// [4, 5]
콜백이 truthy를 반환하는 원소만 남습니다. 길이가 줄어들 수 있습니다.
reduce — 누적
#
const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((acc, n) => acc + n, 0);
// 15
const max = nums.reduce((acc, n) => n > acc ? n : acc, -Infinity);
// 5
두 번째 인자(0, -Infinity)는 초깃값. 이걸로 시작해서 콜백을 한 원소씩 적용하며 값을 누적해 갑니다. 가장 강력한 메서드지만 처음에는 흐름이 보이기 어려워요. map/filter 만으로 안 풀리는 경우에 꺼내는 도구입니다.
체이닝 #
세 메서드는 서로 이어 쓰기 좋습니다.
const nums = [1, 2, 3, 4, 5];
const result = nums
.filter((n) => n % 2 === 0)
.map((n) => n * 2);
// [4, 8]
각 메서드가 새 배열을 반환하기 때문에 자연스럽게 이어집니다.
자주 쓰는 다른 메서드들 #
[1, 2, 3].includes(2); // true
[1, 2, 3].indexOf(2); // 1
[{id: 'a'}].find((x) => x.id === 'a'); // {id: 'a'}
[1, 2, 3].some((n) => n > 2); // true (하나라도)
[1, 2, 3].every((n) => n > 0); // true (전부)
[1, 2, 3].slice(1, 3); // [2, 3]
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]
[3, 1, 2].toSorted(); // [1, 2, 3] (원본 유지)
[1, 2, 3].join(','); // '1,2,3'
[1, 2, 3].join(' / '); // '1 / 2 / 3'
toSorted는 ES2023에 추가된 모던 메서드. 옛날에는 sort가 원본을 바꿔서 사고가 잦았는데, toSorted가 깔끔합니다.
Spread (...) — 펼치기
#
객체와 배열을 풀어서 다시 묶는 문법.
const a = [1, 2, 3];
const b = [...a, 4, 5]; // [1, 2, 3, 4, 5]
const copy = [...a]; // [1, 2, 3] — 얕은 복사
const merged = [...a, ...b]; // 두 배열 이어붙이기
const user = { id: 'u1', name: '커티스' };
const updated = { ...user, age: 30 };
// { id: 'u1', name: '커티스', age: 30 }
const overridden = { ...user, name: '다른 이름' };
// { id: 'u1', name: '다른 이름' } ← 뒤에 적은 게 이김
이 패턴이 자바스크립트로 일하면서 가장 자주 쓰는 관용구 중 하나입니다. 원본을 안 바꾸고 변경된 새 값을 만드는 상황에 적합합니다.
디스트럭처링 (Destructuring) — 풀어 받기 #
객체나 배열의 값을 한 번에 여러 변수로 풀어 받는 문법.
객체 디스트럭처링 #
const user = { id: 'u1', name: '커티스', age: 30 };
const { name, age } = user;
console.log(name); // 커티스
console.log(age); // 30
// 이름 바꿔서 받기
const { name: userName } = user;
console.log(userName); // 커티스
// 기본값
const { email = '없음' } = user;
console.log(email); // 없음
함수 매개변수에서도 자주 씁니다. 리액트 강좌의 props 디스트럭처링이 정확히 이 문법입니다.
function greet({ name, age }) {
console.log(`안녕, ${name} (${age})`);
}
greet({ name: '커티스', age: 30 });배열 디스트럭처링 #
const [first, second, third] = [10, 20, 30];
console.log(first, second, third); // 10 20 30
// 건너뛰기
const [, , third] = [10, 20, 30];
console.log(third); // 30
// rest로 나머지
const [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2, 3, 4]
useState의 const [count, setCount] = useState(0)가 정확히 이 문법입니다.
객체 키 동적으로 — 계산된 속성 #
키 자체를 변수로 만들고 싶을 때.
const key = 'name';
const value = '커티스';
const user = {
[key]: value,
[`is${key}Required`]: true,
};
// { name: '커티스', isnameRequired: true }
대괄호 [...] 안에 표현식을 넣을 수 있습니다. 폼 핸들러에서 name 어트리뷰트를 키로 쓰는 패턴 등 실전에서 자주 등장합니다.
Object.keys / values / entries #
객체를 배열처럼 다루고 싶을 때.
const user = { id: 'u1', name: '커티스', age: 30 };
Object.keys(user); // ['id', 'name', 'age']
Object.values(user); // ['u1', '커티스', 30]
Object.entries(user); // [['id', 'u1'], ['name', '커티스'], ['age', 30]]
순회와 결합하면 강력합니다.
for (const [key, value] of Object.entries(user)) {
console.log(`${key} = ${value}`);
}마무리 #
이번 글에서 정리한 내용:
- 객체 — 키-값 쌍, 단축 표기와 메서드 단축
- 배열 — 순서 있는 묶음, push/pop/shift/unshift는 원본 변경
- map/filter/reduce — 변환/거르기/누적의 세 메서드
- some/every/find/includes 같은 검사 메서드
- spread
...로 객체/배열 펼치기와 얕은 복사 - 디스트럭처링 — 객체/배열을 풀어 변수로 받기
- 계산된 속성 —
{ [key]: value } Object.keys/values/entries로 객체를 배열처럼 다루기
다음 글(#6 문자열과 템플릿 리터럴)에서는 문자열 다루기 — 자주 쓰는 메서드, 템플릿 리터럴, 그리고 정규식 기본을 다룹니다.