이 글은 Firebase Authentication에 저장할 수 없는 추가 정보를 Firestore에 저장하여 회원가입 하는 방법에 대해 다룹니다.
만약 Firebase Authentication을 통한 사용자 생성을 아직 구현하지 않으셨다면,
[React + Firebase] 카카오 로그인 구현하기 (1)
[React + Firebase] 카카오 로그인 구현하기 (2)
윗글을 먼저 보시는 것을 추천해 드립니다.
지난 글에서는 커스텀 토큰만 클라이언트로 전송해 로그인을 구현한 뒤 마쳤습니다. 생성한 유저 ID를 이용해 회원가입을 진행하기 위해 작성한 코드를 수정해야 합니다.
저는 카카오 로그인을 통해 받아온 유저 정보를 활용하기 위해 ID 외에도 몇 가지 추가 정보를 클라이언트로 전송하였습니다.
아래 코드를 참고하여 functions/src/index.ts
를 수정합니다.
app.post("/kakao", async (req, res) => {
...
const kakaoUser = await getKakaoUser(token);
const authUser = await updateOrCreateUser(normalizedUser);
const firebaseToken = await admin
.auth()
.createCustomToken(authUser.uid, { provider: "KAKAO" });
return res.status(200).json({
kakaoUser: {
...kakaoUser,
id: authUser.uid
},
firebaseToken,
});
});
코드를 수정했으므로 다시 배포를 진행합니다.
yarn deploy
firestore에 저장된 유저가 있는지 확인해야 하므로, config.ts
를 수정합니다.
import { getFirestore } from "firebase/firestore";
...
export const auth = getAuth(app);
export const db = getFirestore(app);
Firestore 인스턴스를 export 해준 뒤, 콜백 페이지를 수정해 주어야 합니다.
아래 코드를 참고하여 상황에 맞게 인증 및 사용자 확인을 해 주시면 되고, 코드에 대한 설명은 주석으로 달아두겠습니다.
import { doc, getDoc } from "firebase/firestore";
import { auth as firebaseAuth, db } from "../config";
...
const KakaoLogin = () => {
const [isLoading, setIsLoading] = useState(true);
const [auth, setAuth] = useState<Auth | null>(null);
...
useEffect(() => {
(async () => {
try {
const res: AxiosResponse<Auth> = await axios.post(
"/api/auth/kakao",
{ code }
);
const {
kakaoUser: { id },
firebaseToken,
} = res.data;
// Firebase Auth 사용자 id를 이용해
// Firestore에서 동일한 유저 문서를 가져옴
const user = await getDoc(doc(db, "users", id));
if (user.exists()) {
// Firestore에 저장되어 있다면 회원가입이 되어있다는 뜻이므로
// 커스텀 토큰을 이용해 로그인 처리
await signInWithCustomToken(firebaseAuth, firebaseToken);
} else {
setAuth(res.data);
}
setIsLoading(false);
} catch (error) {}
})();
}, []);
if (isLoading) {
return <Loading.Full />;
}
return (
<Layout>
{auth ? (
// 유저 문서가 없었다면 회원가입 페이지로 이동
<Navigate to="/signup" state={auth} />
) : (
<Navigate to="/" replace />
)}
</Layout>
);
};
회원가입 페이지의 경우 원하는 대로 구현해 주시면 됩니다.
이후 입력받은 유저 정보를 바탕으로 컬렉션에 유저 문서를 추가하면 회원가입이 완료됩니다. 아래는 예시 코드입니다.
import { signInWithCustomToken } from "firebase/auth";
import { doc, setDoc } from "firebase/firestore";
import { auth, db } from "../config";
import { useLocation } from "react-router-dom";
const SignUp = () => {
// Navigate 컴포넌트를 통해 전달한 state
const { state } = useLocation();
const onSubmit = async () => {
const user = convertStateToUser(state);
await setDoc(doc(db, "users", state.kakaoUser.id), user);
await signInWithCustomToken(auth, state.firebaseToken);
};
return (
<button onClick={onSubmit}>회원가입</button>
);
};
❶ API 응답에 유저 ID를 포함한다.
❷ 받아온 유저 ID를 통해 Firestore에 해당 유저 문서가 있는지 확인한다. (회원가입 여부)
❸ 유저가 있다면 받아온 커스텀 토큰을 통해 로그인한다.
❹ 유저가 없다면 회원가입 페이지로 이동해 추가 정보를 입력받는다.
❺ 추가 정보를 가지고 유저 문서를 추가해준 뒤 커스텀 토큰을 통해 로그인한다.
이번 글은 Firestore에 있는 데이터를 읽고 저장하는 방법에 대해 다뤄보았습니다. 부족한 부분은 댓글로 알려주시면 개선하도록 하겠습니다!
다음 글에서는 React Context를 이용하여 로그인된 경우에만 홈 화면에 접근할 수 있도록 처리하는 방법에 대해 작성해 보겠습니다.