Supabase로 서버 인증을 처리하다 보면 getSession과 getUser 중 뭘 써야 할지 헷갈려요. 결론부터 말하면 서버 코드에서 getSession을 쓰면 보안 취약점이 생길 수 있어요. 이 글에서는 두 함수의 차이와 getClaims까지 포함해서 언제 뭘 써야 하는지 완전히 정리합니다.
🤔 그래서 뭐가 다른 건데?
처음엔 단순히 “세션 가져오는 거 아니야?” 했는데, 실제로는 동작 방식이 완전히 달라요. 프론트엔드 입문자도 이해할 수 있게 비유를 들어볼게요.
🏨 호텔 키카드 비유
- getSession은 “키카드 자체를 믿는 것”이에요. 카드 생김새를 보고 “이건 우리 호텔 카드네”라고 판단해요. 근데 위조 카드일 수도 있잖아요?
- getUser는 “프론트 데스크에 카드 들고 가서 확인하는 것”이에요. 시간이 조금 걸리지만, 호텔 서버가 “이 카드 진짜 맞아요”라고 확인해줘요.
⚠️ getSession의 보안 문제
공식 Supabase 문서에는 이렇게 나와 있어요.
⛔ 절대 하지 마세요
“Never trust supabase.auth.getSession() inside server code such as middleware. It isn’t guaranteed to revalidate the Auth token.”
→ 서버 코드(특히 middleware)에서 getSession을 믿지 마세요. Auth 토큰 재검증이 보장되지 않아요.
왜 위험하냐면, getSession()은 쿠키나 로컬스토리지에서 세션 정보를 그냥 읽어와요. 이 데이터는 클라이언트 측에서 조작이 가능해요. 악의적인 사용자가 쿠키 값을 변조해서 다른 사람인 척 할 수 있는 취약점이 생기는 거예요.
✅ getUser가 안전한 이유
getUser()는 호출할 때마다 Supabase 인증 서버에 요청을 보내서 토큰을 검증해요. 내 코드에서 조작된 데이터를 믿는 게 아니라, 서버가 직접 “이 토큰 맞아요”라고 확인해주는 방식이에요.
// ✅ 서버 컴포넌트에서 올바른 사용법
import { createClient } from '@/utils/supabase/server'
import { redirect } from 'next/navigation'
export default async function PrivatePage() {
const supabase = await createClient()
// getUser()로 서버에서 직접 검증
const { data, error } = await supabase.auth.getUser()
if (error || !data?.user) {
redirect('/login') // 미인증 유저는 로그인 페이지로
}
return 안녕하세요, {data.user.email}님!
}
// ❌ 서버 코드에서 잘못된 사용법
const { data: { session } } = await supabase.auth.getSession()
// session이 있다고 해서 진짜 로그인된 유저가 아닐 수 있어요!
if (session) {
// 위험한 로직...
}
🆕 getClaims는 뭔가요?
getClaims()는 JWT 토큰 안의 클레임(claims) 정보를 반환해요. 토큰을 직접 디코딩해서 포함된 데이터를 가져오는 방식이에요.
💡 JWT Claims가 뭔가요?
JWT(JSON Web Token) 안에 담긴 데이터 조각들이에요. 예를 들어 유저 ID, 이메일, 역할(role), 만료 시간 같은 정보가 들어있어요. 이걸 “클레임”이라고 불러요.
공식 문서에 따르면 getClaims()는 대칭 키(symmetric secret)를 사용하는 경우 서버에 검증 요청을 보내서 토큰을 확인해요. 즉, getUser()처럼 안전하게 사용할 수 있어요.
// getClaims() 사용 예시
const { data, error } = await supabase.auth.getClaims()
if (data) {
const userId = data.claims.sub // 유저 ID
const email = data.claims.email // 이메일
const role = data.claims.user_metadata?.role // 커스텀 역할
}
📊 세 함수 한 눈에 비교
| 함수 | 검증 방식 | 속도 | 반환값 | 어디서 쓰나 |
|---|---|---|---|---|
getSession() | 로컬 데이터 신뢰 | ⚡ 빠름 | 세션 전체 (access/refresh token 포함) | 클라이언트 UI 상태 표시용 |
getUser() | 서버에 요청해서 검증 | 🐢 느림 (네트워크 요청) | 유저 객체 (이메일, ID 등) | 서버 인증 보호 필수 상황 |
getClaims() | 토큰 검증 (서버 포함) | 🚀 중간 | JWT 클레임 데이터 | 역할/권한 확인 등 경량 검증 |
🎯 언제 뭘 써야 하나요?
| 상황 | 추천 함수 | 이유 |
|---|---|---|
| 로그인 버튼 표시/숨김 (UI) | getSession() | 서버 요청 없이 빠르게 UI 상태만 확인 |
| 서버에서 페이지 접근 제어 | getUser() | 서버 검증 필수, 보안이 중요한 상황 |
| middleware에서 인증 체크 | getUser() | 공식 문서가 명시적으로 권장하는 방법 |
| 유저 역할(role) 확인 | getClaims() | JWT에서 빠르게 역할 정보 추출 가능 |
| 관리자 페이지 보호 | getUser() | 가장 확실한 서버 검증 |
🔥 내가 실제로 겪은 실수
처음에 서버 컴포넌트에서도 무심코 getSession()을 쓰고 있었어요.
// ❌ 내가 처음에 이렇게 썼던 코드
export default async function Dashboard() {
const supabase = await createClient()
const { data: { session } } = await supabase.auth.getSession()
if (!session) {
redirect('/login')
}
// session.user로 유저 정보 가져옴
return {session.user.email}
}
이게 당장은 동작해요. 근데 공식 문서가 “이렇게 하지 마라”고 명시적으로 경고하고 있어요. 서버 코드에서 getSession을 신뢰하면 토큰 재검증이 보장되지 않는다고요.
올바른 코드는 이거예요 👇
// ✅ 수정한 코드
export default async function Dashboard() {
const supabase = await createClient()
const { data, error } = await supabase.auth.getUser() // getUser로 변경!
if (error || !data?.user) {
redirect('/login')
}
return {data.user.email}
}
🛠️ getSession은 그럼 언제 써요?
완전히 쓸모없는 게 아니에요! 클라이언트 컴포넌트에서 UI 상태 관리에 적합해요.
'use client'
import { useEffect, useState } from 'react'
import { createClient } from '@/utils/supabase/client'
export default function NavBar() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const supabase = createClient()
useEffect(() => {
// 클라이언트에서 로그인 상태 확인 → getSession OK
supabase.auth.getSession().then(({ data: { session } }) => {
setIsLoggedIn(!!session)
})
// 인증 상태 변화 실시간 감지
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
setIsLoggedIn(!!session)
}
)
return () => subscription.unsubscribe()
}, [])
return (
)
}
💡 핵심 규칙
서버 코드 (Server Component, middleware, Route Handler) → 반드시 getUser()
클라이언트 코드 (Client Component, 브라우저) → getSession() 사용 가능
✅ 정리
- 🔴 서버에서 getSession() → 절대 금지! 토큰 재검증이 안 되고 보안 취약점이 생겨요
- 🟢 서버 인증은 getUser() — Supabase 서버에 직접 검증 요청을 보내서 안전해요
- 🟡 클라이언트 UI 상태는 getSession() — 빠르고 가볍게 로그인 여부 확인에 적합해요
- 🔵 역할/권한 확인은 getClaims() — JWT 클레임을 경량으로 검증할 때 써요
👉 다음 편에서는 이 모든 걸 연결하는 핵심 파일인 middleware.ts로 Supabase 인증 처리하는 방법을 실전 코드로 보여드릴게요!



