[면접 회고] JSON 제대로 정리해보자

이주영·2023년 9월 4일
1

Javascript

목록 보기
9/11
post-thumbnail

들어가기 앞서 🌱

지난주 금요일 면접에서 JSON에 대한 답변이 부족했다. 깊은 복사와 얕은 복사를 이야기하던 중, 깊은 복사하는 방법을 설명해달라고 하셨고 여러 가지를 이야기하다 JSON 데이터 형식을 활용해서 복사할 수 있다고 했다. 그리고 단점이 있다고 말해버렸다. 그에 따라 면접관님께서

JSON.parse와 stringify를 사용해 깊은 복사를 하면 어떤 문제점이 있을 수 있는지 설명해주세요.

하셨고 아차 싶었다. 알고 있었는데 기억이 안 났고 왜 Lodash를 썼는지도 갑자기 기억이 안 났다. 그래서 얼버무렸다. 이번 계기로 정확히 이해하고 넘어가야겠다.

📘 JSON 내장 객체란?

상위 개념 : AJAX(Asynchronous JavaScript and XML)

❓왜 비동기 통신을 하려는데 왜 JSON으로 직렬화해야 하는거지 ❓

그러게, 말이다. 생각해보면 바로 답변이 바로 떠오르지 않는다.

첫 번째, 다양한 언어에서 활용할 수 있다.
JSON 데이터 포맷은 다른 프로그래밍 언어로도 사용할 수 있다. 클라이언트는 JS 기반 라이브러리고 서버는 Java 혹은 Python 기반 프레임 워크라고 한다면 서로 다른 언어에서 어떻게 데이터를 주고받을지 고민이 많았을 텐데 감사하게 JSON이라는 데이터 포맷을 통해 그동안 어렵지 않게 통신했던 것에 대해 감사함을 느낀다.

두 번째, 가독성과 디버깅 측면이 우수하다.
JS의 객체와 다를 바 없이 보이는 JSON 형식은 개발자가 읽고 이해하기 쉬우므로 널리 사용된다고 한다.

그럼 이제 하나씩 알아보자

📕 JSON 이란

JSON은 객체, 배열, 숫자, 문자열, 불리언과 null을 직렬화하기 위한 포맷이다. 또한 Javascript에서만 JSON 데이터 포맷을 사용할 수 있는 것이 아니라 다른 프로그래밍 언어에서도 사용할 수 있다.

JSON은 자바스크립트의 객체 리터럴과 유사하고 Key와 value로 구성된 순수한 텍스트이다.

JSON에 내장 매소드는 생각보다 별로 없다. 단 두 가지!!

  1. JSON.stringify() : JS 객체를 문자열로 변환
  2. JSON.parse() : 문자열을 JS에서 사용할 수 있는 객체로 변환

1. JSON.stringify()

서버와 통신할 경우 객체를 보내고자 한다. 그때 직렬화(문자열화)를 하여 서버로 JSON 데이터로 보낼 때 사용한다.

JS 값이나 객체를 JSON 문자열로 변환한다. 마찬가지로 선택적으로 replacer 콜백 함수를 전달하면 값을 변형할 수 있다.

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

예제

const obj = {
  name : 'juyoung',
  age : 27,
  alive : true,
  hobby: ['basketball','drum','swimming']
}

const json = JSON.stringify(obj)
console.log(typeof json) //'string'
console.log(json)

// 'string'
// '{"name":"juyoung","age":27,"alive":true,"hobby":["basketball","drum","swimming"]}'

const prettyJson = JSON.stringify(obj,null,2)
console.log(prettyJson)

// '{
//   "name": "juyoung",
//   "age": 27,
//   "alive": true,
//   "hobby": [
//     "basketball",
//     "drum",
//     "swimming"
//   ]
// }'

function filter(key,value){
  return typeof value === 'number' ? undefined : value
}

const strFilteredObject = JSON.stringify(obj,filter,2)
console.log(strFilteredObject)

// '{
//   "name": "juyoung",
//   "alive": true,
//   "hobby": [
//     "basketball",
//     "drum",
//     "swimming"
//   ]
// }'

특징 (핵심)

  1. undefined, 함수, 심볼은 변환될 때 생략되거나 null로 변환한다. 그래서 위에서 undefined이면 아예 키값이 없어지는 것이었구나!
const obj = {
  age : 27,
  alive : undefined,
  hobby: ['basketball','drum','swimming'],
  name : 'juyoung',
}

const json = JSON.stringify(obj)
console.log(json)

// {
//   age: 27,
//   hobby: [ 'basketball', 'drum', 'swimming' ],
//   name: 'juyoung'
// }
  1. 배열이 아닌 객체의 속성들은 어떤 특정한 순서에 따라 문자열화 될 것이라고 보장되지 않는다. 같은 객체의 문자열화에 있어서 속성의 순서에 의존하지 않는다.

  2. 열거 불가능한 속성들은 무시된다.

// Non-enumerable properties:
JSON.stringify(
  Object.create(null, {
    x: { value: "x", enumerable: false },
    y: { value: "y", enumerable: true },
  }),
);
// '{"y":"y"}'
  1. 심볼을 키로 가지는 속성들은 replacer 함수를 사용하더라도 완전히 무시된다.
// Symbols:
JSON.stringify({ x: undefined, y: Object, z: Symbol("") });
// '{}'
JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, [Symbol.for("foo")]);
// '{}'
JSON.stringify({ [Symbol.for("foo")]: "foo" }, function (k, v) {
  if (typeof k === "symbol") {
    return "a symbol";
  }
});
// '{}'

replacer

문자열화 동작 방식을 변경하는 함수, 혹은 JSON 문자열에 포함될 값 객체의 속성들을 선택하기 위한 화이트리스트(whitelist)로 쓰이는 String 과 Number 객체들의 배열. 이 값이 null 이거나 제공되지 않으면, 객체의 모든 속성들이 JSON 문자열 결과에 포함된다.

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

JSON.stringify(foo, ["week", "month"]); 

두번째인자로 배열이 들어갈 경우 foo 객체에 배열 속성 값에 상응하는 값만 JSON 문자열로 변환할 수 있다.

JSON.stringify(foo, ["week", "month"]); // '{"week":45,"month":7}'

space

가독성을 위한 매개변수로 원하는 공백을 넣어줄 수 있다.

2. JSON.parse()

JSON 포맷 문자열을 객체로 변환한다. 이건 서버로부터 클라이언트로 내려진 데이터는 JSON 문자열 형태이기에 객체로 사용하려면 parse 매소드를 활용하여 객체로 바꿔야하고 이것을 역직렬화라고 한다.

const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);

console.log(obj.count);
// Expected output: 42

console.log(obj.result);
// Expected output: true

선택적으로 reviver 함수를 인스로 전달할 수 있다고 하는데 결과를 반환하기 전에 변형할 수도 있다고 한다.

JSON.parse(text[, reviver])

reviver 매개변수가 뭐지?

MDN 설명에 따르면 reviver 함수가 매개변수로 들어가면 깊게 중첩된 속성부터 밖으로 reviver 함수가 전달된다고 한다. 그래서 중간에 undefined가 있는 속성은 결과값에서 제외되고 결국 리턴 값이 재설정된다는 것이다.

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

//'1'
//'2'
{
  '1': 1,
  '2': 2,
  '3': { '4': 4, '5': { '6': 6 } }
}
// '4'
// '6'
// '5'
// '3'
''

📕 JSON의 한계 정리

JSON.stringify()는 객체를 json?문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 끊어지고 새로운 메모리 주솟값을 가진 객체로 만들어진다.

객체를 json 문자열로 변환 후, JSON.parse()를 이용해 다시 원래 객체(자바스크립트 객체)로 만들어 준다.

여기서 문제점은 다른 방법에 비해 성능이 느리다는 것과 객체의 프로퍼티가 function일 경우, undefined의 값이 있을 경우 프로퍼티에서 제외하여 반환한다는 점입니다. 그래서 예상하지 못하는 에러를 만날 수 있다는 것이 핵심이었다. 아하!!

마치며 🌱

JSON에 대해 정리해보았다. 기본적인 개념을 까먹은 프론트엔드 개발자분들이나 처음 익히시는 분들에게 도움이 됐으면 좋겠다.

출처 💡

  1. 모던 자바스크립트 사이트
  2. 딥 다이브 교제
  3. MDN 공식 문서
  4. 개발 블로그 : https://hello-bryan.tistory.com/462
profile
https://danny-blog.vercel.app/ 문제 해결 과정을 정리하는 블로그입니다.

0개의 댓글