이름이 없는 파일이 S3에 업로드된 이유

Tony Lee
8 min readJan 8, 2023

연말에 있었던 일이다.

사내에서 S3 버킷을 통해 static html파일을 서비스하는 페이지가 있는데, html파일의 간단한 수정 요청이 들어왔다. 별 내용 아니라고 생각하고 버킷을 확인해보았는데, 파일이 심상치 않아보였다.

파일명이 / ?
심지어 URI, ARN, URL에는 파일명이 없다?!?!

무언가 잘못됨을 느꼈다. 콘솔상에서 파일명이 / 로 표시되고 있고, 다운로드 받으면 크롬이 자동으로 파일명을 변경시켰다. 혹시 몰라 cli에서 aws s3 cp로 파일을 다운받았는데, 다운로드가 되지 않았다. / 가 *nix에서는 디렉토리를 의미하니, 해당 파일은 파일임에도 OS상에서 디렉토리로 취급되고 있는 것이다.

$ aws s3 cp s3://$BUCKET_NAME/aaa/bbb/ccc/ .

download failed: s3://$BUCKET_NAME/aaa/bbb/ccc/ to ./ [Errno 21] Is a directory: '/Users/user/Download/.t1OE3me7' -> '/Users/user/Download/'

해당 현상을 재현해보기 위해 콘솔, CLI로 객체 이름을 변경하려고 해도, 파일명을 없애는 것은 불가능했다. 심지어 콘솔 상에서는 파일명에 / (Forward Slash)가 들어갈 수 없다고 명시하고 있다.

파일명에 `/` 가 들어간 객체는 생성이 불가하다.

해당 파일을 생성하고 업로드하는 코드를 확인해보았고, ‘파일명이 없는 파일’이 전임자가 설계한 의도된 동작임을 깨달았을 때, 감탄을 금치 못했다. 이 사람은 천재가 아닐까.

당시 내 심정

우선 이 서비스의 아키텍처는 CloudFront와 S3가 결합된 일반적인 S3 Static web hosting 구조이다. 다만, Bucket properties에서 Static web hosting 옵션은 disable된 상태였다. (이것이 ‘의도된 동작’임을 깨닫게 된 결정적인 단서이다.)

매우 단순한 아키텍처를 가지고 있다.

S3에서 Static web hosting 옵션을 활성화하는 경우, Index page와 Error Page를 지정할 수 있는 옵션이 있다. 그리고, Index page의 기본값은 당연하게도 index.html 이다. 호스팅 옵션이 활성화되어 있으면, S3는 특정 파일이 지정되지 않은 URL 요청에 대해 자동으로 해당 경로의 index.html을 반환하게 된다.

# Request
http://bucket-name.s3-website.Region.amazonaws.com/
# Respond
http://bucket-name.s3-website.Region.amazonaws.com/index.html # 200 OK

---

# Request
http://bucket-name.s3-website.Region.amazonaws.com/photos/
# Respond
http://bucket-name.s3-website.Region.amazonaws.com/photos/index.html # 200 OK

이 내용은 인덱스 문서 구성에 대한 공식 문서에서 확인할 수 있다.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/IndexDocumentSupport.html

웹 호스팅 옵션이 비활성화된 경우, index.html을 명시하지 않은 URL로 요청을 보내게 되면 404를 반환하게 된다. 명시된 파일이 없기 때문에 404가 뜨는 것이 당연하다. 2번 예제처럼 디렉토리 구조 + 파일명을 모두 작성해주어야 객체가 정상적으로 반환된다.

# Request
http://bucket-name.s3.Region.amazonaws.com/
# Respond
http://bucket-name.s3.Region.amazonaws.com/ # 404 Not Found

---

# Request
http://bucket-name.s3.Region.amazonaws.com/photos/index.html
# Respond
http://bucket-name.s3.Region.amazonaws.com/photos/index.html # 200 OK

S3만 사용했을 때 인덱스를 라우팅할 수 없는 문제는 확인했다. 그렇다면 왜 CloudFront에서도 해결이 되지 않았을까?

CloudFront Distribution config에서는 Default root object를 Optional로 지정할 수 있다. 위에서 설명한 S3의 Index page 설정 옵션과 유사한 기능으로, CF Distribution의 루트 URL로 접근하는 요청에 대해 기본 문서를 반환하는 것이다. Default root object가 index.html이라면, 아래와 같이 작동하게 된다.

# Request
https://d111111abcdef8.cloudfront.net/
# Respond
https://d111111abcdef8.cloudfront.net/index.html # 200 OK

CloudFront의 Default root object의 경우, S3의 Index page 설정과 다른 점이 있다. Default ‘root object’라는 이름에 걸맞게, 해당 설정값은 Distribution의 최상단(루트) URL로 접속하는 요청에만 적용된다. 해당 Distribution의 하위 디렉토리의 경우 index.html 로 라우팅이 불가하다는 뜻이다.

# Request
https://d111111abcdef8.cloudfront.net/photos/
# Respond
https://d111111abcdef8.cloudfront.net/photos/ # 404 Not Found

---

# Request
https://d111111abcdef8.cloudfront.net/photos/index.html
# Respond
https://d111111abcdef8.cloudfront.net/photos/index.html # 200 OK

이 내용 역시 CloudFront 공식 문서에서 작동 방식을 확인할 수 있다.
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/DefaultRootObject.html#DefaultRootObjectHow

따라서, CloudFront Origin Path의 최상단에 index.html을 저장했다면 똑똑하게 라우팅이 될 것이나.. 디렉토리가 1 depth라도 들어가게 되면 작동이 불가하다.

위 케이스들을 바탕으로, 자동으로 index.html을 찾아가도록 페이지를 서빙하기 위해 필요한 경우의 수를 아래와 같이 나타내보았다.

이 서비스는 S3 Static web hosting 옵션이 꺼져있었고, index.html 파일은 디렉토리를 3 depth나 파고 들어가서 저장되어 있었으며, CloudFront Origin Path는 1 depth에 위치한 디렉토리를 바라보고 있었다. 절대로 index.html로 자동으로 라우팅될 수 없는 구조를 가지고 있는 것이다.

특정 파일로 라우팅이 불가하다면, 파일명을 없애버리면 된다(!!!)고 전임자께서는 생각하셨던 것 같다. 파일 업로드하는 로직을 보니, 아래 CLI 명령어롤 통해 파일이 업로드되고 있었다.

aws s3api put-object \
--content-type text/html \
--bucket $BUCKET_NAME \
--key aaa/bbb/ccc/ \
---body index.html

s3api put-object 명령어에서는 —key 옵션을 통해 파일을 업로드할 디렉토리+파일명을 지정한다. 일반적인 상황에서는 aaa/bbb/ccc/index.html의 형태로 파일명을 지정하게 될 것이다. 하지만 위 코드에서는 aaa/bbb/ccc/ 와 같이 파일명을 지정하지 않고 있고, 파일명이 없는 객체를 생성하고 있다.

AWS 콘솔에서도, CLI의 aws s3 명령어를 통해서도 ‘파일명이 없는 파일’은 저장이 불가하다. 위 동작은 System glitch가 아닐까 싶을 정도로 황당했다. 어떻게 파일명이 없는 객체가 생성될 수 있을까? S3 내부적으로는 어떻게 이 요청이 동작하게 되는 것인지 또한 궁금해졌다. (작동 방식을 아시는 분이 계시다면 코멘트를 부탁드린다.)

세상은 넓고, 고수는 많다. 새로운 깨달음을 주신 전임자분에게 존경을 담아 감사를 표한다.

--

--