[Unity] Firebase 관련 데이터 타입 정리
댓글 0
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!
타입들을 무작정 외우기 전에, 읽기/쓰기가 어떤 흐름으로 흘러가는지 먼저 잡고 가는 게 좋다.
[읽기]
DatabaseReference (주소)
→ GetValueAsync()
→ DataSnapshot (그 시점의 데이터 사진)
→ GetRawJsonValue()
→ JsonUtility.FromJson
→ 모델 객체 (UserProfile, ScoreData ...)
[쓰기]
모델 객체 → ToJson()
→ DatabaseReference (주소)
→ SetValueAsync() / Push() + UpdateChildrenAsync()
핵심 비유 하나만 기억하면 된다.
Firebase는 앱을 켜자마자 바로 쓸 수 없다. 네이티브 SDK 의존성 체크가 끝나야 한다.
Firebase 전체의 뿌리. 어떤 프로젝트에 연결됐는지를 담은 객체다. Auth든 Database든 전부 이 App에서 파생된다. "내 Firebase 프로젝트 연결 핸들"이라고 보면 된다.
FirebaseApp app = FirebaseApp.DefaultInstance;
CheckAndFixDependenciesAsync()의 결과. 네이티브 의존성이 정상인지 알려준다. Available이면 사용 가능, 아니면 초기화 실패다.
DependencyStatus status = await FirebaseApp.CheckAndFixDependenciesAsync();
if (status != DependencyStatus.Available)
{
// 초기화 중단
}
인증 기능의 핵심 창구. 로그인 / 회원가입 / 로그아웃 메서드, 현재 유저(CurrentUser), 상태 변화 이벤트(StateChanged)를 가진다.
FirebaseAuth auth = FirebaseAuth.GetAuth(app);
auth.StateChanged += OnAuthStateChanged;
현재 로그인된 한 명의 유저 정보. 실제로 자주 쓰는 정보는 두 가지다.
| 프로퍼티 | 설명 |
|---|---|
| UserId | 유저 고유 ID. DB 경로의 {userId}로 쓰이는 가장 중요한 값 |
| 이메일. 익명 로그인이면 null |
로그인/회원가입 호출의 결과 묶음. 여기서 .User로 FirebaseUser를 꺼낸다.
AuthResult result = await auth.SignInWithEmailAndPasswordAsync(email, password);
FirebaseUser user = result.User;
가장 중요하고, 가장 헷갈리는 묶음이다.
Realtime Database는 테이블이 아니라 하나의 거대한 JSON 트리다. 그래서 "경로를 타고 내려가는" 방식으로 데이터를 다룬다.
내가 만든 DB는 이렇게 생겼다.
루트
├── users
│ └── {userId}
│ ├── nickname
│ ├── email
│ └── createdAt
└── scores
└── {userId}
├── bestscore: 42
└── history
├── {pushId}: { score, timestamp }
└── ...
DB 전체 핸들. .RootReference로 트리의 맨 꼭대기를 가져온다.
DatabaseReference root = FirebaseDatabase.DefaultInstance.RootReference;
DB 트리의 특정 위치를 가리키는 주소표다. 실제 데이터가 아니라 "어디를 가리키는지"만 들고 있다. .Child("이름")으로 아래로 내려간다.
DatabaseReference historyRef = root.Child("scores").Child(userId).Child("history");
// scores/{userId}/history 라는 위치를 가리킴
이 주소에 읽기/쓰기 메서드를 붙인다.
| 메서드 | 역할 |
|---|---|
| SetValueAsync(값) | 그 위치에 값 덮어쓰기 |
| SetRawJsonValueAsync(json) | JSON 통째로 쓰기 |
| UpdateChildrenAsync(딕셔너리) | 자식 여러 개 한 번에 갱신 |
| Push() | 고유 키를 만들어 새 자식 추가 (반환값도 DatabaseReference) |
| GetValueAsync() | 읽기 → DataSnapshot 반환 |
쓰기 방식은 두 종류로 나뉜다. 이걸 구분하는 게 중요하다.
// 방식 A — Push(): 계속 쌓이는 리스트 (예: 점수 히스토리)
DatabaseReference newRef = historyRef.Push(); // "-NxxxxAbc" 같은 고유 키 자동 생성
await newRef.UpdateChildrenAsync(scoreData);
// 방식 B — Set: 하나만 유지되는 값 (예: 최고 점수, 프로필)
await root.Child("scores").Child(userId).Child("bestscore").SetValueAsync(newBest);
정리하면 누적되는 건 Push, 갱신되는 건 Set이다.
읽기 결과로 돌아오는, 그 시점의 데이터 사진 한 장이다. Reference가 주소라면 Snapshot은 그 주소에서 실제로 찍어온 데이터다.
| 프로퍼티 / 메서드 | 역할 |
|---|---|
| Exists | 그 위치에 데이터가 있는지 (없으면 false) |
| Value | 값. 단, object 타입 (주의 — 아래 설명) |
| GetRawJsonValue() | JSON 문자열로 꺼내기 → JsonUtility로 변환 |
| Children | 자식들 순회 (history 같은 리스트 읽을 때) |
DataSnapshot snapshot = await someRef.GetValueAsync();
if (snapshot.Exists)
{
foreach (DataSnapshot child in snapshot.Children)
{
ScoreData data = JsonUtility.FromJson<ScoreData>(child.GetRawJsonValue());
}
}
왜 Value를 그냥 (int)로 못 받을까?
Value의 타입은 object다. 그리고 Firebase는 숫자를 그 object 안에 long(정수) 또는 double(실수)로 박싱해서 넣는다. 박싱된 값은 정확히 그 타입으로만 언박싱할 수 있어서, long으로 박싱된 걸 (int)snapshot.Value로 바로 빼내려 하면 InvalidCastException이 터진다. 그래서 실제 타입을 확인해 안전하게 변환하는 헬퍼를 따로 두는 게 좋다.
public static int ToInt(object value)
{
switch (value)
{
case long l: return (int)l;
case double d: return (int)d;
case int i: return i;
default:
return int.TryParse(value?.ToString(), out int parsed) ? parsed : 0;
}
}
Reference에 정렬/필터 조건을 붙인 것. 조건을 건 뒤 GetValueAsync()로 실행한다.
Query query = historyRef.OrderByChild("timestamp").LimitToLast(10);
DataSnapshot snapshot = await query.GetValueAsync();
// timestamp 오름차순으로 최근 10개 → 최신순으로 쓰려면 Reverse()
특수한 placeholder 값. 클라이언트(기기) 시간이 아니라 Firebase 서버의 현재 시간을 넣으라는 표시다. 기기 시간이 틀려도 정확한 시각이 기록된다.
var scoreData = new Dictionary<string, object>
{
{ "score", score },
{ "timestamp", ServerValue.Timestamp } // 서버 시간으로 기록
};
Firebase가 주는 타입이 아니라, DB의 JSON과 C# 객체를 오가기 위해 직접 만든 클래스들이다. [Serializable]을 붙여서 JsonUtility로 변환할 수 있게 한다.
[Serializable]
public class UserProfile
{
public string nickname;
public string email;
public long createdAt;
}
[Serializable]
public class ScoreData
{
public int score;
public long timestamp; // 저장 시 ServerValue.Timestamp가 들어간 값
}
timestamp(long)를 사람이 읽는 날짜로 바꾸는 편의 메서드를 같이 두면 UI에서 쓰기 편하다.
[Serializable]
public class LeaderboardEntry
{
public string userId;
public string nickname;
public int score;
public long timestamp;
}
apiKey, appId, projectId, databaseUrl, storageBucket 같은 프로젝트 설정값을 담는다. ScriptableObject로 빼두면 인스펙터에서 관리하기 편하다.
| 타입 | 한 줄 요약 |
|---|---|
| FirebaseApp | 프로젝트 연결 핸들. 모든 것의 뿌리 |
| FirebaseAuth | 로그인/회원가입 창구 |
| FirebaseUser | 로그인된 유저 정보 (UserId가 핵심) |
| FirebaseDatabase | DB 전체 핸들 |
| DatabaseReference | 주소 — 어디를 가리키는지 |
| DataSnapshot | 사진 — 그 주소에서 찍어온 실제 데이터 |
| Query | 조건이 붙은 주소 — 정렬/필터 |
| ServerValue.Timestamp | 서버 시간으로 기록하는 특수값 |
| 모델 클래스 | JSON ↔ 객체를 오가는 그릇 |
세 가지만 기억하면 흐름이 다 보인다.