데이터베이스를 직접 관리하는 방법 대신, Firebase
의 계정 관리 API를 사용하여 유저의 정보를 관리하는 Algorithm
순서는 다음과 같다.
계정 관리 API를 사용하기 전 Firebase
홈페이지에서 발급받은 각종 인증키를 연결한다.
또한 인증 키는 보안을 위해 .env
파일로 관리한다.
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
// app config 정보 등록
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
// Initialize Firebase
export const fbApp = initializeApp(firebaseConfig);
export const fbAuth = getAuth(fbApp);
Firebase
의 createUserWithEmailAndPassword
_API를 사용하면 회원가입 기능_을 쉽게 구현할 수 있다.
또한 updateProfile
API는 가입된 회원에 정보를 추가 한다.
위 과정을 통해 정상적으로 가입이 완료된 회원은 Firebase
에 고유 uid
생성된다.
import { signup } from '@actions/user';
const SignupForm = () => {
const [email, onChangeEmail] = useInput('');
const [password, onChangePassword] = useInput('');
const onSubmitForm = useCallback(() => {
// 회원가입 액션 실행
dispatch(signup({ email: email, password: password }));
}, []);
return (
<>
<form onSubmit={onSubmitForm}>
<input type="text" id="email" name="email" value={email} onChange={onChangeEmail} />
<input type="text" id="email" name="email" value={password} onChange={onChangePassword} />
</form>
</>
);
};
export default SignupForm;
import { createAsyncThunk } from '@reduxjs/toolkit';
import { fbAuth } from '@pages/api/auth/fBase';
import { createUserWithEmailAndPassword, updateProfile } from 'firebase/auth';
export const signup = createAsyncThunk('user/signup', async (data, { rejectWithValue }) => {
try {
// 입력받은 유저의 이메일, 패스워드를 통해 회원가입
const credential = await createUserWithEmailAndPassword(fbAuth, data.email, data.password);
// 회원가입이 완료된 유저의 닉네임을 추가
await updateProfile(fbAuth.currentUser, { displayName: data.nickname });
}
} catch (error) {
return rejectWithValue(error.response.data);
}
});
Firebase
의 signInWithEmailAndPassword
API를 사용하면 로그인 기능 또한 쉽게 구현할 수 있다.
정상적으로 로그인이 완료된 사용자는 Firebase
에 저장된 고유 uid
와 매칭되는 인증 토큰이 생성된다.
import { login } from '@actions/user';
const LoginForm = () => {
const [email, onChangeEmail] = useInput('');
const [password, onChangePassword] = useInput('');
const onSubmitForm = useCallback(() => {
// 로그인 액션 실행
dispatch(login({ email: email, password: password }));
}, []);
return (
<>
<form onSubmit={onSubmitForm}>
<input type="text" id="email" name="email" value={email} onChange={onChangeEmail} />
<input type="text" id="email" name="email" value={password} onChange={onChangePassword} />
</form>
</>
);
};
export default LoginForm;
import { createAsyncThunk } from '@reduxjs/toolkit';
import { fbAuth } from '@pages/api/auth/fBase';
import { signInWithEmailAndPassword } from 'firebase/auth';
export const signup = createAsyncThunk('user/signup', async (data, { rejectWithValue }) => {
try {
// 입력받은 유저의 이메일, 패스워드를 통해 로그인
const credential = await signInWithEmailAndPassword(fbAuth, data.email, data.password);
} catch (error) {
return rejectWithValue(error.response.data);
}
});
로그인시 발급된 인증 토큰은 접속한 유저를 식별하며, 추후 사용을 위해 Session
, Local Storage
...등 별도의 저장소에 저장한다.
import { createAsyncThunk } from '@reduxjs/toolkit';
import { fbAuth } from '@pages/api/auth/fBase';
import { signInWithEmailAndPassword } from 'firebase/auth';
export const signup = createAsyncThunk('user/signup', async (data, { rejectWithValue }) => {
try {
const credential = await signInWithEmailAndPassword(fbAuth, data.email, data.password);
// 접속한 유저의 토큰
const token = await credential.user.getIdToken();
setToken(token);
} catch (error) {
return rejectWithValue(error.response.data);
}
});
const setToken = token => {
// 전달받은 토큰을 local Storage에 저장
localStorage.setItem('FB_TOKEN', token);
};
API 요청시 저장된 토큰을 요청 헤더에 추가한다.
단, 인증 토큰에는 유효 기간이 있어 onIdTokenChanged
API를 통해 만료 여부를 확인한 뒤 만료된 토큰은 새롭게 교체하여 요청을 수행한다.
axios.interceptors.request.use(
async config => {
const token = await getToken(); // 로컬 스토리지에 저장된 토큰 가져오기
config.headers.Authorization = `Bearer ${token}`; // 요청 헤더에 토큰을 추가
return config;
},
error => {
return Promise.reject(error);
},
);
export const getToken = () => {
fbAuth.onIdTokenChanged(async user => { // 토큰이 만료되었다면
if (user) {
// 새로운 토큰을 로컬스토리지에 저장
const newToken = await user.getIdToken();
setToken(newToken);
}
});
const token = localStorage.getItem('FB_TOKEN') ?? '';
return token;
};
로그아웃은 Firebase
의 signOut
API로 구현할 수 있으며, Local Storage
에 저장된 토큰 또한 함께 삭제한다.
export const logout = createAsyncThunk('user/logout', async () => {
try {
await fbAuth.signOut(); // 사용자 로그아웃
localStorage.removeItem('FB_TOKEN'); // 로컬 스토리지의 토큰도 함께 삭제
} catch (error) {
console.log(error);
}
});
Firebase
는 Google
, Facebook
...등등 많은 소셜 네트워크 서비스의 로그인 연동 기능을 지원한다.
기본 적인 사용방법은 일반 사용자 로그인과 동일하며, 서비스에 맞는 별도의 공급자를 생성하여 계정 정보를 추가한다.
import { createAsyncThunk } from '@reduxjs/toolkit';
import { fbAuth } from '@pages/api/auth/fBase';
import { GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
export const signup = createAsyncThunk('user/signup', async (data, { rejectWithValue }) => {
try {
// 구글 계정 공급자 생성
const provider = new GoogleAuthProvider();
// 팝업창을 이용하여 구글 계정 로그인 연동
credential = await signInWithPopup(fbAuth, provider);
// 접속한 유저의 토큰을 Local Storage에 저장
const token = await credential.user.getIdToken();
setToken(token);
} catch (error) {
return rejectWithValue(error.response.data);
}
});