[Next.js] Recoil 적용하기.

JJ·2024년 1월 16일
4

에러 핸들링

목록 보기
1/1
post-thumbnail

Next.js를 사용하여 과제를 진행하던 중, 만났던 에러를 기억하기 위해 이 글을 작성합니다.

RecoilRoot란?

프로젝트에서 recoil을 사용하기 위해선, RecoilRoot가 필요하다. Recoil은 React 내부의 상태를 관리하기 위한 라이브러리로, 여기서 상태를 관리한다는 것은 컴포넌트 간에 공유되는 상태를 관리한다고 보는 것이 더 정확하겠다. RecoilRoot는 Recoil로 관리되는 상태의 공급자(provider) 역할을 하며, 때문에 Recoil로 관리되는 상태를 사용하는 컴포넌트는 RecoilRoot의 하위에 존재해야 한다.

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  );
}

공식문서에 나와있는 RecoilRoot의 용법과 같이 일반적으로, 최상위 컴포넌트를 RecoilRoot로 감싸줌으로써, 프로젝트에 Recoil을 적용할 수 있다.

export default function RootLayout({
	children,
}: {
	children: React.ReactNode;
}) {
	return (
		<html lang="en">
			<body className={inter.className}>
				<RecoilRoot>{children}</RecoilRoot>
			</body>
		</html>
	);
}

하지만, Next.js의 경우, 이와 같은 형태로 최상위에 존재하는 layout.tsx에 RecoilRoot를 위와 같은 형태로 작성하게 되면, 다음과 같은 에러를 만날 수 있다.

createContext 는 오직 client component에서만 사용가능하므로, 파일의 최상단에 'use client'를 작성하여 사용하라는 에러가 표시된다.

여기서, createContext는 문자 그대로 Context를 생성한다는 의미로 추론된다. 즉, RecoilRoot를 사용하면, 무엇인지 모를 Context라는 것이 생성되는데, 여기서 Context(컨텍스트)는 자바스크립트의 실행 컨텍스트와 같은 역할을 한다. 실행 컨텍스트는 자바스크립트의 소스 코드를 실행하기 위한 정보를 담고 있는 공간을 말한다. 즉, RecoilRoot는 Context를 생성하며, 이 Context는 Recoil을 사용하기 위한 정보가 담긴 공간이라고 볼 수 있다.

사용법

다시 본론으로 돌아와서 결국 'use client'를 파일의 최상단에 위치시키면, 해결되는데 이렇게 하게 되면, 사실 Next.js를 사용하는 의미가 퇴색된다. 왜냐하면, 우리는 RecoilRoot로 최상위 컴포넌트를 감싸줌으로써, Recoil을 적용하려 하기 때문에, 최상위 컴포넌트가 작성된 layout.tsx에 'use client'를 작성하게 되면, 사실상 모든 하위의 컴포넌트가 클라이언트 컴포넌트가 되기 때문이다.

따라서, RecoilRoot를 별도의 클라이언트 사이드 컴포넌트로 만들어준 후, 클라이언트 사이드 컴포넌트를 서버 사이드 컴포넌트인 layout.tsx에 사용함으로써 해결할 수 있다.

RecoilWrapper.tsx

"use client";
import { RecoilRoot } from "recoil";

interface RecoilRootWrapperProps {
	children: React.ReactNode;
}

export default function RecoilRootWrapper({
	children,
}: RecoilRootWrapperProps) {
	return <RecoilRoot>{children}</RecoilRoot>;
}

layout.tsx

import RecoilRootWrapper from "@/components/RecoilWrapper";

export default function RootLayout({
	children,
}: {
	children: React.ReactNode;
}) {
	return (
		<html lang="en">
			<body className={inter.className}>
				<RecoilRootWrapper>{children}</RecoilRootWrapper>
			</body>
		</html>
	);
}
profile
한줄 한줄

1개의 댓글

comment-user-thumbnail
2024년 5월 4일

안녕하세요! 잘봤습니다.
질문이 있는데요, 이방식도 여전히 RecoilWrapper 하위에서 렌더링 되는것같은데 RecoilWrapper이 client 컴포넌트라서, 그 이하 컴포넌트들도 암묵적으로 client 컴포넌트가 되는것같아요.
Approuter 기준으로, 자식 컴포넌트에서 api콜을 nextjs해보니 client컴포넌트라서 안된다고 하네요.

답글 달기