본문 바로가기
개발 · IT/백엔드

Supabase로 로그인 · 회원가입(인증) 로직 만들기 — 실전 가이드

by 플라퉁 2025. 12. 9.
반응형

Supabase로 로그인 · 회원가입(인증) 로직 만들기 — 실전 가이드

supabase logo image

Supabase Auth를 이용해 이메일/비밀번호 기반 회원가입·로그인, 세션 쿠키, 토큰 검증, 비밀번호 재설정까지 안전하게 구현하는 방법을 정리했습니다. 클라이언트 → 서버 → DB 접근 흐름과 보안 권장사항을 포함합니다.

목차
  1. 핵심 흐름 요약
  2. 사전 준비
  3. 클라이언트 예제 (SignUp / SignIn)
  4. 서버(Express) 예제 — 토큰 검증 + HttpOnly 쿠키
  5. 회원가입 후 프로필 생성 / 트리거
  6. 비밀번호 재설정 · 이메일 확인
  7. 세션/토큰 갱신 정책
  8. RLS(행 수준 보안) 추천 패턴
  9. 보안 체크리스트 & 디버깅 팁

 

1. 핵심 흐름 요약

  • 클라이언트에서 이메일/비밀번호로 가입(signUp) 또는 로그인(signInWithPassword).
  • Supabase Auth가 access_token + refresh_token을 발급.
  • 서버는 클라이언트에서 전달받은 access_token을 검증하고 HttpOnly 쿠키로 저장(권장).
  • 보호된 API는 서버에서 토큰 검증 후 사용자 컨텍스트(req.user)로 처리.

 

2. 사전 준비

  • Supabase 프로젝트 생성 → Project URL, anon key, service_role key 확보
  • 필수 패키지: @supabase/supabase-js, (Express 사용 시) express, cookie-parser
  • 보안 원칙: service_role key는 절대 클라이언트 노출 금지 — 서버 전용.

 

3. 클라이언트 예제

브라우저/React에서 간단한 회원가입·로그인 예시

<script type="module">
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm'
const SUPABASE_URL = 'https://your-project.supabase.co'
const SUPABASE_ANON_KEY = 'public-anon-key'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

// 회원가입
async function signUp(email, password) {
  const { data, error } = await supabase.auth.signUp({ email, password });
  console.log({ data, error });
}

// 이메일+비밀번호 로그인
async function signIn(email, password) {
  const { data, error } = await supabase.auth.signInWithPassword({ email, password });
  console.log({ data, error }); // data.session.access_token 등 확인
}
</script>
      

권장: 클라이언트에서 로그인 후 토큰을 서버로 전달 → 서버가 쿠키로 저장

 

4. 서버(Express) 예제 — 토큰 검증 & HttpOnly 쿠키

클라이언트에서 받은 access_token을 서버가 검증하고 HttpOnly 쿠키로 저장하는 안전한 패턴

// server.js (요약)
require('dotenv').config();
const express = require('express');
const cookieParser = require('cookie-parser');
const { createClient } = require('@supabase/supabase-js');

const app = express();
app.use(express.json());
app.use(cookieParser());

const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;
const SUPABASE_SERVICE_ROLE = process.env.SUPABASE_SERVICE_ROLE;
const supabaseAdmin = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE);
const supabasePublic = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);

app.post('/api/auth/login', async (req, res) => {
  const { email, password } = req.body;
  const { data, error } = await supabasePublic.auth.signInWithPassword({ email, password });
  if (error) return res.status(400).json({ error: error.message });

  const accessToken = data.session.access_token;
  const refreshToken = data.session.refresh_token;

  // 토큰 검증
  const { data: userData, error: userErr } = await supabaseAdmin.auth.getUser(accessToken);
  if (userErr) return res.status(401).json({ error: userErr.message });

  // HttpOnly 쿠키로 저장
  res.cookie('sb_access_token', accessToken, { httpOnly:true, secure:process.env.NODE_ENV==='production', sameSite:'lax', maxAge:7*24*3600*1000 });
  res.cookie('sb_refresh_token', refreshToken, { httpOnly:true, secure:process.env.NODE_ENV==='production', sameSite:'lax', maxAge:30*24*3600*1000 });

  return res.json({ user: userData.user });
});
      

보안: service_role 키는 서버 전용으로만 사용. HTTPS 필수.

 

5. 회원가입 후 프로필 생성 / 자동화

가입 직후 프로필 레코드를 추가하려면:

  • 클라이언트에서 signUp 후 서버에 프로필 생성 요청
  • 또는 Supabase의 Auth Trigger / Edge Function으로 자동 생성
// 서버에서 service_role로 profiles 테이블 생성 예
await supabaseAdmin.from('profiles').insert([{ id: user.id, email: user.email, full_name }]);
      

 

6. 비밀번호 재설정 · 이메일 확인

  • signUp 옵션에서 redirectTo 설정으로 이메일 클릭 후 리디렉션 제어
  • 비밀번호 재설정: Supabase의 resetPasswordForEmail API 사용 → 프론트에서 새 비밀번호 제출
  • 문제: 메일이 안 오면 SMTP 설정(프로젝트 설정) 확인

 

7. 세션 / 토큰 갱신 정책

  • access_token 만료(예: 1시간) → refresh_token으로 갱신
  • 클라이언트는 onAuthStateChange로 상태 변화 감지 가능
  • 서버는 refresh_token을 이용해 새 세션을 발급하거나, 필요시 re-login 유도

 

8. RLS(행 수준 보안) 추천 패턴

Supabase RLS와 auth.uid()를 조합해 안전한 테이블 접근 제어를 구성하세요.

-- profiles 테이블 예시 정책
CREATE POLICY "Users can manage their profile."
  ON public.profiles
  USING ( auth.uid() = id );
      

주의: service_role 키로는 RLS를 우회할 수 있으니 서버 내부 로직에만 사용.

 

9. 보안 체크리스트 & 디버깅 팁

  • service_role key: 절대 클라이언트 노출 금지, 환경변수로만 관리
  • HTTPS: 로그인/토큰 전송 시 반드시 HTTPS
  • 쿠키 옵션: HttpOnly, Secure, SameSite 적용
  • 이메일 확인: 스팸/봇 가입 방지 위해 이메일 검증 활성화
  • 브루트포스 방지: 로그인 시도 제한·CAPTCHA 적용 고려
  • 디버깅: Supabase SDK의 dataerror를 모두 로깅
  • 시간 동기화: 토큰 검증 실패 시 서버 시간이 정확한지 확인

 

요약

  1. 클라이언트에서 signUp/signIn 처리 → 토큰 획득
  2. 서버에서 access_token 검증 → HttpOnly 쿠키로 저장(권장)
  3. RLS로 DB 접근 제한, service_role은 서버 전용으로만 사용
  4. HTTPS, 쿠키 보안, 이메일 확인 등 보안 설정 필수
반응형

댓글