더블유클럽
경력근거: 개인정보와 결제 등 민감 영역의 보안
※ 실제 산출물이 아닌 AI 예측 시안입니다.
더블유클럽은 경제력 서류 인증으로 회원 신뢰를 쌓는 매칭 서비스이고, 이 공고는 주요업무에 인증 서류 파기 정책의 기술적 이행을 직접 적었습니다. 소득금액증명원과 재직증명서는 연봉과 직장이 그대로 적힌 서류라 승인 뒤 얼마나 남겨 둘지가 다른 데이팅 앱보다 무겁고, 심사 결과별 보관 일수와 탈퇴 회원 처리가 맞물리는 판정은 이 서비스의 인증 흐름이 있어야 성립합니다. 이 포트폴리오는 서류 기록과 저장소 파일을 맞대어 남은 서류를 판정하는 Nest.js 규칙과 Next.js 어드민 화면을 보여줍니다. 개인정보보호법 해석이나 보관 기간을 얼마로 정해야 하는지에 대한 법률 전문성은 주장하지 않습니다.
주요업무에 결제·본인인증·서류 인증 같은 민감 영역의 개인정보 보호가 있고, 괄호 안에 인증 서류 파기 정책의 기술적 이행을 따로 적었습니다. W클럽은 경제력 서류 인증으로 회원을 받는 서비스라 소득금액증명원·재직증명서가 매일 들어오고, 그 서류가 정책대로 사라졌는지는 서류 기록과 저장소 파일을 맞대어 봐야 압니다.
이 자리는 코드를 처음부터 쓰기보다 Claude Code·Codex 가 만든 결과물에서 설계 결함과 보안 문제를 찾아 고치는 자리입니다. 날짜 계산과 판정 차례처럼 틀리기 쉬운 규칙을 AI 초안으로 받고, 파기 기한 당일·심사 대기 서류 같은 테스트로 걸러 내는 작업 방식이 그 요구와 같은 모양입니다.
공고의 기술스택은 API 가 Nest.js·TypeScript, 어드민이 Next.js 입니다. 판정 규칙은 Nest.js 서비스에, 운영 담당자와 개인정보 보호 담당자가 보는 화면은 Next.js 어드민에 두고, 우대사항의 개인정보보호 점검 대응은 3주차 주간 보고로 닿게 합니다.
W클럽 회원은 가입할 때 소득금액증명원·근로소득 원천징수영수증·재직증명서 같은 경제력 서류를 앱으로 올리고, 운영 담당자가 그 서류를 승인하거나 반려합니다. 이 관리자 페이지는 서류 제출 기록, 저장소 파일 목록, 회원 탈퇴 기록, 서류 종류와 심사 결과별 보관 일수를 적은 보관 기준표 네 가지를 받아 아직 남아 있는 서류마다 다섯 판정 중 하나를 붙입니다. 심사일부터 오늘까지 한국 날짜로 며칠이 지났는지 세어 보관 일수와 견주고, 탈퇴한 회원의 서류와 파기 처리 뒤에도 원본이나 미리보기 이미지가 남은 서류, 서류 기록 없이 저장소에만 있는 파일을 따로 세웁니다. 개인정보 보호 담당자는 이 판정으로 매주 대표에게 파기 이행 상황을 보고하고, 운영 담당자는 같은 화면에서 지금 지워야 할 서류를 고릅니다.
경제력 서류 인증 심사를 맡는 W클럽 운영 담당자와, 인증 서류 파기 정책이 실제로 지켜졌는지 매주 확인해 대표에게 보고하는 개인정보 보호 담당자
지금 파기는 새벽 배치가 서류 종류별로 돌고, 개인정보 보호 담당자는 배치가 성공했다는 알림만 받습니다. 배치가 어떤 서류 종류를 빠뜨렸는지, 서류 기록에는 파기 시각이 찍혔는데 저장소에 미리보기 이미지가 남았는지는 서류 제출 기록과 저장소 파일 목록을 서로 맞대어 보기 전에는 드러나지 않습니다. 네 표를 서류 번호와 회원 번호로 잇고 보관 기준표로 기한을 계산하면, 남아 있으면 안 되는 서류가 판정 다섯 값 중 하나로 서고 담당자는 그 목록을 근거로 파기와 보고를 합니다.
운영 담당자가 서류 제출 기록·저장소 파일 목록·탈퇴 기록·보관 기준표 네 파일을 올리면 남아 있는 서류마다 다섯 판정 중 하나와 경과 일수, 보관 일수, 남은 파일 이름이 서는 Next.js 관리자 페이지입니다. 한국 날짜 기준 경과 일수 계산, 서류 종류와 심사 결과로 보관 기준을 고르는 규칙, 서류 하나에 파일이 둘 이상인 경우를 묶는 집계, 판정 차례가 이 주에 들어갑니다. 저장소는 실제 버킷이 아니라 내려받은 파일 목록을 받는 것으로 시작합니다.
오전 9시 이전 심사, 탈퇴 직후 새벽 배치 전 서류처럼 날짜가 갈리는 서류를 늘린 더미 300건을 만들고, 개인정보 보호 담당자 입장에서 먼저 적어 둔 판정과 몇 건이 갈리는지 판정별로 셉니다. 판정 목록에서 고른 서류를 로컬 MinIO 버킷에서 지우고 서류 기록에 파기 시각을 남기는 Nest.js 파기 작업을 붙이며, 미리 보기(삭제 없이 지울 목록만 출력)로 먼저 돌린 뒤 실행하고 다시 판정해 남은 서류가 0 이 되는지 봅니다. 파기 이력은 별도 테이블로 두고 DB 마이그레이션 파일로 추가합니다.
GitHub Actions 가 매일 새벽 판정을 돌려 탈퇴 회원 서류가 하나라도 남으면 운영 채널에 알림을 보냅니다. 개인정보 보호 담당자는 주간 판정 집계를 고른 뒤 Claude 가 쓴 보고 초안을 받아 대표에게 올리고, 초안의 건수가 집계 값과 다르면 그 주 보고는 집계 표만 보냅니다.
개인정보 보호 담당자가 네 파일을 올리면 D-90377 재직증명서가 탈퇴 회원 서류로 맨 위에 서고, D-91002 원천징수영수증이 미리보기 이미지만 남은 파기 서류로, 저장소의 a81f3c20.pdf 가 서류 기록 없는 파일로 따로 섭니다.
운영 담당자가 서류 하나를 펼쳐 심사일, 경과 일수, 서류 종류와 심사 결과에 맞는 보관 일수, 남은 파일 이름을 읽고 그 숫자로 오늘 지울 서류를 고릅니다.
보관 일수 90일과 경과 일수가 같은 날의 서류, 아직 심사 전이라 심사일이 비어 있는 서류, 원본은 지웠는데 미리보기만 남은 서류, 탈퇴 직후 일부 서류만 지워진 회원을 한 파일에 넣어도 서류마다 판정이 하나씩 서고, 칸이 빠지거나 날짜가 깨진 파일 목록 10종은 오류 줄 번호를 알려 준 뒤 나머지 서류만 판정합니다.
서류 300건에는 제가 먼저 판정을 적어 두고, 파일을 올릴 때마다 그 표와 자동 판정이 몇 건 어긋나는지 판정별로 띄웁니다. 기록과 파일 목록과 정답을 모두 제가 만들었으므로 이 숫자가 0 이라는 것은 날짜 계산과 판정 차례가 적어 둔 대로 돈다는 데까지이고, 파기 정책이 법적으로 충분한지와는 관계가 없습니다.
위 수치는 더미데이터 자가채점이며, 실제 파기 누락 비율은 더블유클럽의 서류 제출 기록과 저장소 파일 목록, 그리고 개인정보 보호 담당자의 실제 점검 결과가 있어야 측정됩니다. 서류 제출 기록과 버킷 파일 목록을 하루치만 받아도 첫 주간 보고에 들어갈 남은 서류 수가 바로 나오도록 네 표의 컬럼을 W클럽 어드민 내보내기 기준으로 맞춰 둡니다.
D-90377 을 탈퇴 회원 서류로 올린 정답은 제가 매긴 것이고, 파기 점검을 실제로 해 본 담당자라면 탈퇴 다음 날 새벽 배치가 돌기 전까지는 유예로 볼 수도 있습니다. 이 화면이 증명하는 범위는 제가 적은 규칙이 오전 9시 이전 심사처럼 한국 날짜와 UTC 날짜가 갈리는 서류에서도 문서대로 도는지까지입니다.
새벽 배치가 날짜대로 깔끔하게 지운 서류만 있으면 다섯 판정은 저절로 맞으니, 더미에는 보관 일수와 경과 일수가 같은 날의 서류, 심사일이 비어 있는 심사 대기 서류, 미리보기 이미지만 남은 파기 서류, 탈퇴 뒤 서류 하나만 지워진 회원, 서류 기록이 없는 2025년 파일을 섞었습니다.
보관 일수를 심사일 기준으로 셀지 제출일 기준으로 셀지, 반려 서류를 며칠 남길지는 W클럽 정책이 정할 일입니다. 더미 기준표의 90일·60일·14일은 판정이 도는지 보이기 위한 값이고, 실제 도입 때는 정책 문서의 숫자로 기준표만 바꿉니다.
저장소 파일 목록을 하루 한 번 내려받는 방식이라 목록을 뽑은 뒤 올라온 파일은 다음 판정에 잡힙니다. 실제 버킷을 직접 읽으면 권한 설계가 따로 필요해 도입 단계 과제로 둡니다.
이 포트폴리오는 서류 기록과 저장소 파일을 맞대어 남은 서류를 판정하는 백엔드 규칙과 어드민 화면을 보여줍니다. 개인정보보호법 해석이나 보관 기간 산정에 대한 법률 전문성은 주장하지 않습니다.
① 인라인 샘플 · ② seed.json 다운로드 · ③ 증강 프롬프트. 세 층 모두 동일한 schema 에서 파생.
회원이 앱으로 올린 경제력 인증 서류의 제출·심사·파기 기록입니다. 심사 전 서류는 review_result 와 reviewed_at 이 비어 있고, 파기 배치가 처리한 서류에만 purged_at 이 찍힙니다. 탈퇴 직후 소득 서류만 지워지고 재직증명서는 남은 회원, 파기 기록은 있는데 저장소에 미리보기가 남은 서류를 일부러 담았습니다.
| doc_id | member_id | doc_type | review_result | submitted_at | reviewed_at | purged_at |
|---|---|---|---|---|---|---|
| D-88213 | M-20431 | 소득금액증명원 | 승인 | 2026-06-10T21:14:00+09:00 | 2026-06-11T14:20:00+09:00 | — |
| D-88540 | M-20977 | 재직증명서 | 반려 | 2026-09-24T23:02:00+09:00 | 2026-09-25T11:00:00+09:00 | — |
| D-89002 | M-21455 | 근로소득 원천징수영수증 | 승인 | 2026-07-03T19:40:00+09:00 | 2026-07-04T08:40:00+09:00 | — |
| D-89117 | M-21890 | 건강보험 자격득실확인서 | 반려 | 2026-09-09T08:15:00+09:00 | 2026-09-10T09:30:00+09:00 | — |
| D-90377 | M-21308 | 재직증명서 | 승인 | 2026-08-19T22:30:00+09:00 | 2026-08-20T10:05:00+09:00 | — |
아래 JSON Schema 에 맞는 W클럽 경제력 인증 서류 제출 기록 행을 더 만들어 주세요. {"type":"object","properties":{"doc_id":{"type":"string","description":"서류 번호입니다."},"member_id":{"type":"string"},"doc_type":{"type":"string","description":"소득금액증명원·근로소득 원천징수영수증·재직증명서·건강보험 자격득실확인서·사업자등록증명 중 하나입니다."},"review_result":{"type":["string","null"],"enum":["승인","반려",null],"description":"심사 전이면 null 입니다."},"submitted_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."},"reviewed_at":{"type":["string","null"],"description":"운영 담당자가 승인하거나 반려한 시각입니다. 심사 전이면 null 입니다."},"purged_at":{"type":["string","null"],"description":"파기 배치가 이 서류를 파기 처리한 시각입니다. 처리 전이면 null 입니다."}},"required":["doc_id","member_id","doc_type","review_result","submitted_at","reviewed_at","purged_at"]} 한 회원이 서류를 1~3건 내고, 심사는 제출 다음 날 안팎에 끝나며, 파기 배치는 새벽 3시에 돌아 purged_at 이 03:00 으로 찍힙니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 심사 전이라 review_result 와 reviewed_at 이 null 인 서류, (2) 심사일부터 기준일까지 경과 일수가 보관 일수와 정확히 같은 서류, (3) 탈퇴한 회원의 서류 중 일부만 purged_at 이 찍힌 경우, (4) purged_at 이 있는데 저장소에 미리보기 파일이 남게 될 서류, (5) 한 회원이 같은 종류 서류를 다시 낸 경우. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"doc_id":{"type":"string","description":"서류 번호입니다."},"member_id":{"type":"string"},"doc_type":{"type":"string","description":"소득금액증명원·근로소득 원천징수영수증·재직증명서·건강보험 자격득실확인서·사업자등록증명 중 하나입니다."},"review_result":{"type":["string","null"],"enum":["승인","반려",null],"description":"심사 전이면 null 입니다."},"submitted_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."},"reviewed_at":{"type":["string","null"],"description":"운영 담당자가 승인하거나 반려한 시각입니다. 심사 전이면 null 입니다."},"purged_at":{"type":["string","null"],"description":"파기 배치가 이 서류를 파기 처리한 시각입니다. 처리 전이면 null 입니다."}},"required":["doc_id","member_id","doc_type","review_result","submitted_at","reviewed_at","purged_at"]}서류 저장소에서 하루 한 번 내려받은 파일 목록입니다. 서류 하나에 원본 파일과 어드민 심사 화면용 미리보기 이미지가 따로 남을 수 있고, 서류 기록 없이 저장소에만 있는 2025년 파일을 한 개 넣었습니다.
| object_key | doc_id | variant | uploaded_at |
|---|---|---|---|
| docs/2026/06/88213-orig.pdf | D-88213 | 원본 | 2026-06-10T21:14:05+09:00 |
| docs/2026/09/88540-orig.jpg | D-88540 | 원본 | 2026-09-24T23:02:10+09:00 |
| docs/2026/07/89002-orig.pdf | D-89002 | 원본 | 2026-07-03T19:40:12+09:00 |
| docs/2026/09/89117-orig.pdf | D-89117 | 원본 | 2026-09-09T08:15:30+09:00 |
| docs/2026/08/90377-orig.pdf | D-90377 | 원본 | 2026-08-19T22:30:20+09:00 |
아래 JSON Schema 에 맞는 서류 저장소 파일 목록 행을 더 만들어 주세요. {"type":"object","properties":{"object_key":{"type":"string","description":"저장소 안의 파일 경로입니다."},"doc_id":{"type":"string","description":"업로드 때 파일 메타데이터에 적힌 서류 번호입니다."},"variant":{"type":"string","enum":["원본","미리보기"]},"uploaded_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."}},"required":["object_key","doc_id","variant","uploaded_at"]} 서류 하나에 원본 파일 한 개가 기본이고, 어드민에서 심사한 서류는 미리보기 이미지가 하나 더 생깁니다. object_key 는 docs/연/월/서류번호-orig 또는 -preview 꼴입니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 파기 처리된 서류의 원본은 지워졌지만 미리보기만 남은 파일, (2) 서류 기록에 없는 서류 번호를 가진 옛 파일(업로드는 됐는데 서류 기록 저장이 실패한 경우), (3) 서류 기록은 있는데 파일이 하나도 없는 서류, (4) 업로드 시각이 제출 시각보다 몇 초 늦은 정상 파일. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"object_key":{"type":"string","description":"저장소 안의 파일 경로입니다."},"doc_id":{"type":"string","description":"업로드 때 파일 메타데이터에 적힌 서류 번호입니다."},"variant":{"type":"string","enum":["원본","미리보기"]},"uploaded_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."}},"required":["object_key","doc_id","variant","uploaded_at"]}서류를 낸 회원의 가입·탈퇴 기록입니다. 탈퇴하지 않은 회원은 withdrawn_at 이 비어 있고, 탈퇴 회원 두 명 중 한 명은 서류가 모두 지워졌고 한 명은 일부만 지워졌습니다.
| member_id | joined_at | withdrawn_at |
|---|---|---|
| M-20431 | 2026-06-10T20:58:00+09:00 | — |
| M-20977 | 2026-09-24T22:40:00+09:00 | — |
| M-21308 | 2026-08-19T22:05:00+09:00 | 2026-09-14T22:10:00+09:00 |
| M-21455 | 2026-07-03T19:21:00+09:00 | — |
| M-21890 | 2026-09-09T07:58:00+09:00 | — |
아래 JSON Schema 에 맞는 W클럽 회원 가입·탈퇴 기록 행을 더 만들어 주세요. {"type":"object","properties":{"member_id":{"type":"string"},"joined_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."},"withdrawn_at":{"type":["string","null"],"description":"탈퇴 시각입니다. 탈퇴하지 않았으면 null 입니다."}},"required":["member_id","joined_at","withdrawn_at"]} 가입 시각은 그 회원의 첫 서류 제출보다 앞섭니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 서류 심사 중에 탈퇴한 회원, (2) 탈퇴 다음 날 새벽 파기 배치보다 몇 분 늦게 탈퇴해 하루 더 남는 회원, (3) 탈퇴했다가 같은 전화번호로 다시 가입해 member_id 가 새로 생긴 회원. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"member_id":{"type":"string"},"joined_at":{"type":"string","description":"+09:00 오프셋이 붙은 ISO 8601 시각입니다."},"withdrawn_at":{"type":["string","null"],"description":"탈퇴 시각입니다. 탈퇴하지 않았으면 null 입니다."}},"required":["member_id","joined_at","withdrawn_at"]}서류 종류와 심사 결과별로 심사일부터 며칠 동안 서류를 남길지 적은 보관 기준표입니다. 판정이 도는지 보이기 위한 더미 기준이며 W클럽의 실제 정책 숫자가 아닙니다.
| doc_type | review_result | keep_days |
|---|---|---|
| 소득금액증명원 | 승인 | 90 |
| 소득금액증명원 | 반려 | 14 |
| 근로소득 원천징수영수증 | 승인 | 90 |
| 근로소득 원천징수영수증 | 반려 | 14 |
| 재직증명서 | 승인 | 60 |
아래 JSON Schema 에 맞는 서류 보관 기준표 행을 더 만들어 주세요. {"type":"object","properties":{"doc_type":{"type":"string"},"review_result":{"type":"string","enum":["승인","반려"]},"keep_days":{"type":"integer","minimum":1,"description":"심사일 다음 날부터 세어 이 일수까지는 남겨 둘 수 있습니다."}},"required":["doc_type","review_result","keep_days"]} 서류 종류마다 승인·반려 두 줄을 쓰고, 반려 서류는 승인 서류보다 짧게 남깁니다. 쉬운 데이터만 만들지 말 것 — 다음 하드 케이스를 의도적으로 섞어라: (1) 새로 받기 시작한 서류 종류라 승인 줄만 있고 반려 줄이 아직 없는 경우, (2) keep_days 가 1 인 즉시 파기 서류. 생성한 행은 위 스키마를 통과해야 하며, 형식을 깨뜨리는 시도는 별도로 3건만 표시해 두세요.{"type":"object","properties":{"doc_type":{"type":"string"},"review_result":{"type":"string","enum":["승인","반려"]},"keep_days":{"type":"integer","minimum":1,"description":"심사일 다음 날부터 세어 이 일수까지는 남겨 둘 수 있습니다."}},"required":["doc_type","review_result","keep_days"]}| TypeScript | 경과 일수 계산, 보관 기준 고르기, 판정 차례를 담은 규칙 모듈과 화면 코드의 언어입니다. |
| Node.js | 네 파일을 읽어 서류 번호와 회원 번호로 잇는 판정 작업이 도는 런타임입니다. |
| NestJS | 업로드 API, 판정 서비스, 2주차 파기 작업과 파기 이력 기록을 모듈로 나눠 둡니다. |
| Next.js | 운영 담당자와 개인정보 보호 담당자가 보는 판정별 서류 목록과 서류 펼침 화면입니다. |
| DB 스키마·마이그레이션 | 2주차에 파기 이력 테이블을 마이그레이션 파일로 추가해 언제 누가 어떤 파일을 지웠는지 남깁니다. |
| Claude Code·Codex | 판정 규칙과 화면의 초안을 맡기고, 파기 기한 당일·심사 대기·미리보기만 남은 서류 테스트를 통과한 코드만 합칩니다. |
| 테스트 자동화·CI/CD | Vitest 로 판정 규칙을 고정하고 GitHub Actions 가 매일 새벽 판정과 정답 대조를 돌립니다. |
| 오류 모니터링 | 3주차에 탈퇴 회원 서류가 남거나 판정 작업이 실패하면 운영 채널로 알림을 보냅니다. |
✗ fail → 날짜 계산을 한국 날짜가 아니라 UTC 로 자른 것은 아닌지부터 보고, 판정 차례가 사람 판단과 어긋나면 탈퇴 회원 서류와 파기 후 남은 파일의 순서를 다시 잡습니다.
✓ pass → 판정 화면만으로도 개인정보 보호 담당자의 주간 점검에 쓸 수 있으니 여기서 멈춰도 됩니다. 이어 간다면 300건 정답 대조와 실제 파기 작업이 2주차 일입니다.
✗ fail → 파기 뒤에도 남는 파일이 있으면 미리보기 이미지 경로 규칙부터 다시 보고, 파기 작업은 미리 보기 단계로만 둡니다.
✓ pass → 매일 새벽 판정과 주간 보고 초안으로 3주차를 엽니다.
위 예측 UI 는 3주차까지 완주했을 때의 화면입니다. 1주차에 실제로 만드는 것은 아래 표시된 화면 하나입니다.
verdict사유 라벨 · doc_id + 한 줄 요약(근거 수치 포함)화면·코드·면접에서 이 단어를 그대로 씁니다. 키는 출력 스키마의 enum 과 같습니다.
탈퇴 회원 서류가 남음탈퇴 회원 서류파기 처리했는데 파일이 남음파기 후 파일 남음기록 없이 저장소에만 있는 파일기록 없는 파일파기 기한을 넘겨 남음기한 넘김기한 안에 보관 중보관 기한 안아직 남아 있는 서류 하나, 또는 서류 기록 없이 저장소에만 있는 파일 하나가 경과 일수 계산과 보관 기준 고르기, 탈퇴 기록·파일 목록 대조를 거쳐 어떤 판정을 받는지 남기는 형식입니다. 판정은 규칙이 정하고, 3주차부터 주간 보고 문단만 모델이 씁니다.
{"type":"object","properties":{"doc_id":{"type":"string","description":"서류 번호입니다. 서류 기록 없는 파일이면 파일 메타데이터의 서류 번호입니다."},"member_id":{"type":["string","null"]},"doc_type":{"type":["string","null"]},"verdict":{"type":"string","enum":["기한 안에 보관 중","파기 기한을 넘겨 남음","탈퇴 회원 서류가 남음","파기 처리했는데 파일이 남음","기록 없이 저장소에만 있는 파일"]},"as_of_date":{"type":"string","description":"판정 기준일(한국 날짜)입니다."},"reviewed_at":{"type":["string","null"]},"keep_days":{"type":["integer","null"],"minimum":1,"description":"retention_policy 에서 doc_type 과 review_result 가 같은 줄의 보관 일수입니다. 심사 전이면 null 입니다."},"elapsed_days":{"type":["integer","null"],"minimum":0,"description":"as_of_date 에서 reviewed_at 의 한국 날짜를 뺀 일수입니다. 심사 전이면 null 입니다."},"withdrawn_at":{"type":["string","null"]},"purged_at":{"type":["string","null"]},"remaining_object_keys":{"type":"array","items":{"type":"string"},"description":"storage_objects 에서 이 doc_id 로 남아 있는 파일 경로입니다."},"evidence":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"agg":{"type":["string","null"]},"value":{}},"required":["field","value"]}},"summary":{"type":"string"},"hedge":{"type":["string","null"]}},"required":["doc_id","verdict","as_of_date","remaining_object_keys","evidence","summary"]}[
{
"doc_id": "D-90377",
"member_id": "M-21308",
"doc_type": "재직증명서",
"verdict": "탈퇴 회원 서류가 남음",
"as_of_date": "2026-10-02",
"reviewed_at": "2026-08-20T10:05:00+09:00",
"keep_days": 60,
"elapsed_days": 43,
"withdrawn_at": "2026-09-14T22:10:00+09:00",
"purged_at": null,
"remaining_object_keys": [
"docs/2026/08/90377-orig.pdf",
"docs/2026/08/90377-preview.jpg"
],
"evidence": [
{
"field": "withdrawn_at",
"value": "2026-09-14T22:10:00+09:00"
},
{
"field": "doc_id",
"value": "D-90377"
},
{
"field": "purged_at",
"value": null
},
{
"field": "object_key",
"agg": "count",
"value": 2
}
],
"summary": "9월 14일에 탈퇴한 회원의 재직증명서가 파기 기록 없이 원본과 미리보기 두 파일로 남아 있습니다.",
"hedge": "같은 회원의 소득금액증명원 D-90412 는 9월 15일 새벽에 파기됐습니다. 탈퇴 파기 배치가 소득 서류만 처리한 것으로 보입니다."
},
{
"doc_id": "D-91002",
"member_id": "M-22014",
"doc_type": "근로소득 원천징수영수증",
"verdict": "파기 처리했는데 파일이 남음",
"as_of_date": "2026-10-02",
"reviewed_at": "2026-06-28T15:12:00+09:00",
"keep_days": 90,
"elapsed_days": 96,
"withdrawn_at": null,
"purged_at": "2026-09-20T03:00:00+09:00",
"remaining_object_keys": [
"docs/2026/06/91002-preview.jpg"
],
"evidence": [
{
"field": "purged_at",
"value": "2026-09-20T03:00:00+09:00"
},
{
"field": "object_key",
"value": "docs/2026/06/91002-preview.jpg"
},
{
"field": "variant",
"value": "미리보기"
}
],
"summary": "9월 20일 새벽에 파기 처리된 원천징수영수증의 원본은 지워졌지만 어드민 심사용 미리보기 이미지가 저장소에 남아 있습니다.",
"hedge": "파기 배치가 원본 경로만 지우고 미리보기 경로는 지우지 않은 것으로 보입니다."
},
{
"doc_id": "D-79540",
"member_id": null,
"doc_type": null,
"verdict": "기록 없이 저장소에만 있는 파일",
"as_of_date": "2026-10-02",
"reviewed_at": null,
"keep_days": null,
"elapsed_days": null,
"withdrawn_at": null,
"purged_at": null,
"remaining_object_keys": [
"docs/2025/07/a81f3c20.pdf"
],
"evidence": [
{
"field": "object_key",
"value": "docs/2025/07/a81f3c20.pdf"
},
{
"field": "doc_id",
"value": "D-79540"
},
{
"field": "uploaded_at",
"value": "2025-07-03T11:26:51+09:00"
}
],
"summary": "2025년 7월에 올라온 파일이 서류 번호 D-79540 을 달고 있는데 서류 제출 기록에는 그 번호가 없습니다.",
"hedge": "누구의 어떤 서류인지 기록이 없어 보관 기한을 셀 수 없습니다. 파일을 열어 보지 않고 지울지는 개인정보 보호 담당자가 정합니다."
},
{
"doc_id": "D-88213",
"member_id": "M-20431",
"doc_type": "소득금액증명원",
"verdict": "파기 기한을 넘겨 남음",
"as_of_date": "2026-10-02",
"reviewed_at": "2026-06-11T14:20:00+09:00",
"keep_days": 90,
"elapsed_days": 113,
"withdrawn_at": null,
"purged_at": null,
"remaining_object_keys": [
"docs/2026/06/88213-orig.pdf"
],
"evidence": [
{
"field": "reviewed_at",
"value": "2026-06-11T14:20:00+09:00"
},
{
"field": "keep_days",
"value": 90
},
{
"field": "purged_at",
"value": null
}
],
"summary": "6월 11일에 승인된 소득금액증명원이 113일째 남아 있어 승인 서류 보관 일수 90일을 넘겼습니다.",
"hedge": null
},
{
"doc_id": "D-89117",
"member_id": "M-21890",
"doc_type": "건강보험 자격득실확인서",
"verdict": "파기 기한을 넘겨 남음",
"as_of_date": "2026-10-02",
"reviewed_at": "2026-09-10T09:30:00+09:00",
"keep_days": 14,
"elapsed_days": 22,
"withdrawn_at": null,
"purged_at": null,
"remaining_object_keys": [
"docs/2026/09/89117-orig.pdf"
],
"evidence": [
{
"field": "review_result",
"value": "반려"
},
{
"field": "reviewed_at",
"value": "2026-09-10T09:30:00+09:00"
},
{
"field": "keep_days",
"value": 14
}
],
"summary": "9월 10일에 반려된 건강보험 자격득실확인서가 22일째 남아 있어 반려 서류 보관 일수 14일을 넘겼습니다.",
"hedge": "같은 서류라도 승인이었다면 60일까지 남길 수 있어, 심사 결과를 잘못 읽으면 판정이 바뀝니다."
},
{
"doc_id": "D-89002",
"member_id": "M-21455",
"doc_type": "근로소득 원천징수영수증",
"verdict": "기한 안에 보관 중",
"as_of_date": "2026-10-02",
"reviewed_at": "2026-07-04T08:40:00+09:00",
"keep_days": 90,
"elapsed_days": 90,
"withdrawn_at": null,
"purged_at": null,
"remaining_object_keys": [
"docs/2026/07/89002-orig.pdf"
],
"evidence": [
{
"field": "reviewed_at",
"value": "2026-07-04T08:40:00+09:00"
},
{
"field": "keep_days",
"value": 90
}
],
"summary": "7월 4일 오전에 승인된 원천징수영수증은 오늘이 90일째라 보관 일수 90일을 아직 넘기지 않았습니다.",
"hedge": "오전 8시 40분 심사라 UTC 로 날짜를 자르면 7월 3일이 되어 91일째, 기한 넘김으로 잘못 올라옵니다. 내일 새벽 파기 배치가 지우지 않으면 내일 판정에서는 실제로 기한 넘김입니다."
},
{
"doc_id": "D-91388",
"member_id": "M-22350",
"doc_type": "소득금액증명원",
"verdict": "기한 안에 보관 중",
"as_of_date": "2026-10-02",
"reviewed_at": null,
"keep_days": null,
"elapsed_days": null,
"withdrawn_at": null,
"purged_at": null,
"remaining_object_keys": [
"docs/2026/09/91388-orig.jpg"
],
"evidence": [
{
"field": "submitted_at",
"value": "2026-09-30T22:48:00+09:00"
},
{
"field": "reviewed_at",
"value": null
}
],
"summary": "9월 30일 밤에 낸 소득금액증명원이 아직 심사 전이라 보관 기한을 세기 시작하지 않았습니다.",
"hedge": "심사 대기가 길어지면 기한 없이 쌓이므로, 심사 전 서류는 제출일부터 센 대기 일수를 2주차 화면에 따로 보여줍니다."
}
][자동 검증 · 코드가 판정합니다] 1. verdict 가 기한 안에 보관 중·파기 기한을 넘겨 남음·탈퇴 회원 서류가 남음·파기 처리했는데 파일이 남음·기록 없이 저장소에만 있는 파일 다섯 값 중 하나인지 확인합니다. 2. doc_id·verdict·as_of_date·remaining_object_keys·evidence·summary 필드가 빠짐없이 존재하는지 확인합니다. 3. keep_days 가 1 이상이고 elapsed_days 가 0 이상인지 확인합니다. 4. elapsed_days 를 as_of_date 에서 reviewed_at 의 한국 날짜를 뺀 일수로 다시 계산하고, keep_days 를 retention_policy 에서 doc_type 과 review_result 가 같은 줄로 다시 고릅니다. 5. storage_objects.doc_id 가 doc_submissions 에 없으면 verdict 는 기록 없이 저장소에만 있는 파일입니다. 6. member_id 의 members.withdrawn_at 이 있고 purged_at 이 비었거나 같은 doc_id 파일이 남아 있으면 verdict 는 탈퇴 회원 서류가 남음입니다. 7. 앞 조건에 걸리지 않고 purged_at 이 있는데 같은 doc_id 파일이 하나라도 남아 있으면 verdict 는 파기 처리했는데 파일이 남음입니다. 8. 앞 조건에 걸리지 않고 purged_at 이 비었으며 elapsed_days 가 keep_days 를 넘으면 verdict 는 파기 기한을 넘겨 남음입니다. 어느 조건에도 걸리지 않으면 verdict 는 기한 안에 보관 중입니다. 9. evidence 의 field 이름이 네 데이터셋 중 하나에 있는 컬럼인지 대조합니다. 10. evidence 의 value 가 수록된 행의 원문 값과 같은지 확인합니다. 여러 줄을 세어 얻은 수는 agg 를 적습니다. 11. summary 가 존댓말로 끝나는지 확인합니다. [판정 대상 · 무엇을 목록에 올리는가] purged_at 이 있고 같은 doc_id 파일이 하나도 없는 서류는 제대로 파기된 것이라 판정 목록에 올리지 않고 화면 위 집계의 파기 완료로만 셉니다. 더미에서는 D-90412 와 D-91230 이 그렇습니다. D-91230 은 탈퇴 회원 M-22106 의 서류지만 파기와 파일 삭제가 모두 끝나 탈퇴 회원 서류 판정에 들지 않습니다. [판정 차례 · 위 규칙이 둘 이상 걸릴 때] 서류 하나에는 판정을 하나만 남깁니다. ① 기록 없이 저장소에만 있는 파일 → ② 탈퇴 회원 서류가 남음 → ③ 파기 처리했는데 파일이 남음 → ④ 파기 기한을 넘겨 남음 → ⑤ 기한 안에 보관 중. [하드 케이스 · 판정이 갈리는 이유] D-89002 는 경과 일수 90일이 보관 일수 90일과 같아 넘지 않았으므로 기한 안입니다. D-91388 은 심사 전이라 reviewed_at 이 null 이고 기한을 세지 않아 기한 안입니다. D-91002 는 원본이 지워져 파일 목록에 미리보기만 있지만 파기 기록이 있으니 파기 후 파일 남음입니다. 경과 일수는 서버 시각이 아니라 한국 날짜로 자릅니다. D-89002 는 오전 8시 40분 심사라 reviewed_at 을 UTC 로 자르면 7월 3일이 되어 경과 91일, 기한 넘김으로 잘못 갑니다. [평가 러너 · 2주차] 더미 서류 300건에 손으로 매긴 정답 판정과 자동 판정을 대조해 판정별 일치율을 내고, 어긋난 서류는 따로 모아 판정 차례와 날짜 자르기를 다시 봅니다. [수동 검증 · 사람이 봅니다] 1. 기록 없는 파일을 지울지는 개인정보 보호 담당자가 정합니다. 2. 보관 기준표의 일수가 W클럽 정책과 맞는지는 정책 문서를 가진 사람이 확인합니다.