인증 경계에서 정밀 좌표를 삭제한 뒤 지역 피드와 1대1 대화로 이어지는 지역 중고거래 흐름을 그린 그림

당근급 지역 중고거래 시스템 설계: 동네 인증·피드·채팅·신뢰

구매자가 동네 인증을 마치고 판매 글을 찾았다고 하자. 판매자에게 메시지를 보내는 사이 물건은 예약되고, 거래 뒤 상대를 차단할 수도 있다. 당근급 지역 중고거래 시스템 설계는 이 짧은 여정에서 정밀 위치, 판매 상태, 메시지 순서와 차단 결과가 서로 섞이지 않게 만드는 일이다.

이 글은 2026년 8월 15일 기준 공개 개인정보 정책과 공식 기술 문서를 대조한 가상 설계다. 실제 당근의 비공개 구조·트래픽·인증 반경·랭킹을 설명하지 않는다. 모바일 사용자가 한 거래를 끝낼 때 통과하는 네 상태 경계를 따라가며 규모 가정, 요청 형식, 데이터 모델, 병목, 장애 복구, 보안·비용과 확장 조건을 정한다. 수치와 보존 기간은 부하 테스트 결과가 아니라 비교를 위한 설계 가정이다.

20초 핵심 요약

  • 무엇: 정밀 좌표를 동네 자격으로 바꾸고 게시물 원본, 지역 피드, 1:1 채팅과 차단을 잇는 가상 중고거래 설계다.
  • 왜: 좌표를 오래 남기거나 판매 완료·차단을 늦게 반영하면 위치 노출, 헛걸음, 중복 메시지와 원치 않는 알림이 생긴다.
  • 어떻게: 한 거래가 통과하는 네 상태 경계마다 저장할 값, 허용할 시차, 감지·완화·복구 방법을 다르게 둔다.

한 거래가 통과해야 할 네 개의 경계

주 사용자는 모바일 앱에서 물건을 사거나 파는 사람이다. 웹은 공개 상세 보기와 계정 보조 기능까지만 지원한다고 가정한다. 구매자는 동네를 인증하고 가까운 판매 글을 찾아 1:1 대화를 시작한다. 판매자는 사진과 가격을 올리고 글을 예약 중, 판매 완료로 바꾼다. 양쪽 모두 거래 뒤 평가하거나 상대를 신고·차단할 수 있다.

이 여정에는 네 번의 중요한 상태 변화가 있다.

  1. 정밀 좌표는 공개 위치가 아니라 유효기간이 있는 동네 자격으로 바뀐다.
  2. 판매 글 하나는 원본으로 확정되고, 빠르게 읽기 위한 지역 피드 사본이 뒤따른다.
  3. 게시물에서 대화방이 열리고, 메시지는 그 방 안의 순서 번호를 얻는다.
  4. 차단이 확정되면 피드·상세·채팅·알림의 후속 접점이 닫힌다.

아래 그림은 왼쪽의 좌표부터 오른쪽의 차단까지 1~4번을 읽는다. 위쪽은 사용자가 확인하는 결과이고 아래쪽은 다음 경계로 넘기는 최소 데이터다.

정밀 좌표가 동네 자격, 게시물 원본과 피드 사본, 대화방을 거쳐 차단 경계에 이르는 거래 여정

그림의 결론은 모든 기능이 사용자 식별자를 공유하더라도 원본 데이터를 한곳에 합치지 않는다는 점이다. 정밀 좌표는 인증을 넘지 않고, 피드는 게시물 원본을 대신하지 않으며, 채팅 본문은 평판 점수의 재료로 복사하지 않는다.

사용자가 기대하는 응답도 경계마다 다르다. 아래 목표는 모두 설계 가정이다. p95(95th percentile, 95백분위)는 요청 100개 중 빠른 95개가 목표 시간 안에 끝나는 기준이며, 평균에 가려진 느린 경험을 찾기 위해 쓴다.

장면 응답 목표 사용자가 먼저 보는 실패
동네 인증 정상 네트워크에서 p95 2초 이내 현재 위치인데 실패하거나 다른 동네로 판정됨
첫 피드 p95 300밀리초 이내, 20개 빈 화면, 중복 카드, 판매 완료 글
게시물 작성 글 정보 승인 p95 500밀리초 중복 등록, 사진만 빠짐
채팅 전송 온라인 수신자에게 p95 1초 이내 표시 전송 중 고착, 순서 역전, 중복
차단 확인 직후 새 상호작용 차단 차단 뒤에도 메시지·알림 도착

동네 자격과 차단은 잘못 허용하지 않는 쪽이 중요하다. 피드 정렬은 잠깐 달라도 되지만 판매 상태를 조용히 덮어써서는 안 된다. 채팅도 전 세계 메시지의 순서가 아니라 한 대화방 안의 순서만 강하게 지키면 된다. 결제, 배송, 광고 경매, 그룹 채팅과 이동 경로 추적은 이 글의 범위에서 제외한다.

첫 경계: 좌표는 자격만 남기고 사라진다

구매자가 동네 인증을 누르면 앱은 위치가 필요한 이유를 설명하고 좌표·정확도·수집 시각을 한 번 얻는다. API(Application Programming Interface, 응용 프로그램 인터페이스)는 앱과 서버가 정해진 요청·응답 형식으로 기능을 호출하는 접점이며, 여기서는 좌표를 동네 자격으로 교환하기 위해 필요하다.

POST /v1/neighborhood-verifications
Idempotency-Key: 8c2f...

{
  "latitude": 37.000000,
  "longitude": 127.000000,
  "accuracy_m": 24,
  "captured_at": "2026-08-15T01:51:00Z",
  "device_attestation": "opaque-proof"
}

요청은 다음 순서로 처리한다.

  1. 인증 접점은 로그인 상태, 요청 횟수, 좌표 범위와 너무 오래된 측정값을 확인한다.
  2. Idempotency-Key는 같은 요청을 다시 보내도 결과를 한 번만 만들게 하는 중복 방지 키다. 사용자가 시간 초과 뒤 버튼을 다시 눌러 인증 기록이 늘어나는 일을 막기 위해 필요하다.
  3. 위치 판정 기능은 좌표가 서비스 동네 폴리곤 안인지 찾는다. 폴리곤은 지도 경계를 여러 좌표로 이은 면이다.
  4. 지오 인덱스는 가까운 좌표와 영역을 빨리 찾는 색인이다. 모든 동네 경계를 매번 처음부터 비교하지 않기 위해 필요하다.
  5. 경계 근처이거나 정확도가 낮으면 억지로 동네를 고르지 않고 재측정을 요청한다.
  6. 성공하면 neighborhood_id, 검증·만료 시각과 위험 신호 요약만 자격 저장소에 남긴다.

PostGIS는 관계형 데이터베이스에 지도 좌표와 영역 검색을 더하는 확장 기능이다. ST_Within으로 점이 폴리곤 안인지 판정하고 ST_DWithin으로 지정 거리 안의 위치를 찾을 수 있어, 계정 데이터와 정확한 동네 판정을 함께 처리하기 좋다. geography 형식은 거리를 미터 단위로 다루며 사용할 수 있는 공간 색인을 먼저 활용한다(PostGIS ST_DWithin, PostGIS 공간 질의).

응답에는 정밀 좌표를 넣지 않는다.

{
  "verification_id": "nv_01...",
  "neighborhood_id": "n_1234",
  "display_name": "예시동",
  "verified_at": "2026-08-15T01:51:01Z",
  "expires_at": "2026-09-14T01:51:01Z"
}

이후 피드 요청은 좌표가 아니라 이 자격을 사용한다. 원시 좌표는 접근을 제한한 별도 저장소에서 가정상 24시간만 두고 삭제한다. 인증 자격은 30일 뒤 만료시키고 이력은 90일 뒤 식별자를 최소화한다고 가정한다. 당근 개인정보 처리방침은 지역정보와 개인위치정보 처리, 위치 기반 콘텐츠와 부정 이용 방지 목적, 서비스 제공에 필요한 최소 범위 수집 원칙을 공개한다. 이 원칙은 최소 수집 방향만 뒷받침하며, 위 기간이나 구조가 당근의 실제 구현이라는 뜻은 아니다(당근 개인정보 처리방침).

인증 버튼이 계속 돌면 요청 시간 초과와 같은 중복 키의 재시도 비율을 감지한다. 앱에는 같은 키로 재시도하게 하고, 서버는 이미 성공한 결과를 돌려준다. 복구 뒤에는 사용자 한 명에게 동시에 유효한 자격이 여러 개 생기지 않았는지 대조한다. 위치 위조 방어는 기기 증명과 속도·거리 이상 신호를 함께 볼 수 있지만, 실제 위조 실험을 하지 않았으므로 영구 제재 기준으로 단정하지 않는다.

둘째 경계: 원본 글은 하나지만 피드는 늦을 수 있다

인증을 마친 구매자가 피드를 연다. 피드는 정확한 좌표가 아니라 유효한 neighborhood_id로 후보를 좁힌다. 이때 원본 게시물과 빠른 피드 사본을 구분해야 판매자가 상태를 바꾼 뒤 어느 값이 정답인지 분명해진다.

  1. 피드 접점은 동네 자격과 차단 목록의 최신 버전을 확인한다.
  2. 후보 조회는 허용 동네와 인접 지역에서 ACTIVE, 즉 판매 중인 글 식별자를 가져온다.
  3. 최신성, 넓은 거리 구간, 게시물 완성도와 신고 신호를 설명 가능한 규칙으로 정렬한다.
  4. 게시물 원본을 한 번에 읽어 지금도 ACTIVE인지 다시 확인한다.
  5. 판매 완료나 삭제된 글을 빼고 썸네일과 요약 20개를 반환한다.

Redis의 지리 자료형은 원이나 사각형 안의 점을 빠르게 찾을 수 있지만 복잡한 동네 경계를 정확히 판정하는 원본은 아니다. 초기에는 PostGIS가 판정을 맡고, 읽기가 몰릴 때 Redis를 캐시로 추가한다. 캐시는 원본에서 다시 만들 수 있는 빠른 임시 사본이며, 반복되는 후보 조회를 줄이기 위해 필요하다(Redis Geospatial). 지오해시는 지구 표면을 격자 문자열로 나눠 가까운 위치를 비슷한 앞글자로 묶는 방법이다. 여러 저장 장치로 나누기 쉽지만 실제 동네 경계와 격자가 맞지 않으므로 주변 격자를 읽은 뒤 정확한 경계를 다시 확인해야 한다.

판매자가 사진을 올릴 때는 글 정보와 파일을 분리한다. 앱은 파일 수·형식·크기를 검사받고 짧게 유효한 업로드 주소를 얻는다. 사진은 객체 저장소로 직접 올라가며, 악성 파일 검사 뒤 화면용 크기로 변환한다. 트랜스코딩은 원본 파일을 화면 크기와 형식에 맞게 다시 인코딩하는 과정으로, 표시 지연과 전송 비용을 줄이기 위해 필요하다. EXIF(Exchangeable Image File Format, 사진에 촬영 정보를 붙이는 형식)의 위치 항목은 제거한다. 파일에 숨은 촬영 위치가 판매자의 생활 반경을 노출하지 않게 하기 위해서다. 사진 속 집 번호나 표지판은 이 작업으로 없어지지 않으므로 별도 안내와 신고가 필요하다.

글 원본은 다음 상태를 지난다.

DRAFT → PENDING_REVIEW → ACTIVE → RESERVED → SOLD
                         │   │         └→ ACTIVE
                         │   ├→ EXPIRED
                         │   └→ DELETED
                         └→ REJECTED

아래 그림은 위쪽의 원본 상태를 먼저 왼쪽에서 오른쪽으로 읽고, 그다음 아래쪽의 피드 사본과 차단 전파를 시간순으로 읽는다. ACTIVE → RESERVED → SOLD와 예약 취소의 RESERVED → ACTIVE 분기를 비교하면 원본과 사본이 잠시 어긋나는 지점이 보인다.

게시물 원본 상태 전이와 피드 사본의 지연, 차단 전파와 복구 흐름

그림에서 원본 상태가 먼저 확정되고 피드 사본은 나중에 따라간다. outbox는 게시물 변경과 나중에 보낼 사건 목록을 같은 데이터베이스 작업으로 저장하는 방식이다. 상태는 바뀌었는데 피드 갱신 사건만 사라지는 틈을 막기 위해 필요하다. 사건을 여러 번 받아도 같은 결과가 되게 하는 성질을 멱등성이라고 하며, status_version을 함께 기록하면 늦게 도착한 과거 사건이 최신 판매 완료를 다시 판매 중으로 되돌리지 못한다.

post(
  post_id, seller_id, neighborhood_id, geo_cell_coarse,
  title, price, status, status_version,
  created_at, updated_at, expires_at
)
-- index: (neighborhood_id, status, created_at DESC, post_id DESC)

post_status_history(
  post_id, from_status, to_status, actor_id,
  reason, occurred_at, request_id
)

status_version은 상태가 바뀔 때 1씩 커지는 번호다. 사용자가 읽은 버전과 현재 버전이 같을 때만 변경을 허용하면, 두 기기에서 판매 완료와 예약 취소를 동시에 눌러도 뒤늦은 요청이 앞선 결정을 조용히 덮어쓰지 못한다.

fan-out은 새 글 하나를 여러 피드에 퍼뜨리는 방식이다. 여기서는 이를 “미리 배달”이라고 부르며, 읽기를 빠르게 하는 대신 복사와 상태 제거 비용이 생겨 대안을 비교할 때 필요하다.

피드 방식 장점 단점
열 때마다 조합 쓰기가 단순하고 상태 변경 반영이 쉬움 인기 동네에서 조회 지연과 비용이 커짐
사용자별 미리 배달 읽기가 빠름 사용자 수만큼 복사되고 판매 완료 제거도 비쌈
동네별 후보 공유 후 요청 시 검사 같은 지역 사용자가 후보를 공유함 상태·차단을 마지막에 다시 확인해야 함

초기에는 요청 때 조합한다. 도시 단위로 커져 읽기가 몰리면 세 번째 방식으로 옮긴다. 판매 완료 글이 남는 현상은 상태 변경부터 피드 제거까지의 지연으로 감지하고, 상세 화면에서 원본 상태를 즉시 다시 확인해 헛걸음을 줄인다. 사건을 재처리하거나 피드 색인을 다시 만든 뒤 원본과 사본의 status_version을 대조해야 복구가 끝난다.

셋째 경계: 대화 순서는 방 안에서만 강하게 지킨다

구매자가 게시물에서 채팅을 열면 최근 기록은 HTTPS(Hypertext Transfer Protocol Secure, 암호화된 웹 요청 규약)로 가져온다. HTTPS는 저장된 메시지를 안전하게 조회하고 누락분을 다시 받는 경로이며, 공용 네트워크에서 본문이 노출되지 않게 하기 위해 필요하다. 새 메시지는 WebSocket을 사용한다. WebSocket은 하나의 연결에서 앱과 서버가 서로 데이터를 보낼 수 있는 표준 통신 방식이며, 새 메시지를 곧바로 밀어 주기 위해 필요하다(WebSocket 표준 문서).

POST /v1/conversations/{conversation_id}/messages
Idempotency-Key: cm_7f...

{
  "client_message_id": "cm_7f...",
  "text": "오늘 저녁에 가능할까요?"
}
  1. 앱은 재시도해도 바뀌지 않는 client_message_id를 만든다.
  2. 채팅 접점은 참가자, 차단 관계, 게시물·대화 상태와 요청 속도를 확인한다.
  3. 같은 사용자·대화방·client_message_id가 있으면 기존 저장 결과를 반환한다.
  4. 새 메시지에는 대화방 안에서만 1씩 늘어나는 conversation_seq를 붙인다.
  5. 저장에 성공한 뒤에만 앱에 전송됨을 표시한다.
  6. 실시간 전달과 알림은 저장 사건을 받은 뒤 수행한다.
  7. 수신 앱에서 181 다음에 183이 보이면 after_seq=181로 누락분을 받아 182를 채운다.
conversation(
  conversation_id, post_id, buyer_id, seller_id,
  last_seq, state, created_at
)

message(
  conversation_id, conversation_seq, sender_id,
  client_message_id, body_ciphertext, created_at
)
-- primary key: (conversation_id, conversation_seq)
-- unique: (conversation_id, sender_id, client_message_id)

Kafka 같은 사건 기록 시스템을 쓴다면 파티션에 같은 대화방 사건을 모은다. 파티션은 큰 사건 흐름을 키별 조각으로 나눈 단위이며, 대화방 순서를 지키면서 여러 대화를 병렬 처리하기 위해 필요하다. Kafka가 한 파티션의 순서를 지원해도 메시지 중복을 자동 제거하지는 않으므로 데이터베이스 고유 조건은 그대로 둔다(Apache Kafka Design).

연결이 끊기면 재시도 간격을 점점 늘리는 지수 백오프로 다시 연결한다. 지수 백오프는 실패가 이어질수록 다음 시도를 늦추는 방법이며, 장애 중 재접속 폭주를 줄이기 위해 필요하다. 연결이 돌아오면 마지막으로 빠짐없이 받은 순서 이후를 동기화한다.

FCM(Firebase Cloud Messaging, 앱이 꺼진 기기에 알림을 보내는 서비스)은 서버가 메시지를 받아도 단말 전달까지 끝났다는 뜻은 아니다. 알림은 만료되거나 새 알림으로 대체될 수 있다. 그래서 푸시는 “서버에서 새 메시지를 확인하라”는 힌트만 맡고, 본문과 순서는 메시지 저장소에서 복구한다(FCM 메시지 수명).

메시지가 두 번 보이면 고유 키 충돌과 화면의 중복 표시율을 감지한다. 서버는 새 행을 만들지 않고 기존 결과를 반환한다. 순서가 비면 누락분을 조회하고, 복구 뒤에는 대화방의 연속 번호와 고유 조건을 대조한다. 푸시 성공률만 보고 메시지 전달을 완료로 판단해서는 안 된다.

마지막 경계: 차단 확인은 모든 후속 접점을 닫아야 한다

거래 뒤 구매자가 차단을 누르면 먼저 block 원본 행을 확정한다. 차단은 화면에서 상대를 숨기는 필터가 아니라 새 상호작용을 거부하는 권한 변경이다. 새 대화와 메시지 쓰기는 이 원본을 매번 강하게 확인한다.

block(blocker_id, blocked_id, created_at)

report(
  report_id, reporter_id, target_type, target_id,
  reason_code, evidence_ref, review_state, created_at
)

rating(
  transaction_id, rater_id, ratee_id,
  dimension, value, created_at
)

차단 확정 뒤에는 접점을 다음 순서로 닫는다.

  1. 피드 후보와 검색 결과에서 상대의 글을 제외한다.
  2. 상세 조회는 캐시보다 차단 원본을 우선한다.
  3. 새 대화 생성과 기존 대화의 메시지 쓰기를 거부한다.
  4. 이미 대기 중인 알림도 실제 발송 직전에 차단을 다시 확인한다.
  5. 각 읽기 캐시는 차단 목록 버전을 키에 넣거나 짧은 수명과 변경 사건을 함께 사용한다.

차단 뒤 알림이 오면 차단 시각 이후 발송 건수를 감지한다. 즉시 발송 전 검사를 강화하고 대기 알림을 취소한다. 원인을 고친 뒤에는 차단 시각과 알림 기록을 대조한다. 서버가 다시 응답하는 순간이 아니라 후속 접점에서 불일치가 없어졌을 때 복구가 끝난다.

신고는 누가 언제 무엇을 신고했고 운영자가 어떤 조치를 했는지 이어 붙이는 감사 사건 기록으로 남긴다. 접수 시점 게시물의 해시와 증거 파일 참조를 보관하면 내용이 바뀐 뒤에도 심사 근거를 재구성할 수 있다. 심사는 OPEN → TRIAGED → ACTIONED/DISMISSED → APPEALED → CLOSED로 진행한다고 가정한다. 자동 점수는 영구 제재가 아니라 심사 순서를 정하는 보조 수단으로 제한하고, 신고자 신원과 상세 내용은 상대에게 공개하지 않는다.

평판은 하나의 숫자로 모든 행동을 상쇄하지 않는다. 거래 완료 수, 최근 평가의 항목별 요약, 확정된 신고 사건, 계정·동네 인증의 최신성을 나눠 보여 준다. 이유를 설명하고 조작을 찾기 쉬운 대신 화면이 복잡해지고 공격자가 기준을 학습할 수 있다. 평가 자격은 실제 대화와 거래 상태에 연결하고 자기 거래와 짧은 기간의 반복 평가는 위험 신호로 둔다.

이 여정이 몰릴 때 먼저 갈라지는 곳

규모는 전체 가입자보다 거래 여정의 어느 경계가 몰리는지 보여 줘야 한다. MAU(Monthly Active Users, 한 달에 한 번 이상 이용한 사용자 수)는 저장 용량의 장기 범위를, DAU(Daily Active Users, 하루에 한 번 이상 이용한 사용자 수)는 하루 요청량을 잡는 입력이며, 서로 다른 비용과 순간 부하를 구분하기 위해 함께 쓴다. 다음은 모두 설계 가정이다.

입력 가정
MAU 1,000만 명
DAU 300만 명
활성 동네 10,000개
하루 피드 열기 사용자당 10회, 총 3,000만 회
하루 신규 게시물 DAU의 2%, 6만 건
게시물 사진 평균 5장, 원본 1장당 2MB(megabyte, 파일 크기를 나타내는 단위)
하루 채팅 1,200만 건
메시지 저장 크기 색인·부가정보 포함 평균 1KB(kilobyte, 작은 데이터 크기 단위)
피크 계수 일평균 요청률의 10배

QPS(Queries Per Second, 초당 요청 수)는 1초에 처리할 요청 개수이며, 서버와 저장소가 견딜 순간 부하를 계산하기 위해 필요하다. 피드는 평균 30,000,000 ÷ 86,400 ≈ 347 QPS, 피크 약 3,470 QPS다. 신규 글은 평균 약 0.7 QPS, 피크 약 7 QPS지만 사진은 하루 약 600GB(gigabyte, 대용량 저장 크기 단위)가 늘어난다.

채팅 쓰기는 평균 약 139 QPS, 피크 약 1,390 QPS다. 메시지 본문만 하루 약 12GB, 연간 약 4.4TB(terabyte, 1,000GB 수준의 저장 크기 단위)가 늘며 복제본과 색인은 별도다. 사진이 10MB라면 하루 원본은 3TB가 된다. 이때는 API 서버가 파일을 전달하지 않고 객체 저장소로 직접 올리는 주소와 업로드 제한이 필요하다.

평균 동네의 100배 트래픽이 번화가에 몰리면 neighborhood_id 하나가 뜨거운 저장 조각이 된다. 피드 후보를 neighborhood_id + 시간 구간으로 나누거나 읽기 캐시를 복제한다. 무작위로 나누면 쓰기는 퍼지지만 최신순 결과를 다시 합치는 비용이 생긴다. 피크가 평균의 30배라면 피드 피크는 약 10,400 QPS이므로, 전체 평균보다 동네별 QPS·후보 수·캐시 적중률의 상위 구간을 본다.

채팅에서는 메시지 QPS와 열린 WebSocket 연결 수를 따로 본다. 말이 없는 연결도 메모리와 연결 유지 신호를 소비한다. 연결은 많고 메시지는 적다면 연결 접점을 먼저 늘리고, 메시지 자체가 많다면 대화방별 저장 조각과 사건 파티션을 늘린다.

이미지는 원본·화면용·썸네일로 나눈다. CDN(Content Delivery Network, 사용자 가까운 서버에서 파일을 전달하는 망)은 사진을 가까운 지점에서 보내 지연과 원본 저장소의 전송 부담을 줄이기 위해 쓴다. 변환 대기 시간, 실패율과 주인 없는 파일 수를 함께 관측한다.

보존과 비용은 같은 삭제 경계에서 줄인다

TTL(Time To Live, 데이터가 자동 폐기되기 전까지의 수명)은 캐시·푸시·임시 좌표가 끝없이 남지 않게 하는 규칙이며, 저장 비용과 불필요한 개인정보 노출을 함께 줄이기 위해 필요하다. 다만 법적 보존 의무를 대신하지는 않는다.

데이터 보존 가정 삭제·복구 판단
인증 원시 좌표 24시간 재시도·부정 이용 확인 창 뒤 자동 삭제
동네 자격 인증 30일, 만료 후 이력 90일 이후 사용자 식별자 최소화
피드 캐시 1~5분 원본과 사건으로 재구성
게시물 원본 삭제 정책과 법적 의무에 따름 일반 조회 삭제와 분쟁 보존 분리
채팅 제품 정책·법률 검토로 결정 안전과 개인정보 최소화 사이에서 결정
신고 증거 사건 종료 뒤 정책 기간 엄격한 접근 통제와 필요 시 보존 잠금
이미지 파생본 원본 상태에 연결 삭제 사건과 주인 없는 파일 대조로 정리

Amazon Simple Storage Service(S3, 파일을 저장하는 객체 저장 서비스)의 Lifecycle 기능은 규칙에 따라 파일을 더 저렴한 저장 등급으로 옮기거나 만료시킨다. 오래된 사진의 비용과 삭제 누락을 줄이기 위해 유용하지만 데이터베이스 행과 파일을 한 번에 지우지는 못한다. 삭제 사건 재시도와 주기적인 대조가 별도로 필요하다(Amazon S3 객체 수명주기).

정밀 좌표 저장소와 공개 게시물은 논리적·물리적으로 분리한다. 위치·채팅·신고 데이터에는 역할별 접근 권한, 전송·저장 암호화와 접근 기록을 적용한다. 운영자가 채팅을 열람해야 한다면 사건 번호, 최소 기간과 감사 기록을 요구한다. 로그에는 좌표, 채팅 본문, 업로드 주소와 푸시 토큰을 남기지 않는다.

삭제 요청은 원본 데이터베이스에서 끝나지 않는다. 피드 색인, 캐시, 객체 저장소와 분석 사본을 찾아 지운다. 법적으로 보존해야 할 데이터는 일반 조회에서 분리하고 이유와 해제 시점을 기록한다. 국가와 사건 유형에 따라 의무가 달라질 수 있으므로 출시 전 법률·보안 검토가 필요하다.

작게 시작하되 경계를 바꾸는 신호를 정한다

초기에는 관계형 데이터베이스와 PostGIS, 객체 저장소, CDN, 단순 작업 큐를 둔 모듈형 단일 서비스로 시작한다. 모듈형 단일 서비스는 한 배포 단위 안에서 위치·게시물·신뢰 기능의 코드와 데이터 접근 규칙을 나누는 방식이다. 팀과 트래픽이 작을 때 변경을 단순하게 유지하기 위해 선택한다.

경계 초기 선택 보류한 대안과 이유 바꿀 신호
위치 판정 PostGIS 원본 Redis만 쓰면 복잡한 경계와 원본·캐시 시차를 보완해야 함 특정 동네의 공간 질의가 계속 느림
지역 피드 요청 때 조합 사용자별 미리 배달은 복제와 삭제 비용이 큼 뜨거운 동네의 p95 지연이 목표 초과
채팅 전달 기록·복구는 HTTPS, 새 사건은 WebSocket HTTPS만 쓰면 빈 요청과 지연이 늘고 WebSocket만 믿으면 누락 복구가 어려움 연결 수와 메시지량 중 어느 한쪽이 독립 한계 도달
서비스 구조 모듈형 단일 서비스 초기 마이크로서비스는 분산 변경·관측 비용을 너무 일찍 만듦 채팅 연결·이미지 처리·피드 색인의 부하와 실패가 명확히 분리됨

제품 검증 단계에는 피드를 요청 때 조합하고 채팅은 제한된 WebSocket 또는 주기 조회로 운영한다. 첫 피드 지연, 동네당 후보 수, 이미지 처리 대기, 동시 연결 수와 신고 처리 시간을 기록한다.

도시 단위에서는 동네별 후보 캐시와 outbox 사건 처리를 붙인다. 채팅 연결 접점을 분리하고 대화방 순서와 재연결 동기화를 적용한다. 판매 상태와 차단 변경이 사본에 반영되는 시간 목표를 두고 원본 대조 작업을 운영한다.

전국 규모에서는 지역별 데이터 조각과 읽기 경로를 나눈다. 뜨거운 동네 후보는 시간 구간으로 분산하고, 한 지역 장애가 다른 지역으로 번지지 않게 격리한다. 신고 자동화는 제재 결정자가 아니라 심사 순서 도구로 남긴다.

선택의 중심은 네 경계를 합치지 않는 데 있다. 정밀 좌표는 동네 자격을 만든 뒤 사라지고, 게시물 원본은 피드 사본보다 우선하며, 메시지 순서는 대화방 안에서 복구하고, 차단 원본은 모든 후속 접점을 닫는다. 아직 위치 위조 실험, 부하 테스트, 실제 지연·비용과 보존 기간의 법률 검토는 끝나지 않았다. 특히 이 네 검증이 끝나기 전에는 가정한 보존 기간이나 제재 기준을 운영 정책으로 확정하면 안 된다.

참고 링크

비슷한 글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다