
이번에 실시간 채팅 어플을 만들기 위해서 로그인 기능을 만들다가 OAuth를 자연스럽게 사용해보게 되어서 관련 내용을 정리합니다.
항상 UI/UX 측면에서 정말 필요하다 생각한 기능인데 이번에 제대로 사용해보고 더 깊이 있게 사용법도 알게 되었습니다..!!
OAuth는 그냥 SNS를 이용한 로그인으로 편리한 기능이다! 로만 알고 있었지 제대로 모르게 있었습니다.
Open Authorization으로 제 3자를 통해(Google, Naver, Kakao 등) 인증받을 수 있는 인증 프로토콜입니다.
사용자의 자격증명을 외부 어플리케이션에 직접 제공하지 않고도 특정 리소스에 접근가능합니다.

이 토큰은 리소스 서버에 접근할 수 있는 권한을 나타냅니다.
보안 강화: 비밀번호와 같은 자격 증명을 외부 서비스에 제공하지 않아도 됩니다.
권한 제어: 사용자는 특정 데이터만 클라이언트에게 접근을 허용할 수 있으며, 필요시 언제든지 권한을 철회할 수 있습니다.
범용성: 여러 서비스(Google, Facebook, GitHub 등)에서 하나의 계정으로 다양한 웹사이트나 앱에 로그인할 수 있습니다.
그렇다면 이제 실제로 Google을 이용한 로그인방식과
기본 이메일, 패스워드 방식을 이용한 회원가입 및 로그인을 설계합니다.
차고로 기능 공부에 몰빵(?)인 상태라 아에 CSS는 생각하지 않았습니다....
이때 Supabase에 Google OAuth ID와 Password를 적어서 설정했습니다.
매번 recoil을 사용하다가 이번에 React-query를 적용하고 싶어 함께 사용해보았습니다.
app/hooks/useSession.ts
하지만 layout.tsx까지 전부 CSR이 되어버려서 조금 더 공부하고 정확하게 사용해야할거 같습니다...
import { supabase } from '@/supabaseClient';
import { useQuery } from '@tanstack/react-query';
const fetchSession = async() => {
const {data} = await supabase.auth.getSession();
return data.session;
}
const useSession = () => {
return useQuery({
queryKey: ['session'],
queryFn: fetchSession,
staleTime: 60000, // 1분간 세션 캐싱,
refetchOnWindowFocus: true, // 창이 포커스될 때 세션 재검증
});
};
export default useSession;
회원가입페이지, 로그인 페이지로 이동이 가능한 기본 페이지입니다.
로그인을 성공하면 아래 로그아웃버튼을 활성화 시킵니다.
app/page.tsx
"use client"
import { useEffect } from "react";
import { supabase } from "@/supabaseClient";
// 생성한 세션 훅
import Link from "next/link";
import useSession from "./hooks/useSession";
export default function Home() {
const {data: session, refetch} = useSession();
useEffect(()=>{
const {data: {subscription} } = supabase.auth.onAuthStateChange(() => {
refetch();
});
return () => {
subscription?.unsubscribe();
};
}, [refetch]);
const signOut = async () => {
await supabase.auth.signOut();
refetch(); // 로그아웃 후 세션 재검증
};
return (
<div>
<h1>OAuth 로그인 및 이메일회원가입 연습</h1>
<br/>
<Link href={"/auth/signup"}>회원가입 페이지</Link>
<br />
<br />
<Link href={"/auth/login"}>로그인 페이지</Link>
<br />
<br />
{
session ? (
<button onClick={signOut}>로그아웃</button>
)
: (<p>로그인 안되어 있습니다.</p>)
}
</div>
);
}
Google로그인은 회원가입이 필요없기에 따로 회원가입페이지가 없습니다.
하지만 일반 이메일과 비밀번호로 로그인하려는 유저도 있을 수 있기 때문에 기본방식의 회원가입 및 로그인도 같이 구현했습니다.
app/auth/signup/page.tsx
"use client";
import { supabase } from '@/supabaseClient';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
export default function SignUp() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(null);
const router = useRouter();
const signUpWithEmail = async (e: React.FormEvent) => {
e.preventDefautl();
const {data, error} = await supabase.auth.signUp({
email,
password,
});
if (error) {
setError(error.message);
} else {
router.push('/auth/login');
}
};
return (
<div className="m-4">
<h2 className="mb-4 text-2xl font-bold">회원가입</h2>
<form onSubmit={signUpWithEmail}>
<div>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="p-2 border border-2"
/>
</div>
<div>
<label htmlFor="password">Password:</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="p-2 border border-2"
/>
</div>
<button
type="submit"
className='flex items-center gap-2 m-4 border border-2'>
<span className='p-2 font-bold text-white bg-blue-500 border'>회원가입</span>
</button>
<button
type="button"
onClick={() => router.push('/auth/login')}
className='flex items-center gap-2 m-4 border border-2'>
<span className='p-2 font-bold text-white bg-blue-500 border'>로그인</span>
</button>
</form>
{error && <p className="text-red-500">{error}</p>}
</div>
);
}
이제 오늘의 핵심인 OAuth와 그리고 기본 로그인 방식의 함수를 정의한 부분입니다.
app/auth/login/page.tsx
google로그인 함수
const signInWithGoogle = async() => {
const {data, error} = await supabse.auth.signInWithOAuth({
provider: 'google',
options: { queryParams: { access_type: "offline", prompt: "consent" } } // 이건 서버 닫으면 다시 로그인하게 -> 연습용
});
if (data) {
console.log("로그인 되었습니다.");
}
if (error) {
console.log("error : ", error);
setError(error.message);
}
}
이메일 로그인 함수
const signInWithEmail = async (e: React.FormEvent) => {
e.preventDefault();
const { user, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
console.log("error : ", error);
setError(error.message);
} else {
console.log("로그인 되었습니다.", user);
}
};
이제 다양한 로그인 방식을 이해했으니 실제에 적용해봐야겠습니다.
ex) 각 사용자 마다의 todo 루틴만들기
ex) 영화 API 불러와서 사용자가 좋아하는 영화 찜하기
요새 빠진 웹툰인 "펜홀더"에 빠져있는데
한번은 실력, 두번부터는 재능이라는 말처럼, 이 기능을 온전히 내껄로 만들어 앞으로 프로젝트를 하는데 적극적으로 사용해보겠습니다!!!