메인 콘텐츠로 건너뛰기

tus 프로토콜

tus는 재개 가능한 파일 업로드를 위한 개방형 프로토콜입니다. 네트워크 중단에도 견딜 수 있는 안정적인 업로드에 사용하세요.

왜 tus인가?

  • 재개 가능: 네트워크 장애 후 업로드 계속
  • 효율적: 처음부터가 아닌 중단된 지점부터 재개
  • 표준화됨: 많은 서버 구현이 있는 잘 정의된 프로토콜
  • 청크 방식: 큰 파일을 자동으로 처리

설치

npm install @samithahansaka/dropup tus-js-client

tus-js-client는 피어 종속성입니다.

기본 사용법

import { useDropup } from '@samithahansaka/dropup';
import { createTusUploader } from '@samithahansaka/dropup/tus';

function TusUploader() {
const { files, actions, state, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://tusd.tusdemo.net/files/',
}),

onUploadComplete: (file) => {
console.log('Upload complete:', file.uploadedUrl);
},
});

return (
<div>
<div {...getDropProps()} style={styles.dropzone}>
<input {...getInputProps()} />
<p>Drop files for resumable upload</p>
</div>

{files.map(file => (
<div key={file.id} style={styles.fileItem}>
<span>{file.name}</span>
<span>{file.progress}%</span>
<span>{file.status}</span>
</div>
))}

<button onClick={() => actions.upload()}>
Upload
</button>
</div>
);
}

구성 옵션

createTusUploader({
// 필수
endpoint: 'https://your-tus-server.com/files/',

// 청크 크기 (기본값: Infinity = 청크 없음)
chunkSize: 5 * 1024 * 1024, // 5MB 청크

// 재시도 구성
retryDelays: [0, 1000, 3000, 5000], // 0초, 1초, 3초, 5초 후 재시도

// 모든 요청에 대한 헤더
headers: {
'Authorization': 'Bearer token',
},

// 서버로 전송되는 메타데이터
metadata: {
filename: 'file.name',
filetype: 'file.type',
// 사용자 정의 메타데이터 추가
userId: 'user123',
},

// localStorage를 통한 재개 기능 활성화
storeFingerprintForResuming: true,

// 성공적인 업로드 후 fingerprint 제거
removeFingerprintOnSuccess: true,

// 병렬 업로드
parallelUploads: 3,
});

이전 업로드 재개

tus는 중단된 업로드를 재개할 수 있습니다:

const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',

// 재개를 위한 업로드 URL 저장
storeFingerprintForResuming: true,

// 선택 사항: 사용자 정의 스토리지 키
urlStorageKey: 'my-app-tus-uploads',

onShouldRetry: (error, retryAttempt) => {
// 사용자 정의 재시도 로직
if (error.message.includes('network')) {
return retryAttempt < 5;
}
return false;
},
}),
});

// 파일은 다시 추가될 때 자동으로 재개될 수 있거나
// retry로 수동으로 재개:
actions.retry(['file-id']);

진행 상황 추적

function TusWithProgress() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
}),

onUploadProgress: (file, progress) => {
console.log(`${file.name}: ${progress}%`);
},

onUploadStart: (file) => {
console.log(`Starting upload: ${file.name}`);
},

onUploadComplete: (file) => {
console.log(`Complete: ${file.uploadedUrl}`);
},
});

return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Drop files here</p>
</div>

{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
{file.status === 'uploading' && (
<div style={styles.progressBar}>
<div
style={{
...styles.progress,
width: `${file.progress}%`,
}}
/>
</div>
)}
<span>{file.status}</span>
</div>
))}
</div>
);
}

일시 중지 및 재개

function PausableUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
storeFingerprintForResuming: true,
}),
});

return (
<div>
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Drop files here</p>
</div>

{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
<span>{file.progress}%</span>

{file.status === 'uploading' && (
<button onClick={() => actions.cancel(file.id)}>
Pause
</button>
)}

{file.status === 'paused' && (
<button onClick={() => actions.retry([file.id])}>
Resume
</button>
)}

{file.status === 'error' && (
<button onClick={() => actions.retry([file.id])}>
Retry
</button>
)}
</div>
))}

<button onClick={() => actions.upload()}>
Start All
</button>
</div>
);
}

서버 설정

tusd (공식 서버)

# tusd 설치
go install github.com/tus/tusd/cmd/tusd@latest

# 기본 옵션으로 실행
tusd -upload-dir=/uploads

# S3 스토리지로 실행
tusd -s3-bucket=my-bucket -s3-endpoint=https://s3.amazonaws.com

Node.js (tus-node-server)

const { Server } = require('@tus/server');
const { FileStore } = require('@tus/file-store');

const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
});

// Express와 함께
app.all('/files/*', server.handle.bind(server));

사용자 정의 메타데이터 처리

// 서버 측: 메타데이터 액세스
const server = new Server({
path: '/files',
datastore: new FileStore({ directory: './uploads' }),
onUploadCreate: async (req, res, upload) => {
// 클라이언트에서 보낸 메타데이터 액세스
console.log(upload.metadata);
// { filename: 'photo.jpg', filetype: 'image/jpeg', userId: 'user123' }
},
onUploadFinish: async (req, res, upload) => {
// 완료된 업로드 처리
console.log('Upload complete:', upload.id);
},
});

tus 확장

Dropup의 tus 통합은 다음 tus 확장을 지원합니다:

확장설명
Creation새 업로드 생성
Termination업로드 취소
Checksum무결성 확인
Concatenation병렬 업로드
Expiration자동 정리

오류 처리

const { files, actions } = useDropup({
upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
retryDelays: [0, 1000, 3000, 5000, 10000],
}),

onUploadError: (file, error) => {
if (error.code === 'TUS_OFFLINE') {
console.log('Network offline, will resume when connected');
} else if (error.code === 'TUS_EXPIRED') {
console.log('Upload expired, starting fresh');
actions.retry([file.id]);
} else {
console.error('Upload failed:', error.message);
}
},
});

대용량 파일 업로드

tus는 대용량 파일에 이상적입니다:

function LargeFileUploader() {
const { files, actions, getDropProps, getInputProps } = useDropup({
maxSize: 10 * 1024 * 1024 * 1024, // 10GB 제한

upload: createTusUploader({
endpoint: 'https://your-tus-server.com/files/',
chunkSize: 50 * 1024 * 1024, // 50MB 청크
retryDelays: [0, 3000, 5000, 10000, 20000],
storeFingerprintForResuming: true,
}),

onUploadProgress: (file, progress) => {
// 대용량 파일에 대한 상세 진행 상황 표시
const uploadedMB = (file.size * progress / 100 / 1024 / 1024).toFixed(0);
const totalMB = (file.size / 1024 / 1024).toFixed(0);
console.log(`${uploadedMB}MB / ${totalMB}MB`);
},
});

return (
<div {...getDropProps()}>
<input {...getInputProps()} />
<p>Upload large files (up to 10GB)</p>
</div>
);
}

비교: tus vs 청크 업로드

기능tus사용자 정의 청크
재개 기능내장됨수동
서버 지원많은 옵션사용자 정의
프로토콜 오버헤드높음낮음
구성표준화됨유연함
최적 용도대용량 파일, 불안정한 네트워크간단한 업로드