시스템 설계 : 캘린더 앱

발행: (2026년 1월 7일 오전 05:41 GMT+9)
5 분 소요
원문: Dev.to

I’m happy to help translate the article, but I need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) so I can translate it into Korean while preserving the formatting and code blocks?

기능 요구 사항

  • 이벤트 생성, 이벤트 수정, 이벤트 취소
  • 캘린더를 일간, 주간, 연간으로 보기
  • 반복 회의 설정
  • 변경 사항을 이메일로 알림 전송

Non‑Functional Requirements

  • High availability → consistency (eventual consistency for syncing events)
  • Should support 1 billion users
  • Low latency for viewing the calendar (read‑heavy > write‑heavy)

데이터 모델

  • User
  • Event
  • Recurrence

API

POST /events/

{
  "title": "Meeting title",
  "userId": "creator_id",          // creator
  "userIds": ["participant1", "participant2"], // optional list of attendees
  "startTime": "2025-01-10T15:00:00Z",
  "endTime": "2025-01-10T16:00:00Z",
  "content": {                     // JSON blob: video call link, description, etc.
    "videoCallLink": "https://example.com/meet/123",
    "description": "Discuss project status"
  },
  "recurrence": "weekly|biweekly|monthly|yearly" // optional
}

GET /events?startDay=&endDay=

요청된 날짜 범위에 해당하는 이벤트 목록을 반환합니다.

고수준 설계

(구성 요소, 캐싱 레이어, 데이터베이스 및 클라이언트 동기화 개요.)

Deep Dives

1. 일일 또는 반복 이벤트 저장

이벤트 생성 및 저장

Case 1 – Simple (One‑Time) Event
예시: 병원 예약

event_idowner_idtitlestart_time_utcend_time_utcrruletzversion
evt_101user_1Doctor Appointment2025‑01‑10 15:002025‑01‑10 16:00NULLUTC1

행이 그대로 반환되고 렌더링됩니다; 확장이 필요하지 않습니다.

Case 2 – Recurring Event (Weekly)
예시: 팀 동기화

event_idowner_idtitlestart_time_utcend_time_utcrruletzversion
evt_201user_1Team Sync2025‑01‑06 10:002025‑01‑06 11:00FREQ=WEEKLY;BYDAY=MOUTC1

GET behavior (Week view) – 기본 행을 가져와 요청된 범위에 대해서만 규칙을 확장하고, 메모리에서 발생을 생성합니다.

Case 3 – Recurring Event with Exception (One Cancellation)
예시: 1월 20일에 취소된 팀 동기화

event_exceptions 테이블:

exception_idevent_idexception_datetype
ex_301evt_2012025‑01‑20CANCELLED

GET behavior (Week of Jan 20) – 주간 규칙을 확장하고 예외와 매칭하여 해당 발생을 제외한 나머지 이벤트를 반환합니다.

저장 요약

  • 일회성 이벤트 → 1 행
  • 수년간의 주간 반복 → 1 행
  • 주간 + N 개의 예외 → 1 + N 행

결과: 최소한의 저장 공간.

2. 뷰 생성 및 저지연

사용자 흐름: 서버가 이벤트 목록을 클라이언트에 전송합니다.

최적화 – Redis 캐시

  1. 클라이언트가 이벤트를 요청합니다.
  2. 서버가 Redis를 확인합니다:
    • Cache hit → 캐시된 이벤트 반환.
    • Cache miss → DB를 조회하고 Redis에 채운 뒤 결과 반환.

장점: 빠른 읽기, DB 부하 감소.
단점: 캐시 무효화 복잡성, 최종 일관성 지연.

결론: 하이브리드 접근 방식 – 클라이언트‑사이드 확장 + 오프라인 저장을 위한 SQLite 조합이 저지연 및 오프라인 기능을 제공합니다.

3. 충돌 감지 및 잠금 전략

  • Optimistic Locking (default) – 경쟁이 적은 개인 캘린더에 적합; 서비스에서 재시도 로직 필요.
  • Pessimistic Locking – 경쟁이 높은 캘린더에 사용; 레이스 컨디션 방지를 위해 여러 행을 잠급니다.

4. 다중 디바이스 동기화 (Push & Pull)

  • Pull (Delta Sync) – 초기 시작, 재연결, 놓친 업데이트에 사용.
  • Push (SSE / Push Notifications) – 업데이트된 규칙을 가져와 로컬에서 재확장하고 SQLite와 UI를 업데이트합니다. SSE가 선호됩니다: 단방향, 가벼움, 배터리 친화적.
  • Hybrid model – 신선도는 푸시, 정확도는 풀을 조합.

5. 데이터베이스 선택: SQL vs NoSQL

SQL이 잘 맞는 이유

  • 쓰기량이 적당해 관계형 DB가 충분히 확장됩니다.
  • 트랜잭션으로 충돌 검사가 간단합니다.
  • 반복 쿼리(RRULE 확장)가 관계형 모델에 자연스럽게 들어맞습니다.
  • 강한 일관성(또는 구성 가능한 최종 일관성)을 보장하기 쉽습니다.

NoSQL 트레이드‑오프

  • 트랜잭션이 없으면 충돌 검사가 어렵습니다.
  • 반복 쿼리는 부적합합니다.
  • 기본적으로 최종 일관성이므로 정확성을 위해 추가 복잡성이 필요합니다.
  • 동일한 보장을 위해 구현 복잡도가 높아집니다.
Back to Blog

관련 글

더 보기 »