이번에 구현한 실시간 채팅에 포함하고 싶었던 기능은 다음과 같았습니다.
- 특정 사용자를 선택해서 채팅방을 만들 수 있습니다.
- 특정 채팅방의 알람을 끄거나 켤 수 있습니다.
위의 기능을 구현하기 위한 데이터 모델입니다.
- Room은 채팅방에 대한 정보를 저장하는 테이블입니다.
- 특정 채팅방에서 전송된 모든 Message는 해당 채팅방의 Room.messages에 연결됩니다.
- 특정 채팅방에 참가한 모든 사용자(User)는 해당 채팅방의 Room.participants에 연결됩니다.
- 특정 채팅방에 전송된 메세지의 알림을 켠 사용자(User)는 해당 채팅방의 Room.subscribers에 연결됩니다.
- Message는 사용자가 보낸 메세지에 대한 정보를 저장하는 테이블입니다.
- 사용자가 채팅방에서 메세지를 전송하면, 이것은 사용자에게 직접 전송되는 것이 아니라, Room에 연결되고, Room.participants와 Room.subscribers에 포함된 사용자에게만 Push Notification을 통해서 전송됩니다.
- 이렇게 구성한 이유는 Message 데이터를 특정 User와 강하게 연결시켜 놓으면, 추후 단체 채팅이 가능하도록 업그레이드할 때 장애가 될 것 같았기 때문입니다.
shema.prisma
위의 모델을 만들기 위해 Prisma 모델링 파일에 추가한 모델과 스키마입니다.
User 모델의 outbox 스키마에는 "outbox"라는 심볼을 정의해서 relation을 명확하게 정의했지만, 사실 필요없는 작업입니다.
다만 나중에 단체 채팅을 구현할 때, User와 Message가 outbox와 from 이외에 다른 스키마를 통해서 연결되어야 하기 때문에 일단 relation을 명확하게 정의했습니다.
model User {
...
rooms Room[] @relation("rooms", references: [id])
subscriptionRooms Room[] @relation("subscribers", references: [id])
outbox Message[] @relation("outbox")
expoPushToken String @default(value: "")
...
}
model Room {
id Int @default(autoincrement()) @id
participants User[] @relation("rooms", references: [id])
subscribers User[] @relation("subscribers", references: [id])
messages Message[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Message {
id Int @default(autoincrement()) @id
room Room @relation(fields: [roomId], references: [id])
roomId Int
from User @relation("outbox", fields: [fromId], references: [id])
fromId Int
text String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
기존 Prisma 모델: [인스타그램 클론코딩] Prisma2 데이터 모델링
위의 Prisma 모델로 부터 자동으로 생성된 MySQL 테이블 생성 코드입니다. FOREIGN KEY와 REFERENCES 부분을 살펴보면 Prisma 모델에서 정의한 모델과 스키마간의 연결이 테이블과 컬럼에 그대로 반영된 것을 확인할 수 있습니다.
참조: Prisma2 CLI를 사용하여 데이터베이스 테이블 생성
Room 테이블
CREATE TABLE `Room` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=110
;
Message 테이블
CREATE TABLE `Message` (
`fromId` INT(11) NOT NULL,
`id` INT(11) NOT NULL AUTO_INCREMENT,
`roomId` INT(11) NOT NULL,
`text` VARCHAR(191) NOT NULL DEFAULT '' COLLATE 'utf8mb4_unicode_ci',
`createdAt` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `roomId` (`roomId`) USING BTREE,
INDEX `fromId` (`fromId`) USING BTREE,
CONSTRAINT `Message_ibfk_1` FOREIGN KEY (`roomId`) REFERENCES `instagureng`.`Room` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `Message_ibfk_2` FOREIGN KEY (`fromId`) REFERENCES `instagureng`.`User` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=284
;
_room 릴레이션
CREATE TABLE `_rooms` (
`A` INT(11) NOT NULL,
`B` INT(11) NOT NULL,
UNIQUE INDEX `_rooms_AB_unique` (`A`, `B`) USING BTREE,
INDEX `_rooms_B_index` (`B`) USING BTREE,
CONSTRAINT `_rooms_ibfk_1` FOREIGN KEY (`A`) REFERENCES `instagureng`.`Room` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `_rooms_ibfk_2` FOREIGN KEY (`B`) REFERENCES `instagureng`.`User` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;
_subscribers 릴레이션
CREATE TABLE `_rooms` (
`A` INT(11) NOT NULL,
`B` INT(11) NOT NULL,
UNIQUE INDEX `_rooms_AB_unique` (`A`, `B`) USING BTREE,
INDEX `_rooms_B_index` (`B`) USING BTREE,
CONSTRAINT `_rooms_ibfk_1` FOREIGN KEY (`A`) REFERENCES `instagureng`.`Room` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT `_rooms_ibfk_2` FOREIGN KEY (`B`) REFERENCES `instagureng`.`User` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;
'백엔드 > Prisma + GraphQL' 카테고리의 다른 글
[인스타그램 클론코딩] upload (0) | 2020.05.31 |
---|---|
[인스타그램 클론코딩] toggleFollow (0) | 2020.05.31 |
[인스타그램 클론코딩] toggleLike (0) | 2020.05.31 |
[인스타그램 클론코딩] createAccount (0) | 2020.05.31 |
[인스타그램 클론코딩] Prisma2를 활용한 GraphQL Resolvers (0) | 2020.05.30 |