DB 스키마에 대응하는 Firestore 스키마 정의하기
계획대로라면 서비스를 이전하는 데 문제가 없을 것 같지만 항상 문제는 생기기 마련입니다. 미처 생각하지 못했던 문제가 있지는 않은지 검증하기 위해서 웹사이트를 먼저 Firebase 기반으로 이전해보기로 했습니다. 그 첫 단계로 이전에 관계형 데이터베이스(RDB)에 저장했던 데이터를 Firestore에 어떻게 저장했는지 살펴보겠습니다.
🔗 기존 스키마 살펴보기
erDiagram School ||--o{ Semester : "" School { string id PK string name } Semester { string id PK string school_id FK integer year string term json periods string lectures_url } User ||--o{ Timetable : has User { uuid id PK string username string password datetime created_at datetime updated_at } Timetable ||--o{ Lecture : contains Timetable }o..|| Semester : "" Timetable { uuid id PK string title uuid user_id FK uuid semester_id FK datetime created_at datetime updated_at } Lecture ||--o{ LectureTime : contains Lecture { uuid id PK string identifier string title string professor decimal credit json category uuid timetable_id FK datetime created_at datetime updated_at } LectureTime { uuid id PK integer weekday "0:월요일 - 6:일요일" integer time_begin "540:9am" integer time_end string room uuid lecture_id FK datetime created_at datetime updated_at }
중심이 되는 테이블은 Timetable
입니다. User
는 여러 Timetable
을 만들 수 있고, Timetable
에 여러 Lecture
를 추가할 수 있습니다. 그리고 다시 Lecture
는 여러 LectureTime
으로 구성돼있습니다.
아래는 시간표 하나를 저장하고 있는 테이블들을 나타낸 예시입니다. 몇몇 필드는 생략했고, UUID는 너무 길어서 축약했습니다.
Timetable
id | title | user_id | semester_id |
---|---|---|---|
7b8a38af69bf | 1교시엔 잠을자자 | c96c5ddff0da | korea_univ_anam/2022-2학기 |
Lecture
id | identifier | title | professor | credit | category | timetable_id |
---|---|---|---|---|---|---|
62a81f025dd4 | COSE372 | 데이터베이스시스템 | 정연돈 | 3.0 | ["정보대학", "컴퓨터학과"] | 7b8a38af69bf |
0128d5638268 | COSE474 | 전산학특강 | 정원기 | 3.0 | ["정보대학", "컴퓨터학과"] | 7b8a38af69bf |
LectureTime
id | weekday | time_begin | time_end | room | lecture_id |
---|---|---|---|---|---|
1b842dd209df | 1 (화) | 630 (10:30) | 705 (11:45) | 정보관 202 | 62a81f025dd4 |
2d9f1a2a520b | 3 (목) | 630 (10:30) | 705 (11:45) | 정보관 202 | 62a81f025dd4 |
ecad3e3edb1b | 1 (화) | 840 (14:00) | 915 (15:15) | 정보관 B101 | 0128d5638268 |
8ee85e9365a0 | 3 (목) | 840 (14:00) | 915 (15:15) | 정보관 B101 | 0128d5638268 |
🔗 Firestore 구조에 맞는 새 스키마
Firestore는 문서 기반의 NoSQL 데이터베이스입니다. Firestore 데이터베이스 아래에는 여러 컬렉션들이 있습니다. 컬렉션은 문서들의 집합이고 ID를 가집니다. 예를들면 HeekTime에는 학교 문서의 집합인 /schools
컬렉션이 있습니다. 컬렉션 아래에는 여러 문서들이 있습니다.
각 문서 역시 ID를 가지고 키-값 쌍으로 이루어진 내용을 가집니다(다른 문서 기반의 DB들이 JSON 형태의 값을 내용으로 가지듯이). 값에는 string
, number
, boolean
, map
, array
, null
, timestamp
, geopoint
, reference
타입이 올 수 있습니다. (문서의 내용을 map
타입이라고 봐도 무리는 없을겁니다.) /schools
컬렉션 아래에 "고려대학교-서울" 이라는 ID를 가진 문서를 만들 수 있을 겁니다. 이 문서의 경로(path)는 /schools/고려대학교-서울
이 됩니다.
그리고 문서 아래에는 다시 하위 컬렉션이 있을 수 있고 그 아래에는 다시 하위 문서를 둘 수 있습니다. 관계형 DB는 아니지만 이처럼 부모-자식 관계는 존재합니다. 컬렉션의 자식은 항상 문서이고, 문서의 부모는 항상 컬렉션입니다. 경로로 살펴보면 /컬렉션/문서/컬렉션/문서
와 같은 형태로 컬렉션과 문서가 교차로 나타납니다.
Firestore에 대한 설명은 이만하고 다시 HeekTime 스키마로 돌아가보겠습니다. Firestore에 대한 보다 자세한 설명을 원하시면 Cloud Firestore 문서(cloud firestore 문서)를 참고해주세요. 아래에 Firestore로 저장한 HeekTime 데이터를 간단한 탐색기를 통해 볼 수 있게 해두었습니다. 데이터도 아주 조금 더 넣어두었습니다. 위에서 예시로 들었던 시간표는 /users/c96c5ddff0da/timetables/7b8a38af69bf
경로에 있으니 참고해주세요.
크게 달라진 부분은 없지만 Firestore의 구조에 알맞게 몇몇 부분을 변경했습니다. 눈여겨 볼만한 부분을 살펴볼까요?
이제는 Lecture 문서가 Timetable 문서 하위에 있기 때문에
timetable_id
를 가리키는 필드는 더이상 필요하지 않습니다. 마찬가지로 Timetable 문서는 User 문서 하위에 있기 때문에user_id
를 가리키는 필드도 필요하지 않습니다.강의시간 정보를 별도의 (LectureTime)컬렉션 대신 Lectue 문서 필드의 값으로 저장합니다.
→ 강의정보 없이 강의시간 정보만 따로 불러올 일이 없는 상황에서 Firestore는 문서 접근에 따른 요금 부과 체계가 있기 때문에 별도 문서로 저장하기보다는 복합 객체 값으로 저장하도록 했습니다.
참고로 Lecture 문서의 category, times 필드는 JSON이 아닌 복합 객체 타입입니다.
username의 고유성을 보장하기 위해 Username Index 컬렉션(
/indices/user/usernames
)을 새로 만들었습니다.RDB와 달리 Firestore에서는 특정 필드에 고유 제약조건을 걸 수 없어 다른 방법을 이용해야합니다. Firestore의 특정 컬렉션 내에서 문서 ID는 고유합니다. 이러한 특성을 이용하기 위해서 별도의 username 컬렉션을 만듭니다. 이 컬렉션과 트랜잭션을 사용해서 username의 고유성을 유지하면서 User를 생성하거나 변경할 수 있고, 보안 규칙을 사용해서 이를 강제할 수 있습니다. 보안 규칙에 대한 내용은 후속편에서 다뤄보겠습니다.
User의 인증 정보는 이제 별개의 서비스(Firebase Authentication)에서 관리하기 때문에 password 해시를 여기서 저장하지 않습니다.
이제 웹사이트를 옮겨 볼 준비를 마쳤습니다. 다음 글에서는 웹사이트가 기존 서버에 HTTP 요청을 날리는 대신 Firestore SDK를 사용해도록 변경해보고 문제없이 작동하는지 확인해보도록 하겠습니다. 〈잠자는 서비스 만들기〉 시리즈