[Next.js@14] next-auth cookie

나라·2023년 12월 15일
1

Trouble Shooting

목록 보기
5/14

개요

  • 로그인 시 json 형태 응답값으로 전달되는 accessToken 값을 저장해 앞으로 모든 API 요청 헤더에 해당 토큰 값을 포함시켜야 함 (axiosInterceptor)

문제 및 해결

  • next-auth에서는 session에 유저 정보를 저장하여 전역에서 쓸 수 있었는데, 다만 사용 가능한 key 값이 너무 한정적이었다 (name, email, image)
  • next-auth.d.ts 에서 세션 인터페이스를 확장하면 가능하다기에 시도해보았지만 key값은 추가되어도 응답값이 value에 정상적으로 등록이 되지 않았다 왤까도대체
  • 일단 로그인 로직 안에서 성공 시 해당 token 값을 어딘가에는 저장해야하는데
  • Next.js 공식문서에서 소개하는 cookie 사용은 오직 서버 컴포넌트 내부에서만 사용 가능하다
    import { cookies } from 'next/headers'

auth.ts는 서버에서 처리가 이루어지고 있으니, 여기서는 next/headers에서 제공하는 cookies를 사용해야 오류가 안난다

  • document.cookie
  • import { Cookies } from 'react-cookie'
    이런거는 사용안됨

반대로 클라이언트 컴포넌트에서는

  • import { cookies } from 'next/headers'
    넥스트@14 공식 cookies 쓰면 에러
    이 때는 위의 react-cookie나 document 에서 쿠키를 설정해줘야 에러가 안난다

즉 쿠키 사용 시 서버 컴포넌트와 클라이언트 컴포넌트 각각 다른 것을 사용해야 하고 전역으로 상태 재사용할 때도 서버 컴포넌트와 클라이언트 컴포넌트를 분리해서 셋팅을 해줘야 한다

일단 로그인 성공 시 서버 측에서 전달한 token 값을 쿠키에 저장하고(서버)

import NextAuth from 'next-auth'
import CredentialsProvider from 'next-auth/providers/credentials'
import KakaoProvider from 'next-auth/providers/kakao'
import axios from 'axios'
import { cookies } from 'next/headers'
import cookie from 'cookie'
export const {
  handlers: { GET, POST },
  auth,
  signIn,
} = NextAuth({
  pages: {
    signIn: '/login',
    newUser: '/createAccount',
  },
  secret: process.env.NEXTAUTH_SECRET,
  providers: [
    CredentialsProvider({
      async authorize(credentials, req) {
        console.log(credentials)
        const authResponse = await axios.post(
          `${process.env.NEXT_PUBLIC_BASE_URL}/member/login`,
          {
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              email: credentials.email,
              password: credentials?.password,
            }),
          },
        )
        if (authResponse.headers['set-cookie']) {
          let setCookie = authResponse.headers['set-cookie'][0] // token값을 받아와
          if (setCookie) {
            const parsed = cookie.parse(setCookie)
            cookies().set('connect.sid', parsed['connect.sid'], parsed)  //브라우저 쿠키에 저장
          }
        }
			
        if (authResponse.status === 401) {
          throw new Error('로그인 실패')
        } else {
          const { user } = await authResponse.data
          return {
            id: user.id, // 임시
            email: user.email,
            name: user.name,
            image: user.image,
            ...user,
          }
        }
      },
    }),
	// 중략

로그인 성공 시 바로 보여지는 레이아웃 내 컴포넌트에서
클라이언트 측에서 사용할 유저 데이터 저장 로직 구현

'use client'

// 중략

  const { data, status } = useSession() // 유저 세션 정보(name, email, image)
  const accessToken = getCookie('connect.sid') 
  // token 값은 별도로 로그인 과정에서 브라우저에 저장한 쿠키에서 가져온다
  
  useEffect(() => {
    LocalStorage.setAccessToken(accessToken)
    axiosInstance.defaults.headers.common.Authorization = `Bearer ${accessToken}`
    // axiosInterceptor header에 토큰 값 포함
  }, [])
  useEffect(() => {
    setUser({
      id: '0',
      name: data?.user?.name!,
      email: data?.user?.email!,
      profileImage: data?.user?.image!,
      accessToken: accessToken,
    })
  }, [data])

며칠동안 next-auth 를 아예 제외하고 처음부터 다시 로직을 짜야할까 정말 많이 고민했었는데,
next-auth에서 자체적으로 제공하는 세션 토큰은 오로지 프론트 서버(로그인) 전용,
백엔드에서 전달하는 토큰은 백엔드 서버 전용으로 아예 분리해서 생각하니까 한결 로직이 간단해졌다

물론 처음에는 받아들이기 힘들었지만 (내 머리로는 동시에 두 인증토큰을 관리한다는 게 굉장히 잘못되가는 느낌이었다) (그래서 계속 초반에 auth session에 백엔드 쪽 토큰값을 넣어서 같이 관리하려고 함)

profile
FE Dev🔥🚀

0개의 댓글