-
[NextJS] Props must be serializable for components in "use client" file 문제 해결Front-end/Next.js 2024. 7. 3. 18:55반응형
NextJS를 처음 도입해서 프로젝트를 하고 있다.
대부분 React만 했었고, NextJS는 React와 거의 같다라고 들어서 큰 문제 없이 사용할 수 있을지 알았는데 생각보다 CSR, SSR을 맞춰서 사용해야하다보니 고민을 해야하는 부분이 많았다.
오늘은 프로젝트 중 만난 Warning 중 하나인 Props must be serializable for components in "use client" file.... 의 해결에 대해서 알아보려고 한다.
배경
어떤 경우 문제가 나타났는지 살펴보자면...
//////////////////////////////////////////////// /////////////// 부모 컴포넌트 /////////////////////////////////////////////// "use client" import { useState } from 'react' import { ChildComponent } from './ChildComponent' export default function ParentComponent() { const [showDialog, setShowDialog] = useState(false) const handleAddMember = () => { setShowDialog(prev => !prev); } return ( <div> <ChildComponent showDialog={showDialog} handleShowDialog={handleShowDialog}/> {showDialog && ( <dialog> <button onClick={handleAddMember}>버튼입니다</button> </dialog> )} </div> ) } //////////////////////////////////////////////// /////////////// 자식 컴포넌트 /////////////////////////////////////////////// "use client" type ChildComponentProps { showDialog: boolean; handleShowDialog: () => void; } export function ChildComponent({showDialog, handleShowDialog}: ChildComponentProps) { return <button onClick={handleShowDialog}>다이얼로그 열기</button> }
이런 식으로 사용할 때 자식 컴포넌트의 handleSHowDialog에 경고창이 나타났다.
이유
그렇다면 왜 나타났고, 무슨 경고창인지 알아보자. 이는 리액트만 접할 때는 보지 못했던 경고창일 것이다.
이는 NextJS에서 "use client"를 사용하면서 함수를 props로 전달 할 때 발생하는 직렬화(serialization) 문제이며, 서버 컴포넌트와 클라이언트 컴포넌트 간의 상호 작용에서 발생하는 문제라고 한다.
더보기직렬화(serialization)란?
데이터 구조나 객체 상태를 저장하거나 전송할 수 있도록 일련의 바이트나 문자열로 변환하는 과정을 말한다.
이렇게 직렬화 된 데이터는 파일, 데이터베이스, 네트워크를 통해서 전송될 수 있다.
Javascript에서는 JSON 형식으로 객체를 직렬화한다.
그런데 setState와 같은 함수 종류들은 JSON이 지원되지 않는다.함수는 실행 가능한 코드 블록으로서 이를 텍스트 형식으로 변환하여 저장하거나 전송하는 것은 불가하다.
내가 참고했던 글에 따르면 "use client"를 작성한다는 것은 컴포넌트가 서버와 클라이언트 사이의 경계(boundary) 역할을 하는 것을 의미한다고 한다.
ParentComponent가 이미 클라이언트 바운더리에 있기 때문에 ParentComponent에서 import 한 것들은 이미 클라이언트 컴포넌트로 간주되는 것이다. 이는 ChildComponent 또한 포함이다.
경고 내용에 따르면 이 바운더리가 서버 컴포넌트에서도 사용될 수 있으므로 직렬화 할 수 없는 props를 가질 수 없다고 하는 것이다.
만약 ChildComponent를 import 하고 서버 컴포넌트 내에서 렌더링한다면, 함수를 props로 전달 할 수 있는 방법이 없다.
방법
그렇다면 어떻게 해야 이 에러를 없앨 수 있을까? 내가 알고 있는 것으로는 총 3가지 정도 방법이 있다.
방법-1
각자의 상황에 따라 다르겠지만 문제만 없다면 "use client"를 빼면 된다.
ChildComponent에서 "use client"를 추가하지 말고, 직접적으로 서버 컴포넌트에 사용되는 곳에서만 "use client"를 사용하면 된다.
방법-2
그런데 방법-1로 했을 때 만약 useState, useEffect와 같은 react에서 사용하는 훅을 사용하게 되면 문제가 된다.
이 때 만약 내가 예시와 같이 export만 해서 컴포넌트를 사용하고 있다면 export default로 컴포넌트를 사용해보자.
그러면 경고창이 사라지게 된다.
그 이유는 React, NextJS의 동작방식 그리고 Javascript에서는 모듈 시스템의 특성 때문이다.
만약 예시처럼 Named export를 하게 되면 여러 값을 개별적으로 보내게 된다. 이 경우 Next.js에서는 각 프로퍼티를 개별적으로 검사하게 되고, 이 과정에서 함수와 같이 직렬화할 수 없는 프로퍼티를 발견하고 오류를 발생시킨다.
그런데 Default export를 하게되면 하나의 주요 값을 내보내게 되고, Next.js는 전체 컴포넌트를 하나의 단위로 취급하며, 이를 직렬화 가능한 것으로 간주하게 된다.
방법-3
그러나 Named export를 사용하고 있는 나는 이런 이유로 하나의 컴포넌트만 Default export를 하고 싶지 않았다.
그래서 사용한 방법은 함수를 객체로 만드는 것이다.
함수는 JSON이 수용하지 않는 코드 블록이므로 나는 객체형태로 만들어줘야겠다고 생각했다.
그래서 아래 예시처럼 객체로 만들어서 넘겨줬더니 경고가 사라졌다.
//////////////////////////////////////////////// /////////////// 부모 컴포넌트 /////////////////////////////////////////////// "use client" import { useState } from 'react' import { ChildComponent } from './ChildComponent' export default function ParentComponent() { const [showDialog, setShowDialog] = useState(false) const handleAddMember = () => { setShowDialog(prev => !prev); } return ( <div> <ChildComponent showDialog={showDialog} handleAddMember={{ handleDialog: handleAddMember }} /> </div> ) } //////////////////////////////////////////////// /////////////// 자식 컴포넌트 /////////////////////////////////////////////// "use client" type ChildComponentProps { showDialog: boolean; handleShowDialog: { handleDialog: () => void }; } export function ChildComponent({showDialog, handleShowDialog}: ChildComponentProps) { return <button onClick={handleShowDialog.handleAddMember}>다이얼로그 열기</button> }
반응형'Front-end > Next.js' 카테고리의 다른 글
프론트엔드 개발자의 화면 렌더링의 변화와 의미 (1) 2024.11.01