01. 요구사항 분석¶
1.1 프로젝트 목적¶
Tiny Second-hand Shopping Platform은 회원이 상품을 등록하고 다른 회원과 대화하며, 문제가 있는 사용자나 상품을 신고할 수 있는 소규모 중고거래 플랫폼입니다. 이 과제의 핵심은 기능 수를 늘리는 일이 아니라 인증·권한·입력값·파일·실시간 통신·상태 변경 과정에서 발생할 수 있는 보안 문제를 설계와 테스트로 확인하는 데 있습니다.
1.2 대상 사용자¶
| 사용자 | 주요 작업 |
|---|---|
| 비회원 | 상품 목록과 상세 정보 조회 |
| 일반 회원 | 회원가입·로그인, 본인 정보 변경, 상품 관리, 채팅, 신고 |
| 관리자 | 신고·제재 상태 확인과 허용된 관리 작업 수행 |
가입 시 수집하는 필수 정보는 아이디와 비밀번호입니다. 이메일, 전화번호, 주소, 결제정보는 수집하지 않습니다.
1.3 범위와 개발 순서¶
1차 핵심 범위¶
- 회원가입, 로그인, 사용자 조회
- 본인 소개글과 비밀번호 변경
- 상품 등록, 본인 상품 관리, 목록·상세 조회
- 상품당 0~4장 이미지 업로드(이미지는 선택 사항)
- 인증 회원의 상품 기반 1대1 채팅과 기존 대화 이력 유지
- 사용자·상품 신고
- 기간이 정해진 상품 비노출과 사용자 휴면 처리
2차 필수 범위¶
- 상품 검색·정렬·페이지 나누기
- 권한이 제한된 관리자 기능
- 실제 결제와 분리된 모의 내부 잔액 이체
2차 기능의 정책과 수락 기준에 맞춰 검색·관리·모의 이체 제품 코드, 마이그레이션, HTTP 경계와 자동 검증을 구현했습니다. 최종 상태는 실제 실행 근거와 함께 체크리스트와 부록에 기록합니다.
범위 밖¶
- 실제 은행·PG 결제, 정산, 환불
- 이메일 인증과 비밀번호 분실 복구
- 전화번호·주소·결제정보 등 추가 개인정보 수집
- 별도 SPA와 토큰 기반 분리 API
- 영구 삭제 방식의 자동 제재
- 상시 공개 운영 서비스와 운영급 SRE
1.4 기능 요구사항¶
| ID | 기능 요구사항 | 현재 상태 |
|---|---|---|
FR-USER-01 |
사용자는 고유 아이디와 비밀번호로 회원가입하고 로그인할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-USER-02 |
사용자는 다른 사용자의 공개 정보만 조회할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-USER-03 |
로그인한 사용자는 본인의 소개글과 비밀번호만 변경할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-PRODUCT-01 |
로그인한 사용자는 이름, 가격, 설명으로 상품을 등록할 수 있어야 하며 이미지는 0~4장까지 선택적으로 첨부할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-PRODUCT-02 |
상품 소유자만 자신의 상품을 수정·삭제할 수 있어야 한다. 판매 상태는 거래 수명주기에서 파생되며 상품 편집 화면에서 임의로 덮어쓰지 않는다. | 구현·자동 검증 PASS |
FR-PRODUCT-03 |
비회원도 공개 상품 목록과 상세 정보를 조회할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-CHAT-01 |
인증된 사용자는 전체 채팅에 참여할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-CHAT-02 |
상품에서 시작한 1대1 채팅은 판매자와 구매자 두 참여자만 입장·열람·전송할 수 있어야 하며, 로그인하지 않은 사용자는 로그인 안내를 받아야 한다. | 구현·자동 검증 PASS |
FR-REPORT-01 |
인증된 사용자는 사유와 함께 사용자 또는 상품을 신고할 수 있어야 한다. | 구현·자동 검증 PASS |
FR-REPORT-02 |
유효 신고가 기준에 도달하면 상품 비노출 또는 사용자 휴면을 중복 없이 적용해야 한다. | 구현·자동 검증 PASS |
FR-SEARCH-01 |
공개 상품을 검색하고 정렬하며 페이지 단위로 조회할 수 있어야 한다. | 구현·자동 검증 수행 |
FR-ADMIN-01 |
관리자는 부여된 권한 안에서 사용자·상품·신고를 관리하고 변경 기록을 남겨야 한다. | 구현·자동 검증 수행 |
FR-TRANSFER-01 |
사용자는 실제 결제가 아닌 내부 모의 잔액을 다른 사용자에게 원자적으로 이체할 수 있어야 한다. | 구현·자동 검증 수행 |
상품 대화는 공개 프로필에서 별도 1:1 방을 새로 만드는 흐름이 아니라 상품 상세에서 판매자와 시작합니다. 이미 만들어진 방과 메시지 이력은 다시 접속해도 유지합니다. 채팅 화면은 세션 인증을 사용하며 WebSocket이 연결되면 상품 대화 · 연결됨을 표시하고, 비정상 종료는 최대 5회까지 지수 백오프로 재시도합니다. 10초 이상 안정적으로 연결된 뒤에만 재시도 횟수를 초기화합니다.
2차 상세 정책¶
아래 한글 ID는 구현과 테스트에서 그대로 사용하는 정책 식별자입니다. 하나의 상위 기능 ID에 여러 정책 ID를 연결해 정상·거부 결과를 모호하지 않게 구분합니다.
모의 잔액 이체¶
| 정책 ID | 확정 요구사항 | 수락 기준 |
|---|---|---|
이체-범위-01 |
잔액은 현금 가치가 없는 Decimal(12,2) 모의 잔액입니다. 사용자당 계정은 하나이고 신규 계정 생성 또는 기존 회원의 2차 기능 도입 시 SEED_ISSUE journal로 100,000.00을 정확히 한 번 부여합니다. journal은 사용자 계정 +100,000.00과 사용자에게 노출되지 않는 SEED_RESERVE 시스템 원장 계정 -100,000.00의 두 항목으로 합계 0을 이룹니다. 충전·출금·환불·환전·외부 결제는 제공하지 않습니다. |
journal_kind=SEED_ISSUE이고 대상 사용자 계정이 있는 행에만 적용되는 부분 고유 제약으로 초기 잔액과 균형 원장이 정확히 한 번만 생기며 TRANSFER·COMPENSATION은 막지 않음 |
이체-금액-01 |
화면·HTTP 입력은 1..99,999,999 정수 원화만 허용하고 사용자에게도 100,000원처럼 소수 없이 표시합니다. 내부 잔액·원장은 호환성과 합계 보존을 위해 Decimal(12,2)로 저장하며, 내부 Decimal 허용 범위는 0.01..99,999,999.99입니다. 레거시 원장 스키마의 전체 Decimal 경계 0.00..1,000,000,000.00은 저장 정밀도 계약으로 보존하되, HTTP·UI에서는 더 좁은 양의 정수 범위를 적용합니다. canonical payload에서는 정수 입력을 고정 소수 둘째 자리로 정규화합니다. |
1원과 99,999,999원은 허용하고 0·음수·소수·범위 초과는 잔액·원장을 바꾸지 않음 |
이체-계정-01 |
인증된 계정 소유자만 CSRF가 유효한 POST로 계정을 닫을 수 있습니다. 종료 트랜잭션은 이체와 같은 MockAccount 행 잠금을 얻은 뒤 잔액 0.00과 열린 상태를 다시 확인하고 닫힘을 원자적으로 기록합니다. 별도 처리 중 카운터는 두지 않습니다. 닫힌 계정의 원장·감사 기록은 보존하며 이후 발신·수신 이체를 모두 거부합니다. |
종료가 잠금을 먼저 얻으면 뒤 이체는 닫힌 상태로 거부되고, 이체가 먼저 끝나면 종료는 갱신된 잔액을 보고 허용 또는 거부됨 |
이체-대상-01 |
발신자는 세션 사용자로만 정하고 수신자는 고유 아이디의 정확한 일치로 찾습니다. 자기 이체와 존재하지 않는 수신자는 거부하며, 발신자와 수신자 모두 DB 현재 시각으로 계산한 활성 상태여야 합니다. | 타인 ID를 발신자로 제출해도 무시하고, 자기·미존재·휴면 사용자 이체는 상태 변경 없이 실패 |
이체-잔액-01 |
발신 잔액이 금액보다 작으면 이체를 거부합니다. 거부 시 두 잔액, 원장, 성공 이체 기록은 모두 변하지 않습니다. | 9.00 잔액의 10.00 이체는 실패하고 양 계정과 합계가 이전과 같음 |
이체-원자성-01 |
SEED_ISSUE, TRANSFER, COMPENSATION journal은 모두 정확히 두 LedgerEntry를 가지며 합은 0입니다. PostgreSQL DEFERRABLE INITIALLY DEFERRED constraint trigger가 journal/entry INSERT를 커밋 시 검증하고 두 테이블의 UPDATE·DELETE trigger가 변경을 거부합니다. 이체·두 잔액·감사도 같은 트랜잭션에서 전부 커밋/롤백합니다. |
빈·한 항목·세 항목·합계 불일치 journal은 커밋 실패하고 정상 두 항목 journal만 저장되며 journal/entry UPDATE·DELETE와 중간 오류는 흔적을 남기지 않음 |
이체-멱등-01 |
각 요청은 발신자 범위 UUID 키를 사용합니다. wire 금액은 정수 원화로 받고 canonical payload는 버전 1, trim·대소문자 변환 없는 수신자 고유 아이디 원문, Decimal 금액의 고정 소수 둘째 자리 문자열을 키 순서가 고정된 UTF-8 JSON으로 직렬화합니다. 100은 canonical 100.00으로 저장됩니다. safety shared lock→멱등성 lock 뒤 같은 payload의 최초 성공·업무 거부 HTTP 상태와 body를 영구 재현하고 다른 payload는 409입니다. TransferSafetyState=OPEN 검사는 저장 결과가 없는 새 키에만 적용하므로 BLOCKED 중에도 저장된 결과를 그대로 재현합니다. 시스템 실패로 전체 롤백된 키만 다시 처리합니다. |
성공 201과 부족 잔액 422 TRANSFER_NOT_ALLOWED를 저장한 뒤 잔액·계정 상태 변경과 BLOCKED 전환을 해도 같은 키의 순차·병렬 재요청은 동일 status/body와 상태 불변을 반환하고, 다른 payload는 409이며 롤백된 키만 재처리 가능 |
이체-동시성-01 |
잠금 순서는 전역 safety shared advisory lock → 멱등성 advisory lock → 계정 PK 오름차순입니다. 새 이체·종료는 shared lock 안에서 TransferSafetyState=OPEN을 확인합니다. 저장된 같은-payload 결과 재현은 멱등성 조회 뒤 OPEN 검사 없이 반환합니다. 대사·재개는 같은 키의 exclusive advisory lock으로 기존 이체·종료가 끝날 때까지 기다리고 새 요청 진입을 막습니다. 40001·40P01만 최초 뒤 새 트랜잭션으로 3회 재시도합니다. |
BLOCKED 전환과 새 이체를 barrier로 경합해도 전환 뒤 늦은 커밋이 없고, 저장된 201·422 재현은 유지되며, 100,000.00의 80,000.00 병렬 이체는 한 건만 성공하고 네 번 충돌은 부분 기록 없이 503 |
이체-보정-01 |
잔액과 원장 항목은 직접 수정·삭제하지 않습니다. 취소·조정은 원본을 참조하는 반대 방향의 보정 원장 항목으로만 표현하며 사용자·일반 관리자 기능에는 제공하지 않습니다. 사용자 휴면은 계정을 동결할 뿐 닫거나 잔액을 없애지 않습니다. | 원본과 보정 항목이 함께 남고 합계가 보존되며 휴면·복구 전후 잔액이 같음 |
이체-대사-01 |
singleton TransferSafetyState가 OPEN일 때만 새 이체·종료를 허용합니다. 대사는 exclusive safety advisory lock을 얻은 뒤 전체 잔액·원장을 검사하고 불일치면 incident UUID와 DB 시각을 기록해 BLOCKED로 바꿉니다. 별도 ledger_maintainer 역할의 reconcile_mock_ledger --resume <incident>도 exclusive lock 안에서 보정/복원 뒤 전체 불일치 0건을 확인하고 감사와 함께 재개합니다. 저장된 같은-payload 멱등 결과는 BLOCKED 중에도 재현합니다. |
전환 전 시작한 요청은 먼저 끝나고 전환 뒤 새 요청은 모두 503이며 저장된 201·422는 동일 재현됩니다. 잘못된 incident·불일치 잔존·권한 없는 재개와 재개 중 신규 이체는 안전하게 차단 |
상품 검색¶
| 정책 ID | 확정 요구사항 | 수락 기준 |
|---|---|---|
검색-공개-01 |
검색은 기존 상품 목록과 같은 공개 정책을 사용합니다. 비회원과 회원은 활성 비노출 제재가 없는 상품만 볼 수 있고, 소유자 소개·신고·채팅·관리 정보는 검색 대상이나 결과에 포함하지 않습니다. | 같은 시점의 목록에서 숨겨진 상품은 제목을 정확히 검색해도 결과·개수에 나타나지 않음 |
검색-입력-01 |
q는 Unicode trim→NFC 뒤 code point 0..100자입니다. 2차 migration은 기존 Product.title·description을 NFC로 backfill하고 DB CHECK로 저장 문자열의 NFC를 강제하며 모든 상품 생성·수정 서비스도 저장 전에 NFC로 정규화합니다. C0/C1은 거부하고 제목·설명 icontains만 검색합니다. |
저장값/검색어의 조합형·분해형 모든 조합이 같게 검색되고 raw DB NFD 저장은 거부되며 정규화 후 101자는 상품 쿼리 전에 400 |
검색-필터-01 |
GET wire 값은 status=available\|sold, min_price, max_price입니다. 현재 모델의 판매중(AVAILABLE), 판매완료(SOLD)와 정수 가격 1..999,999,999,999만 허용하며 역전 범위는 400입니다. 필터는 검색어와 AND로 결합합니다. |
상태·가격 경계 안의 공개 상품만 남고 알 수 없는 상태, 0·음수·소수·역전 범위는 쿼리 전에 거부됨 |
검색-정렬-01 |
GET wire 값은 sort=newest(기본), price_asc, price_desc뿐이며 각각 최신순·가격낮은순·가격높은순입니다. 모든 정렬은 마지막에 상품 ID 내림차순을 적용합니다. |
같은 가격·시각의 상품도 반복 요청에서 같은 순서를 유지하고 알 수 없는 정렬값은 400 |
검색-페이지-01 |
페이지는 1..500 정수이고 페이지당 20건으로 고정합니다. 페이지 번호가 없으면 1, 500 이내에서 마지막 결과를 넘으면 빈 목록과 실제 공개 전체 건수를 반환합니다. 0·음수·소수·비정수·501 이상은 쿼리 전에 400으로 거부합니다. 정렬·필터 후에 페이지를 나눕니다. |
공개 결과 21건은 1쪽 20건·2쪽 1건이고 3쪽은 빈 목록이며, 500은 처리하되 501과 매우 큰 값은 쿼리 없이 거부됨 |
제한된 관리자 기능¶
| 정책 ID | 확정 요구사항 | 수락 기준 |
|---|---|---|
관리-최소권한-01 |
일반 관리 작업은 is_staff, 정확한 custom codename, 활성 AdminScopeGrant를 모두 요구합니다. moderation.manage_admin_scope만 콘텐츠 grant가 없는 meta-scope 예외이며 is_superuser=True와 해당 permission의 직접 user_permissions 행을 모두 검사합니다. 비인증 HTML은 302, staff/codename 부족은 403, 범위 밖·미존재는 404입니다. |
superuser 자동 권한만으로 scope 변경할 수 없고 일반 작업은 grant 없이는 거부되며 meta-scope도 직접 permission과 superuser 조건 중 하나가 빠지면 403 |
관리-범위-01 |
AdminScopeGrant는 staff, codename, USER/PRODUCT FK 중 정확히 하나, version, 취소 메타데이터를 저장하고 활성 부분 고유 제약을 둡니다. 일반 관리 작업만 연결 대상 grant를 요구하며 meta-scope 최고관리자는 대상 grant 없이 모든 대상의 grant를 부여·취소할 수 있습니다. 비밀번호·세션·채팅·잔액·원장은 열람·변경하지 않습니다. |
무대상·양대상·활성 중복은 DB 거부, 범위 밖은 404, grant 취소는 version 증가와 대상 staff 세션 무효화, meta-scope는 별도 조건 없이는 403 |
관리-재인증-01 |
제재와 scope 변경은 모두 UTC 재인증 300초 이하, CSRF POST, NFC code point 10..500자 reason, 최신 version을 요구합니다. grant 생성은 대상 staff의 auth_epoch를 version, 취소는 grant version을 사용합니다. 자기 grant 변경은 금지합니다. |
299/300초 허용·301초 거부, 사유·version·CSRF 경계, stale staff/grant version, 일반 superuser·직접 permission만·자기 변경을 거부 |
관리-가역성-01 |
관리자 제재는 DB 현재 시각부터 정확히 7일짜리 기록이며 영구 삭제하지 않습니다. 조기 해제는 원본과 별도 SanctionRelease를 한 번 기록합니다. 만료 시각과 같거나 이후에는 활성 제재 0건이고 자연 만료는 해제 기록·성공 감사를 만들지 않습니다. 병렬 적용은 활성 제재·적용 성공 감사 각 1건, 병렬 조기 해제는 해제 기록·해제 성공 감사 각 1건만 남깁니다. |
적용 직후 활성 1건, 만료 직전 1건, 정확한 만료 시각 0건, 조기 해제 뒤 활성 0건이며 중복 승자는 기존 결과를 반환하고 기록 수를 늘리지 않음 |
관리-감사-01 |
성공한 제재 적용·해제·grant 변경, 권한 거부, version 충돌은 행위자, 작업, 대상, 사유, 변경 전후, 결과, DB 시각, 상관 ID를 추가 전용 감사로 남깁니다. 감사 INSERT가 실패하면 업무 변경도 롤백하고 민감값 없는 상관 ID 오류를 남긴 뒤 503을 반환하며 DB 감사 행은 0건입니다. 애플리케이션과 감사 DB 역할은 기존 감사 UPDATE·DELETE 권한을 갖지 않습니다. |
정상 결과는 정확히 감사 1건, audit insert 실패는 업무 변경·DB 감사 0건과 503, 직접 UPDATE·DELETE는 원본 불변 상태로 DB에서 거부 |
관리-중복-01 |
같은 대상·종류의 활성 제재가 있으면 추가 적용으로 기간을 늘리거나 기록을 복제하지 않고 기존 결과를 반환합니다. 만료된 제재는 새 적용을 허용하지만 만료 시각 이후 해제 요청은 409와 충돌 감사 1건을 남기고 SanctionRelease는 만들지 않습니다. 이미 조기 해제된 제재의 반복 해제는 저장된 해제 결과를 반환합니다. |
병렬 적용 승자만 활성 제재·적용 성공 감사 각 1건, 병렬 해제 승자만 SanctionRelease·해제 성공 감사 각 1건을 만들며 자연 만료 뒤 해제는 충돌 감사 외 상태 기록 없음 |
최초·복구 meta-scope 권한은 웹이 아니라 scope_bootstrap DB 역할로 실행하는 bootstrap_scope_manager --username USERNAME --reason REASON 관리 명령으로만 직접 부여합니다. 유효 최고관리자는 활성 사용자이면서 is_superuser=True이고 moderation.manage_admin_scope 직접 user_permissions 행을 가진 사용자입니다. 명령은 전용 bootstrap advisory lock을 얻은 단일 트랜잭션 안에서 유효 최고관리자 0명을 다시 확인하고, 대상의 활성·is_superuser 상태를 검사한 뒤 direct permission과 추가 전용 감사 1건을 함께 커밋합니다. REASON은 NFC 정규화 뒤 10..500 code point이며, 감사 실패 시 권한도 롤백합니다. 동시 실행의 패자는 잠금 뒤 재검사에서 결정적으로 거부됩니다. 유효 최고관리자가 한 명이라도 있거나 일반 애플리케이션 DB 역할로 실행하면 요청을 거부합니다.
2차 수락 예시¶
- 초기
SEED_ISSUE로 100,000.00을 보유한 활성 사용자민수가 활성 사용자영희에게 UUID 멱등 키K로 80,000.00을 보내면 잔액은 20,000.00과 기존 수신 잔액+80,000.00이 되고 합이 0인 차변·대변 한 쌍이 생깁니다.K와 같은 payload를 다시 보내면 같은 결과를 반환하고 잔액은 더 바뀌지 않습니다. 민수가 남은 20,000.00으로 20,000.01을 보내거나 휴면 사용자에게 보내면 실패합니다. 실패 전후 계정 잔액 합계와 원장 수는 같습니다.- 비노출 상품 1개를 포함해 검색 조건에 맞는 상품이 21개라면 전체 건수는 20, 1쪽은 20건, 2쪽은 빈 목록입니다. 관리 권한이 있어도 일반 상품 검색 결과에 비노출 상품을 섞지 않습니다.
신고열람만 가진 관리자가 제재 URL을 직접 호출하면403이고 상태는 그대로입니다.제재적용권한과 최근 재인증을 갖춘 관리자가 사유와 함께 처리하면 7일 제재와 민감값 없는 감사 기록이 한 트랜잭션으로 남습니다.
이 절의 2차 정책은 제품 코드·마이그레이션·화면·URL에 구현하고 PostgreSQL·Redis 기반 자동 검증에 반영했습니다. 실행 결과와 보정 내역은 검증 기록에서 추적합니다.
1.5 보안 요구사항¶
| ID | 보안 요구사항 | 확인 기준 |
|---|---|---|
SR-AUTH-01 |
비밀번호를 평문으로 저장하거나 로그에 남기지 않는다. | Django 비밀번호 해시 사용과 저장값 점검 |
SR-AUTH-02 |
로그인 실패 횟수와 요청 속도를 제한하고 계정 존재 여부가 드러나지 않는 응답을 사용한다. | 경계값·병렬 요청·알 수 없는 계정 음성 테스트 |
SR-AUTHZ-01 |
프로필·상품·채팅방·신고·관리 기능은 요청마다 서버에서 권한을 확인한다. | 다른 사용자 객체에 대한 조회·변경 거부 테스트 |
SR-INPUT-01 |
아이디, 소개글, 상품 정보, 채팅, 신고 사유, 검색어의 길이·형식·제어문자를 검증한다. | 정상값, 경계값, 잘못된 값 테스트 |
SR-UPLOAD-01 |
이미지는 JPEG·PNG·WebP만 허용하고 5MiB·4096×4096 이하로 제한한다. | 확장자·MIME 불일치, 손상·과대 파일 거부 테스트 |
SR-UPLOAD-02 |
업로드 이미지를 완전히 디코딩한 뒤 새 파일로 재인코딩하고 메타데이터를 제거한다. | 재인코딩 결과와 저장 경로 점검 |
SR-SESSION-01 |
세션 쿠키, CSRF, 허용 호스트와 HTTPS 설정을 환경에 맞게 적용한다. | 보호 요청·위조 Origin·CSRF 음성 테스트 |
SR-CHAT-01 |
WebSocket 연결에서 인증, Origin, 방 참여자, 계정 상태를 확인한다. | 비인증·타인 방·잘못된 Origin 연결 거부 테스트 |
SR-CHAT-02 |
채팅 저장 성공을 기준으로 응답하고 재전송 시 중복 저장하지 않는다. | 동일 UUID 재전송·저장 후 전파 실패 테스트 |
SR-REPORT-01 |
자기 신고, 중복 신고, 기준 미달 계정 신고를 제재 집계에서 제외한다. | 신고 조건과 경계값 테스트 |
SR-REPORT-02 |
신고 저장, 임계값 판정, 제재, 감사 기록을 원자적이고 멱등적으로 처리한다. | 동시 신고·중복 제재·만료 경합 테스트 |
SR-ERROR-01 |
오류 응답과 로그에 비밀번호, 세션, 내부 경로, 상세 예외를 노출하지 않는다. | 오류 메시지와 로그 내용 점검 |
SR-TRANSFER-01 |
모의 이체는 잔액 보존, 양수 금액, 자기 이체 금지, 멱등성, 동시성을 보장한다. | 동시 이체·재시도·실패 롤백 테스트 |
1.6 신고·제재 정책¶
- 신고자는 인증된 활성 계정이며 가입 후 7일 이상이어야 합니다.
- 자기 자신 또는 본인 상품 신고는 허용하지 않습니다.
- 신고자와 대상의 조합당 한 번만 유효합니다.
- 최근 7일 동안 서로 다른 유효 신고자 5명이 모여야 제재를 검토합니다.
- 상품 신고와 사용자 신고는 별도로 집계합니다.
- 상품은 7일 동안 비노출 처리하고, 사용자는 서로 다른 신고 맥락이 2개 이상일 때 7일 휴면 처리합니다.
- 제재는 영구 삭제하지 않으며 만료 시 현재 시각을 기준으로 효력이 사라져야 합니다.
이 정책은 설계 기준입니다. 실제 서비스·경합 테스트가 끝나기 전에는 적용 완료로 판단하지 않습니다.
1.7 완료 판단 원칙¶
- 코드 경로만 존재하면
구현 중으로 표시합니다. - 실제 명령과 결과가 없으면
PASS로 표시하지 않습니다. - 코드나 환경이 바뀌면 영향받는 테스트를 같은 조건에서 다시 실행합니다.
- 요구사항과 코드가 다르면 문서가 아니라 코드·테스트 상태를 기준으로 차이를 기록합니다.