| 항목 | 내용 |
|---|---|
| 발생 환경 | 프론트엔드에서 Presigned URL로 S3 직접 업로드 시 |
| 증상 | “net::ERR_FAILED”, “TypeError: Failed to fetch” 에러 발생 |
| 영향 범위 | 공지사항 첨부파일, 교육자료 썸네일, 케어로그 사진/오디오 등 모든 파일 업로드 |
문제 상황:
PUT <https://soulbridge-tt.s3.ap-northeast-2.amazonaws.com/>...
net::ERR_FAILED
Console Error:
Access to fetch at '<https://soulbridge-tt.s3.amazonaws.com/>...'
from origin '<http://localhost:5173>' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
| 방안 | 장점 | 단점 |
|---|---|---|
| A. S3 버킷 CORS 설정 | 근본적 해결, 직접 업로드 가능 | AWS 콘솔 접근 필요 |
| B. 서버 프록시 | 버킷 설정 변경 없음 | 서버 부하 증가, Presigned URL 장점 상실 |
| C. CloudFront 경유 | CDN 캐싱 가능 | 추가 인프라 비용, 복잡성 증가 |
선택: A안 (S3 버킷 CORS 설정)
AWS S3 CORS 설정:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedOrigins": ["주소"],
"ExposeHeaders": ["ETag", "Content-Length", "Content-Type"],
"MaxAgeSeconds": 3600
}
]
프론트엔드 업로드 코드 개선:
// Content-Type 헤더 정확히 전달
const uploadToS3 = async (presignedUrl: string, file: File) => {
const response = await fetch(presignedUrl, {
method: 'PUT',
body: file,
headers: {
'Content-Type': file.type, // 정확한 MIME 타입
},
});
if (!response.ok) {
throw new Error(`S3 upload failed: ${response.status}`);
}
return response;
};
| 관점 | 이유 |
|---|---|
| 아키텍처 | Presigned URL의 핵심 가치(서버 부하 감소)를 유지 |
| 성능 | 클라이언트-S3 직접 통신으로 최적의 업로드 속도 |
| 보안 | 와일드카드(*) 대신 명시적 Origin 지정으로 보안 유지 |
| 확장성 | 대용량 파일도 서버 트래픽 없이 처리 가능 |