사람이 모이기 시작하면, 꼭 생기는 일이 있다.
진짜 친절한 사람들 사이에서도 스팸, 스토킹, 도배, 악성 신고 같은 것들이 슬금슬금 들어온다.
그건 인간이 나빠서가 아니라… 그냥 세상이 원래 그렇게 생겼다. (슬프지만 사실)
그래서 Week 6의 목표는 딱 하나였다.
“완벽한 치안”이 아니라, 폭주를 ‘멈출 수 있는 버튼’을 확보한다.
최소 장비로 폭주를 막을 수 있어야 한다.
이번 주 목표: Trust/Safety MVP 4종 세트
Week 6에서 최소로 잡은 범위는 아래 4개였다.
- 차단(Block)
- 신고(Report)
- 레이트리밋(Rate Limit)
- 관리자 최소(Admin)
여기서 중요한 건 “디테일”이 아니라 작동하는 안전장치다.
어떤 기능이든 “있는 척만 하고 안 막히는” 순간, 그건 기능이 아니라 장식이니까.
✅ 1) 차단(Block): 원치 않는 접촉을 끊는 가장 빠른 스위치
차단의 철학은 단순하다.
- 상대를 차단하면 DM 생성 자체를 막는다
- 이미 대화가 있으면 접근/메시지 전송을 막는다
- “한쪽이 싫다는데 연락이 되는 상태”를 없앤다
DB는 단순하고 강하게:
blocks(blocker_id, blocked_id, reason, timestamps)[blocker_id, blocked_id] unique index로 중복 차단 방지
그리고 실전에서 가장 중요한 건 이거다:
차단 체크가 “한두 군데”만 빠져도, 사고가 난다.
그래서 가드는 Concern으로 묶고, 필요한 컨트롤러에 확실히 꽂는다.
Guard/Concern으로 묶어서 확실히 꽂는다.
✅ 2) 신고(Report): ‘문제 있어요’ 버튼을 시스템이 받아먹게 만들기
신고는 감정이 아니라 프로세스여야 한다.
- 신고자는 “저 사람 이상해요”를 남기고
- 시스템은 그걸 “관리 가능한 업무”로 변환해준다
그래서 Report는 다형성으로:
reports(reporter_id, reportable_type, reportable_id, category, detail, status, admin_note, handled_by_id, handled_at...)
신고 대상은 확장 가능하지만, MVP에선 딱 핵심만:
- Post
- Message (또는 User, 상황에 맞게)
그리고 “상태”는 과하게 복잡하게 안 갔다.
new / triaged / resolved정도면 운영 시작에는 충분하다.
✅ 3) 관리자 최소(Admin): 운영자가 할 수 있어야 하는 3가지
관리자 도구는 “멋진 대시보드”가 아니라 처리 버튼이다.
Week 6에서 운영자가 할 수 있어야 하는 건 딱 3개로 제한했다.
- 신고 목록 보기 (기본은 new만)
- 신고 상세 보기 (컨텍스트 포함)
- 상태 변경 + 메모 남기기 (누가/언제 처리했는지 남김)
그리고 여기서 내가 제일 예민하게 보는 지점:
admin 페이지는 처음부터 강하게 막아야 한다.
라우트/권한 실수 한 번이면 바로 “관리자 화면 공개” 사고로 간다.
그래서 Admin::BaseController에서 current_user.admin?로 초반부터 락을 걸었다.
운영도구는 “예쁜 UI”가 아니라 “업무 루프”다.
✅ 4) Rate Limiting: 세상에 공짜 트래픽은 없다 (특히 스팸은)
스팸러는 진짜 부지런하다.
그래서 레이트리밋은 기능 완성도보다 기본 방어력이 중요하다.
최소로 걸어도 체감 큰 지점은 보통 이거:
- 로그인/회원가입 (브루트포스)
- 메시지 전송 (도배)
- 신고 생성 (악성 신고 도배)
구현은 Rack::Attack 같은 방식으로 시작하면 효율이 좋다.
(Week 6의 목적은 “멈출 수 있는 버튼”이니까, 일단 막고 다음에 고도화.)
이번 주 작업 정리: 커밋 흐름(8개)
Week 6은 작업 흐름이 비교적 정석이었다.
- DB 마이그레이션: blocks / reports 확장
- 모델 & 연관관계: Block/Report + validation
- BlockGuard Concern: 차단 체크 공통화
- 차단 UI: 대화/프로필에서 빠르게 동작
- 신고 기능: 최소 폼 + 중복/권한 가드
- Admin 패널: 신고 처리 루프 완성
- Rate limiting: 남용 방지 기본장치
- 테스트: 핵심 시나리오 통합 테스트로 고정
“완료”의 기준은 이렇게 잡았다
나는 기능 완료 선언을 항상 “버튼이 실제로 사고를 멈추는지”로 한다.
- 차단한 상대와 DM 생성/접근/전송이 실제로 막히는가
- 신고가 관리자 화면에 쌓이고 상태 변경이 되는가
- 레이트리밋이 걸리면 429 등 명확한 신호가 오는가
- 테스트가 최소한 “핵심 사고 시나리오”를 덮고 있는가
Week 6은 이 기준을 통과했다.
즉, 이제 플랫폼은 “선의만 믿는 단계”를 졸업했다.
버튼이 실제로 사고를 멈추는지가 완료의 기준.
이번 주에 내가 배운 것
- Safety는 기능이 아니라 습관이다
체크가 한 군데 빠지면 끝. 그래서 Guard/Concern이 중요함. - Admin은 작게 시작할수록 오래 산다
운영도구는 “예쁜 UI”가 아니라 “업무 루프”다. - 레이트리밋은 싸움이 아니라 보험이다
안 쓰길 바라지만, 없으면 바로 터진다.
다음 주(Week 7)로 넘어가기 전에
이제 최소 안전장치가 생겼다.
다음 단계는 보통 이런 식으로 커진다:
- 신고 누적 시 자동 조치(숨김/제한) 룰
- 악성 신고자 페널티
- 운영 통계(신고 카테고리/사용자 히스토리)
- 더 정교한 레이트리밋(유저 기준, Redis 기반)
근데… 나는 아직 욕심 안 부릴 거다.
Week 6의 목적은 “경찰서 만들기”가 아니라 비상정지 버튼이었으니까.
끝맺음: 안전은 ‘성장하는 서비스의 예의’다
플랫폼을 만든다는 건, 결국 사람을 모으는 일이고,
사람이 모이면 반드시 “문제”도 따라온다.
그래서 Week 6은
기능 개발이라기보다, 운영자로서의 태도를 한 줄 추가한 주간이었다.
“우리 서비스는, 멈출 수 있다.”
다음 주는 또 어떤 현실이 튀어나올지 모르지만,
적어도 이제는 도망치지 않고 스위치를 누를 수 있다.
우리 서비스는, 멈출 수 있다.
