실시간 채팅 서버를 구축할 때, 데이터 저장 방식과 처리 속도는 시스템의 전반적인 성능과 직결된다. 본 프로젝트에서는 데이터 저장소로 PostgreSQL과 MongoDB를 함께 사용하는 폴리글랏 퍼시스턴스(Polyglot Persistence) 아키텍처를 채택하였다. 본 글에서는 두 데이터베이스의 차이를 정리하고, 이 둘을 함께 사용하는 이유를 기술한다.
| 항목 | PostgreSQL | MongoDB |
|---|---|---|
| 데이터 모델 | 관계형(Relational) | 문서 기반(Document-Oriented) |
| 스키마 | 엄격한 스키마(Schema-on-Write) | 유연한 스키마(Schema-less) |
| 쿼리 언어 | SQL | BSON 기반의 쿼리 문법 |
| 트랜잭션 | ACID 완전 지원 | 4.0 이후 다큐먼트 수준 트랜잭션 지원 |
| 인덱싱 | 다양한 인덱싱 기능 제공 | 복합 인덱스 및 텍스트 인덱싱에 강점 |
| 성능 특성 | 복잡한 조인과 정규화에 적합 | 읽기/쓰기 속도 및 확장성에 우수 |
| 저장 단위 | 테이블, 로우 | 컬렉션, 도큐먼트 |
채팅 시스템은 본질적으로 다음과 같은 이질적인 데이터 특성을 가진다.
이러한 데이터는 명확한 스키마가 존재하고 정합성이 중요한 특성을 가지므로, PostgreSQL의 관계형 모델과 ACID 트랜잭션이 유리하다.
메시지는 대량으로 쌓이는 로그성 데이터이며, 읽기/쓰기의 속도와 확장성이 핵심이므로, MongoDB의 도큐먼트 지향 모델과 수평 확장 기능이 적합하다.
이를 통해, 각 DB의 강점을 살리면서도 병목을 최소화할 수 있다.
📦 backend/
┣ 📂 db/
┃ ┣ 📜 postgres.go // 사용자, 채팅방 관련 CRUD
┃ ┗ 📜 mongo.go // 메시지 저장 및 조회
┣ 📂 handlers/
┃ ┣ 📜 auth.go
┃ ┣ 📜 chatroom.go
┃ ┗ 📜 message.go
┣ 📜 main.go
// 메시지 저장 로직 예시 (MongoDB)
func SaveMessage(msg Message) error {
collection := mongoClient.Database("chat").Collection("messages")
_, err := collection.InsertOne(context.TODO(), msg)
return err
}
// 사용자 조회 로직 예시 (PostgreSQL)
func GetUserByID(id int) (User, error) {
row := pgDB.QueryRow("SELECT id, nickname FROM users WHERE id=$1", id)
var user User
err := row.Scan(&user.ID, &user.Nickname)
return user, err
}
PostgreSQL과 MongoDB는 상반된 철학을 가진 데이터베이스이지만, 서로 보완적으로 사용될 수 있다. 채팅 서버와 같이 정형/비정형 데이터가 공존하는 시스템에서는 단일 DB로 모든 요구사항을 해결하기보다, 역할에 따라 분리된 저장 전략을 설계하는 것이 더 안정적이고 확장 가능한 아키텍처를 만든다.
추후에는 Redis를 도입하여 메시지 전송의 실시간성과 캐시 처리 성능까지 향상시킬 예정이다.