inflearn logo
강의

講義

知識共有

AI時代でも生き残るエンジニアの条件、米国ビッグテックのシステムデザイン、アルゴリズム思考、オープンソース実務の完成

WhatsApp(ワッツアップ)メッセージングサービス システムデザインおよびスケーリング DeepDive [アップデート]

차단 등 검증 로직의 위치

解決済みの質問

27

jaehyeonhan

投稿した質問数 8

0

WhatsApp 채팅 아키텍처 설계에 대한 질문입니다. 영상에서는 참가자가 ws 서버에 메시지를 보내면 바로 Redis pub/sub으로 들어가고, 람다나 Stream을 통해 DB로 저장하는 방식을 설명하고 있습니다.

하지만 DB 저장에 앞서 채팅방 참가 여부 검증, 메시지 전송 차단/해제, 구독자만 전송 가능 등의 검증(validation)이 필요한 경우가 있을 것 같습니다. 또한 이 경우 사용자에게 메시지 전송 실패/불가라는 즉각적인 피드백도 제공해줘야 할 것입니다. ws 서버에서는 보통 검증 로직은 담당하지 않는 것으로 알고 있는데, 이 경우 어디에 검증 로직을 넣는 게 적당할까요?

dbms/rdbms 소프트웨어-설계 시스템-디자인 인공지능(ai) ood

回答 2

0

americasnail

안녕하세요. 한재현님,

좋은 질문해주셔서 감사합니다

말씀하신 것처럼 실제 채팅 시스템에서는 메시지를 Redis pub/sub이나 Stream으로 넘기기 전에 검증 단계가 필요합니다.

질문 주신 부분은 앞단의 message acceptance / validation layer에 해당합니다.

보통은 검증을 한 곳에만 두기보다 역할을 나눕니다.

첫 번째로, WebSocket 서버 또는 API Gateway 근처에서 즉시 판단 가능한 검증을 합니다.

사용자가 바로 피드백을 받아야 하기 때문에, Redis나 DB 저장 파이프라인으로 넘기기 전에 처리하는 것이 좋습니다.

두 번째로, DB 저장 직전 또는 메시지 서비스 계층에서도 최종 검증을 한 번 더 하는 경우가 많습니다.

이유는 WebSocket 서버는 여러 대일 수 있고, 네트워크 지연이나 상태 변경 때문에 앞단 검증 시점과 실제 저장 시점 사이에 조건이 바뀔 수 있기 때문입니다.

예를 들어 사용자가 메시지를 보내는 순간에는 채팅방 멤버였지만, 처리되기 전에 방에서 제거될 수도 있습니다. 또는 차단 상태가 바뀔 수도 있어서, 중요한 정합성 검증이 보통 한 번 더 필요할 수 있습니다.

 

따라서, 검증 로직은 Redis pub/sub이나 DB 저장 이후가 아니라 그 전에 위치하는 것이 맞습니다.
다만 모든 검증을 WebSocket 서버에 몰아넣기보다는, WebSocket 서버는 빠른 1차 검증을 담당하고, 메시지 서비스 또는 DB 트랜잭션 계층에서 최종 정합성 검증을 한 번 더 수행하는 구조가 더 적합할 수 있음을 알려드립니다.

하지만, 매번 DB를 조회하면 성능이 저하될 수 있으므로, ”차단 리스트“ 와 같은 검증을 Redis에 캐싱하여 1차 검증 시에도 어느 정도 정교한 비즈니스 검증을 수행할 수 있음을 참고해주시면 좋습니다

감사합니다.

좋은 하루 되세요

0

jaehyeonhan

답변 감사합니다. 사용자 응답을 위한 권한 확인, 실제 저장을 위한 권한 확인 두 번의 확인이 필요하며, websocket 서버가 검증 책임을 가질 수 있다고 이해했습니다.

하지만 캐시는 고민이 됩니다. 참가 여부와 차단 여부를 ws 서버에서 검증하려면 ws 서버의 부하 특성 상 캐시를 사용하는 것이 커넥션 고갈 방지와 처리량을 위해 필수적이라고 보여집니다. 그런데 캐시는 네트워크 파티션이나 접속 장애 등 상황에서 일관성을 보장하기 어렵다고 알고 있습니다.

A가 B를 API 서버를 통해 차단하는 경우를 예로 들겠습니다.

  1. DB에는 차단 기록 커밋 후 Redis 캐시에 invalidate 또는 write-through가 실패

  2. 메시지 큐를 통해서 BlockedEvent를 발행한다면, 캐시에 쓰는 소비자와 Redis 간 파티션 등이나 접속 장애로 쓰기 실패

이 경우에 사용자는 API 서버로부터 차단 요청에 성공 응답을 받지만, ws 서버가 실제로 검증에 사용하는 Redis에는 반영되지 않은 상태가 됩니다. 특히 네트워크 장애나 파티셔닝은 언제 복구될지 모르기 때문에 더 위험하다고 생각됩니다. 채팅에서 사용자는 차단 시 거의 즉시 차단되는 것을 기대할 것이기에 캐시 반영 지연은 서비스에서 허용 불가할 것으로 생각됩니다.

그렇다면

  1. Redis 캐시를 사용하면서 일관성을 보장하려면 어떻게 해야하는지

  2. 강한 일관성을 위해 DB를 사용하는 것은 성능 상 불가능에 가깝다고 봐야하는지

  3. 일반적으로는 invalidation 정도만으로 타협을 해도 괜찮은건지

를 추가로 여쭙고 싶습니다. 감사합니다.

 

 

0

americasnail

안녕하세요. 한재현님,

추가 질문 감사합니다. 아주 좋은 질문을 해주셨습니다.

말씀해주신 우려에 동의하는 부분 입니다.
차단 여부나 채팅방 참가 여부를 Redis에 캐싱하면 WebSocket 서버가 빠르게 검증할 수 있다는 장점이 있습니다. 하지만 Redis 캐시는 결국 DB의 복사본에 가깝기 때문에, 말씀해주신 것처럼, DB와 항상 완전히 같은 상태인 일관성을 보장하기는 어렵습니다.

예를 들어, 아래처럼 A가 B를 차단하는 경우를 생각해보면,

1. A가 B를 차단한다.

2. DB에는 차단 기록이 정상적으로 저장된다.

3. 그런데 Redis 캐시 갱신 또는 invalidate가 실패한다.

4. WebSocket 서버는 아직 이전 Redis 값을 보고 “차단되지 않았다”고 판단할 수 있다.

5. 그러면 B의 메시지가 잠깐 허용될 수 있다.

이러한 경우가 캐시를 사용할 때 생기는 대표적인 정합성 문제입니다.

따라서 Redis를 “진짜 원본 데이터”로 보면 안 되고,

보통은 아래처럼 책임을 나누는 것이 좋습니다.

DB / 권한 서비스 → 원본 데이터

Redis → 빠른 조회를 위한 캐시

WebSocket 서버 → Redis를 보고 빠르게 1차 검증

즉, Redis는 성능을 위해 사용하는 1차 검증 레이어이고, 실제 권한과 차단 상태의 기준은 DB나 권한 서비스에 있어야 합니다.

실제로 공개된 빅테크 사례를 봐도 비슷한 방향을 확인할 수 있습니다.

제가 빅테크 사례를 알아보니, Meta의 Memcache 관련 아티클에서 Meta도 캐시를 “원본 데이터”로 보지 않고, DB의 변경을 캐시에 전파하는 구조를 사용하고 있습니다.

그리고 캐시 불일치는 사용자 입장에서 데이터 유실처럼 보일 수 있기 때문에, Polaris라는 별도 시스템으로 캐시가 DB와 일치하는지 계속 측정하고 추적하여 이 문제를 해결하고 있음을 알 수 있습니다. 그래서 다른 방안으로는 캐시 불일치를 측정하고 복구하는 별도 시스템도 고려해보는 것도 좋은 방법으로 생각됩니다.

다른 사례를 보면, Uber도 비슷합니다. Uber의 Docstore 통합 캐시 사례를 보면, 캐시 invalidation만으로는 기본적으로 eventual consistency가 됩니다.

그래서 읽기-쓰기 직후 일관성이 더 중요한 경우에는 write가 끝난 뒤 해당 캐시 row를 명시적으로 invalidate하는 API를 따로 두었다고 설명합니다. 즉, 일반적인 캐시 경로와 더 강한 정합성이 필요한 경로를 분리하는 것 입니다. 강한 정합성이 필요한 요청은 캐시를 우회하거나 별도 경로로 처리해야 한다는 의미입니다.

그래서 아래와 같이 합리적인 설계방안으로 하는 것을 고려해야 합니다.

1. WebSocket 서버에서 Redis를 보고 빠르게 1차 검증한다.

2. 메시지 서버에서 중요한 권한을 한 번 더 확인한다.

3. DB 저장 직전에도 필요한 경우 최종 검증을 한다.

4. 차단 이벤트는 유실되지 않게 전파한다.

5. 캐시 상태가 불확실하면 메시지를 허용하기보다 거절하거나 보류한다.

여기서 차단 이벤트의 경우에는 DB에 차단 기록을 저장할 때 “이 차단 사실을 다른 시스템에도 알려야 한다”는 기록을 DB에 같이 남겨두는 방식입니다.

DB transaction 안에서 같이 저장

1. A가 B를 차단했다.

2. 이 차단 이벤트를 Redis / WebSocket 서버 / 메시지 서버에 알려야 한다.

이렇게 하면 Redis 갱신이나 이벤트 발행이 일시적으로 실패하더라도, DB에 남아 있는 미처리 이벤트를 보고 나중에 다시 시도할 수 있습니다.

DB에 차단 기록 저장 → BlockedEvent 저장

→ 별도 worker가 Redis 갱신 / 이벤트 발행 시도

→ 실패하면 재시도 → 성공하면 처리 완료

하지만 위와 같이 설계한다고 해서 Redis가 항상 즉시 100% 최신 상태가 된다는 뜻은 아닙니다. DB와 Redis는 서로 다른 시스템이기 때문에 아주 짧은 순간이라도 상태가 어긋날 수 있고, 네트워크 장애가 길어지면 그 차이도 길어질 수 있습니다.

그래서 질문 주신 “그럼 강한 일관성을 위해 DB를 사용하는 것은 성능 상 불가능에 가깝다고 봐야하는지” 부분에 대해서는, 무조건 불가능하다고 볼 수는 없지만, 대규모 채팅 시스템에서는 모든 메시지마다 DB를 직접 조회하는 방식은 보통 부담이 큽니다.

메시지 하나를 보낼 때마다 매번 아래 정보를 DB에서 확인하면 트래픽이 많아질수록 DB가 병목이 될 수 있습니다. 그래서 DB 의 병목을 해결하기 위해 아래와 같이 설계하는 것을 권장드릴 수 있습니다.

일반적인 메시지 전송 → Redis 캐시로 빠르게 1차 검증

캐시 miss / Redis 장애 / 상태가 불확실한 경우 → DB 또는 권한 서비스로 fallback 처리

차단 직후처럼 민감한 상태 → 더 강하게 검증하거나, 불확실하면 메시지를 막음

DB 저장 직전 → 메시지 서비스에서 최종 검증

또 하나 중요한 기준은 데이터의 성격입니다.

온라인 상태, 입력 중 표시, 읽음 여부처럼 잠깐 늦게 반영되어도 되는 정보는 eventual consistency 최종 일관성으로 타협할 수 있습니다.

반면 차단 여부, 채팅방 참가 권한, 유료 구독자 권한, 관리자 권한처럼 잘못 허용되면 문제가 큰 정보는 단순 Redis invalidation만으로는 부족할 수 있습니다.

정리하면 다음과 같습니다.

Redis 캐시만으로 차단/권한 검증의 강한 일관성을 보장하기는 어렵습니다. 하지만 모든 메시지마다 DB를 조회하는 것도 대규모 채팅 시스템에서는 부담이 큽니다.

그래서 Redis는 빠른 1차 검증에 사용하고, DB 또는 권한 서비스는 원본 데이터로 유지하며, 차단처럼 중요한 기능은 여러 단계에서 한 번 더 검증하는 구조가 더 합리적인 시스템이 될 것으로 생각합니다.

그래서 차단, 권한, 구독 여부처럼 잘못 허용되면 문제가 큰 데이터는 캐시만 믿지 않고, 캐시 갱신 실패를 대비한 재시도 구조와 최종 검증 레이어를 함께 두는 것을 권장 해드리고 싶습니다.

다른 질문 사항이 있으시면 언제든지 남겨주세요!

감사합니다.
좋은 밤 되시고, 즐거운 한 주가 되시길 바랍니다.

0

communityai8509

안녕하세요, 인프런 AI 인턴입니다. 한재현님께서는 WhatsApp 채팅 아키텍처의 설계 관련하여 검증 로직에 대한 질문을 주셨습니다. 이 질문은 백엔드 시스템 설계에서 메시지 전송을 위한 검증 로직의 위치를 결정하는 중요한 주제입니다.

검증 로직의 위치

  1. 웹소켓(ws) 서버:

    • 즉각적인 피드백 제공: 사용자가 메시지를 보낼 때, 즉각적인 피드백을 제공하기 위해서는 웹소켓 서버에서 가능한 검증을 수행하는 것이 유리할 수 있습니다. 예를 들어, 채팅방 참가 여부와 같은 간단한 검증은 웹소켓 서버에 캐싱된 데이터를 이용하여 빠르게 처리할 수 있습니다.
  2. 미들웨어 또는 API 게이트웨이:

    • 공통 검증 로직 적용: 여러 서비스가 연결되는 환경에서는 미들웨어나 API 게이트웨이를 통해 검증 로직을 관리할 수 있습니다. 이는 코드 중복을 줄이고 공통 로직을 한 곳에서 관리하게 해줍니다.
  3. Lambda 또는 Stream 처리 전에:

    • 최종 검증 단계: Lambda나 Stream 처리 전에 최종 검증을 통해 데이터의 무결성을 한 번 더 확인할 수 있습니다. 이 단계에서 데이터베이스 조회와 같은 복잡한 검증이 수행될 수 있습니다.

피드백 제공

즉각적인 피드백이 필요한 검증 로직은 요청이 처리될 때마다 클라이언트에게 바로 결과를 반환하는 구조가 필요합니다. 이는 비동기적으로도 구현할 수 있으며, 사용자 친화적인 경험을 제공합니다.

관련된 질문과 답변을 통해 더 많은 정보를 얻고자 하신다면 아래 링크를 참고하세요:

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

Substack 1년 제공

1

39

3

특별 학습 자료 프로모션 1년 멤버십 무료 제공 문의드립니다

0

33

2

특별 학습 자료 프로모션 1년 멤버십 제공 관련 문의 드립니다.

0

78

2

simcached를 검색을 했을때 해당 자료가 나오지 않는데 혹시 MemcachedGPU 라고 불리는 기술을 말씀하시는건가요?

0

72

1

유튜브 예제에서 흐름 관련 질문있습니다

0

57

2

Spotify 서비스 설계에서 transcoder service에 대해 문의 드립니다.

0

78

1

특별 학습 자료 프로모션 1년 멤버십 무료 제공 지원 확인 방법

0

105

2

[위치 이름 기반으로 호텔을 조회하는 메서드] 코드 질문 드립니다.

0

55

2

14강. 영화 DVD 대여 시스템 데이터베이스 스키마 설계에서 Inventory 테이블 질문있습니다.

0

86

2

Matching Service의 MQ 필요성, Cassandra의 필요성, Cassandara - RDB 동기화

0

96

2

Imgur 이미지 호스팅 서비스 질문

0

93

2

쿠폰 문의

0

96

1

Redis 랜덤 값 추가 시 메타데이터 저장

0

94

1

Blob Storage에서 파일 업로드에 대한 동시성을 어떻게 제어할 수 있을까요?

0

113

2

채팅을 영속할 DB로 RDB를 선택한 이유도 궁금합니다

0

290

2

URL 단축 서비스에서 redis counter를 사용하는 이유가 무엇인지 궁금합니다.

0

219

2

long url을 파티션키로 지정했을때 장점이 생각 안나네요 ㅎㅎ;;

0

143

2

실제 인터뷰에서도 Object Oriented Design 을 이런 과정으로 하는걸까요?

0

174

1

able to get all students who got a "d+" grade or lower 요구사항

0

123

1

number of copies 컬럼은 Inventory Table에 들어가야하지않을까요?

0

177

3

Spotify 서비스 시스템 디자인 및 스케일링에서 백업 클러스터 사용에 대한 문의

0

98

2

URL 단축 서비스 시스템 디자인 및 스케일링 질문있습니다.

0

198

2

예시 두개가 납득이 잘 가지않네요 ㅠㅠ

1

213

2

강의자료는 제공 불가능할까요?

0

231

1