앞 4-1단계에서 이어집니다.
이 단계부터는, 아직 경험이 없기 때문에. 많은 시행착오가 있을 예정.. 차근 차근 해보면 되겠지 ?!
mini project - 영화 페이지 만들기(4-1단계) - 백엔드 연결 전 input창 구성하기 로그인 / 회원가입 구
부캠에서 제시해준 구현단계 네번째 최종 구현 단계는 다음과 같습니다. Supabase(백엔드)를 연결하기 전에, 우선 프론트만으로 로직을 구성해봅니다.1. 회원가입/로그인 페이지 구현하기기존 navBa
creamymood.tistory.com

부캠에서 제시해준 구현단계 네번째 최종 구현 단계는 다음과 같습니다.

1. 기본 이메일 / 비밀번호 기반의 로그인 / 회원가입 과정 (Supabase 백엔드 이용 하는 방식)
2. 소셜 로그인 / 회원가입(카카오, 구글, 네이버 등 소셜 연동)
이렇게 두가지 방법으로써 로그인과 회원가입을 구현해봅니다.
해당 게시글에서는 1. supabase 서버에 관련되는 방식을 다룹니다.
1. 기본 로그인/회원가입 = Supabase Auth 시스템에 저장하는 과정
Supabase에는 "Authentication" 기능이 따로 있고,
지금 너가 하고 있는 이메일/비밀번호 기반 로그인/회원가입은 Supabase Auth에 유저 데이터를 저장하는 거야.
Supabase에서 어떤 데이터가 저장되냐면?
- Supabase 프로젝트의 "Auth" > "Users" 탭에 저장돼.
- 여기에 회원가입한 유저들의 정보가 들어가.
- 이메일
- 등록일
- 메타데이터 (userName 같은 추가 정보)
- 로그인 이력 등
예시로 설명
회원가입 시:
signUp({
email: 'jane@email.com',
password: 'secure123',
options: {
data: { userName: 'Jane' }
}
})
이러면 Supabase의 Auth 영역에
- 이메일: jane@email.com
- userName: 'Jane' (metadata로 저장)
- 비밀번호: 암호화된 상태로 저장됨
이렇게 저장돼.
데이터베이스(Table)에 저장되는 건 아님
Supabase에는 보통 users라는 테이블이 있을 수도 있는데,
Auth의 유저 정보는 그 테이블에 자동 저장되진 않아!
필요하면 "Trigger"를 써서, 회원가입 시 자동으로 테이블에 정보도 같이 넣게 만들 수 있어.
하지만 기본은 Auth 시스템 내에만 유저 정보가 저장되는 구조야.
정리하면
항목 저장 위치 설명이메일 / 비밀번호 | Supabase Auth | 회원 인증용 |
userName 같은 추가 정보 | Supabase Auth (metadata) | 옵션으로 넣음 |
사용자 커스텀 데이터 (ex: 프로필) | 너가 만든 Supabase Table | 원하면 따로 저장해야 함 |
궁금하면 Supabase 대시보드의 "Auth > Users" 메뉴 들어가서
실제로 등록된 유저 목록을 한번 보는 것도 추천해! ✨
1. 기본 이메일 / 비밀번호 기반 회원가입 / 로그인 구현

그래도 가이드가 잘 안내 되어, 하나씩 차근 차근 따라 해봅시다.



🪄 Supabase 서버 이메일 기반 회원가입 로그인 구현단계
1. Supabase SDK를 설치합니다.
npm install @supabase/supabase-js
2. .env 파일을 생성하여 VITE_SUPABASE_API_KEY 와VITE_SUPABASE_PROJECT_URL 라는 변수를 선언합니다.
3. 각 변수에 알맞은 값을 Supabase 사이트 내에서 탐색하여 지정합니다.
(.env 파일을 사용하여 Supabase의 Project URL과 API Key를 관리)
//API Key → VITE_SUPABASE_API_KEY
//Project URL → VITE_SUPABASE_PROJECT_URL
.env 환경 변수에 등록 후, 사용하는 방법은 ? ↓
settings 파트에, Data API라고 하는 곳을 참고 하면 됩니다.


✨꼭 체크 해주세요 : 이메일 활성화 되어 있는지 ↓

4. 주어진 파일의 useSupabaseAuth 함수를 활용하여 회원가입과 로그인을 구현합니다.
* 아래 파일을 프로젝트에 포함하고, useSupabaseAuth() 에서 불러온 함수를 활용하여 회원가입, 로그인, 로그아웃 기능을 UI에 적용 합니다.
파일 : Supabase.zip
4-1 Context 설정 구현 단계 :
main.js에서 제공받은 Supabase.zip 파일 내 SupabaseProvider를 import합니다.
main.js에 작성되어 있는 <App/> 컴포넌트를 감싸줍니다.
예시 :
// Context를 쓴다면... <SupabaseProvider> <App /> // 하위 컴포넌트 어디서든 supabase, session 접근 가능! </SupabaseProvider>
이걸 하는 이유 .. ? ↓
SupabaseProvider + Context의 역할
SupabaseProvider는 내부적으로 React Context를 사용해서
- supabase 인스턴스
- 현재 로그인한 사용자 정보 (session, user)
이런 중요한 정보를 앱 전체에서 쉽게 꺼내 쓸 수 있게 만들어줘.
개념 | 이유 |
Context 사용 | 전역 상태 관리 (예: 로그인 정보) |
SupabaseProvider | 앱 전체에서 supabase 인스턴스와 로그인 상태 쉽게 사용 |
useSupabaseAuth | Context로부터 로그인 상태 등 필요한 값들을 꺼내쓰는 hook |


언제나 도움주시는 비스킷 동기님 감사합니다 . .🤍
4-2 회원가입(signUp) 구현 단계
❶ useSupabaseAuth 함수를 import 합니다.
❷ useSupabaseAuth()에서 signUp 함수를 꺼내 사용합니다.
❸ 전달인자로 email , password, userName을 전달하여 사용합니다.
❹ 사용자 데이터는 회원가입 시 입력받은 값을 입력합니다.(email, password , userName)
❶ useSupabaseAuth 함수를 import 합니다.
useSupabaseAuth 함수는 커스텀 훅으로써 코드는 이렇게 생겼다.
여러 인증 관련 함수들을 하나로 모아주는 역할을 한다.
export const useSupabaseAuth = () => {
const { getUserInfo, logout } = useAuth();
const { login, signUp } = useEmailAuth();
const { loginWithGoogle, loginWithKakao } = useOAuth();
return {
login,
signUp,
getUserInfo,
logout,
loginWithKakao,
loginWithGoogle,
};
};
함수형 컴포넌트나 다른 훅에서 이 훅을 불러서 인증 기능들을 사용할 수 있게 해줍니다.
❷ useSupabaseAuth()에서 signUp 함수를 꺼내 사용합니다.
❸ 전달인자로 email , password, userName을 전달하여 사용합니다.
❹ 사용자 데이터는 회원가입 시 입력받은 값을 입력합니다.(email, password , userName)
우선 공홈 설명은 다음과 같다.
이건 이렇다~ 하는 가이드이니 참고만 해준다.

우리는 위에 import 해온 useSupabaseAuth여기서 signUp 꺼내서 사용할 것이기 때문에..
const SignUpForm = () => {
const { signUp } = useSupabaseAuth() // context에서 signUp 함수 꺼냄
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [userName, setUserName] = useState('')
const handleSubmit = async (e) => {
e.preventDefault()
const { data, error } = await signUp({
email,
password,
options: {
data: {
userName, // user metadata로 저장됨
},
},
})
if (error) {
console.error('회원가입 에러:', error.message)
} else {
console.log('회원가입 성공:', data)
}
}
이건 GPT가 짜준 코드로, 이걸 참고해서
// 이 부분과
const { signUp } = useSupabaseAuth() // context에서 signUp 함수 꺼냄
//이 부분을
const handleSubmit = async (e) => {
e.preventDefault()
const { data, error } = await signUp({
email,
password,
options: {
data: {
userName, // user metadata로 저장됨
},
},
})
if (error) {
console.error('회원가입 에러:', error.message)
} else {
console.log('회원가입 성공:', data)
}
}
이 두가지 부분을 활용해서, 기존에 백엔드 없이 구현해둔 곳에
비동기로써 이렇게 몇가지 수정을 거쳐서 코드를 구현해주면
아래가 최종코드 입니다.
const handleJoin = async () => {
// if (!emailError && !passwordError && !nameError && email && password && name && confirmPassword && !confirmPasswordError) {
// alert("회원가입 성공, 로그인 창으로 넘어갑니다.");
// navigate("/login");
// }
const { data, error } = await signUp({
email,
password,
options: {
data: {
name, // user metadata로 저장됨
},
},
})
if (error) {
alert('회원가입 에러:', error.message)
} else {
console.log("회원가입 성공:", data);
alert("회원가입 성공! 로그인 창으로 이동합니다.");
navigate("/login");
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!emailError && !passwordError && !nameError && email && password && name && confirmPassword && !confirmPasswordError) {
await handleJoin();
} else {
alert("입력한 값을 다시 확인해주세요.");
}
};
*** 회원가입 시도 해봤을 때, 성공적으로 됩니다!
* 주의 : Supabase에서 이메일 포맷 검사를 더 엄격하게 하거나 도메인 제한이 있을 수 있음. 해당 이유 때문에
실제로 있는 회사의 이메일로 해야 할 것..
*** 회원가입을 시도해봤을 때, 잘 되고 서버에서는 아래 처럼 확인 할 수 있습니다.
→ 유저가 저장됐는지 확인 해보려면?
- Supabase 웹 콘솔 접속
https://app.supabase.com - 프로젝트 클릭 → 왼쪽 메뉴에서 Authentication > Users 클릭
→ 방금 가입한 유저가 리스트에 보이면 성공적으로 회원 가입된 것이에요!

* 참고로 이메일로 가입하면, 이메일 인증 해야 합니다!
그래야, 로그인 시도 시, 에러가 뜨지 않습니다
에러 처리 파트는 공식문서 설명에 없었는데 .. ? ↓
1. 에러 처리 코드가 꼭 필요한가?
- 꼭 필요하다!
supabase.auth.signUp 같은 함수는 비동기적으로 서버와 통신을 해.
그 과정에서 네트워크 문제, 이미 가입된 이메일, 비밀번호 조건 미달 등 여러 이유로 에러가 발생할 수 있어.
에러를 확인하지 않으면, 사용자가 무슨 일이 일어났는지 전혀 알 수 없고, 프로그램도 문제를 제대로 처리하지 못해. - 예를 들어, 에러를 처리 안 하면:
- 사용자 화면에 아무 메시지도 안 뜰 수 있고,
- 내부적으로 문제가 있어도 그냥 넘어갈 수 있어.
- 그 결과 앱이 이상하게 작동할 수도 있고, 디버깅도 어려워져.
2. 왜 공식 문서엔 에러 처리가 없는가?
- 공식 문서 예제는 핵심 기능만 보여주기 위해 간단하게 작성된 경우가 많아.
- 보통 실제 개발 시에는 에러 처리를 꼭 해야 하는데, 문서에선 그 부분을 생략해서 빠르게 개념 전달하는 목적이 강해.
3. 에러 처리를 어떻게 생각해내야 하는가?
- 비동기 함수 사용 시 기본적인 습관!
- await로 API 호출 후에는 항상 error나 catch 구문으로 예외를 처리해야 한다고 생각해.
- supabase 함수들이 대부분 { data, error } 형태로 리턴하니, error가 있는지 확인하는 것이 표준이다.
- 에러 처리 생각하는 법
- 네트워크 호출은 실패할 가능성이 항상 있다.
- 사용자 입력이 잘못됐을 수도 있다.
- 서버 상태가 안 좋을 수도 있다.
→ 따라서, 항상 결과를 확인하고 적절히 대응해야 한다는 생각을 갖는 게 중요해.
- 실제 개발자들은 보통:
const { data, error } = await someAsyncFunction()
if (error) {
// 에러 처리 (로그 출력, 사용자에게 알림, 재시도 등)
} else {
// 정상 처리
}
이런 구조가 기본 패턴이야.
와.. 어느정도 이해 했다. 대견합니다. doing amazing !
4-3 로그인(login) 구현 단계
❶ useSupabaseAuth 함수를 import 합니다.
❷ useSupabaseAuth()에서 login 함수를 꺼내 사용합니다.
❸ 전달인자로 email , password을 전달하여 사용합니다.
❹ 로그인 후에는 메인페이지로 이동해야 합니다.
회원가입이랑 같은 맥락으로 이렇게 작성해주면 됩니다
간단합니다~
const handleLogin = async () => {
// if (email === "test@test.com" && password === "123456") {
// alert("로그인 성공");
// navigate("/");
// //로그인 한 것 상태 관리
// setIsLoggedIn(true)
// } else {
// alert("아이디 또는 비밀번호가 잘못되었습니다.");
// }
const { data, error } = await login({
email,
password,
options: {
data: {
},
},
})
if (error) {
alert('로그인 에러:', error.message)
} else {
console.log("로그인 성공:", data);
alert("로그인 성공!");
setIsLoggedIn(true)
navigate("/");
set
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!emailError && !passwordError && email && password) {
await handleLogin();
} else {
alert("입력한 값을 다시 확인해주세요.");
}
};


로그인까지 성공 된 것을 보실 수 있습니다!
* 로그인이 안된다면? -> 이메일 인증 했는지 확인 해보세요. 또는 콘솔창을 열어 에러메세지를 확인 해보세요
하지만.. 로그인 후, 새로 고침 해보면 로그인이 풀립니다.
* 상태를 프론트로써 (?이유는 정확히 모름 공부 필요) 관리하는 isLoggedIn으로 쓰고 있어서.
> 이걸 이제.. 추가적으로 해보면 좋을 단계 중 상태를 서버 인증 기반으로 바꾸는 것. (이건 추후에 하고)
> 지금은 구현 5단계인 로컬스토리지에 우선 저장해보는 걸로 공부해봅니다.
와.. 조금은 쉬웠다. awesome... !!!
4-4 로그아웃(logout) 구현 단계
❶ useSupabaseAuth 함수를 import 합니다.
❷ useSupabaseAuth()에서 logout 함수를 꺼내 사용합니다.
❸ 함수를 호출하여 전달인자 없이 바로 사용 합니다.
❹ 로그인 후에는 메인페이지로 이동해야 합니다.
const handleLogout = async () => {
// setIsLoggedIn(false); // 로그아웃 시 상태 변경
// alert("로그아웃되었습니다.");
const { data, error } = await logout()
if (error) {
alert('로그아웃 에러:', error.message)
} else {
console.log("로그아웃 성공:", data);
alert("로그아웃 성공!");
setIsLoggedIn(false)
navigate("/");
}
};

역시 간단합니다!
4-5 App.jsx 에 useEffect를 활용하여, 마운트시 getUserInfo() 함수 실행
❶ useSupabaseAuth 함수를 import 합니다.
❷ useSupabaseAuth() 에서 getUserInfo 함수를 꺼내 사용합니다.
❸ getUserInfo() 함수를 실행하면 LocalStorage에 유저 정보가 저장됩니다.
마운트될때, 정보가(로그인 되어) 있다면 정보를 받아오는데,
그 값은, 실제로 개발자도구 > Application에 저장 되고, 로그아웃 하면 다시 사라집니다.

function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false)
const {getUserInfo} = useSupabaseAuth()
useEffect(()=>{
const fetchUser = async () => {
await getUserInfo()
}
fetchUser()
},[])
return (
정보를 받아오는 것도 비동기이니까, 비동기로 작성합니다.
getUserInfo() 이 함수는 뭐 하는 함수? ↓
getUserInfo()가 무엇을 하는 함수일까?
이 함수는 보통 이런 일을 해:
- 지금 로그인된 유저 정보를 Supabase에서 가져오기
- 그 정보를 LocalStorage나 전역 상태(예: Zustand, Recoil 등)에 저장하기
- 나중에 앱 전반에서 "누가 로그인했는지" 확인할 수 있도록 해주는 함수야
const getUserInfo = async () => {
const { data, error } = await supabase.auth.getUser();
if (error) {
console.error('유저 정보 가져오기 실패:', error);
return;
}
const user = data?.user;
if (user) {
// 예: localStorage에 저장
localStorage.setItem('user', JSON.stringify(user));
}
};
이런 식으로 로그인된 유저의 이메일, ID, 이름 같은 정보를 받아와서 저장하는 역할이야.
아 그니까 그 말은.. 일단 로그인을 성공 하게 되면 > 다시 마운트 (렌더링) 되니까, 유저 정보가 담긴다 이 말인거지!
-> 정확해. 흐름은 아래와 같아.
- 앱 처음 실행
- App.jsx 마운트됨
- getUserInfo() 실행됨
- 아직 로그인 안 했으면 → 정보 없음
- 유저가 로그인 성공함
- Supabase가 토큰을 localStorage에 저장
- auth 상태가 바뀜
- React에서 그걸 감지하거나, 페이지가 새로고침됨
- 다시 App.jsx가 마운트됨 (혹은 useEffect가 다시 실행됨)
- getUserInfo() 다시 실행됨
- 이번엔 토큰이 있으니까 Supabase가 유저 정보 줌 🙌
- localStorage에도 저장 가능
로그아웃 하면, 다시 렌더링 되고, 그 때는 정보가 없으니까 로컬에 안뜨는건가?
-> 정확해!
로그아웃하면
- Supabase가 자동으로 토큰(LocalStorage에 있는 sb-... 세션 정보 등)을 지워줌
- React 컴포넌트가 다시 렌더링되거나 onAuthStateChange() 이벤트 발생
- 이때 getUserInfo() 같은 함수 실행하면...
- 유저 정보 없음(null) 반환됨
- 그래서 너가 만든 localStorage.setItem('user', ...) 같은 코드가 실행이 안 되거나 빈값 저장됨
실제 흐름 예시
useEffect(() => {
const user = await getUserInfo(); // 로그아웃 상태면 null 반환됨
if (user) {
localStorage.setItem('user', JSON.stringify(user));
} else {
localStorage.removeItem('user'); // 👈 이렇게 지워주기도 해!
}
}, []);
그래서 Jane의 말은 100% 정확해:
로그아웃하면 → 렌더링 다시 됨
→ getUserInfo() 실행됨
→ 유저 정보가 없으니까 로컬에 안 뜸!
요것도, 간단히 완성할 수 있습니다~
4-6 LocalStorage에서 유저 정보 가져와, ContextAPI를 사용하여 전역 상태로 관리
❶getUserInfo()를 호출하여 유저 정보를 가져온 후, 전역 상태의 유저 정보를 업데이트해야 합니다.
❷로그인 시 사용자 정보를 받아 Context API를 활용하여 사용자 정보를 전역에서 관리합니다.
❸사용자 정보는 Context API를 사용하여 LocalStorage에 저장된 값을 전역 상태로 저장합니다.
→ useEffect()를 사용하여 컴포넌트가 마운트 될 때마다 LocalStorage에서 유저 정보를 가져온 후 전역 상태에 저장합니다.
❹getUserInfo() 함수는 비동기 함수이므로, 전역 상태를 업데이트할 때 이전 작업이 완료된 후에 다음 작업이 실행되도록 순서를 보장해야 합니다.
❺getUserInfo() 함수는 비동기 함수이므로, getUserInfo()실행이 끝난 후에, 전역 상태를 업데이트 해야합니다.이걸 토대로, user의 객체를 상태로 관리 해줘야 한다는 것을 알 수 있다.
이 파트에서 한번 크게 또 멘붕이 왔었다. 그 이유는 context API를 제대로 공부하지 않았던 것.
여기서는 context API 설정법을 완전히 익혔다는 전제로 가볍게 아래 적어두었다. 이해가 안된다면 밑에 첨부해둔 게시글을 읽어보자 !
우선 요구 조건을 정리 하자면,
- getUserInfo()는 user 객체 전체를 가져오는 비동기 함수야.
- 그 user 객체 안에는 다음과 같은 정보가 있음: email, id, profileImageUrl, userName
- 그리고 조건은 이 유저 정보를 전역 상태(Context API)로 관리하라는 거야.
context API 설정하는 방법 ? ↓
코드는 다음과 같습니다.
1. context 폴더의 UserState.jsx
//createContext
import {createContext } from "react";
export const UserState = createContext();
2. context 폴더의 UserProvider.jsx
import { useEffect, useState } from "react"
import { useSupabaseAuth } from "../supabase"
import { UserState } from "./UserState"
export const UserProvider = ({children}) => {
const [user, setUser] = useState(null)
const {getUserInfo} = useSupabaseAuth()
useEffect(()=>{
const fetchUser = async () => {
await getUserInfo()
}
fetchUser()
},[getUserInfo])
return (
<UserState.Provider value={{user, setUser}}>
{children}
</UserState.Provider>
);
}
create랑 provider 파일 분리 해줘야 하더군요..
3. useProvider를 main.jsx에서 자식들 묶어주기
//main.jsx
createRoot(document.getElementById('root')).render(
<StrictMode>
<SupabaseProvider>
<UserProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</UserProvider>
</SupabaseProvider>
</StrictMode>,
)
내가 너무 헷갈렸던 파트..
1. 우선 이걸 하는 이유 -> 해결 완료 : 로컬 스토리지에 저장된 user를 전역 상태로써 관리하기 위해 ↓
앞 단계 4-5 " App.jsx 에 useEffect를 활용하여, 마운트시 getUserInfo() 함수를 실행합니다." 이 단계는, 정보를 로컬에만 저장 하는 단계 였습니다. 이 시점에서는 Context API로 전역 상태에 저장하지는 않았고, 오직 브라우저 로컬 저장소에만 저장했죠.
이 단계는 **"로그인 시 정보를 잃지 않도록 저장하는 준비 단계"**고,
실제로 앱 전역에서 이 정보를 쓰기 위해선 → Context API에 또 넣어야 해요 !
왜 이렇게 해야 할까?
1. 앱 전체에서 유저 정보를 공유하기 위해 (전역 상태 관리)
- 사용자가 로그인하면 이름, 이메일, 권한 등의 정보가 필요하잖아요?
- 이 정보를 여러 컴포넌트에서 반복해서 불러오지 않고, 한 번만 가져와서 앱 전체에서 **공유(전역 상태)**하면 편리해요.
- 예: Header, Profile, Dashboard, Settings 등 여러 곳에서 유저 정보를 공통으로 사용하게 됨.
2. LocalStorage는 로컬 저장소일 뿐, 상태(State)가 아님
- localStorage는 브라우저에 저장된 정적인 값일 뿐이에요.
- 리액트는 state가 바뀌면 UI가 자동으로 반영되지만, localStorage는 바뀌어도 UI가 자동으로 업데이트되지 않아요.
- 그래서 localStorage에서 값을 읽고, 이를 Context API를 통해 state로 관리해야 해요.
3. 초기 렌더링 시 유저 정보를 복원하기 위해 (자동 로그인 효과)
- 사용자가 이전에 로그인한 적이 있다면, 새로고침 후에도 그 정보를 유지하려면 localStorage에 저장되어 있는 정보를 다시 읽어와야 해요.
- 이를 위해 컴포넌트가 처음 렌더링 될 때(useEffect) getUserInfo()를 통해 정보를 가져오고, Context에 저장하는 거예요.
2. context API로 관리할 값(?)들 ?
내가 궁금했던 것 :
로컬에 저장되는 값은 user라는 객체 : email, id, profileImageUrl, userName이 저장 되거든.
이 값들을 전역으로 저장하라는거잖아?
그러면 createContext 해서,
안에 상태들을
import React, { createContext, useState } from "react";
export const UserContext = createContext();
export const UserProvider = ({ children }) => {
user 객체 내부 값들을 하나하나 상태로 따로 관리해야 하는지?
A : 꼭 그렇진 않아. 객체 전체를 한 상태로 관리하는 게 더 자연스럽고 깔끔해.
만약 각각을 따로 관리하면 이런 식이 되겠지:
const [email, setEmail] = useState(null);
const [id, setId] = useState(null);
...
이렇게 하면 코드가 불필요하게 길어지고 관리도 힘들어져.
단, 유저의 한 필드만 자주 바뀌는 상황 (예: profileImageUrl만 자주 바뀌는 경우)이라면, 필드별로 상태를 분리하는 것도 고려해볼 수는 있어.
하지만 지금 상황에서는 한 번에 받아서 user 객체로 저장하는 게 딱 좋다.
큰 덩어리인 user 객체 전체를 전역으로 저장하고, 자식에서 꺼내서 쓰는 구조가 맞는지?
맞아! 이게 가장 일반적이고 실용적인 방법이야.
const [user, setUser] = useState(null);
그리고 context에 이렇게 넘겨주면 돼:
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
자식 컴포넌트에서는 이렇게 사용해:
const { user } = useContext(UserContext);
console.log(user?.email);
이렇게 하면 유저 정보가 필요한 컴포넌트에서 필요한 정보만 뽑아쓰면 되고, 관리도 간단해져.
3. 여긴 구현 단계랑은 관계가 없는데.
user 객체 안에는 user: email, id, profileImageUrl, userName 이렇게 있고, getUserInfo 함수 실행 하면 저 값들을 받아오잖아? 궁금한 부분은, 그 함수를 실행함으로써 그게 로컬에 저장되고 전역 상태로 관리되는데 그 값중에 로그인 유무를 판단하는 기능의(?) 값도 있는거야? 왜냐하면 기존에는 로그인 유무를 isLoggedIn으로 app에서 내려주는 형식으로 useState를 썼는데, 이렇게 하니까 새로고침 하니까 풀리더라고.. 그래서 로컬스토리지에 저장하고 싶어서.
getUserInfo() 실행해서 받은 user 객체에 로그인 유무 판단하는 값이 있나?
- 일반적으로 email, id 같은 값이 있으면 로그인 된 상태로 판단해도 무방해.
- 근데 isLoggedIn 같은 별도의 명시적 플래그는 없을 수 있어.
- 즉, user 객체가 null 이면 로그아웃 상태, user 객체에 값이 있으면 로그인 상태로 볼 수 있어.
로그인 상태 관리 로직 어떻게 할까? (새로고침 시 유지 문제)
문제
- 기존에 useState(isLoggedIn)만 쓰면 새로고침하면 상태가 초기화됨.
- 그래서 isLoggedIn 상태를 Context에 두고 로컬스토리지와 연동해서 유지해야 함.
추천 방법
- user 상태가 있으면 로그인 상태로 판단하는 식으로 로직을 짜는 게 깔끔함.
const isLoggedIn = !!user; // user가 있으면 true, 없으면 false
- 이렇게 하면 user가 있으면 로그인, 없으면 로그아웃 상태로 바로 판단 가능.
그리고 로컬스토리지 저장은 이렇게!
// 로그인 시 user 정보를 받고
const handleLogin = (userData) => {
localStorage.setItem("user", JSON.stringify(userData)); // 로컬에 저장
setUser(userData); // context 상태 업데이트
};
// 로그아웃 시
const handleLogout = () => {
localStorage.removeItem("user");
setUser(null);
};
이렇게 두가지 방법중,
1. 기본 이메일 / 비밀번호 기반의 로그인 / 회원가입 과정 (Supabase 백엔드 이용 하는 방식)은 완성 했습니다.
다음 게시글에서,
2. 소셜 로그인 / 회원가입(카카오, 구글, 네이버 등 소셜 연동) 구현 해보겠습니다 !
추가 해보고 싶은 기능
1. 마이페이지에서 > 회원 탈퇴 (이메일 기반 회원가입 했을 때, 소셜로그인일 때)
2. 회원가입 후 자동 로그인
3. 가입 후 이메일 인증 해야 한다는 것 알려주기. 이메일 인증 대기하기.. 이런 기능?
4. 로그인 페이지에서 > 비밀번호와 이메일을 잊었나요? 페이지
5. 회원 정보 찾기 기능에서 (이메일 전송)
6. 비밀번호 재설정
7. 구현 단계인 로컬 스토리지에 로그인 상태값을 저장하는게 아닌, 아예 서버에 상태를 저장하고 싶음
참고할 공식문서.
https://supabase.com/docs/guides/auth/passwords?queryGroups=flow&flow=implicit
Password-based Auth | Supabase Docs
Allow users to sign in with a password connected to their email or phone number.
supabase.com
추가적으로 해볼만한 것 ↓
추가적으로 해보면 좋을 다음 단계는?
이제 진짜 백엔드처럼 더 발전시키고 싶으면:
회원 정보를 따로 저장하고 싶다면 | profiles라는 테이블 만들어서 회원가입 후 insert |
로그인 기능 | supabase.auth.signInWithPassword() 사용 |
로그인한 유저 상태 관리 | supabase.auth.getSession() 혹은 onAuthStateChange() |
회원 탈퇴 | supabase.auth.admin.deleteUser() (관리자 권한 필요) |
로그인과 로그아웃에서 궁금한것.
지금은 setIsLoggedIn으로 프론트에서 true,false로써 상태 관리를 해주고 있고. 이걸 App.jsx에서부터 상태를 내려주고 있는데..
로그인 됐을 때,
👀 현재 상황 요약
- App.jsx에서 isLoggedIn 상태를 전역으로 props로 내려주고 있음
- 로그인은 setIsLoggedIn(true)로 프론트에서만 처리
- Supabase를 도입하면서 서버 인증 기반으로 상태를 바꾸고 싶음
로그인 유무 상태를 setisLoggedIn에서 getSession, onAuthStateChage이런걸로 바꿔주는 이유는 ? ↓
차이점 한눈에 보기
항목 | 프론트엔드 상태관리 (setIsLoggedIn) | Supabase 인증 (getSession, onAuthStateChange) |
어디서 로그인 정보 저장? | 브라우저 메모리 (React 상태) | Supabase 서버 + 클라이언트 세션 (JWT) |
새로고침 시 유지? | ❌ 리셋됨 | ✅ 자동 로그인 유지 (세션 저장됨) |
보안 | ❌ 사용자가 조작 가능 | ✅ 서버에서 검증 가능 |
실사용 가능성 | 연습, 데모 용도에 적합 | 실제 서비스에 적합 |
Supabase에서 로그인 상태를 확인하려면?
1. getSession()
현재 로그인한 유저가 있는지 확인하는 메서드
const { data: { session } } = await supabase.auth.getSession();
if (session) {
console.log("로그인 중인 유저:", session.user);
} else {
console.log("로그인 안 됨");
}
2. onAuthStateChange()
로그인, 로그아웃 될 때 자동으로 감지하는 리스너 (React에서 많이 씀)
useEffect(() => {
const { data: listener } = supabase.auth.onAuthStateChange((event, session) => {
if (event === "SIGNED_IN") {
console.log("로그인됨:", session.user);
setIsLoggedIn(true);
}
if (event === "SIGNED_OUT") {
console.log("로그아웃됨");
setIsLoggedIn(false);
}
});
return () => {
listener.subscription.unsubscribe(); // 정리
};
}, []);
✨ 왜 이렇게 해야 해?
정확히 Jane이 말한 것처럼:
"전역으로 관리하는 걸 떠나서 서버에서도 알아야 하니까 그런거야?"
정답이에요!
- 서버(=Supabase)가 유저 인증을 맡음
- 그래서 프론트엔드는 "지금 로그인 중인지" 를 Supabase에서 확인해야 함
- 그래야 보안도 강력하고, 로그인 유지(세션) 도 가능해짐
정리: Supabase로 로그인 상태를 관리하는 방법
- 로그인 시 signInWithPassword() 사용
- 로그인 성공하면 자동으로 세션이 생김
- getSession() 또는 onAuthStateChange()로 로그인 상태 추적
- 로그아웃은 supabase.auth.signOut()
출처: 오즈코딩스쿨 학습자료, 챗지피티, 구글링, Supabase 공식홈페이지
'Studying > 미니 프로젝트' 카테고리의 다른 글
mini project - 영화 페이지 만들기(5단계) - 마이페이지 구현. 보호된 페이지 (4) | 2025.05.22 |
---|---|
mini project - 영화 페이지 만들기(4-3단계) - 백엔드 연결 (supabase) 소셜 로그인 (카카오톡, 구글) (2) | 2025.05.21 |
mini project - 영화 페이지 만들기(4-1단계) - 백엔드 연결 전 input창 구성하기 로그인 / 회원가입 구현 (1) | 2025.05.17 |
mini project - 영화 페이지 만들기(3단계) (1) | 2025.05.15 |
mini project - 영화 페이지 만들기 (2단계) (0) | 2025.05.05 |