Skip to content

이미지 호스팅 서버를 통해 알아보는 웹 성능 개선 기법들

Notifications You must be signed in to change notification settings

bugoverdose/MyS3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MyS3: 이미지 호스팅 서버

배경

  • 원래 해당 프로젝트는 우테코에서 S3 사용이 금지됨에 따라 만들어본 간단한 토이 프로젝트였습니다.
  • 학습 목적으로 정적 데이터를 제공하는 서버측에서 할 수 있는 성능 개선 기법들을 몇 가지 적용해보았습니다.

API 개요

  • 이미지 조회 API : GET /images/{uploadPath}/{fileName}
  • 이미지 저장 API : POST /api/images/{uploadPath}
    • image 필드의 값으로 업로드할 이미지를 추가해줘야 합니다.
    • (선택) 파라미터 값으로 fileName, version 설정할 수 있습니다.
      • filename을 명시하지 않으면 업로드하는 파일의 이름을 그대로 활용하되, 확장자만 webp로 변경됩니다.
      • version은 파일을 filename-version.webp 형식으로 저장해줍니다. 버전에 따른 URL 변경을 통한 캐쉬 버스팅에 활용될 수 있습니다.
  • 이미지 삭제 API : DELETE /api/images/{uploadPath}/{fileName}

캐쉬 기반 성능 개선

1) ETag 헤더

  • ETag/If-None-Match 헤더를 통해 웹 사이트의 성능 개선을 시도하였습니다.
  • 특정 클라이언트의 캐쉬 저장소에 저장되어있던 파일을 서버로 다시 요청한 경우, 서버에서는 etag 값의 비교를 통해 캐쉬 저장소의 데이터와 서버의 데이터가 동일한지를 확인합니다.
  • 만일 클라이언트가 서버에서 전송하려는 데이터와 동일한 데이터를 이미 지니고 있다면 304 Not Modified를 응답하여 해당 캐쉬를 재사용하도록 합니다.
  • 이를 통해 네트워크 상에서 오가는 데이터의 양(bandwidth)을 줄임으로써 응답 속도를 크게 감소시킬 수 있습니다.
  • 다만, etag를 생성하기 위해 해당 이미지 파일을 조회하는 작업에 따른 서버 측의 부담 자체는 여전히 존재합니다.

ShallowEtagHeaderFilter 동작 방식

  1. 이미지 조회시, 서버는 응답하기 직전에 HTTP 요청의 If-None-Match 헤더 값과 응답하려는 이미지 파일에 해당되는 etag 값을 비교합니다.

    • 이를 위해 서버에서는 클라이언트로 보내려는 이미지 파일의 내용을 토대로 etag 값을 생성합니다.
    • 이미지 파일의 내용이 변하지 않았다면 매번 동일한 etag 값이 생성됩니다.
    • 해당 경로에 저장된 파일의 내용이 다른 파일로 변경된 경우, 응답될 때 다른 etag 값이 생성됩니다.
  2. HTTP 요청의 If-None-Match 헤더 값이 없는 경우 해당 클라이언트에서는 해당 이미지를 최초로 보낸 것으로 간주하며, 200 OK 로 응답합니다.

    • HTTP 응답에는 요청한 이미지 파일의 내용이 담기며, ETag 헤더 값에 해당 이미지 파일에 대해 생성된 etag 값이 담깁니다.
    • 브라우저에서는 캐쉬 저장소에 ETag 헤더의 값을 해당 캐쉬에 대해 함께 저장합니다.
    • 이후 브라우저에서는 캐쉬 저장소에 등록된 해당 데이터를 서버에 다시 요청할 때 If-None-Match 헤더에 해당 etag 값을 담아 요청하게 된다.
  3. HTTP 요청의 If-None-Match 헤더의 etag 값이 서버에서 응답하려는 이미지 파일의 etag 값과 동일한 경우 304 Not Modified를 응답합니다.

    • 이때 응답에는 요청한 이미지 파일이 담기지 않으므로, 네트워크 상을 오가는 데이터의 양은 훨씬 적고 응답은 더 빠릅니다.
    • 브라우저에서는 304 응답 코드를 받게 되면 자동으로 캐쉬 저장소의 데이터를 계속 재사용하게 됩니다.
  4. HTTP 요청의 If-None-Match 헤더의 etag 값이 서버에서 응답하려는 이미지 파일의 etag 값과 다른 경우 200 OK를 응답합니다.

    • 이 경우 해당 이미지 파일이 이전과는 달라졌으므로 HTTP 응답에는 요청한 이미지 파일의 내용과 그에 대응되는 ETag 헤더 값이 담깁니다.
    • 브라우저에서는 응답 내용을 토대로 캐쉬 저장소의 캐쉬를 갱신한다.

2) Cache-Control 헤더

  • 기본적으로 이미지 조회시 응답에는 다음과 같은 헤더값이 추가됩니다.

    • Cache-Control: max-age=600, public
    • 이는 캐쉬 저장소에 해당 URI에 대한 응답 정보가 존재하는 경우 10분 동안 해당 캐쉬 값을 그대로 사용하라는 의미입니다.
    • 브라우저와 서버 사이의 컴포넌트들로부터 Shared Cache의 이점을 볼 수 있도록 기본적으로 public 옵션을 설정하였습니다.
  • max-age 값은 application.yml 파일의 cache.max-age 프로퍼티의 값에 해당하므로 쉽게 수정할 수 있습니다.

3) Cache Busting 지원

  • 기본적으로 캐쉬 버스팅이란 캐쉬의 유효기간을 최대한 높게 설정하여 오랜기간 재활용하되, 버전이 변경되었을 때에 즉시 서버에 요청을 보내도록 하는 기법입니다.
    • 이를 위해서는 자원의 URI를 버전에 따라 다르게 설정함으로써 캐쉬 저장소에 관리 중인 캐쉬를 사용하지 않도록 해야 합니다.
  • 이를 구현할 수 있도록 해당 서버에서는 캐쉬의 유효기간을 수정하는 것만이 아니라 이미지를 업로드할 때 version 정보를 받아 이미지의 파일명에 추가하는 기능을 구현했습니다.

구현 방법 예시

  1. 우선 Cache-Control 헤더의 max-age 값을 31536000초로 설정함으로써 1년 동안 캐쉬를 재사용하도록 합니다.
  2. 특정 이미지를 업로드할 때 version 정보를 명시하면 해당 파일명에는 해당 버전 정보가 추가됩니다.
  • 예를 들어 user/profile1라는 파일을 저장할 때 asdfds라는 해쉬값을 버전으로 명시하면, 파일은 user/profile1_asdfds.webp와 같이 저장됩니다.
  1. 특정 클라이언트에서 GET /images/user/profile1_asdfds.webp로 요청을 보내는 경우, 해당 URI에 대한 응답은 1년 동안 재사용됩니다.
  2. 이때 해당 파일을 새로운 버전으로 수정해야 하는 경우, 새로운 버전의 이미지를 업로드하고 사용자에게 이를 사용하도록 강제하도록 하면 됩니다.
  • 예를 들어 기존과 같은 user/profile1라는 파일을 저장하되 bkfefd처럼 다른 해쉬값을 버전으로 명시하면, 파일은 user/profile1_bkfefd.webp와 같이 저장됩니다.
  • 이후 기존 버전의 파일은 불필요해졌으므로 DELETE /api/images/user/profile1_asdfds와 같은 요청으로 제거할 수 있습니다.
  1. 이제 클라이언트에서 GET /images/user/profile1_bkfefd.webp로 요청을 보내도록 한다면 당연히 캐쉬 저장소에 있는 asdfds 버전 데이터를 사용하지 않고 서버로 요청을 보내게 됩니다.

기타 성능 개선

1) .webp

  • 해당 서버는 업로드되는 이미지 파일을 .webp 확장자로 저장하여 제공합니다.

  • 이를 통해 응답 성능 개선만이 아니라, 서버의 부담을 줄이고 하드 디스크에 더 많은 파일을 저장할 수 있도록 해줍니다.

  • 물론 업로드하려는 파일이 이미지 파일이 아닌 경우 400 Bad Request로 응답합니다.

  • cf) webp의 장점

    • 무손실 압축을 통해 이미지 품질은 유지하면서도 파일 크기 자체를 감소시키기 위한 확장자.
    • I.E.를 제외한 모든 브라우저들에서 호환되는 형식. (참고)
    • gif 등 모든 이미지 형식에 대응 가능.

2) HTTP 압축

  • HTTP 응답을 압축함으로써 웹 사이트의 성능을 향상시키고자 하였습니다.
  • server.compression.enabled 프로퍼티를 통해 Spring Boot에서 제공하는 기본 설정인 gzip 압축 알고리즘을 적용하였습니다.
  • HTTP 응답에 다음 헤더들이 추가됩니다.
    • Content-Encoding: gzip
    • Transfer-Encoding: chunked

실습 방법

  • 직접 해당 서버를 실행해보고 싶은 경우, 아래 두 가지 정보를 알아야 합니다.
  • 데모 내용이 궁금한 경우 블로그를 참고하기 바랍니다.
    • 주의. API 및 구현된 기능면에서 현재 서버와 다소 차이가 있습니다.

이미지 파일 저장 경로

  • 디폴트 경로: ~/static/images/{uploadPath}/{fileName}
    • static/imagesapplication.yml 파일의 image.storage.root-directory 프로퍼티의 값에 해당함.
    • 해당 yml 파일의 프로퍼티 값을 수정하거나, 구동 시점에 값을 주입해줌으로써 저장 경로 변경 가능.

인증 기능

  • 이미지를 업로드/제거하려는 경우, 해당 API를 호출할 때 Authorization 헤더 값으로 서버에서 설정한 비밀 키를 추가해줘야 합니다.
  • 비밀 키는 application.yml 파일의 security.authorization-key 프로퍼티 값에 해당합니다.
    • prod 프로파일로 실행하는 경우 SECRET_KEY 환경변수 값을 활용하게 됩니다.

About

이미지 호스팅 서버를 통해 알아보는 웹 성능 개선 기법들

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy