Next + React Query で SNS サービスを作成する
React19 & Next15 & ReactQuery5 & Next Auth5 & MSW2 & socket.io4 & zustand 스택을 사용하여 Twitter(X.com)와 유사한 SNS 서비스를 만들어보겠습니다. 마지막으로 검색 엔진 최적화를 위한 SSR까지!
受講生 3,399名
難易度 中級以上
受講期間 無制限

next-auth サーバー エラーの受け取り & セッションにカスタム データの挿入 & 権限に応じてページにアクセス
こんにちは。ゼロ秒です。
多くの人がnext-authで一度苦しんでいたようです。まだベータなので、これまで少し不安定なことが多かったのですが、もう少し捕まっていくようです。そこで講義ではまだ機能がないので扱っていないが、追加されたものの3つを紹介します。
signIn時にフロントからサーバーエラーを受け取る
ログイン時にサーバーはさまざまなエラーを与えることができます。たとえば、1. ユーザーがいない場合 2. パスワードが間違っている場合 3. その他など。ところで、このようなものをフロントに渡さなければ状況に合ったメッセージを表示するのではないでしょうか。しかし、この基本的な機能がこれまでにないが今や追加されました。
auth.tsで次のように修正します。
import NextAuth, {CredentialsSignin} from "next-auth" ... providers: [ CredentialsProvider({ async authorize(credentials) { const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, { ... }) // 여기 주목!!! 서버에서 에러가 발생할 때 그 에러 내용이 서버에 담겨 있을 겁니다. console.log(authResponse.status, authResponse.statusText) if (!authResponse.ok) { const credentialsSignin = new CredentialsSignin(); if (authResponse.status === 404) { credentialsSignin.code = 'no_user'; } else if (authResponse.status === 401) { credentialsSignin.code = 'wrong_password'; } throw credentialsSignin; } const user = await authResponse.json() console.log('user', user); // id, name, image, email만 허용 return { id: user.id, name: user.nickname, image: user.image, } }, }), ]これで return null しないでCredentialsSignin エラーをスローします。エラーの属性であるコードにエラーメッセージを書き込むだけです。
const response = await signIn("credentials", { username: id, password, redirect: false })ログインに失敗した場合、responseにエラーコードとメッセージが含まれます。ただし、response.okはまだtrueになります(redirectがfalseの場合は無条件trueです)
session オブジェクトにカスタムデータを入れる
公式文書によると、現在のauthorize関数のreturnにはid、email、name、imageのみを入れることができます。これだけでも制限が大きいのにidは入れられるとしても入れるとuseSession()のデータでは消えてしまいます。
それでは、idはどこに行きましたか?
他のカスタムデータはどのように入れますか?
次のようにsessionを直接作成できます。
auth.ts
export const { handlers: { GET, POST }, auth, signIn, } = NextAuth({ pages: { signIn: '/i/flow/login', newUser: '/i/flow/signup', }, callbacks: { async session({ session, token }) { console.log('session callback', session, token); const authResponse = await fetch(내정보를 가져오는 서버 API); const userData = await authResponse.json(); (session as any).userData = userData; return session; } }, providers: [ CredentialsProvider({ async authorize(credentials) { ... // id, name, image, email만 허용 return { id: user.id, name: user.nickname, image: user.image, }このようにcallbacksプロパティにasync sessionメソッドを作成します。ここで私の情報をサーバーからもう一度呼び出すだけです。そしてその応答値を session オブジェクトに入れて返すのです。
さっき消えたidはこのメソッドのtoken.subに入っています。
ここで return する値が auth() や useSession() のデータになります。 authorizeでreturnした値が最終値ではなく、ここでもう一度修正されることです。
これにより、 auth() や useSession() で user.userData を確認できます。
権限に基づいてページにアクセスする
これで、sessionオブジェクトにカスタムデータを配置できるようになりましたので、権限に応じてページにアクセスできます。 callbacks.session async 関数で role のようなものをサーバーから受け入れて入れればいいです。 session.userData.roleにadminまたはnormal権限があるとしましょう。そして、 roleがadminの場合、アドミンページ(/admin)に接続可能でnormalであってはならないとしましょう。これをどのように実装できますか?
現在、middleware.tsは次のようになっていますが、これはconfigに少ないルートでのみ実行されるため、configをすべて削除します。
import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (!session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } } // See "Matching Paths" below to learn more export const config = { matcher: ['/compose/tweet', '/home', '/explore', '/messages', '/search'], }そしてmiddleware関数の中に手動で書くだけです。 request.nextUrl.pathnameに現在アクセスしたいパス名が含まれています。
import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (['/compose/tweet', '/home', '/explore', '/messages', '/search'].includes(request.nextUrl.pathname) && !session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } }このように修正した後、私たちは auth() の session から session.userData.role で権限にアクセスできるので、次のように検査後にリダイレクトするだけです。
import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (['/compose/tweet', '/home', '/explore', '/messages', '/search'].includes(request.nextUrl.pathname) && !session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } if (request.nextUrl.pathname.startsWith('/admin') && session?.userData.role !== 'admin') { return NextResponse.redirect('http://localhost:3000/권한없음_알리는_모달_주소'); } }このように不足しているnext-authを何らかの形で活用してみることができます。
次回も更新されることがありましたらお知らせします。ありがとうございます!




