B 바이낸스 · 전 세계 1위 암호화폐 거래소바이낸스 가입 → AD OKX OKX · 글로벌 대형 암호화폐 거래소OKX 가입 → AD
💻 코딩 기초 · 6강 / 10

배열과 객체 — 데이터를 담고 다루기

배열은 순서가 있는 목록이고, 객체는 이름(키)으로 값을 찾는 묶음입니다. 둘을 합친 '객체의 배열'은 표와 같아서, 반복문과 `map`·`filter`·`reduce`로 합계를 내고 원하는 행을 골라낼 수 있습니다.

⏱ 약 24분 ✍️ 확인 문제 5개 🧪 코드 실습 4개 업데이트 2026-10-09
🎯 이 강의의 학습 목표
  • 배열의 인덱스·length·push로 값을 읽고 더할 수 있다
  • 객체의 속성을 점 표기와 대괄호 표기로 읽고 바꿀 수 있다
  • 객체의 배열을 표로 보고 반복문이나 map·filter·reduce로 가공할 수 있다
  • JSON.stringify와 JSON.parse로 데이터를 글자로 바꾸고 되돌릴 수 있다

1.배열 — 순서가 있는 목록

지금까지는 변수 하나에 값 하나를 담았습니다. 그런데 학생 30명의 점수를 변수 30개에 담을 수는 없습니다. 배열은 여러 값을 순서대로 한 이름 아래 담는 자료형입니다. 대괄호 안에 쉼표로 값을 늘어놓아 [70, 85, 90]처럼 만듭니다. 4강에서 for...of로 하나씩 꺼낸 그 목록이 배열입니다.

배열의 값은 위치 번호인 인덱스로 꺼냅니다. 인덱스는 1이 아니라 0부터 시작하므로 첫 값은 arr[0], 두 번째 값은 arr[1]입니다. 값의 개수는 arr.length이고, 마지막 값의 인덱스는 언제나 arr.length - 1입니다. 4강에서 본 off-by-one 실수가 바로 여기서 생깁니다.

없는 인덱스를 읽어도 오류가 나지 않고 undefined가 나옵니다. 길이가 3인 배열에서 arr[3]이나 arr[-1]을 읽으면 둘 다 undefined입니다. 뒤에서부터 세고 싶다면 arr.at(-1)을 쓰면 마지막 값이 나옵니다. 배열에는 숫자·문자열·불리언을 섞어 담을 수도 있지만, 실제로는 같은 종류의 값을 담아야 다루기 쉽습니다.

인덱스는 0부터, 없는 칸은 undefined
const fruits = ['사과', '배', '귤'];
console.log(fruits[0], fruits[2]);
console.log(fruits.length);
console.log(fruits[fruits.length - 1]);
console.log(fruits[3]);
console.log(fruits.at(-1));
출력
사과 귤
3
귤
undefined
귤

2.배열 바꾸기와 찾기 — `push`, `pop`, `includes`, `indexOf`

push는 배열 끝에 값을 더하고, pop은 끝의 값을 꺼내 돌려주면서 배열에서 뺍니다. arr[1] = '포도'처럼 인덱스에 직접 넣어 값을 바꿀 수도 있습니다. 여기서 헷갈리기 쉬운 점이 있습니다. const로 만든 배열도 내용은 바꿀 수 있습니다. const가 막는 것은 '그 이름에 다른 배열을 다시 넣는 것'이지 배열 속 칸을 바꾸는 것이 아닙니다.

값이 들어 있는지 확인할 때는 includes가 참·거짓을, indexOf가 위치를 돌려줍니다. indexOf는 찾지 못하면 -1을 돌려줍니다. 4강에서 '못 찾음'을 -1로 표시한 것과 같은 약속입니다. 그래서 if (arr.indexOf(x))처럼 쓰면 첫 칸(0)에 있을 때 거짓이 되고, 못 찾은 경우(-1)는 참이 되어 버립니다(3강의 truthy·falsy). 있는지만 알고 싶다면 includes를 쓰세요.

배열을 다른 변수에 넣으면 복사가 아니라 '같은 배열을 가리키는 이름'이 하나 더 생깁니다. 아래 코드에서 b에 push하면 a도 바뀝니다. 같은 집의 주소를 두 사람이 나눠 가진 것과 같습니다. 정말 따로 된 사본이 필요하면 [...a]처럼 펼쳐서 새 배열을 만듭니다. 객체도 똑같이 동작합니다.

push·pop·includes·indexOf
const cart = ['우유', '빵'];
cart.push('달걀');
console.log(cart, cart.length);
const last = cart.pop();
console.log(last, cart);
console.log(cart.includes('빵'), cart.indexOf('빵'));
console.log(cart.indexOf('김'));
출력
[ '우유', '빵', '달걀' ] 3
달걀 [ '우유', '빵' ]
true 1
-1
같은 배열을 가리키는 두 이름 vs 펼쳐서 만든 사본
const a = [1, 2];
const b = a;
b.push(3);
console.log(a);
const c = [...a];
c.push(4);
console.log(a, c);
출력
[ 1, 2, 3 ]
[ 1, 2, 3 ] [ 1, 2, 3, 4 ]

3.객체 — 이름표 붙은 칸

배열이 '몇 번째'로 값을 찾는다면, 객체는 '이름'으로 값을 찾습니다. 중괄호 안에 키: 값을 쉼표로 늘어놓아 { name: '민수', age: 20 }처럼 만듭니다. 각 키와 값의 쌍을 속성이라고 합니다. 회원 한 명, 상품 하나처럼 여러 정보가 모여 한 대상을 이룰 때 객체가 잘 맞습니다.

속성은 user.name처럼 점으로 읽는 것이 보통이고, 키 이름이 변수에 들어 있을 때는 user[key]처럼 대괄호로 읽습니다. 없는 속성을 읽으면 배열의 없는 칸처럼 undefined가 나옵니다. 그런데 그 undefined에서 다시 속성을 읽으면 그때는 TypeError가 납니다. 아래 코드가 그 경우로, 키 이름을 nmae로 잘못 쓴 것이 원인입니다. 메시지의 정확한 문구는 브라우저마다 조금씩 다릅니다.

이 오류는 AI가 짠 코드를 실제 데이터에 붙였을 때 가장 자주 보는 오류 가운데 하나입니다. 대개 데이터에 있는 키 이름과 코드가 기대한 키 이름이 다르거나, 어떤 항목에만 그 속성이 빠져 있을 때 생깁니다. 오류 메시지의 괄호 안 (reading 'length')는 '무엇을 읽으려다 실패했는지'를 알려 주므로, 그 바로 앞의 값이 왜 undefined인지 거슬러 찾아가면 됩니다(8강).

읽기·바꾸기·더하기, 없는 속성은 undefined
const user = { name: '민수', age: 20 };
console.log(user.name, user.age);
user.age = 21;
user.city = '부산';
console.log(user);
const key = 'name';
console.log(user[key]);
console.log(user.email);
출력
민수 20
{ name: '민수', age: 21, city: '부산' }
민수
undefined
undefined에서 다시 읽으면 TypeError
const user = { name: '민수' };
console.log(user.nmae);
console.log(user.nmae.length);
출력
undefined
TypeError: Cannot read properties of undefined (reading 'length')

4.객체의 배열 = 표

실제 데이터는 대부분 '객체의 배열' 모양입니다. 배열의 각 칸이 표의 한 행이고, 객체의 키가 열 이름입니다. 엑셀 같은 스프레드시트의 표 한 장을 코드로 옮겼다고 생각하면 됩니다. 아래 표와 코드는 같은 가상의 판매 기록을 두 가지로 나타낸 것입니다.

이 모양에서는 4강의 반복문과 지금 배운 속성 읽기를 합치면 거의 모든 계산을 할 수 있습니다. 행을 하나씩 꺼내(for...of) 필요한 열을 읽고(row.qty) 누적하거나 셉니다. 9강에서는 텍스트로 된 표를 이 모양으로 바꾸어 합계·평균·그룹별 합계를 직접 구해 봅니다.

가상의 판매 기록 — 행 하나가 객체 하나
(인덱스)`item``price``qty`
0공책15004
1연필50010
2지우개8003
예제위 가상의 판매 기록에서 전체 매출(가격 × 수량의 합)은 얼마일까요?
const sales = [
  { item: '공책', price: 1500, qty: 4 },
  { item: '연필', price: 500, qty: 10 },
  { item: '지우개', price: 800, qty: 3 },
];
let revenue = 0;
for (const row of sales) {
  revenue += row.price * row.qty;
}
console.log(revenue);
출력
13400
  1. 1단계 무엇을 쌓는가: 매출 합계. 시작값 0.
  2. 2단계 한 행의 일: row.price * row.qty를 더합니다. 공책 6,000, 연필 5,000, 지우개 2,400.
  3. 3단계 결과: 6,000 + 5,000 + 2,400 = 13,400원.
  4. 검산: 행이 3개이니 바퀴도 3번이어야 합니다. 행마다 중간 합계를 찍어 보면 6000 → 11000 → 13400 순서로 늘어납니다.
답13,400원 — 행마다 가격 × 수량을 누적한다

5.`map`·`filter`·`reduce` 맛보기

배열에는 반복문을 대신하는 메서드가 있습니다. 가장 많이 쓰는 셋이 map·filter·reduce입니다. 셋 모두 5강의 화살표 함수를 받아서 배열의 값마다 그 함수를 불러 줍니다. map은 값마다 바꾼 결과로 새 배열을 만들고(길이가 같음), filter는 함수가 참을 돌려준 값만 골라 새 배열을 만들며(길이가 같거나 짧음), reduce는 값들을 하나로 접어 합계 같은 값 하나를 만듭니다.

중요한 점은 map과 filter가 원래 배열을 바꾸지 않고 새 배열을 돌려준다는 것입니다. 5강의 순수 함수와 같은 성질이라 결과를 예측하기 쉽습니다. reduce는 두 번째 인자로 시작값을 꼭 넣으세요. reduce((s, x) => s + x, 0)의 0이 4강 누적 패턴의 let sum = 0에 해당합니다. 시작값을 빼면 빈 배열에서 TypeError가 납니다.

처음에는 반복문이 더 읽기 쉬울 수 있고, 그래도 괜찮습니다. 반복문으로 할 수 있는 일을 짧게 쓰는 방법일 뿐입니다. 다만 AI가 짠 코드에는 이 셋이 사슬처럼 이어져 나오는 경우가 많으므로, 한 단계씩 끊어 중간 결과를 찍어 보며 읽는 연습을 해 두면 좋습니다.

map은 바꾸기, filter는 고르기, reduce는 접기 — 원래 배열은 그대로
const prices = [1500, 500, 800, 12000];
const withTax = prices.map((p) => p * 1.1);
console.log(withTax.map((p) => Math.round(p)));
const cheap = prices.filter((p) => p < 1000);
console.log(cheap);
const total = prices.reduce((s, p) => s + p, 0);
console.log(total);
console.log(prices);
출력
[ 1650, 550, 880, 13200 ]
[ 500, 800 ]
14800
[ 1500, 500, 800, 12000 ]
세 메서드 비교
메서드하는 일돌려주는 것반복문으로 치면
map값마다 바꾸기같은 길이의 새 배열새 배열에 바꾼 값을 push
filter조건에 맞는 값 고르기같거나 짧은 새 배열조건이 참일 때만 push
reduce하나로 접기값 하나(합계 등)누적 패턴

6.JSON — 데이터를 주고받는 글자 형식

JSON은 객체와 배열을 글자(문자열)로 적는 형식입니다. 이름은 JavaScript에서 왔지만 지금은 거의 모든 프로그래밍 언어와 웹 서비스가 데이터를 주고받을 때 씁니다. 설정 파일, 서버의 응답, 앱이 저장하는 데이터 상당수가 JSON입니다. JSON.stringify는 값을 JSON 글자로 바꾸고, JSON.parse는 JSON 글자를 다시 값으로 되돌립니다.

JSON은 JavaScript 객체 문법과 닮았지만 더 엄격합니다. 키는 반드시 큰따옴표로 감싸야 하고, 문자열도 큰따옴표만 됩니다. 마지막 항목 뒤 쉼표는 허용되지 않고, 주석도 쓸 수 없습니다. 함수와 undefined는 담을 수 없어서, stringify하면 객체 속성일 때는 빠지고 배열 안에서는 null로 바뀝니다. 규칙을 하나라도 어기면 JSON.parse가 SyntaxError를 냅니다.

JSON이 길게 한 줄로 붙어 있으면 읽기 어렵습니다. 그럴 때는 JSON 포맷터 도구에 붙여 넣으면 들여쓰기를 해 주고, 규칙이 깨진 곳을 짚어 주기도 합니다. 다만 실제 회원 정보처럼 개인정보가 든 데이터는 외부 도구나 AI에 붙여 넣지 말고, 구조만 같은 가상의 데이터로 바꿔서 확인하세요(10강).

값 → JSON 글자 → 다시 값
const item = { name: '공책', price: 1500, tags: ['문구'] };
const text = JSON.stringify(item);
console.log(text);
console.log(typeof text);
const back = JSON.parse(text);
console.log(back.price + 500);
출력
{"name":"공책","price":1500,"tags":["문구"]}
string
2000
키에 따옴표가 없고 작은따옴표를 썼다 — JSON 규칙 위반
const bad = "{ name: '공책' }";
console.log(JSON.parse(bad));
출력
SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)
JSON.stringify(값, null, 2)처럼 세 번째 인자에 2를 주면 두 칸 들여쓰기를 한 여러 줄 글자로 바꿔 줍니다. 이 페이지의 실습장도 여러 줄을 그대로 보여 주므로, 긴 데이터를 찍어 구조를 살필 때 써 보세요.

📌 핵심 정리

  • 배열은 순서 있는 목록 — 인덱스는 0부터, 마지막은 arr.length - 1, 없는 칸은 undefined
  • 객체는 이름(키)으로 값을 찾는 묶음 — undefined에서 다시 속성을 읽으면 TypeError
  • 배열·객체를 다른 변수에 넣으면 복사가 아니라 같은 것을 가리킨다
  • 객체의 배열은 표다 — 행마다 꺼내 열을 읽어 누적하거나 map·filter·reduce로 가공한다
  • JSON은 큰따옴표 키, 끝 쉼표 금지 — stringify로 글자로, parse로 값으로

✍️ 확인 문제

먼저 풀어 본 뒤 '정답과 해설'을 펼치세요.

Q1. const a = [10, 20, 30];에서 a[a.length]의 값은?

⭕ 정답입니다

❌ 다시 생각해 보세요

정답과 해설
정답 ② undefined

a.length는 3이고 인덱스는 0~2까지만 있으므로 a[3]은 undefined입니다. 마지막 값은 a[a.length - 1]입니다.

Q2. const list = ['a', 'b']; list.push('c');의 결과로 옳은 것은?

⭕ 정답입니다

❌ 다시 생각해 보세요

정답과 해설
정답 ② list는 ['a', 'b', 'c']가 된다

const는 이름에 다른 값을 다시 넣는 것만 막습니다. 배열 속 칸을 바꾸거나 push로 더하는 것은 됩니다. push는 끝에 더합니다.

Q3. [3, 8, 1, 6].filter((x) => x > 4)의 결과는?

⭕ 정답입니다

❌ 다시 생각해 보세요

정답과 해설
정답 ① [8, 6]

filter는 함수가 참을 돌려준 값만 골라 새 배열을 만듭니다. 참·거짓 배열이 나오는 것은 같은 함수를 map에 넣었을 때입니다.

Q4. 다음 가운데 올바른 JSON은?

⭕ 정답입니다

❌ 다시 생각해 보세요

정답과 해설
정답 ④ { "name": "민수", "age": 20 }

JSON에서는 키와 문자열 모두 큰따옴표여야 하고, 마지막 항목 뒤 쉼표는 허용되지 않습니다. 첫째는 키에 따옴표가 없고, 둘째는 작은따옴표, 셋째는 끝 쉼표가 문제입니다.

Q5. TypeError: Cannot read properties of undefined (reading 'price')가 났습니다. 무엇부터 확인해야 할까요?

정답과 해설
정답 .price 바로 앞의 값이 왜 undefined인지 — 키 이름 오타, 없는 인덱스, 그 속성이 빠진 항목이 있는지 확인한다.

오류는 'undefined에서 price를 읽으려 했다'는 뜻입니다. 문제는 price가 아니라 그 앞의 값(예: items[i]나 row.info)에 있습니다.

🧪 코드 실습

배열과 객체를 읽고, 객체의 배열(표)에서 값을 쌓고 고르는 함수를 만듭니다. 빈 배열 같은 경계값도 함께 채점됩니다.

코드는 이 브라우저 안의 격리된 공간에서만 실행되고 서버로 보내지 않습니다. 네트워크에는 접속할 수 없고, 2초가 넘으면 멈춥니다. 고친 코드는 이 브라우저에만 저장됩니다. Ctrl+Enter(⌘+Enter)로 실행하고, Tab 키는 공백 두 칸입니다(Esc 다음 Tab 은 다음 칸으로 이동).

자바스크립트가 꺼져 있어 실행할 수는 없지만, 문제와 처음 코드, 자동 검사 내용, 예시 답안은 그대로 읽을 수 있습니다.

1마지막 값 꺼내기

배열 arr의 마지막 값을 돌려주는 함수 lastItem(arr)를 만드세요. 빈 배열이면 null을 돌려줍니다.

자동 검사
  • lastItem([1,2,3])기대값 3
  • lastItem(["사과"])기대값 "사과"
  • lastItem([])기대값 null
  • lastItem([0,false])기대값 false
💡 힌트

빈 배열에서 arr[arr.length - 1]은 arr[-1]이라 undefined입니다. 그래서 빈 배열은 먼저 따로 처리합니다.

예시 답안 보기
function lastItem(arr) {
  if (arr.length === 0) {
    return null;
  }
  return arr[arr.length - 1];
}

2수량 합계

{ item, qty } 모양 객체의 배열 rows를 받아 qty의 합계를 돌려주는 함수 totalQty(rows)를 만드세요. 빈 배열이면 0입니다.

자동 검사
  • totalQty([{"item":"공책","qty":4},{"item":"연필","qty":10}])기대값 14
  • totalQty([])기대값 0
  • totalQty([{"item":"지우개","qty":3}])기대값 3
  • totalQty([{"item":"a","qty":0},{"item":"b","qty":0}])기대값 0
💡 힌트

행 하나는 객체입니다. 점 표기로 row.qty를 읽으세요. reduce로 한 줄에 쓸 수도 있습니다.

예시 답안 보기
function totalQty(rows) {
  let total = 0;
  for (const row of rows) {
    total += row.qty;
  }
  return total;
}

3조건에 맞는 이름만 고르기

{ item, price } 모양 객체의 배열 rows와 기준 가격 min을 받아, 가격이 min 이상인 행의 item만 원래 순서대로 담은 배열을 돌려주는 함수 namesFrom(rows, min)을 만드세요. 맞는 행이 없으면 빈 배열 []입니다.

자동 검사
  • namesFrom([{"item":"공책","price":1500},{"item":"연필","price":500},{"item":"가방","price":20000}], 1000)기대값 ["공책","가방"]
  • namesFrom([], 100)기대값 []
  • namesFrom([{"item":"연필","price":500}], 500)기대값 ["연필"]
  • namesFrom([{"item":"연필","price":500}], 501)기대값 []
💡 힌트

filter로 행을 고른 뒤 map으로 이름만 뽑는 두 단계입니다. 반복문으로 if와 push를 써도 됩니다.

예시 답안 보기
function namesFrom(rows, min) {
  return rows
    .filter((r) => r.price >= min)
    .map((r) => r.item);
}

4항목별 개수 세기

문자열 배열 words를 받아, 각 단어가 몇 번 나왔는지 담은 객체를 돌려주는 함수 countBy(words)를 만드세요. 예: ['사과', '배', '사과'] → { 사과: 2, 배: 1 }. 빈 배열이면 빈 객체 {}입니다.

자동 검사
  • countBy(["사과","배","사과"])기대값 {"배":1,"사과":2}
  • countBy([])기대값 {}
  • countBy(["a"])기대값 {"a":1}
  • countBy(["x","x","x","y"])기대값 {"x":3,"y":1}
💡 힌트

키 이름이 변수 w에 들어 있으니 점 표기가 아니라 대괄호 counts[w]를 씁니다. 처음 나온 단어는 undefined입니다.

예시 답안 보기
function countBy(words) {
  const counts = {};
  for (const w of words) {
    if (counts[w] === undefined) {
      counts[w] = 1;
    } else {
      counts[w] += 1;
    }
  }
  return counts;
}

🤖 AI에게 이렇게 물어보세요

복사해서 [ ] 부분을 내 상황으로 바꿔 쓰세요. 답을 그대로 믿지 말고 본문과 비교해 확인하세요.

데이터를 배열·객체로 어떻게 담을지 고민될 때

다음 가상의 데이터를 JavaScript의 '객체의 배열'로 바꿔 줘. 키 이름은 영어 소문자로 짓고, 숫자는 문자열이 아닌 숫자로 해 줘. 그리고 이 데이터로 할 수 있는 계산 3가지(합계·조건 고르기·그룹별 세기)를 반복문 버전과 `map`/`filter`/`reduce` 버전으로 나란히 보여 줘. 데이터: [가상의 표 붙여 넣기]

`map`·`filter`·`reduce`가 이어진 코드를 읽기 어려울 때

아래 코드의 `map`/`filter`/`reduce` 사슬을 한 단계씩 끊어서, 단계마다 중간 결과가 어떤 배열이나 값이 되는지 작은 예시 입력으로 보여 줘. 같은 일을 하는 `for...of` 반복문 버전도 써 줘. 코드: [붙여 넣기]

`JSON.parse`가 오류를 낼 때

다음 JSON 문자열이 왜 `JSON.parse`에서 오류가 나는지 규칙 위반을 모두 찾아 줘(따옴표, 끝 쉼표, 주석 등). 고친 JSON도 보여 줘. 내용은 가상의 예시 데이터야: [붙여 넣기]

🧰 관련 도구

이 강의에서 다룬 JSON·정규식·텍스트를 바로 확인하고 다듬어 볼 수 있는 도구입니다. 실제 개인정보나 비밀번호는 붙여 넣지 마세요.

참고
  • MDN Web Docs — JavaScript 안내서(Guide): 인덱스 기반 컬렉션(Indexed collections), 객체로 작업하기(Working with objects)
  • MDN Web Docs — Array.prototype.map / filter / reduce, JSON.stringify / JSON.parse 참조
  • ECMA-404 JSON 데이터 교환 형식 표준의 일반적 규칙

학습 목표를 모두 이뤘다면 완료를 눌러 진도에 기록하세요.

💻 코딩 기초

  1. 1프로그램이란 — 일을 순서·조건·반복으로 쪼개는 생각
  2. 2값·변수·자료형 — 이름표를 붙여 값을 다루기
  3. 3조건문 — 상황에 따라 다른 길로 가기
  4. 4반복문 — 같은 일을 정확히 여러 번 시키기
  5. 5함수 — 이름 붙인 작은 기계로 나누기
  6. 6배열과 객체 — 데이터를 담고 다루기
  7. 7문자열·텍스트 처리 — 글자를 자르고 찾고 바꾸기
  8. 8디버깅과 오류 읽기 — 빨간 글씨를 단서로 바꾸기
  9. 9간단한 자동화 예제 — 표 계산과 텍스트 정리를 코드로
  10. 10AI가 짠 코드 읽고 검증하기 — 테스트·보안·라이선스·개인정보
📚 함께 읽으면 좋은 글
📊인공지능으로 표 계산 작업 돕기, 수식은 반드시 검증→ 💻인공지능 코딩 도우미 쓰는 법과 위험, 보안과 라이선스→ 💬외국어 회화 연습 상대로 인공지능 쓰는 법→ ⚙️업무 자동화 아이디어 찾기, 인공지능에 맡길 일 고르는 법→
← AI 시대 기초 학력 강의