본문 바로가기
Dev. Handbook/Javascript

[JSON] JSON 활용하기 - JavaScript 로 JSON 다루기

by breezyday 2023. 11. 13.

JSON은 여러 프로그래밍 언어에서 활용할 수 있습니다. 

 

기본적으로 웹 개발에서 JavaScript로 다루는 경우가 많고, Node 환경이나 React 등의 JavaScript 기반의 언어에서 자주 활용하고 있으므로 JavaScript에서 다루는 방법을 소개합니다.

 

1. JSON 객체

JavaScript는 표준 내장 객체를 가지고 있습니다.

표준 내장 객체(Standard Built-in Object)JavaScript 엔진에 기본으로 포함되어 있는 객체입니다. 

 

JavaScript(ECMAScript) 기술 개요를 알아보려면 아래 링크를 정독하면 좋습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/JavaScript_technologies_overview

 

대표적인 표준 내장 객체로 값에서 볼 수 있는 NaN, undefined 가 있으며, 함수로 eval(), parseInt() 등이 있습니다. 기초 객체로 Object, Boolean, 오류 객체로 Error, 숫자 및 날짜를 다루기 위한 Number, Math, Date, 텍스트 처리를 위한 String, RegExp 도 있습니다. 이 외에도 Array, Map, Set Promise 등을 자주 사용하고 있습니다. 이런 객체들은 Import 없이 바로 사용이 가능합니다.

 

JSON 객체JavaScript의 표준 내장 객체입니다.

JSON 객체는 JSON 문자열을 분석하거나 값(Object, Array, List 등)을 JSON으로 변환하는 메서드를 가지고 있습니다.

JSON을 직접 호출하거나 인스턴스를 생성할 수 없으며, 두 개의 메서드를 제외하면 특별한 기능은 없습니다.

 

2. JSON 객체의 메서드

2.1 JSON.stringify()

이 메서드는 JavaScript 값이나 객체JSON 문자열로 변환합니다.

선택적으로 replacer를 함수 전달할 경우 변환 전 값을 변경할 수 있고, 배열로 전달할 경우 지정한 속성만 결과에 포함합니다.

 

/*
 * value : JSON 문자열로 변환할 값(객체, 배열 등)
 * replacer : 문자열 변환 과정에서 결과를 수정.
              null 이거나 제공하지 않으면, 객체의 모든 속성들을 변환함.
 * space : 값을 기준으로 JSON 문자열을 formatting 함
           공백으로 사용할 스페이스(space)의 수를 나타냄.
           null 이거나 제공하지 않으면 formatting 하지 않음.
           숫자인 경우
           - 1보다 작은 값은 스페이스가 사용되지 않음.
           - 최대값은 10으로 그 이상일 경우 10으로 제한함.
           문자인 경우
           - 제공한 문자열이 공백으로 사용됨
           - 문자열 길이가 10보다 길다면 10개의 문자만 사용
           
 * 반환값 : 주어진 값과 대응하는 JSON 문자열 
 * 예외 : 순환 참조를 발견할 경우 TypeError
 */

JSON.stringify(value[, replacer[, space]])

 

* 배열이 아닌 객체의 속성순서에 따라 문자열화 되지 않음.
* Boolean, Number, String 객체들은 문자열화될 때 기본형(primitive) 값으로 변환됨.
* undfined, 함수, 심벌(symbol)은 변환될 때 생략되거나(객체의 속성) null로 변환됨(배열)
* 심벌(symbol)을
키로 가지는 속성들은 replacer 함수를 사용하더라도 완전히 무시됨.
*
열거 불가능한 속성들은 무시됨.

 

예시

JSON.stringify({}); 
// {}
JSON.stringify([1, "true", true]); 
// [1,"true",true]
JSON.stringify(new Date(2023, 10, 12, 15, 10, 30));
// "2023-11-12T06:10:30.000Z"

JSON.stringify({ c: 5, b: "6", a: 7 });
// {"c":5,"b":"6","a":7}
JSON.stringify({ c: 5, b: "6", a: 7 }, null, 2);
/*
{
  "c": 5,
  "b": "6",
  "a": 7
}
*/
JSON.stringify(["b", "c", "a"]);
// ["b","c","a"]
JSON.stringify(["b", "c", "a"], null, "*_ ");
/*
[
*_ "b",
*_ "c",
*_ "a"
]
*/

JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// {}
JSON.stringify([undefined, Object, Symbol("")]);
// [null,null,null]

 

replacer 매개 변수

replacer 매개변수는 함수 또는 배열이 될 수 있습니다.

 

함수는 문자열화 될 key와 value, 2개의 매개변수를 받습니다. key가 발견된 객체는 replacer의 this 매개변수로 제공됩니다. 맨 처음에는 문자열화 될 그 객체를 나타내는 비어 있는 key와 함께 호출되고, 그다음에는 문자열화 될 그 객체나 배열의 각 속성에 대해 호출됩니다. 이 함수는 JSON 문자열에 추가되어야 하는 값을 반환해야 합니다.

 

* Number를 반환하면, 그 수를 나타내는 문자열이 그 속성의 값으로 사용됨
* String을 반환하면, 속성의 값으로 사용됨
* Boolean을 반환하면, "true" 혹은 "false"가 속성 값으로 사용됨
* 다른 객체를 반환하면, 그 객체는 replacer 함수를 각각의 속성에 대해 호출하며 순환적으로 JSON 문자열로 변환함. 그 객체가 함수인 경우에는 무시됨

 

예시 : 함수

function replacerFunction(key, value) {
  if (key === "foundation") {
    return "<" + value + ">";
  }

  if (key === "100") {
    return parseInt(value);
  }

  switch (typeof value) {
    case "string":
      return undefined;
    case "number":
      return value > 10 ? true : value;
  }

  return value;
}

let foo = {
  foundation: "Mozilla",
  model: "box",
  week: 45,
  transport: "car",
  month: 7,
  100: "100",
};

const jsonString = JSON.stringify(foo, replacerFunction, 2);
console.log(jsonString);

// 나열된 속성의 순서는 변경될 수 있음.
/*
{
  "100": 100,
  "foundation": "<Mozilla>",
  "week": true,
  "month": 7
}
*/

 

replacer 가 배열인 경우, 그 배열의 값은 결과에 포함될 속성의 이름들을 나열합니다.

 

예시 : 배열

const foo = {
  foundation: "Mozilla",
  model: "box",
  week: 45,
  transport: "car",
  month: 7,
  100: "100",
};

const replacerArray = ["foundation", "week", "100"];

const jsonString = JSON.stringify(foo, replacerArray, 2);
console.log(jsonString);

/*
{
  "foundation": "Mozilla",
  "week": 45,
  "100": "100"
}
*/

 

toJSON 메서드

객체에 toJSON이라는 메서드가 구현되어 있으면 JSON.stringify에서 toJSON을 자동으로 호출해 줍니다.

 

예시

var obj = {
  foo: "foo",
  toJSON: function () {
    return "bar";
  },
};

JSON.stringify(obj); // '"bar"'
JSON.stringify({ x: obj }); // '{"x":"bar"}'

 

내장 객체에도 toJSON이 구현되어 있는 객체들이 있습니다.

대표적으로 JSON에서 지원하지 않는 Date 객체와,  Web API의 URL 객체등에도 toJSON이 구현되어 있습니다.

 

예시 : toJSON 메서드 제공 객체

const event = new Date('August 19, 1975 23:15:30 UTC');

const jsonDate = event.toJSON();

console.log(jsonDate);
// Expected output: "1975-08-19T23:15:30.000Z"
console.log(new Date(jsonDate).toUTCString());
// Expected output: "Tue, 19 Aug 1975 23:15:30 GMT"

const url = new URL(
  "https://developer.mozilla.org/en-US/docs/Web/API/URL/toString",
);
url.toJSON(); // should return the URL as a string

 

사용자가 구현하는 객체에서도 toJSON 메서드를 구현해서 사용할 수 있습니다.

 

예시 : 커스텀 toJSON 메서드

// this의 scope 문제로 arrow function은 오동작 함.
const getCode = function () { 
  switch (this.key) {
    case "apple":
      return { key: 1, value: "apple" };
    case "orange":
      return { key: 2, value: "orange" };
    case "banana":
      return { key: 3, value: "banana" };
    default:
      return { key: 0, value: "unknown" };
  }
};

let obj = {
  key: "orange",
  toJSON: getCode,
};
console.log(JSON.stringify(obj));
// {"key":2,"value":"orange"}

let reqData = [
  {
    name: "고길동",
    choice: { key: "apple", toJSON: getCode },
  },
  {
    name: "둘리",
    choice: { key: "banana", toJSON: getCode },
  },
];
console.log(JSON.stringify(reqData, null, 2));
/*
[
  {
    "name": "고길동",
    "choice": {
      "key": 1,
      "value": "apple"
    }
  },
  {
    "name": "둘리",
    "choice": {
      "key": 3,
      "value": "banana"
    }
  }
]
*/

2.2 JSON.parse()

이 메서드는 JSON 문자열의 구문을 분석하고, 그 결과에서 JavaScript 값이나 객체를 생성합니다.

선택적으로, reviver 함수를 인수로 전달할 경우 결과를 반환하지 전에 변경할 수 있습니다.

 

/*
 * text    : JSON으로 변환할 문자열
 * reviver : 함수라면, 변환 결과를 반환하기 전에 이 인수에 전달해 변경함.
 
 * 반환값   : 입력한 JSON 문자열에 대응하는 Object
 * 예외     : 유효한 JSON이 아닌 경우 SyntaxError
 */
 
JSON.parse(text[, reviver]);

 

예제

JSON.parse("{}");    // {}
JSON.parse("true");  // true
JSON.parse('"foo"'); // "foo"
JSON.parse("null");  // null
JSON.parse("{ name: "홍길동", age: 20 }")  // Object { name: "홍길동", age: 20 }
JSON.parse('[1, 5, "false"]');            // Array [1, 5, "false"]

 

reviver 매개변수 사용하기

reviver 가 주어지면 분석한 값을 반환하기 전에 변환합니다. 

reviver는 속성 명(문자열)과 을 인자로 전달받습니다. 함수가 중간에 실패하면 그 속성은 최종 결과에서 제외됩니다. 에러가 없다면 반환 값으로 속성의 값을 재설정합니다.

 

예시 1

JSON.parse(
  '[{"p": 5}, {"b": "b"}, {"k": 2}]',
  (key, value) =>
    typeof value === "number"
      ? value * 2 // 숫자라면 2배
      : value, // 나머진 그대로
);

// Array [Object { p: 10 }, Object { b: "b" }, Object { k: 4 }]

 

중첩된 값이나 속성이 있다면 재귀로 분석하여 가장 안쪽부터 바깥쪽의 순서로 결과를 출력합니다.

 

예시 2

JSON.parse('{"1": 1, "2": 2, "3": {"4": 4, "5": {"6": 6}}}', (key, value) => {
  console.log(key); // 현재 속성명 출력, 마지막은 빈 문자열("")
  return value; // 변환하지 않고 그대로 반환
});

// 1
// 2
// 4
// 6
// 5
// 3
// ""

 

예시 3 : 

const jsonString = '[{"p": 2}, {"b": "2"}, {"k": 1}, {"o": true}]';

const reviver = function (key, value) {
  switch (value && typeof value) {
    case "string":
      value = "[" + value + "]";
      return value;
    case "number":
      return value * 2;
  }
  
  return value; // 반드시 기본으로 값을 반환
                // 반환하지 않으면 변환 결과가 undefined가 됨
};

let parsed = JSON.parse(jsonString, reviver);
console.log(JSON.stringify(parsed, null, 2));
/*
[
  {
    "p": 4     // 숫자이므로 2배
  },
  {
    "b": "[2]" // 문자열이므로 "[]" 추가
  },
  {
    "k": 2     // 숫자이므로 2배
  },
  {
    "o": true  // 그대로 반환
  }
]
*/

 

 

 

 

 

참고문헌

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON

https://developer.mozilla.org/en-US/docs/Web/API/URL/toJSON

https://ko.javascript.info/json#ref-231

 

 

 

 

 

 

댓글