시스템 설계 예상문제 (고급)

시스템 설계 예상문제 (고급)

Backend Engineering · System Design Practice

시스템 설계 예상문제

대규모 시스템 설계 면접에서 자주 다루는 주제를 실제 운영 상황으로 재구성한 연습 자료입니다. 요구사항을 좁히고, 병목을 찾고, 선택의 근거와 실패 대응을 설명하는 데 초점을 둡니다.

활용 안내 · 답변 관점은 채점용 정답이 아닙니다. 먼저 본인의 가정과 우선순위를 말한 뒤, 설계 선택·트레이드오프·장애 대응을 설명하고 마지막에 비슷한 경험 또는 검증 방법을 연결해 보세요.

글로벌 URL 단축·리다이렉트 플랫폼 설계

상황

글로벌 커머스 기업의 마케팅·문자 발송 조직이 공통으로 사용하는 URL 단축 서비스 ‘LinkZip’을 재설계해야 합니다. 평소에는 안정적이지만 라이브 커머스와 유명 아티스트 캠페인이 겹치면 하나의 링크로 트래픽이 몰립니다. 지난 분기에는 인기 링크 조회가 원본 MySQL의 단일 행에 집중되어 리다이렉트 p99가 4초까지 치솟았고, 캐시 장애 중 생성 API가 재시도되면서 서로 다른 긴 URL에 같은 단축 키가 배정되는 사고도 있었습니다. 최근 피싱 링크 신고가 늘어 보안팀은 전 세계 차단 반영 시간을 30초 이내로 요구합니다. [제품·운영 제약] “기존에 배포된 링크는 주소가 바뀌면 안 되고, 정상 링크는 일부 리전 장애에도 열려야 합니다. 다만 법무·보안 차단은 캐시가 오래됐다는 이유로 우회되어서는 안 됩니다. 첫 출시까지 3개월이며 운영 인력은 4명입니다.”

조건

규모와 시스템 구성

  • 월 30억 리다이렉트, 평균 12만 RPS·이벤트 피크 150만 RPS
  • 일 2천만 링크 생성, 7년 보존, 읽기:쓰기 약 500:1
  • 서울·도쿄·버지니아 3개 리전, CDN/Redis/MySQL 기반
  • 사용자 지정 alias, 만료, 클릭 통계, 신고·차단 API 제공

현재 문제와 의사결정 제약

  • Hot Key상위 0.01% 링크가 전체 조회의 35% 차지
  • Correctness키 충돌·재시도에도 매핑은 불변이어야 함
  • Availability기존 링크 월 99.99%, 리다이렉트 p99 80ms 목표
  • Security차단은 30초, 오탐 해제는 5분 이내 전파

꼬리 질문

1

요구사항을 어떤 순서로 구체화하고, 단축 키 생성과 저장소를 어떻게 선택하겠습니까? 단순히 Base62를 사용한다는 답을 넘어 충돌, 재시도, 다중 리전 생성, 사용자 지정 alias가 동시에 존재할 때 지켜야 할 불변식과 선택하지 않은 대안이 더 적합해지는 조건을 설명해 주세요.

답변 예시 보기

요구사항과 불변식부터 좁히기

먼저 링크를 수정 가능한가, 같은 원본 URL을 여러 번 줄이면 같은 키를 주는가, 삭제한 alias를 재사용하는가를 확인하겠습니다. 이 사례에서는 발급된 key는 정확히 하나의 longUrl에만 매핑되고 재사용하지 않는다를 핵심 불변식으로 둡니다. 리다이렉트 가용성과 생성 일관성은 분리합니다. 기존 링크 조회는 리전 단절에도 허용하지만 새 alias의 유일성은 일시적으로 생성을 거부하더라도 지키는 식입니다.

선택과 트레이드오프

  • 운영 인력과 기간을 고려해 각 리전에 겹치지 않는 ID 구간을 임대하고, 단조 증가 ID를 Base62로 표현하는 방식을 우선 검토합니다. 구간 임대는 중앙 sequence 의존도를 줄이지만 ID가 어느 정도 예측 가능하므로 난독화 permutation이나 별도 랜덤 suffix가 필요합니다.
  • 완전 랜덤 7~8자 키는 중앙 조정이 없지만 생일 역설에 따른 충돌 검사와 재시도가 필요합니다. DB의 unique constraint가 최종 방어선이며, API의 idempotency key와 요청 fingerprint를 저장해 응답 유실 후 재시도가 새 링크를 만들지 않게 합니다.
  • 사용자 alias는 자동 키와 namespace를 분리하거나 동일 unique index에서 강하게 예약합니다. alias 생성은 home region의 writer로 라우팅하고 합의가 안 되면 409/503을 반환해 split-brain 중복을 피합니다.

저장소는 key 단건 조회가 대부분이므로 분산 KV도 맞지만, 3개월·4명이라는 제약에서는 샤딩된 RDB와 캐시로 시작할 수 있습니다. 용량·쓰기 처리량·리전 독립성이 임계점을 넘으면 Dynamo 계열 KV로 옮기되, TTL·2차 조회·백업 복구 기능까지 비교합니다.

경험 연결 포인트 · ID 생성기나 unique constraint를 직접 설계하지 않았다면 주문번호·멱등키·중복 이벤트 처리 경험을 연결합니다. “충돌 가능성이 낮다”보다 실제로 어떤 제약 조건과 부하 테스트로 불변식을 검증했는지, 실패 시 어떤 응답과 재처리 절차를 뒀는지 말합니다.
2

유명 캠페인 링크가 150만 RPS를 만들고 동시에 한 리전의 Redis가 장애 났습니다. CDN·지역 캐시·원본 저장소의 역할과 TTL/무효화 정책을 어떻게 나누겠습니까? 캐시 stampede, 존재하지 않는 키 공격, 잘못 캐시된 매핑, 리전 장애 시의 degraded mode까지 포함해 설명해 주세요.

답변 예시 보기

읽기 경로와 실패 모드

정상 경로는 Anycast/DNS → edge redirect cache → 리전 서비스 → 로컬 Redis → 영속 저장소로 둡니다. 매핑이 불변이라는 특성을 활용해 정상 링크는 긴 TTL과 stale-while-revalidate를 사용할 수 있습니다. 다만 만료·차단 여부는 같은 값에 긴 TTL로 묶지 않고 더 짧고 빠르게 갱신되는 정책 계층으로 분리해야 합니다.

  • hot key는 CDN에서 흡수하고 리전 내부에서는 요청 병합(single flight), soft TTL, TTL jitter로 동시 원본 조회를 줄입니다. Redis 장애 때 모든 miss를 DB로 보내지 않고 DB 보호용 concurrency limit와 admission control을 둡니다.
  • 존재하지 않는 키는 짧은 negative cache와 Bloom filter로 막되, 방금 생성된 링크가 404로 고정되지 않도록 생성 이벤트로 negative cache를 무효화합니다. 공격자가 무작위 키를 순회하면 IP·ASN·tenant 단위 rate limit을 별도로 적용합니다.
  • 리전 원본이 끊기면 이미 검증된 정상 링크는 last-known-good 값을 제한 시간 동안 제공하고, 신규·미확인 링크는 다른 리전으로 조회합니다. 다른 리전도 불확실하면 보안 성격과 사업 영향을 기준으로 fail-open 범위를 명시합니다.

검증과 관측

계층별 hit ratio, origin RPS, single-flight 대기 수, stale 응답 비율, 잘못된 목적지 검증 오류를 대시보드로 분리합니다. hot key 부하와 Redis primary failover를 함께 주입해 DB QPS 상한, p99, 정상 링크 성공률이 유지되는지 검증하고, 오래된 캐시로 인한 위험은 별도 보안 SLO로 측정합니다.

경험 연결 포인트 · 캐시 장애가 원본 장애로 확대된 경험이 있다면 적중률만 말하지 말고 miss 원인, 원본 보호 장치, 개선 전후 p99와 DB 사용률을 설명합니다. CDN 경험이 없어도 로컬 캐시·Redis·요청 병합을 적용한 근거를 연결할 수 있습니다.
3

보안팀이 피싱 URL을 30초 안에 전 세계에서 차단하려 하지만, CDN purge API 일부가 실패하거나 잘못된 탐지 규칙이 정상 캠페인 10만 개를 차단할 수 있습니다. 빠른 전파와 오탐 피해 사이에서 정책 데이터, 승인, 캐시 일관성, 복구 절차를 어떻게 설계하겠습니까?

답변 예시 보기

매핑과 정책의 수명주기 분리

longUrl 매핑은 불변 데이터로 유지하고, ALLOW·QUARANTINED·BLOCKED와 정책 버전을 별도 저장하겠습니다. 리다이렉트 edge는 매핑 캐시가 있어도 더 최신인 signed deny-list 또는 정책 버전을 확인합니다. 차단 이벤트는 고우선 topic으로 모든 리전에 전파하고, 각 소비자가 적용한 version과 시각을 중앙에서 수집해 30초 SLO 미달 지역을 식별합니다.

  • 확신도가 높은 악성 도메인이나 법적 요청은 자동 BLOCKED, 휴리스틱 탐지는 짧은 QUARANTINED와 검토 큐로 분리합니다. 대량 변경은 예상 영향 링크·트래픽을 preview하고 비율 상한과 2인 승인을 적용합니다.
  • CDN purge만 성공 조건으로 믿지 않고 edge에서 작은 고우선 deny-list를 먼저 조회하게 합니다. purge 재시도는 멱등하게 하고, 적용 확인이 안 된 POP는 우회하거나 보안 요구에 따라 요청을 보수적으로 차단합니다.
  • 모든 변경은 actor·근거·정책 버전·영향 범위를 immutable audit log에 남기며, 해제는 이전 버전으로 되돌리는 새 이벤트로 처리합니다. 오탐 해제 시 negative/deny cache까지 역방향 전파되는지 점검합니다.

운영 훈련에는 일부 POP 이벤트 누락, 오래된 consumer, 잘못된 대량 규칙을 포함합니다. 차단 전파 p95/p99뿐 아니라 오탐 링크 수, 롤백 시간, 사용자 이의제기 처리 시간을 함께 측정해야 속도만 최적화하는 오류를 피할 수 있습니다.

경험 연결 포인트 · feature flag·권한·설정 변경을 빠르게 전파한 경험을 연결해도 좋습니다. 실제 사고에서 blast radius를 어떻게 제한했고, 누가 어떤 근거로 롤백했으며, 전파 완료를 무엇으로 확인했는지를 구체적으로 말합니다.
4

클릭 통계팀은 국가·기기·캠페인별 실시간 수치를 1분 이내에 보고 싶고 광고 정산팀은 중복 없는 확정 수치를 요구합니다. 리다이렉트 지연을 늘리지 않으면서 이벤트 수집·중복 제거·늦은 이벤트·개인정보·온라인 수치와 배치 정산의 불일치를 어떻게 다루겠습니까?

답변 예시 보기

사용자 경로와 분석 경로 분리

리다이렉트 응답은 통계 저장 성공을 기다리지 않습니다. edge/서비스가 eventId, linkId, eventTime, 거친 지역·기기 정보를 비차단 로그나 지역 collector에 기록하고 즉시 3xx를 반환합니다. 버퍼가 가득 차면 정산 대상 이벤트와 일반 분석 이벤트의 우선순위를 나누고, 어느 정도의 분석 손실을 허용하는지 명시합니다.

잠정치와 확정치의 계약

  • 스트림 처리는 event time과 watermark로 1분 집계를 만들고, 늦은 이벤트는 일정 기간 update/retraction합니다. eventId 중복 제거의 보존 기간은 재시도 최대 시간과 비용을 근거로 정합니다.
  • 실시간 대시보드는 ‘잠정’ 표식을 두고, 원본 immutable log를 재처리한 일별 배치 결과를 정산 기준으로 삼습니다. 두 경로가 같은 캠페인 귀속·봇 필터 규칙을 공유하도록 버전된 규칙 라이브러리나 데이터 계약을 둡니다.
  • IP와 user agent는 목적에 필요한 최소 수준으로 축약·해시하고 보존 기간과 접근 권한을 제한합니다. 삭제 요청이 원본·파생 집계에 어떻게 적용되는지도 법무와 합의합니다.

실시간/확정 수치 차이율, 미수집률, 중복률, watermark 지연을 운영 지표로 두고 임계치를 넘으면 정산 확정을 보류합니다. 리다이렉트 p99와 수집 손실률을 함께 부하 테스트해 관측 기능이 핵심 경로를 방해하지 않는지 확인합니다.

경험 연결 포인트 · 클릭 집계가 아니어도 로그·업무 이벤트를 비동기로 수집하고 온라인/배치 수치를 대사한 경험을 이야기합니다. 정확히 한 번이라는 표현보다 중복이 생기는 위치와 최종 수치의 책임 경계를 설명하는 편이 좋습니다.