본문으로 건너뛰기
HexaTransfer
블로그로 돌아가기
기술 심층 분석

Create Your Own 파일 전송 Service: Dev 튜토리얼

Build a 파일 전송 service from scratch. Backend API design, storage integration, and user interface development tutorial.

개인정보 보호법(PIPA)과 개인정보보호위원회(PIPC) 가이드라인에 따라, 파일 전송 서비스를 직접 구축하면 수집하는 개인정보의 종류와 보존 기간을 완전히 제어할 수 있습니다. 자체 파일 전송 서비스 구축에는 다섯 가지 요소가 필요합니다: 브라우저에서 파일을 슬라이싱하고 선택적으로 암호화하는 프론트엔드, 업로드 세션을 생성하고 스토리지 URL을 프리사인하는 REST API, S3 호환 오브젝트 스토리지(Cloudflare R2, Backblaze B2, MinIO), 소형 메타데이터 데이터베이스(SQLite 또는 Postgres), TLS 1.3 리버스 프록시. Node 22, Fastify, Vite로 약 600줄의 코드로 WeTransfer 방식의 서비스를 구축하고 적정 트래픽에서 월 $5~15로 호스팅할 수 있습니다.

먼저 스토리지 계층 선택

스토리지 경제학이 파일 전송 서비스를 만들거나 망칩니다. AWS S3는 이그레스당 $0.09/GB를 청구해, 2GB 다운로드가 1,000번 공유되면 대역폭만으로 $180이 발생합니다. Cloudflare R2는 이그레스 없이 저장 $0.015/GB를 청구합니다. Backblaze B2는 이그레스 $0.01/GB, 저장 $0.006/GB입니다. 무료 티어 전송 서비스에는 R2가 명확한 선택입니다. MinIO는 이미 유휴 대역폭과 디스크가 있다면 자체 하드웨어에서 S3 시맨틱을 제공합니다.

클라이언트가 서버를 거치지 않고 스토리지에서 직접 업로드·다운로드하는 프리사인 URL을 사용하세요. 이렇게 하면 API가 대역폭 바운드 대신 CPU 바운드가 됩니다.

API 표면 스케치

API를 작게 유지하세요. 다섯 개의 엔드포인트가 필요한 것의 95%를 커버합니다:

  • POST /sessions — 업로드 세션 생성, ID와 프리사인 멀티파트 URL 반환
  • POST /sessions/:id/complete — 멀티파트 업로드 완료, 공유 URL 반환
  • GET /shares/:id — 파일 메타데이터(크기, 만료, 남은 다운로드) 반환
  • GET /shares/:id/download — 프리사인 다운로드 URL 반환
  • DELETE /shares/:id — 소유자의 조기 취소(취소 토큰 필요)

POST /sessions에 공격적인 레이트 리밋—시간당 IP당 20개면 충분합니다. 모든 엔드포인트는 JSON을 반환하고, Cache-Control: no-store를 사용하며, HTML 페이지에 Content-Security-Policy를 설정하세요.

Fastify로 백엔드 연결

Fastify는 JSON 스키마 검증, 합리적인 로깅, 노력 없이 코어당 30k req/s를 제공합니다. 최소 POST /sessions:

fastify.post('/sessions', {
  schema: {
    body: {
      type: 'object', required: ['size'],
      properties: {
        size: { type: 'integer', minimum: 1, maximum: 10737418240 },
        contentType: { type: 'string', maxLength: 120 }
      }
    }
  }
}, async (req) => {
  const id = nanoid(16);
  const key = `blobs/${id}`;
  const upload = await s3.createMultipartUpload({
    Bucket: 'transfers', Key: key, ContentType: req.body.contentType
  });
  const partCount = Math.ceil(req.body.size / (8 * 1024 * 1024));
  const urls = await Promise.all(
    Array.from({ length: partCount }, (_, i) =>
      getSignedUrl(s3, new UploadPartCommand({
        Bucket: 'transfers', Key: key, UploadId: upload.UploadId, PartNumber: i + 1
      }), { expiresIn: 3600 }))
  );
  db.prepare('INSERT INTO sessions VALUES (?, ?, ?, ?)')
    .run(id, upload.UploadId, key, Date.now());
  return { id, uploadId: upload.UploadId, partUrls: urls };
});

데이터베이스 스키마 설계

better-sqlite3의 SQLite는 수천만 행을 거뜬히 처리합니다. 스키마를 간결하게 유지하세요:

CREATE TABLE sessions (
  id TEXT PRIMARY KEY, upload_id TEXT, object_key TEXT, created_at INTEGER
);
CREATE TABLE shares (
  id TEXT PRIMARY KEY, object_key TEXT, size_bytes INTEGER,
  content_type TEXT, expires_at INTEGER, max_downloads INTEGER,
  download_count INTEGER DEFAULT 0, revocation_token TEXT
);
CREATE INDEX idx_shares_expires ON shares(expires_at);

사용자 테이블 없음, 이메일 없음, IP 로그 없음. 이는 의도적인 선택으로 개인정보 보호법 준수를 거의 사소하게 만듭니다. 야간 크론이 만료된 행과 S3 객체를 삭제합니다.

공유 링크와 다운로드 구현

클라이언트 측 암호화를 추가한 경우 공유 URL은 https://yourapp.com/f/abc123#k=<base64key>처럼 보입니다. 프래그먼트는 서버 로그에서 키를 제외합니다. 다운로드 페이지는 /shares/abc123을 가져오고, 프리사인 S3 URL을 받아, R2에서 직접 바이트를 스트리밍하며, 브라우저에서 복호화하고, StreamSaver.js나 File System Access API로 결과를 전달합니다.

Content-Disposition: attachment; filename*=UTF-8''encoded-name을 설정해 브라우저가 파일을 인라인으로 렌더링하는 대신 저장 대화상자를 표시하게 하세요. 이는 PDF, HTML 파일, SVG에 특히 중요합니다—브라우저 컨텍스트에서 스크립트를 실행할 수 있습니다.

남용에 대한 강화

파일 전송 서비스는 남용자를 끌어들입니다. 악성 코드 배포, 피싱 페이로드 호스팅, 저작권 침해 업로드 세 가지 카테고리를 예상하세요. 영향 순서의 완화책: IP당 업로드 레이트 리밋, 최소 크기 요건(10KB 미만은 거의 항상 쓰레기), SHA-256으로 알려진 불량 해시 거부 목록과 대조, 마찰 없는 남용 신고 양식. 복호화된 콘텐츠의 ClamAV는 키가 필요해 종단 간 암호화를 무력화합니다—올바른 답은 콘텐츠 무관 레이트 리밋과 신속한 테이크다운입니다.

저렴하게 배포하고 운영

단일 $10 Hetzner CX22(2 vCPU, 4GB RAM)가 Fastify + SQLite + Caddy 리버스 프록시를 실행하며 수천 명의 활성 사용자를 처리합니다. Caddy는 Let's Encrypt를 통해 자동으로 TLS 1.3을 제공합니다. DDoS 보호와 무료 WAF를 위해 Cloudflare를 앞에 배치하세요. Uptime Kuma로 모니터링하고 journald가 캡처하는 stdout으로 로깅하세요.

지리적 중복을 위해 Litestream으로 R2에 SQLite를 복제하고 두 번째 지역이 같은 오브젝트 스토어를 가리키게 하세요. 장애 조치는 DNS 수준입니다.

이 전체 접근 방식은 HexaTransfer 구축 방식을 반영합니다—Fastify API, R2 스토리지, 브라우저의 Web Crypto, SQLite 메타데이터, 계정 없음.

https://hexatransfer.com — 무료, 계정 불필요, 최대 10GB.

엔드투엔드 암호화로 대용량 파일을 안전하게 전송

엔드투엔드 암호화로 최대 10GB의 파일을 무료로 전송하세요. 계정이 필요하지 않습니다. 업로드 전에 브라우저에서 파일이 암호화되어 다른 사람은 읽을 수 없습니다.

파일 보내기