자바스크립트

Section 8. 자바스크립트 Object 깊게 다루기 (JSON)

포칼이 2023. 4. 6. 11:56

I. JSON JavaScript Object Notation

이런 형태로 작성된 문서를 XML이라고 한다. XML은 간단히 말해서 복잡할 수 있는 정보를 서버와 클라이언트에서 주고 받기 적합한 형태로 표현한 것이라고 한다. tag를 사용하여 정보와 구조가 명확하게 드러나는 특징이 있다.

하지만 가독성이 조금 떨어질 수 있는데 여기서 강점을 가질 수 있는 것이 바로 JSON이다. 

JSON은 보다 간결한 형태로 구조화된 정보를 표기한다. 

JSON은 정리를 하자면 다음과 같다

  • 복잡한 구조를 가질 수 있는 데이터를 한 줄의 문자열로 표현한 것.
  • 서버와 클라이언트 등 데이터들을 주고받는 주체들 사이에 널리 사용된다.

 

II. JSON 객체의 정적 메서드

1. stringify - 객체를 문자열로 직렬화 serialize 하는 메서드

const person = {
  name: '김달순',
  age: 23,
  languages: ['Korean', 'English', 'French'],
  education: {
    school: '한국대',
    major: ['컴퓨터공학', '전자공학'],
    graduated: true,
  }
};

const personStr = JSON.stringify(person);

console.log(typeof personStr); //string
console.log(personStr);
//{"name":"김달순","age":23,"languages":["Korean","English","French"],
"education":{"school":"한국대","major":["컴퓨터공학","전자공학"],"graduated":true}}

객체 personStr이 문자열로 직렬화가 되어서 출력이 되었다. 

 

데이터 형태별 직렬화 결과는 다음과 같다

[
  JSON.stringify(1), //1                  
  JSON.stringify(Infinity), //null
  JSON.stringify(NaN), //null
  JSON.stringify('가나다'), //"가나다"
  JSON.stringify(true), //true
  JSON.stringify(null), //null
  JSON.stringify(undefined), //undefined
  JSON.stringify([1, 2, 3]), //[1,2,3]
  JSON.stringify({x: 1, y: 2}), //{"x":1,"y":2}
  JSON.stringify(new Date()), //"2023-04-06T02:02:13.397Z"
]
.forEach(i => console.log(i));

Infinity와 NaN은 직렬화 처리가 되지 않아 null이 된 것을 알 수 있고 Date는 부분적으로만 직렬화가 된 것을 알 수 있다.

 

값이 함수인 프로퍼티는 직렬화되지 않는다

const obj = {
  x: 1,
  y: 2,
  z: function () { return this.x + this.y }
}
console.log(obj.z()) //3

const objStr = JSON.stringify(obj); 
console.log(objStr); //{"x":1,"y":2}

z는 직렬화가 되지 않는다.

 

2번째 인자 : replacer 함수

직렬화될 방식을 지정하는 함수이다.

const obj = {
  a: 1,
  b: '2',
  c: 3,
  d: true,
  e: false
}

// 1. key와 value 매개변수
const objStr1 = JSON.stringify(obj, (key, value) => {
  if (key && key < 'a' || key > 'c') {
    // 해당 프로퍼티 생략
    return undefined;
    // ⚠️ 조건에 key && 을 붙이지 않으면 항상 undefined가 반환됨
    // key가 공백('')일 때(value는 객체 자체) undefined를 반환하므로...
    // key와 value를 로그로 출력해보며 확인해 볼 것
  }
  if (typeof value === 'number') {
    return value * 10;
  }
  return value;
});
console.log(objStr1); //{"a":10,"b":"2","c":30}

key가 a보다 작거나 c보다 크면 undefined를 반환하라고 해서 d와 e는 직렬화가 되지 않는 것을 확인할 수 있다. 즉, undefined를 반환하라는 것은 생략하라는 의미이다.

그리고 값이 숫자인 경우에는 10을 곱해서 반환하라고 해서 값에 10이 곱해져서 직렬화가 된 것을 볼 수 있다. 

이렇게 어떤 객체를 직렬화 할때 조건을 넣어 줄 수 있다.

 

아니면 어떤 프로퍼티만 직렬화가 되게끔 명시를 하는 방법도 있다.

// 2. 반환한 key의 배열 매개변수
const objStr2 = JSON.stringify(obj, ['b', 'c', 'd']);
console.log(objStr2); //{"b":"2","c":3,"d":true}

 

3번째 인자 : 인덴트

만약에 직렬화된 문자열이 조금 알아보기 쉽게 되었으면 좋겠다~ 라고 한다면 3번째 인자를 통해 인덴트 한다.

const obj = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
};

[
  JSON.stringify(obj, null), //{"a":1,"b":{"c":2,"d":{"e":3}}}
  JSON.stringify(obj, null, 1),
  JSON.stringify(obj, null, 2),
  JSON.stringify(obj, null, '\t')
]
.forEach(i => console.log(i));

이처럼 3번째 인자에 어떤 조건을 주느냐에 따라서 직렬화의 모양을 바꿀 수 있다.

 

객체의 toJSON 프로퍼티

const obj = {
  x: 1,
  y: 2,
  toJSON: function () {
    return '철권8 언제 나와?';
  }
}

console.log(
  JSON.stringify(obj)
); //"철권8 언제 나와?"

toJSON은 어떤 객체가 직렬화가 될때 이런 값을 리턴해라~ 라고 정의 해주는 것이다. 

어떤 객체에다가  toJSON 프로퍼티를 함수로 넣어주게 되면 직렬화가 될때 그 함수가 실행 된다. 

 

2. parse - 역직렬화

[
  JSON.parse('1'),
  JSON.parse('"가나다"'), // ⚠️ 안쪽에 따옴표 포함해야 함
  JSON.parse('true'),
  JSON.parse('null'),
  JSON.parse('[1, 2, 3]'),
  JSON.parse('{"x": 1, "y": 2}') // ⚠️ key도 따옴표로 감싸야 함
]
.forEach(i => console.log(i));

//1
//가나다
//true
//null
//[1, 2, 3]
//{x: 1, y: 2}

 

직렬화와 반대로 작동하는 것을 볼 수 있다. 

자바스크립트 코드가 문자열로 들어가는 것이기 때문에 코드가 문자열일 경우는 따옴표로 감싸주어야 한다.

 

2번째 인자 : receiver 함수

const objStr = '{"a":1,"b":"ABC","c":true,"d":[1,2,3]}';

const obj = JSON.parse(objStr, (key, value) => {
  if (key === 'c') { 
    // 해당 프로퍼티 생략
    return undefined;
  }
  if (typeof value === 'number') {
    return value * 100;
  }
  return value;
});

console.log(obj); // ⚠️ 내부까지 적용(배열 확인해 볼 것)
//a: 100, b: 'ABC', d: Array(3)}

내부까지 적용이 된 것을 볼 수 있다.

 

III. 깊은 복사 deep copy

JSON을 이용하면 깊은 복사가 가능하다.

const obj1 = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3,
      f: {
        g: 4
      }
    }
  }
}

const obj2 = JSON.parse(JSON.stringify(obj1));

console.log(obj1);
console.log(obj2);

obj1.a++;
obj1.b.c++;
obj1.b.d.e++;
obj1.b.d.f.g++;

console.log(obj1);
console.log(obj2);

obj2에 obj1을 직렬화 한것에 역직렬화를 한것을 할당했고 obj1의 프로퍼티를 1씩 증가시킨다음 둘다 출력을 해보면 다음과 같다.

obj2는 obj1의 영향을 받지 않은 것을 볼 수 있다. 

이유는 obj2는 obj1과 같은 값을 참조하는 게 아니라 문자열을 받아서 그걸 해석해서 값이 나온(역직렬화) 완전 새로운 것을 obj2에 할당 했기 때문이다. 완전히 다른 객체가 할당이 된 것.

 

함수, Date, Symbol, BigInt 프로퍼티는 JSON 방식으로는 불가 또는 제한적이라는 사실을 알고 넘어가자.

 

structuredClone이라는 것이 있다. 특징은 다음과 같다

  • 아직은 일부 브라우저 및 환경에서만 지원한다.
  • JSON 방식보다 빠르고 효율적인 깊은 복사가 된다.
  • Date와 BigInt를 제대로 복사한다.

일단은 이런게 있다는 사실만 알고 넘어가자.