FE
[SvelteKit] SvelteKit과 Firebase로 구현하는 진짜 사용자 인증: Mock 데이터에서 실제 UID로
| 서론
안녕하세요, 팡일입니다!
오늘은 SvelteKit으로 개발 중인 회고 기록 애플리케이션 ‘Re-Log’의 인증 시스템을 개선한 과정을 공유하려고 합니다.
초기 단계에서는 빠르게 기능을 구현하기 위해, 사용자 ID(userId)를 코드에 하드코딩된 임시 값으로 사용했습니다.
하지만 여러 사용자가 동시에 이용하는 실제 서비스에서는,
각자의 데이터를 안전하게 분리하고 관리하기 위해 로그인된 사용자의 고유 ID(UID)를 기반으로 동작해야 합니다.
이번 포스팅에서는 이러한 ‘가짜 인증’을
Firebase Authentication을 활용한 실제 인증 시스템으로 전환한 리팩토링 과정을 단계별로 소개하겠습니다.
| 1. 기존 코드의 한계: 하드코딩된 userId
초기 버전에서는 회고를 저장하거나 불러올 때, 모든 사용자가 'temp-userId'라는 동일한 값을 사용했습니다.
1) src/lib/services/retrospectService.ts (수정 전)
export async function saveRetrospect(data: RetrospectData) {
try {
const docRef = await addDoc(collection(db, 'retrospectives'), {
...data,
createdAt: serverTimestamp(),
userId: 'temp-userId' // ? 임시 사용자 ID
});
return { success: true, id: docRef.id };
} catch (error) {
// ...
}
}2) src/routes/(private)/list/+page.svelte (수정 전)
const userId = 'temp-userId'; // ? 임시 사용자 ID
onMount(async () => {
const { success, retrospects: data } = await getRetrospectListByUser(userId);
});이 구조에서는 모든 사용자가 같은 ID로 데이터를 저장하기 때문에, 회고가 서로 뒤섞여 누가 작성했는지를 구분할 수 없었습니다.
| Svelte 스토어로 전역 인증 상태 관리
이 문제를 해결하기 위해, Firebase의 onAuthStateChanged 옵저버와 Svelte의 writable 스토어를 결합했습니다.
이를 통해 로그인 상태를 앱 전역에서 실시간으로 공유할 수 있게 되었습니다.
1) src/lib/stores/user.ts
import { writable } from 'svelte/store';
import type { User } from 'firebase/auth';
export const currentUser = writable<User | null>(null);그리고 최상위 +layout.ts에서 Firebase 인증 상태 변화를 감지해 스토어를 업데이트하도록 설정했습니다.
2) src/routes/+layout.ts
import { onAuthStateChanged } from 'firebase/auth';
import { auth } from '$lib/firebase';
import { currentUser } from '$lib/stores/user';
export const load = async () => {
onAuthStateChanged(auth, (user) => {
currentUser.set(user);
});
};이제 어디서든 $currentUser를 구독하거나, get(currentUser)로 로그인된 사용자 정보를 불러올 수 있게 되었습니다.
| 3. 페이지에 실제 사용자 ID 연동
전역 스토어가 준비되었으니, 각 페이지에서 하드코딩된 userId를 실제 로그인된 사용자 UID로 교체합니다.
1) src/routes/(private)/list/+page.svelte (수정 후)
onMount(async () => {
const user = get(currentUser);
if (!user) {
error = '로그인이 필요합니다.';
return;
}
const { success, retrospects: data } = await getRetrospectListByUser(user.uid);
if (success) retrospects = data ?? [];
});2) src/routes/(private)/write/+page.svelte (수정 후)
async function handleSubmit() {
const user = get(currentUser);
if (!user) {
alert('로그인이 필요합니다.');
return;
}
const { success, id } = await saveRetrospect({ title, answers }, user.uid);
if (success && id) {
// ...
}
}get(currentUser)를 이용해 로그인된 사용자 정보를 쉽게 가져올 수 있으며,
이제 모든 저장 로직은 실제 UID를 기반으로 동작하게 되었습니다.
| 4. 서비스 로직 수정: userId를 인자로 전달
마지막으로, 서비스 함수가 userId를 인자로 받아 Firestore 쿼리에 직접 활용하도록 변경했습니다.
1) src/lib/services/retrospectService.ts (수정 후)
export async function saveRetrospect(data: RetrospectData, userId: string) {
try {
const docRef = await addDoc(collection(db, 'retrospectives'), {
...data,
createdAt: serverTimestamp(),
userId: userId // ✅ 전달받은 userId 사용
});
return { success: true, id: docRef.id };
} catch (error) {
// ...
}
}
export async function getRetrospectListByUser(userId: string) {
const q = query(
collection(db, 'retrospectives'),
where('userId', '==', userId),
orderBy('createdAt', 'desc')
);
// ...
}| 마무리
이제 Re-Log는 더 이상 임시 데이터에 의존하지 않고,
모든 회고가 로그인된 사용자의 UID와 연결되어 안전하게 관리됩니다.
이번 리팩토링을 통해 얻은 변화는 다음과 같습니다.
데이터 분리와 보안 강화 — 사용자별 데이터가 독립적으로 저장됩니다.
확장성 확보 — 다중 사용자 환경을 위한 기반이 마련되었습니다.
코드 구조 단순화 — 인증 상태가 중앙에서 관리되어 예측 가능성이 높아졌습니다.
SvelteKit의 스토어 구조와 Firebase의 인증 시스템을 함께 사용하니, 개발 흐름이 훨씬 깔끔하고 직관적으로 개선되었습니다.
프로토타입 단계를 넘어 실제 서비스를 만들 계획이시라면, 이번처럼 “진짜 인증” 구조로의 전환을 꼭 경험해보시길 추천드립니다.