웹 뷰어에서 간헐적으로 S3 파일을 불러올 때 404 Not Found 에러가 발생했다.
문제의 원인을 파악해본 결과, 파일 이름이 URL에 포함될 때 한글 인코딩 이슈로 인해 S3에서 리소스를 찾지 못하는 상황이었고, 이를 해결하기 위해 파일명을 해시로 변환하는 방식으로 문제를 해결했다.
404 에러가 간헐적으로 발생async function issuePresignedUrl(ctx: Context, params: Params, q: Query): Promise<string> {
require(ctx.req && ctx.user && params.classId, '필수 입력 누락');
const safeName = toHashedName(q.fileName); // ← 핵심: 안전한 해시 파일명 생성
const base = normalizeKeyPrefix(q.keyName);
const objectKey = `${base}/${safeName}`;
const common = { bucket: env.S3_BUCKET, key: objectKey, expiresIn: 120 };
const presign =
q.action === 'download'
? await storage.presignGetObject({ ...common, responseContentDisposition: asDownload(q.fileName) })
: await storage.presignPutObject({ ...common, contentType: guessMime(q.fileName) });
audit.log({
userId: ctx.user.id,
action: q.action,
keyPrefix: base,
fileHashName: safeName,
classId: params.classId,
});
require(presign, 'URL 발급 실패');
return presign;
}
/**
* 파일명을 해시로 변환하여 S3에 안전하게 저장
*/
private generateHashFileName(originalFileName: string): string {
const hash = crypto.createHash('md5').update(originalFileName).digest('hex').substring(0, 12);
const extension = originalFileName.split('.').pop() || '';
return `${hash}.${extension}`;
}
crypto 모듈을 활용해 파일명을 해싱Blob을 사용해 응답 파일을 메모리에 담고window.URL.createObjectURL(blob) 으로 로컬 URL 생성link.download = decodeURIComponent(fileName) 을 통해 실제 사용자에게 보일 이름 설정const fileRes = await axios.get(presignedUrl, { responseType: 'blob' });
const blob = new Blob([fileRes.data]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = decodeURIComponent(originalFileName); // 원래 파일명
link.click();
link.remove();
404 Not Found 에러가 완전히 해결됨