Supabase를 활용해 Google 또는 Kakao 소셜 로그인을 구현.
users 테이블)에 사용자 정보가 없을 경우 자동 생성,getUser() 사용const {
data: { user },
error,
} = await supabase.auth.getUser();
user = null/auth/auth-code-error)getUser()자체가 안먹힘)처음에 우연히 작동함: 같은 브라우저 세션에서 이전에 로그인된 세션이 살아있어
getUser()가 작동해서 성공한 줄 알았으나. 로그아웃 이후 다시 테스트 해보니 실패
getSession()으로 대체 시도const {
data: { session },
} = await supabase.auth.getSession();
session = null, session.user = undefined옳은 방법이였으나 서버컴포넌트라는 것을 망각: 여기서부터 삽질,,
-쿠키는 브라우저가 세션 저장을 끝내기 전까지는 생성되지 않기 때문.
-Supabase는 로그인 직후 세션을 브라우저에서 저장해야 하며, 서버는 이 쿠키를 받지 못함
exchangeCodeForSession() 직접 사용const code = new URLSearchParams(window.location.search).get("code");
await supabase.auth.exchangeCodeForSession(code);
400 Bad Request: both auth code and code_verifier should be non-empty
code_verifier는 로그인 시 자동 저장되므로, 직접 호출하면 실패함getSessionFromUrl({ storeSession: true }) 시도await supabase.auth.getSessionFromUrl({ storeSession: true });
getSessionFromUrl 없음), 또는 undefinedcreateBrowserClient() 사용 중이었음createClient()로 변경useEffect(async () => {
const { data: { session }, error } = await supabase.auth.getSession();
...
}, []);
이때도 코드 자체는 맞았지만, “비동기(async) 처리 위치”가 틀려서 또 삽질
✅ 최종 성공 코드 (getSession 기반, 클라이언트 컴포넌트 + useEffect 내부 async)
useEffect(() => {
const handleCallback = async () => {
const { data: { session }, error } = await supabase.auth.getSession();
if (error || !session?.user) {
router.replace("/auth/auth-code-error");
return;
}
const user = session.user;
const { data: existingUser, error: fetchError } = await supabase
.from("users")
.select("nickname")
.eq("id", user.id)
.maybeSingle();
if (fetchError) {
router.replace("/auth/auth-code-error");
return;
}
if (!existingUser) {
const { error: insertError } = await supabase.from("users").insert({
id: user.id,
nickname: "",
});
if (insertError) {
router.replace("/auth/auth-code-error");
return;
}
router.replace("/auth/nickname");
return;
}
router.replace(existingUser.nickname ? "/" : "/auth/nickname");
};
handleCallback();
}, [router, supabase]);
getSession()에서 세션이 정상적으로 읽힘users 테이블에 유저 존재 여부 판단 후 없다면 (user_id)자동 삽입/), 없으면 닉네임 등록(/auth/nickname)으로 리디렉션getUser() : 서버에서 세션 쿠키 없으면 실패할 수 있음 (불안정)
getSession() : 클라이언트에선 쿠키 기반으로 안정적으로 작동
getSessionFromUrl() : 로그인 직후 세션 저장용이지만 현재 구조에서는 불필요
Supabase 클라이언트 : @supabase/supabase-js의 createClient() 사용해야 함
클라이언트 처리 : useEffect 안에서 비동기 함수 실행해야 함 (async component X)
이 고비를 넘기며 단순 로그인 구현이 아니라 Supabase의 인증 구조, 서버/클라이언트 세션 처리 방식, OAuth 플로우까지 깊이 있게 체득할 수 있는 과정이었습니다.