Next.js의 개념이 100% 이해되지 않은 상태에서 과제를 시작하려니 막막함이 앞섰다.
하지만 고민만 하기보다 직접 부딪히며 배우기로 했고, 그 과정에서 겪은 에러와 해결 과정을 기록으로 남긴다.
처음에는 useState와 useEfect 조합으로 서버 액션을 호출했을 때는 데이터가 정상적으로 불러와짐.
const [rotationData, setRotationData] = useState<Champion[]>([]);
useEffect(() => {
const fetchData = async () => {
const data = await getChampionRotation(); // 서버액션 호출
setRotationData(data);
};
fetchData();
}, []);
하지만 가독성과 상태 관리를 위해 TanStack Query로 교체하자마자 에러가 발생
// ❌ 에러 발생 코드
const { data: rotationData } = useQuery({
queryKey: ["rotation"],
queryFn: getChampionRotation, // 서버 액션을 직접 전달
});
🚨 발생한 에러 메시지
Only plain objects, and a few built-ins, can be passed to Server Actions. Classes or null prototypes are not supported.
=> Server Actions로 전달할 수 있는 값은 일반 객체(plain objects)와 일부 내장된 객체(built-ins)만 가능하다는 의미라고 한다.
에러의 원인을 파악하기 위해 TanStack Query의 동작 원리와 Next.js 서버 액션의 제한 사항을 살펴보았다.
① TanStack Query의 동작 방식
useQuery의 queryFn에 함수를 전달하면, TanStack Query는 자동으로
QueryFunctionContext 라는 객체를 인자로 넘겨준다. 이 객체 안에는 다음과 같은 정보가 들어있다.
queryKey: 현재 쿼리의 키signal: 요청 취소를 위한 AbortSignal 객체② Next.js 서버 액션의 제한 사항
Next.js의 서버 액션은 클라이언트에서 서버로 데이터를 보낼 때 모든 인자를 JSON으로 직렬화해야한다.
문제점: QueryFunctionContext 안에 포함된 AbortSignal은 복잡한 내부 구조를 가진 객체로, JSON 직렬화가 불가능하다.
결과: 직렬화할 수 없는 값을 서버 액션의 인자로 넘기려 했기 때문에 에러가 발생한 것이다.
[정리] Next.js Server Actions의 직렬화 제한
Next.js Server Actions는 다음과 같은 조건을 요구한다
- 모든 인자와 반환값은 JSON 직렬화가 가능해야 한다.
- 직렬화 불가능한 객체(예: AbortSignal, 클래스 인스턴스)는 허용되지 않는다.
해결 방법은 의외로 간단하다. 서버 액션이 QueryFunctionContext를 인자로 받지 않도록 익명 함수로 한 번 더 감싸주면 된다.
// 수정된 코드
const { data: rotationData } = useQuery({
queryKey: ["rotation"],
queryFn: () => getChampionRotation(), // 인자 없이 호출되도록 익명 함수 사용
});
💡 왜 이렇게 하면 해결될까?
1. queryFn 자체는 여전히 TanStack Query로부터 컨텍스트를 전달받는다.
2. 하지만 우리가 작성한 익명 함수 () => ... 내부에서는 그 컨텍스트를 무시하고
getChampionRotation()을 아무 인자 없이 실행한다.
3. 결과적으로 직렬화 불가능한 AbortSignal이 서버 액션으로 전달되지 않아 에러가 해결된다.
참고자료
https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr
https://github.com/TanStack/query/issues/6264
TanStack Query의 내부 동작: queryFn이 단순히 실행만 되는 게 아니라 컨텍스트 객체를 인자로 넘겨준다는 사실을 알게 됨.
서버 액션의 직렬화: 서버와 클라이언트 간 데이터를 주고받을 때 '직렬화 가능 여부'가 중요함
프로젝트를 시작하면서 강의를 들었을때보다 Next.js의 혼란스러웠던 개념들이 이전보다 정리되었다
물론 아직 완벽하지는 않지만ㅋㅋㅋ 일단 시작해보자!! 해결과정에서 더 많이 배울 수 있다.