지금까지 우리는 값, 변수, 조건문, 반복문, 함수를 배웠습니다. 이제 진짜 프로그램다운 프로그램을 만들려면 여러 개의 데이터를 한꺼번에 다루는 방법이 필요합니다. 학생 여러 명의 이름, 장바구니 상품 목록, 게임 캐릭터 정보, 할 일 목록처럼 말이죠.
이때 가장 자주 등장하는 주인공이 바로 배열(Array) 과 객체(Object) 입니다.
아주 간단히 말하면,
배열은 순서가 있는 목록입니다.
객체는 이름표가 붙은 정보 묶음입니다.
둘 다 데이터를 정리하는 상자이지만, 쓰는 목적이 조금 다릅니다. 이 장에서는 그 차이를 감각적으로 이해하고, 실제로 꺼내고 바꾸고 반복하는 방법까지 익혀보겠습니다.
배열과 객체는 왜 필요할까?
예를 들어 과일 세 개를 저장한다고 생각해 봅시다.
let fruit1 = "사과";
let fruit2 = "바나나";
let fruit3 = "포도";세 개 정도는 괜찮아 보입니다. 그런데 과일이 30개라면 어떨까요? 변수 이름을 fruit27, fruit28처럼 계속 만드는 순간, 코드가 갑자기 숙제가 됩니다.
이럴 때 배열을 쓰면 훨씬 자연스럽습니다.
const fruits = ["사과", "바나나", "포도"];이번에는 한 사람의 정보를 저장한다고 해보겠습니다.
const userName = "민수";
const userAge = 20;
const userCity = "서울";이것도 정보가 서로 관련되어 있으니 하나로 묶고 싶어집니다. 그럴 때 객체를 씁니다.
const user = {
name: "민수",
age: 20,
city: "서울"
};배열과 객체를 쓰면 데이터가 읽기 쉬워지고, 반복하기 쉬워지고, 수정하기 쉬워집니다. 실제 JavaScript 프로그램에서 이 둘은 밥과 국처럼 거의 늘 같이 나옵니다.
배열이란 무엇인가
배열은 여러 값을 순서대로 저장하는 자료구조입니다. 자료구조라는 말이 조금 낯설다면, 그냥 데이터를 담는 방식이라고 생각하면 됩니다.
const colors = ["red", "blue", "green"];이 배열에는 값이 세 개 들어 있습니다. 각각의 값은 인덱스(index) 로 구분합니다. 인덱스는 배열 안에서 값의 위치를 뜻하는 번호인데, 아주 중요하게도 0부터 시작합니다.1
console.log(colors[0]); // "red"
console.log(colors[1]); // "blue"
console.log(colors[2]); // "green"처음 보면 왜 1부터가 아니라 0부터일까 싶지만, JavaScript 배열은 원래 그렇게 동작합니다. 초보자가 가장 자주 하는 실수 중 하나가 바로 이 0부터 시작하는 규칙을 잊는 것입니다.
배열에는 다양한 자료형을 함께 넣을 수도 있습니다.1
const mixed = [10, "hello", true, null];물론 실제 코드를 작성할 때는 가능한 한 같은 종류의 데이터를 묶는 편이 읽기 쉽습니다. 예를 들어 이름 목록만 넣거나, 점수 목록만 넣는 식이 더 깔끔합니다.
배열 만들기
배열을 만드는 가장 쉬운 방법은 대괄호 [] 를 사용하는 것입니다.1
const numbers = [1, 2, 3, 4, 5];빈 배열부터 만들 수도 있습니다.
const todos = [];그리고 나중에 값을 넣을 수 있습니다.
const todos = [];
todos[0] = "청소하기";
todos[1] = "공부하기";배열은 보통 const 로 선언해도 괜찮습니다. const 라고 해서 배열 안의 값을 절대 못 바꾸는 것은 아닙니다. 변수 자체를 다른 배열로 다시 연결하지 못할 뿐, 배열 안의 요소는 바꿀 수 있습니다.2
const fruits = ["사과", "바나나"];
fruits[0] = "포도"; // 가능
// fruits = ["딸기"]; // 오류배열 값 꺼내기
배열의 값은 인덱스로 꺼냅니다.
const animals = ["고양이", "강아지", "토끼"];
console.log(animals[0]); // 고양이
console.log(animals[2]); // 토끼마지막 값을 가져오고 싶을 때는 length 를 자주 사용합니다. length 는 배열의 길이, 즉 요소 개수를 알려줍니다.1
const animals = ["고양이", "강아지", "토끼"];
console.log(animals.length); // 3
console.log(animals[animals.length - 1]); // 토끼마지막 인덱스가 length - 1 인 이유는 배열이 0부터 시작하기 때문입니다. 길이가 3이면 인덱스는 0, 1, 2입니다.
배열 값 바꾸기
특정 위치의 값을 새 값으로 바꾸려면 해당 인덱스에 다시 대입하면 됩니다.
const menu = ["김밥", "라면", "우동"];
menu[1] = "떡볶이";
console.log(menu); // ["김밥", "떡볶이", "우동"]이 방식은 간단하지만, 어느 위치를 바꾸는지 정확히 알고 있을 때 가장 잘 맞습니다.
배열에 값 추가하고 제거하기
배열은 동적으로 크기가 늘어나고 줄어듭니다.1 그래서 데이터를 추가하거나 제거하는 메서드를 자주 사용합니다.
가장 기본적인 메서드는 다음과 같습니다.3
push(): 맨 뒤에 추가pop(): 맨 뒤에서 제거unshift(): 맨 앞에 추가shift(): 맨 앞에서 제거
const fruits = ["사과", "바나나"];
fruits.push("포도");
console.log(fruits); // ["사과", "바나나", "포도"]
fruits.pop();
console.log(fruits); // ["사과", "바나나"]
fruits.unshift("딸기");
console.log(fruits); // ["딸기", "사과", "바나나"]
fruits.shift();
console.log(fruits); // ["사과", "바나나"]이 메서드들은 원래 배열 자체를 바꿉니다. 즉, 원본이 수정됩니다.
원하는 위치에서 자르기와 끼워 넣기
배열 중간을 다루고 싶다면 splice() 와 slice() 를 구분해서 기억하면 좋습니다.
splice() 는 원본 배열을 바꾸고, slice() 는 잘라낸 새 배열을 돌려줍니다.4
const items = ["A", "B", "C", "D"];
items.splice(1, 1, "X");
console.log(items); // ["A", "X", "C", "D"]위 코드는 인덱스 1부터 1개를 지우고 "X" 를 넣습니다.
const items = ["A", "B", "C", "D"];
const part = items.slice(1, 3);
console.log(part); // ["B", "C"]
console.log(items); // ["A", "B", "C", "D"]초보자 입장에서는 이렇게 외우면 편합니다.
splice()는 수술처럼 원본을 직접 건드린다.slice()는 조각만 복사해 온다.
배열 반복하기
배열이 빛나는 순간은 반복문과 만날 때입니다. 같은 작업을 여러 요소에 적용하기가 아주 쉬워집니다.
가장 기본은 for 문입니다.
const fruits = ["사과", "바나나", "포도"];
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}이 방식은 인덱스가 필요할 때 좋습니다.
좀 더 읽기 쉬운 방법으로 for...of 도 있습니다.
const fruits = ["사과", "바나나", "포도"];
for (const fruit of fruits) {
console.log(fruit);
}값만 하나씩 꺼내고 싶다면 for...of 가 자연스럽습니다.
또 자주 쓰는 방법이 forEach() 입니다.5
const fruits = ["사과", "바나나", "포도"];
fruits.forEach(function (fruit, index) {
console.log(index, fruit);
});화살표 함수로 더 짧게 쓸 수도 있습니다.
fruits.forEach((fruit, index) => {
console.log(index, fruit);
});다만 forEach() 는 중간에 멈추기 어렵고, 새로운 배열을 만들지도 않습니다.5 그래서 "각 요소에 대해 실행만 하고 싶다"는 상황에 잘 맞습니다.
배열에서 자주 쓰는 메서드
이제 초보자가 가장 많이 만나는 배열 메서드를 살펴보겠습니다. 전부 한 번에 외울 필요는 없습니다. 많이 쓰다 보면 손이 기억합니다.
indexOf()와 includes()로 찾기
const fruits = ["사과", "바나나", "포도"];
console.log(fruits.indexOf("바나나")); // 1
console.log(fruits.indexOf("딸기")); // -1
console.log(fruits.includes("포도")); // trueindexOf() 는 위치를 알려주고, 없으면 -1 을 반환합니다. includes() 는 있는지 없는지만 true 또는 false 로 알려줍니다.
map()으로 바꿔서 새 배열 만들기
map() 은 배열의 각 요소를 바꿔서 새 배열을 만듭니다.6
const numbers = [1, 2, 3];
const doubled = numbers.map((num) => {
return num * 2;
});
console.log(doubled); // [2, 4, 6]
console.log(numbers); // [1, 2, 3]원래 배열은 그대로 두고, 변형된 결과를 얻고 싶을 때 아주 좋습니다.
filter()로 조건에 맞는 것만 고르기
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = numbers.filter((num) => {
return num % 2 === 0;
});
console.log(evenNumbers); // [2, 4, 6]이 메서드는 "짝수만", "완료된 할 일만", "가격이 1만 원 이상인 상품만" 같은 상황에서 정말 자주 등장합니다.6
find()로 하나 찾기
const users = [
{ name: "민수", age: 20 },
{ name: "지수", age: 22 },
{ name: "하늘", age: 19 }
];
const result = users.find((user) => {
return user.name === "지수";
});
console.log(result); // { name: "지수", age: 22 }조건에 맞는 첫 번째 요소 하나를 찾을 때 씁니다.
객체란 무엇인가
객체는 관련 있는 데이터를 이름표와 함께 묶어 놓은 것입니다.
const person = {
name: "민수",
age: 20,
isStudent: true
};여기서 name, age, isStudent 는 속성(property) 이라고 부릅니다. 속성은 이름: 값 형태로 들어갑니다.
배열이 "순서가 있는 목록"이라면, 객체는 "설명이 붙은 정보 꾸러미"에 가깝습니다.
예를 들어 사람 정보를 저장할 때,
const person = ["민수", 20, true];이렇게 배열로 만들면 0번이 이름인지, 1번이 나이인지 매번 기억해야 합니다. 하지만 객체는 훨씬 읽기 쉽습니다.
const person = {
name: "민수",
age: 20,
isStudent: true
};이제 person.name 만 봐도 의미가 바로 보입니다.
객체 만들기
객체는 중괄호 {} 로 만듭니다.
const book = {
title: "자바스크립트 입문",
author: "홍길동",
price: 18000
};값으로는 문자열, 숫자, 불리언, 배열, 다른 객체, 심지어 함수도 넣을 수 있습니다.
const user = {
name: "지수",
hobbies: ["독서", "게임"],
address: {
city: "부산",
district: "해운대"
}
};즉, 객체 안에 배열을 넣을 수도 있고, 배열 안에 객체를 넣을 수도 있습니다. 실제 프로그램은 이런 중첩 구조를 자주 사용합니다.1
객체 값 꺼내기: 점 표기법과 대괄호 표기법
객체의 속성에 접근하는 방법은 두 가지입니다.
먼저 점 표기법입니다.
const person = {
name: "민수",
age: 20
};
console.log(person.name); // 민수
console.log(person.age); // 20두 번째는 대괄호 표기법입니다.
console.log(person["name"]); // 민수
console.log(person["age"]); // 20둘 다 자주 쓰이지만, 초보 단계에서는 점 표기법이 더 읽기 쉽습니다. 다만 속성 이름을 변수로 다룰 때는 대괄호가 필요합니다.
const person = {
name: "민수",
age: 20
};
const key = "name";
console.log(person[key]); // 민수person.key 라고 쓰면 key 라는 이름의 속성을 찾게 되므로 원하는 결과가 나오지 않습니다. 이 차이는 은근히 자주 헷갈립니다.
객체 값 수정, 추가, 삭제하기
객체의 속성은 쉽게 바꿀 수 있습니다.
const person = {
name: "민수",
age: 20
};
person.age = 21;
console.log(person.age); // 21없는 속성에 값을 넣으면 새 속성이 추가됩니다.
person.city = "서울";
console.log(person);
// { name: "민수", age: 21, city: "서울" }삭제할 때는 delete 를 사용할 수 있습니다.
delete person.city;
console.log(person);
// { name: "민수", age: 21 }하지만 실제 코드에서는 삭제보다 값을 바꾸거나 새 객체를 만들어 쓰는 방식도 자주 사용합니다.
객체 반복하기
객체는 배열처럼 length 가 있는 순서 목록이 아니기 때문에 반복 방식이 조금 다릅니다.
가장 기본적인 방법은 for...in 문입니다.
const person = {
name: "민수",
age: 20,
city: "서울"
};
for (const key in person) {
console.log(key, person[key]);
}여기서 key 는 속성 이름입니다. 값은 person[key] 로 꺼냅니다.
또 자주 쓰는 도구가 Object.keys(), Object.values(), Object.entries() 입니다.
const person = {
name: "민수",
age: 20,
city: "서울"
};
console.log(Object.keys(person));
// ["name", "age", "city"]
console.log(Object.values(person));
// ["민수", 20, "서울"]
console.log(Object.entries(person));
// [["name", "민수"], ["age", 20], ["city", "서울"]]이 메서드들은 객체를 배열처럼 다루게 도와줍니다. 예를 들어 Object.keys(person) 는 속성 이름만 모은 배열을 돌려주므로, 배열 메서드를 이어서 쓸 수 있습니다.
Object.keys(person).forEach((key) => {
console.log(key, person[key]);
});배열과 객체의 차이
둘은 비슷하게 생긴 듯하지만 목적이 다릅니다.
배열은 순서가 중요한 목록에 어울립니다.
객체는 이름이 있는 정보 묶음에 어울립니다.
배열은 숫자 인덱스로 접근합니다.
객체는 속성 이름으로 접근합니다.1
예를 들어 학생 이름 목록은 배열이 잘 맞습니다.
const students = ["민수", "지수", "하늘"];학생 한 명의 상세 정보는 객체가 잘 맞습니다.
const student = {
name: "민수",
age: 20,
grade: 3
};그리고 실제로는 이 둘을 합쳐서 많이 씁니다.
const students = [
{ name: "민수", age: 20 },
{ name: "지수", age: 22 },
{ name: "하늘", age: 19 }
];이 구조는 앞으로 아주 많이 보게 됩니다. 서버에서 받아오는 데이터, 상품 목록, 댓글 목록, 사용자 목록 대부분이 이런 모양입니다.
배열 안의 객체 다루기
이제 실전 느낌이 나는 구조를 보겠습니다.
const products = [
{ name: "마우스", price: 15000, inStock: true },
{ name: "키보드", price: 30000, inStock: false },
{ name: "모니터", price: 200000, inStock: true }
];첫 번째 상품 이름을 꺼내려면 이렇게 씁니다.
console.log(products[0].name); // 마우스반복하면서 모든 상품 이름을 출력할 수도 있습니다.
products.forEach((product) => {
console.log(product.name);
});재고가 있는 상품만 골라낼 수도 있습니다.
const availableProducts = products.filter((product) => {
return product.inStock === true;
});
console.log(availableProducts);가격만 따로 뽑아 새 배열을 만들 수도 있습니다.
const prices = products.map((product) => {
return product.price;
});
console.log(prices); // [15000, 30000, 200000]이쯤 오면 배열, 객체, 반복문, 함수가 한 팀처럼 움직인다는 것이 보이기 시작합니다.
배열 안의 객체 수정하기
배열 속 객체를 수정하는 상황도 아주 흔합니다. 예를 들어 사용자 목록에서 특정 사용자의 나이를 바꾸고 싶을 수 있습니다.
const users = [
{ id: 1, name: "민수", age: 20 },
{ id: 2, name: "지수", age: 22 }
];
const user = users.find((item) => {
return item.id === 2;
});
if (user) {
user.age = 23;
}
console.log(users);이 방법은 원래 객체를 직접 바꿉니다. 간단하고 직관적이죠.7
원본을 바꾸지 않고 새 배열을 만들고 싶다면 map() 을 사용할 수 있습니다.
const updatedUsers = users.map((item) => {
if (item.id === 2) {
return { ...item, age: 23 };
}
return item;
});이 코드는 조금 더 현대적인 방식입니다. 당장은 낯설어 보여도, "원본은 두고 새 결과를 만든다"는 생각은 앞으로 꽤 중요해집니다.7
중첩된 데이터 읽기
객체 안에 배열이 있고, 그 안에 또 객체가 있을 수도 있습니다.
const classroom = {
name: "프론트엔드 반",
students: [
{ name: "민수", score: 90 },
{ name: "지수", score: 85 }
]
};이 구조에서 지수의 점수를 읽으려면 이렇게 합니다.
console.log(classroom.students[1].score); // 85처음에는 복잡해 보이지만, 차근차근 읽으면 됩니다.
classroom객체에 접근한다.그 안의
students배열에 접근한다.배열의 두 번째 요소인
[1]에 접근한다.그 객체의
score를 읽는다.
길이 조금 길어졌을 뿐, 규칙은 똑같습니다.
초보자가 자주 하는 실수
배열과 객체는 너무 자주 쓰이기 때문에, 실수도 자주 나옵니다. 미리 알고 있으면 덜 당황합니다.
1. 배열 인덱스를 1부터 생각하는 실수
const fruits = ["사과", "바나나", "포도"];
console.log(fruits[1]); // 바나나fruits[1] 이 첫 번째가 아니라 두 번째입니다. 첫 번째는 fruits[0] 입니다.
2. 배열과 객체를 헷갈리는 실수
const user = {
name: "민수"
};
console.log(user.name); // 맞음
console.log(user["name"]); // 맞음반면 배열은 보통 숫자로 접근합니다.
const names = ["민수", "지수"];
console.log(names[0]);3. 없는 값을 읽어서 undefined 를 만나는 실수
const colors = ["red", "blue"];
console.log(colors[5]); // undefined객체도 마찬가지입니다.
const person = { name: "민수" };
console.log(person.age); // undefined오류가 아니라 undefined 가 나오는 경우가 많으니, "값이 없구나" 하고 이해하면 됩니다.
4. 배열에 너무 큰 인덱스를 바로 넣는 실수
const arr = ["A", "B"];
arr[5] = "F";
console.log(arr);이렇게 하면 중간이 비어 있는 드문 배열이 생길 수 있습니다.2 초보 단계에서는 push() 나 splice() 를 사용하는 편이 더 안전합니다.
5. forEach()가 새 배열을 만들어 준다고 착각하는 실수
forEach() 는 반복만 하고 끝납니다. 새 배열이 필요하면 map() 이나 filter() 를 생각해야 합니다.5
언제 배열을 쓰고, 언제 객체를 쓸까?
판단 기준은 생각보다 단순합니다.
순서대로 여러 항목을 보관하고 싶다면 배열이 맞습니다. 예를 들면 댓글 목록, 점수 목록, 메뉴 목록입니다.
하나의 대상을 설명하는 여러 속성을 묶고 싶다면 객체가 맞습니다. 예를 들면 사용자 정보, 상품 정보, 게시글 정보입니다.
그리고 현실에서는 둘을 함께 씁니다.
const comments = [
{ id: 1, writer: "민수", text: "좋아요!" },
{ id: 2, writer: "지수", text: "도움됐어요!" }
];댓글은 여러 개이므로 배열이고, 댓글 하나하나는 작성자와 내용 같은 속성을 가지므로 객체입니다. 이 패턴은 꼭 기억해 두세요.
작은 실전 예제: 장바구니 다루기
이제 배운 내용을 모아 간단한 예제를 보겠습니다.
const cart = [
{ name: "노트", price: 3000, count: 2 },
{ name: "펜", price: 1000, count: 3 },
{ name: "지우개", price: 500, count: 1 }
];장바구니 상품 이름 출력하기:
cart.forEach((item) => {
console.log(item.name);
});총 수량 구하기:
let totalCount = 0;
cart.forEach((item) => {
totalCount += item.count;
});
console.log(totalCount); // 6펜 찾기:
const pen = cart.find((item) => {
return item.name === "펜";
});
console.log(pen);가격이 1000원 이상인 상품만 고르기:
const expensiveItems = cart.filter((item) => {
return item.price >= 1000;
});
console.log(expensiveItems);상품 이름만 새 배열로 만들기:
const itemNames = cart.map((item) => {
return item.name;
});
console.log(itemNames); // ["노트", "펜", "지우개"]이런 패턴은 앞으로 DOM 조작, 이벤트 처리, API 데이터 다루기에서도 그대로 이어집니다.
이 장의 핵심 정리
배열과 객체는 JavaScript에서 데이터를 정리하는 가장 중요한 도구입니다.
배열은 여러 값을 순서대로 저장할 때 사용합니다. 인덱스는 0부터 시작하며, push(), pop(), map(), filter(), find() 같은 메서드를 자주 씁니다.
객체는 하나의 대상을 여러 속성으로 설명할 때 사용합니다. 점 표기법과 대괄호 표기법으로 속성에 접근할 수 있고, Object.keys(), Object.values(), Object.entries() 로 반복에도 활용할 수 있습니다.
그리고 실제 프로그램에서는 배열 안에 객체, 객체 안에 배열 같은 형태가 아주 자주 등장합니다. 이 구조에 익숙해지면, 이제부터 쓰게 될 코드는 훨씬 현실적인 모습이 됩니다.
다음 장에서는 이런 데이터를 이용해 웹 페이지의 실제 요소를 선택하고 바꾸는, 아주 재미있는 세계로 들어가 보겠습니다. 화면이 드디어 움직이기 시작합니다.
참고
5Array.prototype.forEach() - JavaScript | MDN
7How to Update an Object in an Array of Objects in JavaScript
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.