❌ Unhandled Runtime Error
Error: Objects are not valid as a React child (found: object with keys {redirect}). If you meant to render a collection of children, use an array instead.
오류가 발생한 코드입니다.
// app/api/auth/signin/page.tsx
import SocialLoginBtn from '@/app/components/SocialLoginBtn';
import { authOptions } from '../[...nextauth]/route';
import { getServerSession } from 'next-auth';
import { getProviders } from 'next-auth/react';
export default async function SignInPage() {
const session = await getServerSession(authOptions);
if (session) {
return { redirect: { destination: '/' } };
}
const providers = (await getProviders()) ?? {};
return (
<section>
<div>
<h2>Sign-in</h2>
<p>지금 바로 로그인하세요!</p>
</div>
<SocialLoginBtn providers={providers} />
</section>
);
}
// src/app/components/SocialLoginBtn.tsx
'use client';
import { ClientSafeProvider, signIn } from 'next-auth/react';
type Props = {
providers: Record<string, ClientSafeProvider>;
};
export default function SocialLoginBtn({ providers }: Props) {
return (
<div>
{Object.values(providers).map((provider) => (
<button
key={provider.name}
onClick={() => signIn(provider.id)}
>
{provider.name}로 로그인
</button>
))}
</div>
);
}
next-auth sign-up 페이지를 custom page로 바꾸기 위해 코드를 리펙토링하던 중 위 사진과 같은 오류가 발생했습니다. next-auth의 signIn 함수가 실행되면 url이 callbackUrl 파라미터를 전달받은 후 Objects are not valid as a React child (found: object with keys {redirect})
런타임 오류가 발생합니다.
하지만, url을 임의로 메인 페이지인 localhost:3000
경로로 이동하면 로그인이 잘 되어있는 것을 확인할 수 있었습니다.
오류메시지는 Objects는 리액트 컴포넌트에 직접적으로 렌더링될 수 없다고 합니다. 혹시 providers
를 map
함수로 처리하는 과정에서 렌더링시 생긴 문제는 아닐까 생각했습니다.
providers의 생김새를 보려고 콘솔을 한 번 찍어봤습니다.
// src/app/components/SocialLoginBtn.tsx
'use client';
import { ClientSafeProvider, signIn } from 'next-auth/react';
type Props = {
providers: Record<string, ClientSafeProvider>;
};
export default function SocialLoginBtn({ providers }: Props) {
console.log(providers);
}
providers
의 구조는 github, kakao, naver 세 가지의 provider을 가진 객체인 것을 확인했습니다.
map
함수의 대상인 Object.values(providers)
가 배열이 잘 나오는지 콘솔에 또 찍어봤습니다.
// src/app/components/SocialLoginBtn.tsx
'use client';
import { ClientSafeProvider, signIn } from 'next-auth/react';
type Props = {
providers: Record<string, ClientSafeProvider>;
};
export default function SocialLoginBtn({ providers }: Props) {
console.log(Object.values(providers));
}
배열도 잘 나오는 것을 확인했습니다. providers
를 map
함수로 처리하는 과정에서 렌더링할 때 생기는 문제가 아니라는 것을 확인했습니다.
앞서 문제 상황에서 설명했듯이 로그인은 잘 이루어지고 있었습니다.
(found: object with keys {redirect})
오류 메시지에서 볼 수 있듯이
로그인이 성공한 후 페이지가 redirect 경로가 설정되어있지 않아서 생기는 문제가 아닐까 생각해보았습니다.
현재 page가 api/auth/signin경로에서 쿼리 스트링으로 callbackUrl 파라미터를 받고 있기 때문에 Next.js 공식문서에서 해당 쿼리 스트링의 값을 가져오는 방법을 찾았습니다.
useSearchParams
은 React Hooks이기 때문에 클라이언트 컴포넌트에서만 현재 url의 쿼리스트링 값을 가져올 수 있습니다.
// src/app/components/SocialLoginBtn.tsx
'use client';
import { ClientSafeProvider, signIn } from 'next-auth/react';
type Props = {
providers: Record<string, ClientSafeProvider>;
};
export default function SocialLoginBtn({ providers }: Props) {
const searchParams = useSearchParams();
const callbackUrl = searchParams.get('callbackUrl') as string;
return (
<div>
{Object.values(providers).map((provider) => (
<button
key={provider.name}
onClick={() => signIn(provider.id,
{ callbackUrl } // 두 번째 인자로 전달
)}>
{provider.name}로 로그인
</button>
))}
</div>
);
}
callbackUrl의 값을 next-auth의 signIn 두 번째 인자로 optional하게 객체로 전달해주었습니다.
문제가 해결되었습니다!
Objects are not valid as a React child (found: object with keys {redirect})
오류에 대한 글은 많았지만, next-auth 로그인 구현하다가 발생한 문제가 아니라서 대부분 객체를 불러오는 방법을 바꾸는 등으로 문제를 해결하는 것을 볼 수 있었습니다.
오류를 해결을 위해서는 오류 메시지 한 단어에 꽂히지 말고 다방면으로 시도하고, 그 과정에서 공식문서와 많이 친해져야겠다고 깨달았습니다.