JSON.stringify의 첫 번째 인자는 (당연히) 사용해 봤을 것이고, 세 번째 인자(indent level)도 자주 사용한다. 그러면 두 번째 인자는 무엇인가? 우연히 두 번째 인자를 사용하는 코드를 봐서 찾아보게 되었다.
두 번째 인자(replacer)에는 배열(unknown[])이나 함수((key: string, value: unknown) => any)가 들어갈 수 있다. (다른 게 들어가면 안 준 거나 마찬가지로 동작한다.)
key: string | number만 stringified JSON에 포함된다.JSON.stringify({ hello: "world", hi: "there", [Symbol. }, ["hi"])
> '{"hi":"there"}'
key와 그 value를 파라미터로 하여 함수를 호출한다.number | string | boolean | null이면 그 값을 그대로 사용한다.Function | Symbol | undefined이면 그 값은 결과에 포함되지 않는다.object이면 재귀적으로 다시 replacer 함수를 호출한다.주의할 점은, 최상단 오브젝트 자체에 대해서도 replacer가 호출된다는 것이다. 부모 오브젝트에 대해서 호출하고 나서 각 key와 상응하는 value로 replacer를 호출한다. 예를 들어, JSON.stringify(<아무거나>, () => 42)는 언제나 42가 된다.
JSON.stringify(
// value
{ createdAt: new Date() },
// replacer
(key, value) =>
value instanceof Date ?
value.toLocaleString("ko-KR") :
value,
// space
2,
)
> `{
"createdAt": "2025-07-26T11:23:10.961Z"
}`
...?
다시 읽어보니 replacer의 value는 stringified가 되어서 온다고 한다. (대체 왜???)
그래서 이 문제를 타파하기 위해서는 value 대신 this[key]를 사용해야 한다. Arrow function에서는 this가 상위 환경의 값이 되어버리므로 function으로 수정하자.
JSON.stringify(
// value
{ createdAt: new Date() },
// replacer
function (key, value) {
return (
this[key] instanceof Date ?
this[key].toLocaleString("ko-KR") :
value
);
},
// space
2,
)
> `{
"createdAt": "2025. 7. 26. 오후 8:39:20"
}`
Date나 Map, Set 등이 포함되어 있을 때에 superjson 없이 간단하게 사용하기 좋을 것 같다.
JSON.parse에도 같은 역할을 하는 reviver라는 인자가 있다고 한다!