알림 기능을 구현을 위한 DB 뼈대를 세우던 중 다양한 유형의 알림을 처리해야했다. 이 과정에서 서로 다른 DB에 있는 CP와 USER 두 유형을 대상으로 여러 유형의 알림(리뷰, 게시글, 댓글 등)을 관래해야 했고, 각각 다른 테이블을 관리해야 했다. 그리고, 외래키 제약 조건을 사용할 수 없는 상황에서 데이터 무결성, 확장성을 유지하기 위한 방법을 찾아보았다.
문제
: 알림 받는 대상이 CP와 USER로 나뉜다. 알림의 유형 또한 리뷰, 게시글, 댓글 등 다양하다.
1. 외래 키 제약 사용 어려움 : 진행 프로젝트의 데이터베이스 분리 구조 때문에 CP에 외래 키 제약을 적용할 수 없어 데이터 일관성 유지 어려운 상황
2. 확장성 문제 : 알림기능의 경우 추후 여러 기능에서 계속 추가 될 예정인데 기존의 기본테이블 형식이라면 추가될 때 마다 해당 알림 유형 칼럼이 추가되어야 하는 상황
선택
다형성 관계 테이블
-> 확장성 해결 : 새로운 알림 유형이 추가될 때 테이블 구조 변경 없이 ENUM값 추가 만으로 확장할 수 있도록 함
추가 해결 문제
하지만 여전히 CP와 USER의 데이터 무결성 문제가 있었다. 추가로 다형성 관계 테이블을 사용함으로써 해당 테이블에 외래키 제약을 걸지 못하다보니 오히려 데이터 무결성 문제가 추가되었다.
그리고 다형성 테이블을 사용함으로써 쿼리가 복잡해져 성능개선이 필요했다.
추가 문제 해결
데이터 무결성 문제
-> 애플리케이션 레벨에서 validator를 통해 해결
-> 해당 방식으로 외래키 제약없이도 무결성을 유지할 수 있도록 함
- 수신자 무결성 유지를 위한 validator
async validateRecipient(recipientUid: string, recipientType: RecipientType): Promise<void> {
let recipientExists: boolean
switch (recipientType) {
case RecipientType.USER:
recipientExists = !!(await this.userRepository.findOne({ where: { uid: recipientUid } }))
break
case RecipientType.CP:
recipientExists = !!(await this.cpRepository.findOne({ where: { uid: recipientUid } }))
break
default:
throw new NotFoundException(`Unknown recipient type: ${recipientType}`)
}
if (!recipientExists) {
throw new NotFoundException(`Invalid ${recipientType} ID: ${recipientUid}`)
}
}
}
복잡해진 쿼리 문제
-> 복합 인덱스를 적용
-> 수신자와 관련 엔티티 기준 필터링하는 쿼리가 많은 문제를 인덱스를 적절하게 추가하여 성능 개선
@Entity('notifications')
@Index(['recipientUid', 'recipientType'])
@Index(['relatedEntityUid', 'relatedEntityType'])
export class Notification {
@PrimaryGeneratedColumn('uuid')
uid: string
@Column()
recipientUid: string
@Column({ type: 'enum', enum: RecipientType })
recipientType: RecipientType
@Column()
notificationType: string
@Column({ type: 'text' })
content: string
@Column()
relatedEntityUid: string
@Column({ type: 'enum', enum: RelatedEntityType })
relatedEntityType: RelatedEntityType
@Column()
isRead: boolean
@CreateDateColumn()
createdAt: Date
@UpdateDateColumn({ nullable: true })
readAt: Date
}
이러한 방식으로 다양한 알림 유형의 엔티티를 유연하게 관리하면서도, 데이터 일관성을 유지하며 성능도 유지할 수 있도록 고민해서 만들어보았다. 다형성 관계라는 것을 이번 기회에 처음 접하고 이를 통해 복합인덱스 개념도 처음으로 알게 되었다. 앞으로도 구현해나가는 과정에서 다양한 방식으로 고민하고 그로써 새로운것들을 많이 배워가고싶다 !
'개발 기초 다지기' 카테고리의 다른 글
Redis Pub/Sub 구현 (구독자모드의 제약 사항) (0) | 2024.08.21 |
---|---|
알림기능 기술 선택(SSE, Redis Pub/Sub, Socket.IO) (0) | 2024.08.12 |
SSL 연동 후 Socket 연결 트러블슈팅 (0) | 2024.08.11 |
socket io 1:1 채팅 구현 (2)jwt 토큰 전달 (0) | 2024.08.02 |
socket io 1:1 채팅 구현 (1)채팅방 DB 저장 (1) | 2024.08.01 |
댓글