콘텐츠 대표 이미지 - 자바스크립트, 데이터의 언어 JSON을 만나다:파싱과 직렬화, 완벽 정복 가이드

자바스크립트, 데이터의 언어 JSON을 만나다:
파싱과 직렬화, 완벽 정복 가이드

안녕! 코딩의 세계를 여행하는 우리 모두를 위한 안내서야.
오늘은 웹 개발의 심장부에서 데이터를 주고받는 핵심 기술, JSON에 대해 속 시원하게 파헤쳐 볼 거야.
어렵게 느껴졌다면 오늘부로 절친 먹는 거다! 준비됐지? 바로 시작하자!

1. JSON, 너 정체가 뭐야? (What is JSON?)

코딩하다 보면 'JSON'이라는 단어, 정말 귀에 딱지가 앉도록 듣게 되지 않아?
API 통신할 때도, 설정 파일 만들 때도, 어디서나 불쑥 튀어나오는 이 녀석.
대체 정체가 뭘까?

상상해봐. 내가 미국에 있는 친구랑 편지를 주고받는다고.
나는 한국어로 쓰고, 친구는 영어로 쓴다면? 서로 "이게 뭔 소리야?" 하겠지.
그래서 우리 사이에 약속이 필요해. "우리, 앞으로 무조건 영어로만 편지 쓰자!" 라고.
이처럼, 프로그램 세계의 여러 시스템(서버, 브라우저, 앱 등)이 서로 데이터를 주고받기 위해 사용하는 '공용어'가 바로 **JSON**이야.

JSON은 **J**ava**S**cript **O**bject **N**otation의 줄임말이야.
이름에서부터 자바스크립트의 향기가 물씬 풍기지? 맞아.
자바스크립트의 객체 표기법에서 유래한, 아주 가볍고 단순한 **텍스트 기반의 데이터 교환 형식**이지.

중요한 건 '텍스트'라는 점이야.
사람이 눈으로 읽고 이해하기 쉬울 뿐만 아니라, 컴퓨터도 부담 없이 읽고 쓸 수 있어.
이게 바로 JSON이 전 세계 개발자들의 사랑을 독차지하게 된 비결이야.

JSON vs XML: 왜 JSON이 대세가 됐을까?

옛날 옛적, 데이터 교환의 왕좌에는 'XML(e**X**tensible **M**arkup **L**anguage)'이라는 친구가 있었어.
HTML이랑 비슷하게 태그를 사용해서 데이터를 표현했는데, 솔직히 좀... 장황했어.
똑같은 정보를 표현해도 JSON보다 훨씬 길고 복잡했거든.

백문이 불여일견! 직접 눈으로 비교해보자.

XML (The Old Way) JSON (The New Way)
<user>
  <name>김코딩</name>
  <age>28</age>
  <isDeveloper>true</isDeveloper>
  <skills>
    <skill>JavaScript</skill>
    <skill>React</skill>
    <skill>Node.js</skill>
  </skills>
</user>
{
  "name": "김코딩",
  "age": 28,
  "isDeveloper": true,
  "skills": [
    "JavaScript",
    "React",
    "Node.js"
  ]
}
시작과 끝 태그가 모두 필요해 더 길다. 훨씬 간결하고 직관적이다.

어때? 같은 정보를 담고 있는데도 JSON이 훨씬 깔끔하고 보기 편하지?
데이터 양이 적을 땐 별 차이 없어 보일 수 있지만, 수천, 수만 건의 데이터를 주고받는다고 생각하면 이 차이는 어마어마해.
데이터가 가볍다는 건 곧 **네트워크 트래픽 비용이 줄어들고, 처리 속도가 빨라진다**는 의미니까.
게다가 JSON은 자바스크립트에서 별도의 복잡한 파싱 라이브러리 없이 내장 객체만으로 쉽게 다룰 수 있다는 치명적인 장점까지 있어.
이러니 대세가 될 수밖에!

2. JSON 문법, 이것만 알면 끝!

JSON의 문법은 정말 간단해. 딱 두 가지만 기억하면 돼.
바로 **'이름-값' 쌍 (key-value pairs)** 과 **데이터 타입**이야.

기본 구조: 이름(Key)과 값(Value)의 향연

JSON 데이터는 중괄호 `{}` 로 감싸인 객체(Object) 형태로 시작하는 경우가 많아.
그리고 그 안에는 '이름(key)'과 '값(value)'이 한 쌍을 이루어 존재해.
마치 자바스크립트 객체처럼 말이야. 하지만 몇 가지 중요한 규칙이 있어.

{
  "key1": "value1",
  "key2": "value2"
}

여기서 가장 중요한 규칙! **JSON의 키(key)는 반드시 큰따옴표("")로 감싸야 해.**
자바스크립트에서는 따옴표를 생략하거나 작은따옴표를 써도 되지만, JSON에서는 절대 안 돼! 이게 초보자들이 가장 많이 하는 실수 중 하나야.

🚨 주의! 또 주의! 🚨JSON의 키(key)는 무조건, 반드시, 예외 없이 **큰따옴표("")**로 감싸야 합니다.
{ name: "홍길동" } (X)
{ 'name': "홍길동" } (X)
{ "name": "홍길동" } (O)

JSON이 허용하는 6가지 데이터 타입

JSON의 값(value)으로는 딱 6가지 종류의 데이터만 올 수 있어. 이것만 알면 JSON 문법은 거의 마스터한 거나 다름없어.

  1. 문자열 (String): 큰따옴표("")로 감싼 텍스트. "안녕하세요, JSON!"

  2. 숫자 (Number): 정수, 실수 모두 가능. 따옴표 없이 사용. 123, 3.141592

  3. 불리언 (Boolean): true 또는 false. 따옴표 없이 소문자로만. true

  4. 객체 (Object): 중괄호 `{}` 안에 '이름-값' 쌍들이 들어있는 구조. 값으로 또 다른 JSON 객체가 올 수 있어 중첩 구조를 만들 수 있지. { "city": "Seoul", "population": 9400000 }

  5. 배열 (Array): 대괄호 `[]` 안에 여러 개의 값을 쉼표로 구분해서 나열한 것. 배열 안에는 위 6가지 타입의 데이터를 자유롭게 섞어 넣을 수 있어. [ "apple", "banana", 100, true ]

  6. null: '값이 없음'을 나타내는 특별한 값. 따옴표 없이 소문자로만. null

이 6가지 외의 것들, 예를 들어 자바스크립트의 `undefined`, `function`, `Symbol` 같은 것들은 JSON 표준에 포함되지 않아.
그래서 JSON으로 변환할 때 사라지거나 `null`로 바뀌어버리지. 이 부분은 뒤에서 더 자세히 다룰게!

자, 그럼 이 모든 걸 조합해서 만든 완전체 JSON 예시를 한번 볼까?

{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "secretBase": "Super tower",
  "active": true,
  "members": [
    {
      "name": "Molecule Man",
      "age": 29,
      "secretIdentity": "Dan Jukes",
      "powers": [
        "Radiation resistance",
        "Turning tiny",
        "Radiation blast"
      ],
      "partner": null
    },
    {
      "name": "Madame Uppercut",
      "age": 39,
      "secretIdentity": "Jane Wilson",
      "powers": [
        "Million tonne punch",
        "Damage resistance",
        "Superhuman reflexes"
      ],
      "partner": {
        "name": "Eternal Flame",
        "age": 1000000,
        "secretIdentity": "Unknown"
      }
    }
  ]
}

어때? 객체 안에 배열이 있고, 그 배열 안에는 또 다른 객체들이 들어있지.
이런 식으로 복잡한 데이터 구조도 아주 깔끔하게 표현할 수 있다는 게 JSON의 강력함이야.

3. 직렬화: 객체를 텍스트로! `JSON.stringify()`

이제 진짜 마법을 부릴 시간이야.
자바스크립트 세상에 살고 있는 데이터(객체, 배열 등)를 다른 세상(서버, 파일 등)으로 보내려면, '텍스트'라는 여권이 필요해.
이때 자바스크립트 객체를 JSON 텍스트로 변환해주는 마법 주문이 바로 **`JSON.stringify()`** 야.

이 과정을 전문용어로 **직렬화(Serialization)** 또는 **마샬링(Marshalling)** 이라고 불러.
쉽게 말해, 컴퓨터 메모리 속에 존재하는 복잡한 구조의 데이터를 전송하거나 저장하기 쉬운 일렬로 쭉 늘어선 텍스트 형태로 바꾸는 것이라고 생각하면 돼.

JavaScript Object
const user = {
  name: '재능이',
  level: 99,
  isOnline: true,
  skills: ['JS', 'CSS']
};
직렬화 (Serialization) JSON.stringify() JSON String
'{"name":"재능이","level":99,"isOnline":true,"skills":["JS","CSS"]}'

기본 사용법: `JSON.stringify(value)`

사용법은 정말 간단해. `JSON.stringify()` 함수에 자바스크립트 값(객체, 배열 등)을 인자로 넣어주기만 하면 돼.

const user = {
  name: "김개발",
  age: 30,
  isStudent: false,
  courses: [
    { title: "JavaScript Basics", hours: 10 },
    { title: "React Deep Dive", hours: 25 }
  ],
  partner: null
};

const jsonString = JSON.stringify(user);

console.log(jsonString);
// 출력 결과:
// {"name":"김개발","age":30,"isStudent":false,"courses":[{"title":"JavaScript Basics","hours":10},{"title":"React Deep Dive","hours":25}],"partner":null}

console.log(typeof jsonString); // "string"

봐봐. `user`라는 자바스크립트 객체가 한 줄짜리 완벽한 JSON 문자열로 변신했어.
이제 이 `jsonString` 변수를 서버로 보내거나, `localStorage`에 저장할 수 있게 된 거야.

고급 기술 1: `replacer`로 데이터 필터링하기

`JSON.stringify()`에는 숨겨진 두 개의 인자가 더 있어. 그 첫 번째가 바로 `replacer`야.
`replacer`는 직렬화 과정에 개입해서 특정 프로퍼티를 제외하거나 값을 바꿔치기할 수 있는 필터 같은 역할을 해.

`replacer`는 **함수** 또는 **배열**이 될 수 있어.

1. `replacer`가 배열일 경우:

배열을 전달하면, 그 배열 안에 포함된 이름의 프로퍼티만 JSON 문자열에 포함시켜줘. 화이트리스트 방식이지.

const user = {
  id: "user123",
  name: "박코더",
  email: "park@coder.com",
  level: 5
};

// name과 level 프로퍼티만 포함시키고 싶을 때
const filteredJson = JSON.stringify(user, ['name', 'level']);

console.log(filteredJson);
// 출력 결과: {"name":"박코더","level":5}

서버에 데이터를 보낼 때, 민감한 정보(id, email 등)는 빼고 보내고 싶을 때 아주 유용하겠지?

2. `replacer`가 함수일 경우:

함수를 전달하면, 객체의 각 프로퍼티를 순회하면서 이 함수를 호출해. 함수의 반환값이 실제 값으로 사용돼.
만약 함수가 `undefined`를 반환하면, 해당 프로퍼티는 결과에서 제외돼.

const product = {
  name: "고성능 키보드",
  price: 150000,
  secretCode: "XYZ-SECRET-777",
  stock: 10
};

function replacer(key, value) {
  if (key === "secretCode") {
    // secretCode는 제외시키자
    return undefined;
  }
  if (typeof value === 'number') {
    // 숫자는 10% 할인된 가격으로 보여주자
    return value * 0.9;
  }
  return value; // 나머지는 그대로
}

const salesJson = JSON.stringify(product, replacer);

console.log(salesJson);
// 출력 결과: {"name":"고성능 키보드","price":135000,"stock":9}

`replacer` 함수는 `(key, value)` 두 개의 인자를 받는데, 이를 이용해 아주 정교한 제어가 가능해져. 정말 강력한 기능이지.

고급 기술 2: `space`로 예쁘게 출력하기 (Pretty-Printing)

`JSON.stringify()`의 세 번째 인자는 `space`야.
이건 결과로 나온 JSON 문자열을 사람이 보기 좋게 꾸며주는 역할을 해. 디버깅할 때 정말 꿀 기능이지!

`space` 인자로는 **숫자**나 **문자열**을 넣을 수 있어.

1. `space`가 숫자일 경우:

숫자(최대 10)를 넣으면, 그 숫자만큼의 공백(space)으로 들여쓰기를 해줘.

const user = { name: "이디버그", skills: ["Debugging", "Problem Solving"] };

// space 인자로 2를 전달
const prettyJson = JSON.stringify(user, null, 2); 
// replacer를 사용하지 않을 땐 null을 전달

console.log(prettyJson);
/*
출력 결과:
{
  "name": "이디버그",
  "skills": [
    "Debugging",
    "Problem Solving"
  ]
}
*/

훨씬 보기 편하지? 보통 들여쓰기는 2칸이나 4칸을 많이 사용해.

2. `space`가 문자열일 경우:

문자열을 넣으면, 그 문자열로 들여쓰기를 해줘. 예를 들어 탭(`\t`) 문자를 사용할 수도 있어.

const user = { name: "탭마스터", lang: "JavaScript" };

const tabbedJson = JSON.stringify(user, null, '\t');

console.log(tabbedJson);
/*
출력 결과:
{
	"name": "탭마스터",
	"lang": "JavaScript"
}
*/

💡 프로의 팁: `toJSON` 메서드 활용하기
어떤 객체를 `JSON.stringify()` 할 때, 만약 그 객체에 `toJSON`이라는 이름의 메서드가 정의되어 있다면, 직렬화는 그 객체 자체 대신 `toJSON` 메서드가 반환하는 값을 사용해.
객체 스스로가 "나를 JSON으로 만들 땐 이렇게 만들어 줘!"라고 말하는 것과 같아. 모델(Model) 클래스 같은 곳에서 데이터 형식을 제어할 때 아주 유용해.

const user = {
  name: "컨트롤러",
  joined: new Date(), // Date 객체
  
  toJSON: function() {
    return {
      userName: this.name,
      // 날짜를 'YYYY-MM-DD' 형식의 문자열로 변환
      joinedDate: this.joined.toISOString().slice(0, 10) 
    };
  }
};

const json = JSON.stringify(user, null, 2);
console.log(json);
/*
출력 결과:
{
  "userName": "컨트롤러",
  "joinedDate": "2023-10-27" // (실행 시점의 날짜)
}
*/

원래 `Date` 객체는 복잡한 ISO 형식의 문자열로 변환되지만, `toJSON` 메서드를 통해 우리가 원하는 깔끔한 형식으로 제어했어. 정말 멋지지?

4. 파싱: 텍스트를 객체로! `JSON.parse()`

자, 이제 반대 과정을 해볼 차례야.
서버로부터 JSON 형식의 텍스트를 받았다면, 이걸 자바스크립트가 실제로 가지고 놀 수 있는 객체나 배열로 바꿔야겠지?
이때 사용하는 마법 주문이 바로 **`JSON.parse()`** 야.

이 과정을 **파싱(Parsing)** 또는 **역직렬화(Deserialization)**, **언마샬링(Unmarshalling)** 이라고 불러.
일렬로 늘어선 텍스트 데이터를 읽어서 컴퓨터 메모리 속에 실제 데이터 구조(객체, 배열 등)로 재조립하는 과정이야.

기본 사용법: `JSON.parse(text)`

사용법은 `stringify`만큼이나 간단해. `JSON.parse()` 함수에 JSON 형식의 문자열을 인자로 넣어주면 끝!

const jsonString = '{"name":"김개발","age":30,"isStudent":false,"courses":[{"title":"JavaScript Basics","hours":10},{"title":"React Deep Dive","hours":25}],"partner":null}';

const userObject = JSON.parse(jsonString);

console.log(userObject);
/*
출력 결과:
{
  name: '김개발',
  age: 30,
  isStudent: false,
  courses: [
    { title: 'JavaScript Basics', hours: 10 },
    { title: 'React Deep Dive', hours: 25 }
  ],
  partner: null
}
*/

console.log(userObject.name); // "김개발"
console.log(userObject.courses[0].title); // "JavaScript Basics"
console.log(typeof userObject); // "object"

문자열이었던 데이터가 이제 점(.)이나 대괄호(`[]`)로 프로퍼티에 접근할 수 있는 진짜 자바스크립트 객체로 부활했어!

고급 기술: `reviver`로 데이터 부활시키기

`JSON.parse()`에도 두 번째 인자가 있어. 바로 `reviver`라는 함수야.
`stringify`의 `replacer`와는 반대 역할을 해. 파싱 과정에 개입해서 값을 원하는 대로 변형하거나 되살릴 수 있어.

`reviver` 함수는 파싱된 모든 `(key, value)` 쌍에 대해 호출돼. 그리고 `reviver`가 반환하는 값이 최종적으로 객체의 값으로 사용되지.

가장 대표적인 활용 사례는 **날짜(Date) 문자열을 실제 `Date` 객체로 되살리는 거야.**

const jsonString = '{"name":"타임 트래블러","eventDate":"2025-12-25T12:00:00.000Z"}';

const eventObject = JSON.parse(jsonString, function(key, value) {
  // ISO 8601 형식의 날짜 문자열인지 간단히 확인
  if (key === 'eventDate' && typeof value === 'string' && value.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
    // 새로운 Date 객체를 생성해서 반환
    return new Date(value);
  }
  // 나머지는 그대로 반환
  return value;
});

console.log(eventObject.name); // "타임 트래블러"
console.log(eventObject.eventDate); // Date 객체가 출력됨 (예: Wed Dec 25 2025 21:00:00 GMT+0900 (한국 표준시))
console.log(eventObject.eventDate.getFullYear()); // 2025

만약 `reviver`를 쓰지 않았다면 `eventObject.eventDate`는 그냥 문자열이라 `getFullYear()` 같은 `Date` 메서드를 쓸 수 없었을 거야.
이처럼 `reviver`를 사용하면 서버에서 받은 데이터를 우리가 원하는 클래스의 인스턴스로 되살리는 등 아주 유용한 작업들을 할 수 있어.

에러 핸들링: `try...catch`는 필수!

우리가 다루는 데이터가 항상 완벽한 JSON 형식이라는 보장은 없어.
네트워크 오류로 데이터가 깨졌을 수도 있고, 서버에서 잘못된 형식의 문자열을 보냈을 수도 있지.
만약 `JSON.parse()`가 유효하지 않은 JSON 문자열을 만나면, 가차 없이 **`SyntaxError`** 를 던져버려. 그리고 프로그램은 그 자리에서 멈추게 되지.

이런 대참사를 막으려면, `JSON.parse()`는 반드시 `try...catch` 문으로 감싸주는 습관을 들여야 해.

🛡️ 방어 코딩의 기본: `try...catch`
외부(서버, 파일 등)에서 온 데이터를 파싱할 때는 언제나 최악의 상황을 가정해야 해. `try...catch`는 선택이 아닌 필수!

const malformedJson = '{"name": "오류", "age": }'; // age 뒤에 값이 없음 -> 잘못된 형식

try {
  const user = JSON.parse(malformedJson);
  console.log("파싱 성공:", user);
} catch (error) {
  // 에러가 발생하면 catch 블록이 실행됨
  console.error("JSON 파싱 중 에러가 발생했습니다!");
  console.error("에러 이름:", error.name); // SyntaxError
  console.error("에러 메시지:", error.message); // 예: Unexpected token } in JSON at position ...
}

console.log("프로그램은 멈추지 않고 계속 실행됩니다.");

이렇게 `try...catch`로 감싸주면, 파싱에 실패하더라도 프로그램이 죽지 않고 에러를 우아하게 처리한 뒤 다음 로직을 이어나갈 수 있어. 정말 중요해!

5. 실전 투입! JSON, 어디서 어떻게 쓸까?

이론은 이제 빠삭해. 그럼 이 JSON이라는 무기를 들고 실제 전쟁터에서는 어떻게 싸워야 할까?
가장 흔하게 마주치는 세 가지 시나리오를 통해 알아보자.

시나리오 1: API 통신 (feat. `fetch`)

프론트엔드 개발자라면 하루에도 수십 번씩 하는 일, 바로 서버와 데이터를 주고받는 API 통신이야.
이때 클라이언트(브라우저)와 서버는 JSON이라는 공용어로 대화를 나눠.

예를 들어, '재능넷' 같은 플랫폼에서 내 프로필 정보를 저장하거나 다른 개발자의 재능 정보를 불러올 때, 서버와 클라이언트는 바로 이 JSON 형식으로 데이터를 주고받는 거야. 아주 효율적이지?

브라우저의 `fetch` API를 사용해서 서버로부터 사용자 목록을 가져오는 예시를 보자.

// 가상의 API 엔드포인트
const API_URL = 'https://api.example.com/users';

async function fetchUsers() {
  try {
    const response = await fetch(API_URL);

    // 서버 응답이 성공적이지 않으면 에러 처리
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    // response.json() 메서드를 호출!
    // 이 메서드가 바로 응답 본문(body)의 JSON 텍스트를 파싱해서
    // 자바스크립트 객체로 변환해주는 역할을 해.
    // 내부적으로 JSON.parse()를 안전하게 실행하는 것과 같아.
    const users = await response.json();

    console.log("성공적으로 사용자 목록을 가져왔습니다:", users);
    // 이제 users 배열을 가지고 화면에 목록을 그리는 등 원하는 작업을 할 수 있어.
    displayUsers(users);

  } catch (error) {
    console.error("사용자 목록을 가져오는 데 실패했습니다:", error);
  }
}

function displayUsers(users) {
  // users 배열을 순회하며 화면에 표시하는 로직...
  console.log(`${users.length}명의 사용자를 화면에 표시합니다.`);
}

fetchUsers();

반대로 서버에 데이터를 보낼(POST, PUT 등) 때는 `JSON.stringify()`를 사용해서 우리 객체를 JSON 문자열로 만들어 `body`에 실어 보내야 해.

async function createUser(userData) {
  try {
    const response = await fetch(API_URL, {
      method: 'POST', // 생성 요청
      headers: {
        // 우리가 보내는 데이터가 JSON 형식임을 서버에 알려줌
        'Content-Type': 'application/json',
      },
      // 자바스크립트 객체를 JSON 문자열로 직렬화해서 body에 담는다
      body: JSON.stringify(userData),
    });

    if (!response.ok) {
      throw new Error('서버 응답 오류');
    }

    const newUser = await response.json();
    console.log('새로운 사용자가 생성되었습니다:', newUser);

  } catch (error) {
    console.error('사용자 생성 실패:', error);
  }
}

const newUserInfo = { name: '신입개발자', job: '프론트엔드' };
createUser(newUserInfo);

이처럼 API 통신에서 `JSON.stringify()`와 `JSON.parse()`(또는 `response.json()`)는 동전의 양면과도 같은 한 쌍이야.

시나리오 2: 설정 파일 (`package.json`, `tsconfig.json` 등)

개발을 하다 보면 수많은 설정 파일들을 만나게 돼.
Node.js 프로젝트의 심장과도 같은 `package.json`, 타입스크립트 컴파일러 설정 파일인 `tsconfig.json`, VS Code 에디터 설정인 `settings.json` 등...
이 파일들의 공통점이 뭔지 알아? 바로 **모두 JSON 형식**이라는 거야.

왜 하필 JSON일까?
* **구조적(Structured):** 설정 항목들을 계층적으로 깔끔하게 정리할 수 있어. * **인간 친화적(Human-readable):** 개발자가 파일을 열었을 때 설정을 쉽게 읽고 수정할 수 있어. * **기계 친화적(Machine-readable):** 프로그램(Node.js, tsc, VS Code 등)이 파일을 쉽게 파싱해서 설정을 읽어 들일 수 있어.

이 세 가지 장점을 모두 만족시키는 포맷으로 JSON만 한 게 없었던 거지.

시나리오 3: 웹 스토리지 (`localStorage`, `sessionStorage`)

브라우저에는 간단한 데이터를 저장할 수 있는 `localStorage`와 `sessionStorage`라는 공간이 있어.
그런데 이 웹 스토리지는 치명적인 단점이 하나 있는데, 바로 **문자열(String)만 저장할 수 있다**는 거야.

만약 우리가 사용자 설정 객체를 저장하고 싶다면 어떻게 해야 할까?

const userSettings = {
  theme: 'dark',
  fontSize: 14,
  showNotifications: true
};

// 객체를 그대로 저장하려고 하면?
localStorage.setItem('settings', userSettings);

// 저장된 값을 다시 불러와보면?
const loadedSettings = localStorage.getItem('settings');
console.log(loadedSettings); // "[object Object]"
console.log(typeof loadedSettings); // "string"

이런! 객체가 그냥 `"[object Object]"`라는 의미 없는 문자열로 저장되어 버렸어. 데이터가 완전히 유실된 거지.
이때 바로 `JSON.stringify()`와 `JSON.parse()`가 구원투수로 등판해!

const userSettings = {
  theme: 'dark',
  fontSize: 14,
  showNotifications: true
};

// 1. 저장할 때: 객체를 JSON 문자열로 직렬화한다.
localStorage.setItem('settings', JSON.stringify(userSettings));
console.log("저장된 값:", localStorage.getItem('settings'));
// "{"theme":"dark","fontSize":14,"showNotifications":true}"

// 2. 불러올 때: JSON 문자열을 다시 객체로 파싱한다.
const loadedString = localStorage.getItem('settings');
const loadedSettings = JSON.parse(loadedString);

console.log("불러온 객체:", loadedSettings);
// { theme: 'dark', fontSize: 14, showNotifications: true }
console.log(loadedSettings.theme); // "dark"

이제 완벽하게 객체를 저장하고 복원할 수 있게 됐어.
웹 스토리지에 객체나 배열을 저장해야 할 땐 `JSON.stringify/parse` 콤비를 사용하는 것이 국룰이야!

6. 고수들의 비밀 노트: JSON 심화 & 함정 피하기

JSON은 단순하지만, 깊게 파고들면 몇 가지 주의해야 할 함정들이 있어.
이것까지 알아두면 넌 이제 어딜 가서 "JSON 좀 안다"고 말할 수 있을 거야.

함정 1: 순환 참조 (Circular References)

객체가 자기 자신을 참조하거나, 두 객체가 서로를 참조하는 경우를 '순환 참조'라고 해.
이런 구조를 가진 객체를 `JSON.stringify()`에 넣으면 어떻게 될까?

const a = { name: 'A' };
const b = { name: 'B' };

a.sibling = b; // a가 b를 참조
b.sibling = a; // b가 a를 참조 (순환 참조 발생!)

try {
  JSON.stringify(a);
} catch (error) {
  console.error(error.name);    // TypeError
  console.error(error.message); // Converting circular structure to JSON ...
}

`JSON.stringify()`는 이 무한 루프를 감지하고 `TypeError`를 발생시켜.
JSON은 계층적인 트리 구조만 표현할 수 있지, 이런 순환적인 그래프 구조는 표현할 수 없기 때문이야.
이런 경우엔 `replacer` 함수를 사용해서 순환 참조를 일으키는 프로퍼티를 제외하거나, `flatted` 같은 전문 라이브러리를 사용해서 처리해야 해.

함정 2: 데이터 타입의 유실

이게 정말 중요한 포인트야. 자바스크립트 객체를 JSON으로 바꿨다가 다시 되돌릴 때, 모든 정보가 100% 그대로 보존되는 건 아니야.
JSON이 표현할 수 없는 특정 데이터 타입들은 변환 과정에서 사라지거나 변형돼.

🚨 직렬화 과정에서 사라지는 아이들 🚨

  • `undefined`: 객체의 프로퍼티 값이 `undefined`이면, 그 프로퍼티는 아예 사라져. 배열 안에 있으면 `null`로 변해.

  • 함수 (Function): `undefined`와 동일하게, 프로퍼티는 사라지고 배열 안에선 `null`이 돼.

  • 심볼 (Symbol): `undefined`와 동일하게, 프로퍼티는 사라지고 배열 안에선 `null`이 돼.

🚨 직렬화 과정에서 변형되는 아이들 🚨

  • `Date` 객체: `toJSON()` 메서드가 호출되어 ISO 8601 형식의 문자열로 변환돼. (예: `"2023-10-27T08:30:00.000Z"`) 다시 `parse`해도 `Date` 객체로 돌아오지 않고 문자열로 남아있어. (`reviver`를 써야만 복원 가능)

  • `NaN`, `Infinity`, `-Infinity`: 모두 `null`로 변환돼.

코드로 직접 확인해보자.

const weirdObject = {
  a: 1,
  b: "hello",
  c: undefined, // 사라짐
  d: function() { console.log('d'); }, // 사라짐
  e: Symbol('e'), // 사라짐
  f: new Date(), // 문자열로 변형
  g: [1, undefined, function(){}, Symbol('')], // 배열 안에서는 null로 변형
  h: NaN, // null로 변형
  i: Infinity // null로 변형
};

const json = JSON.stringify(weirdObject, null, 2);
console.log(json);
/*
{
  "a": 1,
  "b": "hello",
  "f": "2023-10-27T08:30:00.000Z", // (실행 시점의 날짜)
  "g": [
    1,
    null,
    null,
    null
  ],
  "h": null,
  "i": null
}
*/

이런 특성 때문에, 자바스크립트 객체를 깊은 복사(deep copy)할 목적으로 `JSON.parse(JSON.stringify(obj))` 트릭을 사용하기도 하는데, 위와 같은 데이터 타입이 포함된 객체에는 사용하면 안 돼! 데이터가 유실될 수 있으니까.

함정 3: `BigInt` 지원 문제

아주 큰 정수를 다루기 위한 `BigInt` 타입은 비교적 최신 스펙이야.
그래서 `JSON.stringify()`는 `BigInt`를 어떻게 처리해야 할지 몰라서 `TypeError`를 발생시켜.

const obj = { bigNumber: 123456789012345678901234567890n };

try {
  JSON.stringify(obj);
} catch (error) {
  console.error(error.name);    // TypeError
  console.error(error.message); // Do not know how to serialize a BigInt
}

해결책은 `BigInt`를 문자열로 직접 변환해주는 거야. `replacer`를 쓰거나, `BigInt.prototype.toJSON`을 직접 정의해줄 수 있어.

// 해결책: BigInt.prototype에 toJSON 메서드 추가
BigInt.prototype.toJSON = function() {
  return this.toString();
};

const obj = { bigNumber: 123456789012345678901234567890n };
const json = JSON.stringify(obj);

console.log(json); // {"bigNumber":"123456789012345678901234567890"}

이렇게 하면 `BigInt`도 안전하게 JSON으로 변환할 수 있어.

7. 마무리하며: 데이터 소통의 중심, JSON

정말 긴 여정이었어! 하지만 이제 너는 JSON에 대해 누구보다 자신 있게 이야기할 수 있게 됐을 거야.
다시 한번 정리해볼까?

  • JSON은 자바스크립트 객체 표기법에 기반한, 가볍고 읽기 쉬운 **텍스트 데이터 형식**이다.

  • 자바스크립트 객체를 JSON 문자열로 바꾸는 것을 **직렬화(Serialization)**라 하고, **`JSON.stringify()`**를 사용한다.

  • JSON 문자열을 자바스크립트 객체로 바꾸는 것을 **파싱(Parsing)**이라 하고, **`JSON.parse()`**를 사용한다.

  • API 통신, 설정 파일, 웹 스토리지 등 현대 웹 개발의 거의 모든 곳에서 데이터를 주고받는 **표준 언어**로 사용된다.

  • 직렬화 과정에서 `undefined`, 함수, `Date` 객체 등이 유실되거나 변형될 수 있다는 점을 항상 기억해야 한다.

JSON은 단순히 데이터를 담는 그릇이 아니야.
서로 다른 환경과 언어로 만들어진 수많은 프로그램들이 서로 막힘없이 소통할 수 있게 해주는 **'데이터 세계의 링구아 프랑카(공용어)'** 인 셈이지.

이제 너도 JSON 마스터! 앞으로 API 문서를 보거나, 데이터를 다루거나, 설정 파일을 만질 때 훨씬 더 자신감이 생길 거야.
코딩의 세계에서 너의 재능을 마음껏 펼쳐봐! 혹시 더 깊이 있는 백엔드 지식이나 프론트엔드 프레임워크가 궁금하다면, 실력 있는 멘토를 찾아보는 것도 좋은 방법이야. 예를 들어 **재능넷** 같은 플랫폼에서 말이지.

오늘 배운 내용이 너의 개발 여정에 든든한 무기가 되길 바라! 화이팅!

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.