https://nuxt.com/docs/getting-started/data-fetching#serializing-data-from-server-to-client
Nuxt 는 서버에서 획득한 데이터를 payload 를 통해서 클라이언트로 동일하게 복사하여 전달합니다.
https://nuxt.com/docs/api/composables/use-nuxt-app#payload
<script lang="ts" setup>
const {data: timeData} = useAsyncData<Date>(() => {
return new Date();
}, {key: 'time', url: ''});
// get on the client slide
onMounted(() => {
alert(timeData.value?.getHours());
});
</script>
위 코드에서 볼수 있는 것처럼 서버에서 획득한 데이터를 클라이언트 사이드에서 확인 할수 있다.
Nuxt 서버에서는 devalue 라는 라이브러리를 사용하여 payload 를 만든다음 클라이언트로 전달합니다.
https://www.npmjs.com/package/devalue
Nuxt 는 서버 endpoint 에서 데이터를 반환할때는 devalue 를 사용하지 않습니다.
그로 인해 아래 코드는 오류가 발생합니다.
// /api/date.ts
export default defineEventHandler(async (event) => {
return new Date();
});
// .vue
<script lang="ts" setup>
const {data: dateData} = await useFetch<Date>('/api/date');
onMounted(() => {
alert(dateData.value?.getHours());
})
</script>
nuxt 가 payload 를 직렬화 하는 과정 도 참고바랍니다.
위 코드의 경우 data 는 직렬화된 string 임으로 getHours 라는 메서드에 대한 참조가 이루어지지 않습니다.
그러면 어떻게 internal api endpoint data를 직렬화해야 client 에서 사용 가능할까요?
https://nuxt.com/docs/getting-started/data-fetching#custom-serializer-function
아래와 같은 형태로 개발자가 원하는 속성들이 선언된 object를 반환하는 방법입니다.
export default defineEventHandler(() => {
const data = {
createdAt: new Date(),
toJSON() {
return {
createdAt: {
year: this.createdAt.getFullYear(),
month: this.createdAt.getMonth(),
day: this.createdAt.getDate(),
},
}
},
}
return data
})
하지만 위 경우는 단순히 몇몇 property 들만 있으며, date object 자체가 응답되진 않습니다.
운영 리소스가 많이 발생합니다.
https://nuxt.com/docs/getting-started/data-fetching#using-an-alternative-serializer
대체 serializer 의 사용은 아직 nuxt 에서 제공되지 않습니다.
하지만 우리는 toJSON 이라는 메서드가 선언된 객체를 생성하고 이를 직렬화하여 제공하는 방식으로 우회할수 있습니다.
이는 typescript의 타입 시스템에서 추론 가능하고 또한 runtime 에서 모두 동작하는 방법입니다.
import superjson from 'superjson'
export default defineEventHandler(() => {
const data = {
createdAt: new Date(),
// Workaround the type conversion
toJSON() {
return this
}
}
// Serialize the output to string, using superjson
return superjson.stringify(data) as unknown as typeof data
})
createAt 를 선언하여 date 객체를 담아 둔다음 toJSON 메서드를 선언하고 this 를 반환합니다.
toJSON 메서드를 선언함으로써 타입 추론이 가능하도록 만듭니다.
nuxt 내에서 toJSON 이 동작하는 과정 : https://opendive.tistory.com/760
그리고 위 예제에서는 superjson 을 이용하여 data 를 직렬화하며, client 에서는 다시 superjson.parse 를 이용해 역 직렬화하였습니다. 우리는 devalue 를 사용하겠습니다.
import { stringify } from 'devalue';
export default defineEventHandler(async (event) => {
const data = {
createAt: new Date(),
toJSON() {
return this;
}
}
return stringify(data) as unknown as typeof data;
});
위 코드에 보시면 as unkonwn as typeof data 의 표현식이 있습니다.
타입 추론과 런타임 참조를 위한 표현 방식입니다.
api endpoint 에서 어떤 타입을 반환하건 data 타입으로 추론하도록 강제합니다.
https://opendive.tistory.com/759
client 는 다음과 같습니다.
const {data: dateData} = await useFetch('/api/date')
// dateData.value?.createAt.getHours()
onMounted(() => {
console.log(dateData.value?.createAt.getHours()); // runtime 시 undefined
})
에디터가 지원해 준다면 data 에 마우스 오버시 타입 추론되는 것이 보일 것입니다.
하지만 runtime 에는 undefined 가 발생하는 것을 볼수 있습니다. (client side: onMounted hook)
브라우저 주소창을 통해 api endpoint url 을 직접 입력해보면 Cannot stringify a function 오류가 발생하는 것을 알수 있습니다.
애초에 server api endpoint 에서 stringify 의 사용시 toJSON 에 대한 충돌이 발생하는 것을 알수 있습니다.
stringify 함수는 object 타입이 아닌 function type 을 만날시 오류를 발생시킵니다.
The error "Cannot stringify a function" occurs because the stringify function does not support serializing functions. In the data object includes a toJSON method, which is a function. When stringify tries to process this object, it encounters the toJSON method and throws an error.
Stringify Function: The stringify function is designed to convert various data types into a string representation. However, it explicitly throws an error when it encounters a function.
if (typeof thing === 'function') {
throw new DevalueError(`Cannot stringify a function`, keys);
}
그리하여 여기서는 data 와 타입을 구분할 필요가 있습니다.
import { stringify } from 'devalue';
export default defineEventHandler(async (event) => {
const data = {
createAt: new Date()
}
const dataToJSON = {
...data,
toJSON() {
return this;
}
}
return stringify(data) as unknown as typeof dataToJSON;
});
data 가 실질적인 데이터를 가지고 dataToJSON 으로 타입을 지정합니다.
이 경우 데이터는 직렬화되어 있음으로 역 직렬화가 필요합니다.
const {data: dateData} = await useFetch('/api/date', {
transform(data) {
return parse(data as unknown as string) as typeof data;
}
});
<template>
<div>
<h1>Pick Data</h1>
<div>
{{ dateData?.createAt.getDate() }}
</div>
</div>
</template>
마찬가지로 parse 는 string 타입의 매개변수가 필요함으로 data as unknown as string 형태로 타입 우회합니다.
어떤 타입이 올지 알수 없음으로 unknown 이 필요합니다.
또한 parse 된 데이터는 data 타입으로 선언되어야 타입추론이 가능함으로 as typeof data 가 추가되게 됩니다.
'코드 > Nuxt3' 카테고리의 다른 글
[Nuxt] toJSON 이 선언된 api 응답 (0) | 2025.01.23 |
---|---|
[Nuxt] Payload serialized by devalue (1) | 2025.01.17 |
[Nuxt] useFetch options (0) | 2025.01.15 |
[Nuxt] getCachedData 사용하기 (0) | 2025.01.13 |
[useAsyncData, useFetch] cache 처리 방식 비교 (0) | 2025.01.09 |