jangjunha blog

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

idtitleuser_idsemester_id
7b8a38af69bf1교시엔 잠을자자c96c5ddff0dakorea_univ_anam/2022-2학기

Lecture

ididentifiertitleprofessorcreditcategorytimetable_id
62a81f025dd4COSE372데이터베이스시스템정연돈3.0["정보대학", "컴퓨터학과"]7b8a38af69bf
0128d5638268COSE474전산학특강정원기3.0["정보대학", "컴퓨터학과"]7b8a38af69bf

LectureTime

idweekdaytime_begintime_endroomlecture_id
1b842dd209df1 (화)630 (10:30)705 (11:45)정보관 20262a81f025dd4
2d9f1a2a520b3 (목)630 (10:30)705 (11:45)정보관 20262a81f025dd4
ecad3e3edb1b1 (화)840 (14:00)915 (15:15)정보관 B1010128d5638268
8ee85e9365a03 (목)840 (14:00)915 (15:15)정보관 B1010128d5638268

🔗 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를 사용해도록 변경해보고 문제없이 작동하는지 확인해보도록 하겠습니다.

〈잠자는 서비스 만들기〉 시리즈

잠자는 서비스 만들기

DB 스키마에 대응하는 Firestore 스키마 정의하기

Firebase로 웹사이트 만들기

Firestore 보안 규칙 작성 시작하기

Firestore 보안 규칙으로 필드 고유성 보장하기

Firestore 보안 규칙 기타 예제

Firebase 인증에서 자체 인증 서버 사용하기