FileSharer 개발 노트 2 — ‘한 번만 다운로드’라는 조건을 코드로 옮기면
English version · 1편: 파일 공유 서버의 구조
FileSharer 개발 노트 · 02 / 10

“이 링크는 한 번만 사용할 수 있게 해 주세요.” 간단해 보이는 요청입니다. 그런데 받는 사람이 70%까지 내려받은 뒤 연결이 끊겼다면 재시도도 한 번으로 세어야 할까요? 페이지를 새로고침했을 때는 어떨까요? 다운로드 제한을 구현하려면 먼저 무엇을 한 번으로 셀지 정해야 합니다.

FileSharer는 공유 항목에 만료 시각, 최대 다운로드 수, 현재 횟수, 해제 상태를 저장합니다. 이 글에서는 실제 조건식을 확인하고, 원본 함수를 실행해 기본 상태 판정을 점검했습니다.
공유 상태를 판정하는 세 줄
ShareItem.IsActive()의 핵심은 다음과 같습니다. 원본 메서드에서 가져온 조건입니다.
if (IsRevoked) return false;
if (ExpiresAt.HasValue && DateTime.Now > ExpiresAt.Value) return false;
if (MaxDownloads.HasValue && DownloadCount >= MaxDownloads.Value) return false;
return true;
상태 판정에 PIN은 들어 있지 않습니다. 공유가 유효한지와 요청자가 비밀번호를 맞혔는지는 별도 검사입니다. 이렇게 나누어 보면 “PIN을 맞혔는데 만료된 파일도 받을 수 있는가” 같은 조합을 놓치기 어렵습니다.
실제 함수에 다섯 상태를 넣었다
운영 중인 공유 데이터 대신 새 ShareItem을 만들고 값을 바꿨습니다. 만료 시험에는 현재 시각에서 하루가 지난 시각을 사용해 경계 오차를 피했습니다.
| 입력 상태 | IsActive 반환값 |
|---|---|
| 만료·횟수 제한이 없고 해제하지 않음 | true |
| IsRevoked가 true | false |
| 만료 시각이 하루 전 | false |
| 최대 1회, 현재 1회 | false |
| 최대 1회, 현재 0회 | true |
이 결과는 상태 함수 다섯 경우에 대한 확인입니다. 동시에 두 사람이 요청하는 상황이나 모든 HTTP 경로를 통과하는 시험은 아닙니다. 만료 시각도 현재 구현에서는 DateTime.Now로 직접 읽으므로, 정확히 같은 시각의 경계 시험을 반복하려면 시계를 인자로 받는 식으로 개선할 여지가 있습니다.
안내 페이지를 막는 것만으로는 충분하지 않다
일반적인 흐름은 공유 페이지를 열고 다운로드 버튼을 누르는 것입니다. 하지만 서버 입장에서는 페이지 요청과 파일 요청이 각각 독립된 요청입니다. 사용자가 반드시 첫 화면을 거쳐 왔다고 가정할 수 없습니다.
현재 소스의 공유 페이지 경로는 해제, 만료, 횟수 제한, PIN을 검사합니다. 전체 파일·ZIP 다운로드 경로에도 관련 검사가 있습니다. 반면 하위 파일 요청 분기에는 PIN과 경로 검사가 있지만, 해당 분기에서 공유의 만료·해제·횟수 조건을 동일하게 재검사하는 코드는 보이지 않습니다. 따라서 모든 다운로드 경로에 동일한 제한이 이미 적용됐다고 소개하는 것은 정확하지 않습니다.
개선한다면 요청 종류와 무관하게 공통 권한 검사 함수를 먼저 통과시키는 편이 명확합니다. 이번 작업은 원고 작성과 소스 검토이며, 이 개선을 실제 제품에 반영한 것은 아닙니다.
이어받기는 ‘같은 다운로드’라는 증거가 필요하다
전체 다운로드 분기는 Range 헤더가 있으면 재개 요청으로 취급해 횟수 제한과 증가를 일부 건너뜁니다. 연결이 끊길 때마다 허용 횟수를 소진하지 않으려는 의도는 이해할 수 있습니다. 하지만 Range 헤더 자체가 이전 다운로드의 신원을 증명하지는 않습니다.
엄밀한 횟수 제한이 필요하다면 다운로드 세션을 발급하고, 그 세션이 어떤 공유·파일 버전·사용 범위에 연결되는지 저장하는 설계가 필요합니다. 새 다운로드 허용 여부 판정과 횟수 예약도 같은 임계 구역에서 처리해야 동시 요청 사이의 빈틈을 줄일 수 있습니다.
또 현재 횟수 증가는 전체 파일·ZIP의 전송 완료 뒤가 아니라 전송 준비 단계에 있습니다. 따라서 표시되는 횟수를 “수신자가 저장을 완료한 횟수”라고 해석하면 안 됩니다. 제품 문구를 “다운로드 시작 횟수”처럼 구현과 맞추는 것도 개선 방법입니다.
PIN 화면과 비밀 관리의 경계
위 화면은 원본 HTML 생성 함수로 만들었습니다. 입력 양식과 오류 문구는 있지만, 화면 모양만으로 비밀 관리의 강도를 판단할 수는 없습니다. 현재 방식은 PIN을 URL 쿼리로 전달하고 공유 저장 파일에도 값을 기록합니다. 인증 정보를 URL에서 분리하고 저장 정책을 정하는 문제는 별도로 남습니다.
이 편에서 얻는 실용적인 기준은 세 가지입니다. 유효한 공유인지, 요청이 허용되는지, 사용 횟수를 언제 확정하는지를 분리해 정의해야 합니다. 그래야 “한 번만 다운로드”라는 문장이 서버가 실제로 보장하는 조건과 일치합니다.
FileSharer.cs:43–49 ShareItem.IsActive · FileSharer.cs:4740–4804 공유 페이지 검사 · FileSharer.cs:4807–4855 하위 파일 요청 · FileSharer.cs:4899–4939 전체 다운로드 제한 · FileSharer.cs:5406–5445 BuildPinInputHtml
원본 프로젝트 파일은 수정하지 않았습니다. 본문의 개선안은 적용된 기능과 구분합니다.
댓글
댓글 쓰기