본문으로 바로가기

[HTTP 기초_2] 웹 캐쉬 (캐쉬 정리)

category 3. 웹개발/3_1 웹개발 기본 2019. 1. 26. 16:47
반응형

[HTTP 기초_2] 웹 캐쉬 (캐쉬 정리)



안녕하세요. 갓대희 입니다. 이번 포스팅은 캐쉬 ] 에 대해 전반적으로 정리 해보려 합니다. : ) 




들어가기 앞서 사실 오늘의 주된 포스팅의 목적은 이전 포스팅 주제인  [ http Header 정리 ] (클릭) 다음으로 이어지는  [ HTTP 헤더를 통한 캐싱 ]이 주된 목적이었는데  이참에 기초 캐싱처리에 대한 여러 내용을 정리하고 넘어 가려고 한다.



요즘 많은 앱, 웹 사용시 사용자들은 조금만 싸이트가 느려도 스트레스를 받는다고 한다.
이러한 고객 니즈에 의해 싸이트를 빠르게 만들기 위해 개발자들은 정말 많은 노력을 한다.

- 코드 압축 (Javascript를 minify 하던지 gzip으로 압축 한다던지 등)
- 이미지 처리 (CDN, Split 등)
- Lazy Loading (느린 로딩 기법)
- Critical Rendering Path
- 캐싱 처리 (WebCache 다양한 웹 개시 사용방법이 존재, 캐시 서버 도입 또는 소스에서 내부 캐싱 처리 등, DBCache 등)


이 중 오늘은 캐싱 처리에 대한 여러 내용을 정리해 보려고 합니다.



 웹 서버 기준 캐싱 처리

 - 웹에 요청을 호출 하면, 요청은 웹 브라우저로 부터 하드 디스크의 파일 시스템에 있는 정적 리소스를 제공하는 웹 서버로 전달 한다.

 - 이때 성능을 고려하여 캐싱 시스템을 잘 활용하고 있는 시스템 환경상이라고 한다면
   웹 서버로 첫 요청시, 하드 디스크는 캐시를 확인하고  "캐시 미스(Cache miss)"를 발생시킨다. 그리고 추후 다시 요청받을 수 있다고 하면 캐시를 하드디스크에 저장한다.

 - 이후 요청부터는 캐시를 사용할 수 있는 경우  "캐시 히트(Cache hit)"를 발생시키고, 캐시 미스가 발생하기 전까지 버퍼에서 캐시가 제공 된다.

 - Database캐싱과 같은 경우도 매우 시스템 성능에 있어 중요 하다. 
   (우리 싸이트와 같은 경우는 Arcus Cache를 사용하여 디비 캐싱 처리 하고 있다. 성능도 확실히 좋아진 걸 WebTune APM을 통해 확인 가능하였다.)
   데이터베이스 쿼리는 데이터 베이스 서버에서 수행되기 때문에 사용자가 급격히 증가함에 따라 속도가 매우 느려지고 부하가 몰릴 수 있다.
   이런 쿼리들이 반복되면 그 결과값을 데이터베이스에 캐싱하여 응답시간 향상 및 서버 부하 감소가 가능하다.
특히 다수의 머신이 동일한 데이터베이스에 동일한 쿼리를 사용할 수록 그 효과는 커지게 된다.



 응답 캐싱

 - 웹 서버는 응답을 캐싱하도록 구성하여 유사한 요청이 애플리케이션 호스트로 전달되지 않도록 할 수 있다.

 - 이와 비슷한 방식으로, 애플리케이션 호스트는 비용이 높은 데이터베이스 쿼리나 자주 요청되는 파일들에 대한 응답을 캐시할 수도 있다.

 - 웹 서버의 응답은 메모리에 캐싱하고, 애플리케이션 캐시는 로컬 인메모리에 저장하거나 캐시 서버 위에서 실행되는 레디스와 같은 인메모리 데이터베이스에 저장할 수 있다.


 함수 메모이제이션 (Memoization)

 (메모이제이션 : 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술. 캐싱의 한 형태)

 - 이는 함수의 입력 파라미터에 해당하는 키와 결과값에 해당하는 값을 매칭시켜둔 조회 테이블을 통해 구현할 수 있다.

 - 프로그램의 성능을 높이는데 사용되는 일반적인 기술이지만 드물게 요청되는 함수나 빠른 응답 속도를 가진 함수에 대해서는 적합하지 않을 수도 있다.



 HTTP 헤더를 통한 브라우저 캐싱

 - 모든 브라우저는 HTML 페이지, 자바스크립트 파일 및 이미지와 같은 웹 문서의 임시 저장을 위해 HTTP 캐시 (웹 캐시)의 구현을 제공하고있다.

 - HTTP 헤더 지시문을 제공하여 브라우저가 응답을 캐싱할 수 있는 시기와 지속 기간을 지시할 때 사용된다.

 - 리소스가 로컬 캐시로부터 빠르게 로드되기 때문에 점점 하드웨어 성능이 급격하고 있는 최근 잘만 활용한다면 엄청 강력한 속도를 제공 한다.

 - 요청이 네트워크를 통해 전송되지 않기 때문에 왕복 시간(RTT, Round Trip Time)이 발생하지 않는다.

 - 애플리케이션 서버 및 파이프라인의 다른 구성 요소에 대한 부하가 줄어든다.



▶ 프록시 서버

컴퓨터 네트워크에서 프록시 서버는 컴퓨터 시스템, 하드웨어 어플라이언스 또는 애플리케이션 형태로 나타난다.
다른 서버의 리소스를 찾는 클라이언트의 요청에 대한 중개 역할을 하며 그 반대의 경우도 마찬가지이다.

다양한 형태의 프록시 서버가 존재한다. 사용자의 로컬 컴퓨터, 네트워크 라우터 또는 클라이언트와 대상 호스트 사이의 다양한 중개 서버에 상주할 수 있다. 모든 프록시 서버는 캐싱이 가능하다.


프록시 캐시 특징
 - Browser Cache와 동일한 원리, 내부 디스크가 아닌 네트워크 상에서 동작.
 - 다양한 회사, IPS의 방화벽에 설치 되며 대기시간 & 트래픽 감소, 접근정책 & 제한 우회, 사용률 기록등 수행
 - 한정된 수의 클라이언트을 위하여 무한대의 웹서버의 컨텐츠를 캐쉬
 
Gateway Caches (REVERSE OR SURROGATE PROXY) 특징
 - 서버 앞 단에 설치되어 요청에 대한 캐쉬 및 효율적인 분배를 통해 가용성, 신뢰성, 성능등을 향상
 - Encryption / SSL acceleration, Load balancing, Serve/cache static content, Compression등을 수행
 - 무한대의 클라이언트들에게 한정된 수(또는 하나)의 웹서버 컨텐츠를 제공

1. 게이트 웨이
나가는 요청 또는 들어오는 응답을 변형없이 단순히 포워딩해주는 프록시 서버로 게이트웨이, 터널링 프록시, 웹 프록시, 프록시 또는 애플리케이션 수준 프록시라고도 한다.
이러한 프록시는 일반적으로 방화벽 내부의 모든 클라이언트가 공유하므로 요청 캐싱에 적합한 후보가 된다.

2. 포워드 프록시
포워드 프록시 (프록시 서버)는 일반적으로 클라이언트측 인프라에 설치된다.
포워드 프록시를 사용하도록 설정된 웹 브라우저는 나가는 요청을 프록시로 전달한다.
다음은 전달된 요청을 인터넷을 통해 대상 서버로 포워딩한다.
포워드 프록시의 장점중 하나는 클라이언트의 식별 아이디를 숨긴다는 것이다. (그러나 VPN이 익명성에 더 안전하다)

3. 웹 가속기
웹 가속기는 웹 사이트의 엑세스 시간을 줄여주는 프록시 서버이다.
이는 가까운 미래에 엑세스할 가능성이 있는 문서를 미리 페치한다.
이는 또한 문서 압축, 암호화 속도 향상, 이미지 품질을 낮추는 작업등을 수행할 수 있다.

4. 리버스 프록시
리버스 프록시는 일반적으로 사설 네트워크의 서버에 직접 엑세스하지 못하도록하는 내부 프록시이다.
이는 내부 서버들간의 요청을 로드 밸런싱하거나 SSL 인증을 제공하거나 요청을 캐싱하는데 사용된다.
서버측 호스트는 캐시를 하고 많은 수의 요청을 관리하도록 도울 수 있다.

5. 엣지 캐싱
한편, 엣지 캐싱 (콘텐츠 전송 네트워크 (CDN))은 콘텐츠를 최종 사용자에게 더 가까이 저장하기 위해 캐싱 서버를 사용하는 것을 의미한다.
예를 들어, 인기있는 웹사이트를 방문하여 캐싱될 정적 콘텐츠를 다운로드하는 경우가 있다. 각 후속 사용자들은 캐시가 만료될 때까지 캐싱 서버로부터 직접 콘텐츠를 제공받는다.




※ [ HTTP 헤더를 통한 브라우저 캐싱 ] 에 대해 좀더 자세히 알아보자.


▶ 웹 캐쉬 (WEB-CACHE (TIME-SPACE TRADEOFF))

웹 캐쉬란 client가 요청하는 Resource(html, image, js, css등)에 대해 
최초 요청 시 파일을 내려받아 특정 위치에 복사본을 저장, 이후 동일한 URL의 Resource요청은 내부에 저장한 파일(캐시)을 사용하여 더 빠르게 서비스하기 위한 것 이다. 

서버를 통하지 않아 응답 시간이 감소하고 네트워크 트레픽이 감소되니 잘만 사용한다면 정말 좋은일이다.

하지만 잘못 사용한다면 고객들에게 실시간으로 정보를 올바르게 주지 못하고, 오히려 고객 CS가 발생하는 등 
해당 업무 프로세스와, 시스템 환경을 잘 분석하여 적용하는 것이 중요할 것이다. 



▶ Header 지시문을 통한 캐싱

모든 브라우저에는 HTTP 캐시 구현이 포함되어 있기 때문에 서버 응답이 브라우저에 응답을 캐시할 수 있는 시점, 
그 기간을 지시하기 위한 올바른 HTTP 헤더 지시문을 제공하는지 확인하면 캐시 적용이 가능하다.

 이런 것들에 대한 설정정보는 헤더를 통해 전달할 수 있다.
쿠키나 캐시에 대한 정보는 개발자 도구나 피들러같은 도구를 통해 쉽게 확인 할 수 있다.

캐시 설정을 통해 브라우저에 응답으로 온 HTML, JSON 등의 이미 저장된 데이터를 통해 서버에 요청을 보내지 않고도 사용할 수 있다.

일반 적으로 캐싱은 GET 요청에서 처리한다.
일반적으로 200(가져오기 성공), 206 (부분 컨텐츠 응답), 301(다른 주소로 이동 후 가져옴), 404(가져올 게 없음) 상태 코드로 온 응답등을 캐싱할 수 있다.

가장 흔하게 볼 수 있던 캐싱 페이지가 오류 페이지 이다.
어떤 오류가 발생하면 그 싸이트 특색에 맞게 오류 페이지를 보여주도록 해둔 싸이트도 많고, 알게모르게 자주 봤을 것이다.



▶ Header 지시문을 통한 캐싱 문법

여러 싸이트들을 웹 패킷 분석툴을 열어 놓은 채로 써핑 해보자.
그러다 보면 다음과 같은 헤더문을 볼 수 있을 것이다.


이 부분이 오늘 작성하려고 하는 헤더 예시문이다.


1. Cache-Control
 - 일반적으로 캐싱은 GET Method에 대한 응답을 캐싱하는 것으로 제한한다.
 - Cache-Control HTTP 헤더를 통해 캐싱 정책을 정의할 수 있다.
 - Cache-Control 지시문은 응답을 캐시할 수 있는 사용자, 해당 조건 및 기간을 제어한다.
 - Cache-Control 헤더는 HTTP/1.1 사양의 일부로 정의되었고, 응답 캐싱 정책을 정의하는 데 사용된 이전 헤더(ex) Expires)를 대체한다.
 - 모든 최신 브라우저는 Cache-Control을 지원한다.
 - Cache-Control는 응답 헤더 뿐만아니라, 요청 헤더로도 사용할 수 있다. 
   프론트 - 중개 서버 - 서버와 같은 구조인 경우 중개 서버에 있는 캐시를 가져오지 않도록 하려면 요청 시 Cache-Control을 헤더로 넣어주어 해결하기도 한다.

 
2. 'no-cache' vs 'no-store'
 - 'no-cache' 캐시를 사용하지 말라는 뜻이 아니라 캐시를 쓰기 전에 서버에 이 캐시 진짜 써도 되냐고 물어보라는 뜻이다.
 - 'no-store' 가 아무것도 캐싱하지 않으려면 사용한다.
 ex) Cache-Control: no-cache, no-store


3. 'public' 및 'private'
 - 'public' 이면 공유 캐시(또는 중개 서버)에 저장해도 된다는 뜻이다.
   ex) public, max-age=3600
   max-age로 캐시 유효시간을 줄 수 있다. 초 단위이므로 1시간이 지나면 이 응답 캐시는 만료된 것으로 여겨진다.
   대부분의 경우, 명시적 캐싱 정보(예: 'max-age')가 응답이 어떠한 경우든지 캐시가 가능하다고 나타내므로 'public'이 필요하지 않다.

 - 'private'은 브라우저같은 특정 사용자 환경에만 저장하라는 뜻이다.
   일반적으로 단일 사용자를 대상으로 하므로 중간 캐시가 이 응답을 캐시하는 것은 허용되지 않는다.
   예를 들어, 비공개 사용자 정보가 포함된 HTML 페이지는 사용자의 브라우저가 캐시할 수 있지만, CDN은 이 페이지를 캐시할 수 없다.


4. must-revalidate
 - 만료된 캐시만 서버에 확인을 받도록 한다.
 - max-age=<seconds>
 ex) Cache-Control: max-age=86400

5.1 max-age
 - 캐시 유효시간, 즉 리소스가 유효하다고 판단되는 최대 시간 이다.
 - 초단위로 설정하며, 해당 시간이 지나면 만료된것으로 여겨진다. 
ex) Cache-Control: max-age=31536000

5.2 Age
 - 캐시 응답 때 나타나는데, max-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려준다.  
 

6. Expires
 - Cache-Control과 동시에 동작하진 않지만, Expires라는 헤더를 지정하여 응답 컨텐츠 만료 시간을 나타낼 수 있다.
   ex) Expires: Wed, 23 Jan 2019 03:44:00 GMT
 - Cache-Control의 max-age가 있는 경우 이 무시된다.
 - 유효 수명 : Expires 헤더에서 Date 헤더의 값을 뺀 결과

7. ETag
 - ETag로 캐시된 응답에 대한 유효성 검사 수행한다. (HTTP 컨텐츠가 바뀌었는지를 검사할 수 있는 태그)
 - 서버는 ETag HTTP 헤더를 사용하여 유효성 검사 토큰을 전달한다. 유효성 검사 토큰을 사용하여 리소스 업데이트 검사하며 리소스가 변경되지 않은 경우 데이터가 전송되지 않는다.
 - [ Expires ]나 [ max-age ]등을 사용하여 특정 시간동안만 캐시를 사용한다고 했을때 특정 시간, 시점이 지나야 브라우저가 새 요청을 발송하고 전체 새 응답을 가져온다.
   그러나, 리소스가 변경되지 않은 경우엔 이미 캐시에 있는 동일한 정보를 다운로드할 이유가 없으므로 이렇게 시간을 지정하여 캐쉬를 갱신하는 작업은 비효율적이다.
 - ETag 헤더에 지정된 유효성 검사 토큰은 바로 이 문제를 해결하기 위해 고안되었다. 서버는 일반적으로 파일 콘텐츠의 해시나 기타 일부 지문인 임의 토큰을 생성하고 반환한다.
   클라이언트는 다음 요청 시 지문을 서버에 전송하기만 하면 된다. 지문이 여전히 동일한 경우 리소스가 변경되지 않고 이 다운로드를 건너뛸 수 있다.
   EX) 클라이언트에 최대 120초 동안 캐시하도록 지시하고, 응답이 만료된 후 리소스가 수정되었는지 확인하는 데 사용할 수 있는 유효성 검사 토큰('x456dff')을 제공한다.
   

8. If-None-Match
 - 서버보고 ETag가 달라졌는 지 검사하고 ETag가 다를 경우에만 컨텐츠를 새로 받는다.
   ex) If-None-Match: "0-2b800-5c466dda"
 - 만약 ETag가 같다면 서버는 304 Not Modified를 응답하고 캐시를 그대로 사용하게 한다.
 
우리 안드로이드 폰에서는 위와 같은 설정값을 아무리 설정해도 적용되지 않아 고생좀 하였다.
우리 안드로이드 폰에서 분명 데이터가 변경 되었는데 계속 캐쉬 데이터를 읽어오는 문제가 발생하였다.
no-store, max-age=0 등등 캐시를 아예 타지 않도록 헤더정보를 날려보았으나 계속 실패햐였다.


나와 같은 경우 특정 요청을 하여 JSON 형태로 응답을 받는 호출이 있었는데 이 호출시 응답이 계속 304Error로 왔고,  이런 캐쉬 데이터를 사용하는 원인은 [ Google Volley library ]에 있었다.

스택 오버 플로우에서 원인 및 해결 방법을 찾아 적용하여 해결 하였다.


[ 스택 오버 플로우 질문 / 답변 ]
--------------------------------------------------------------------
Is there a way I could disable the Volley cache management? My app is using Google Volley library to manage the transport layer, 
but I have my own cache managerimplementation because the server does not uses Cache-Control header. 
I want to save the space that Volley cache is using because it is totally useless.

Is there any easy way? or should I implement my own version of RequestQueue?

If you use any of the default Request classes implemented in volley(e.g. StringRequest, JsonRequest, etc.), then call setShouldCache(false) right before adding the request object to the volley RequestQueue:

request.setShouldCache(false);

myQueue.add(request);

If you have your own implementation of the Request class, then you can call setShouldCache(false) in the constructor of your class.

This solution disables caching for each requests individually. If you want to disable caching globally from the volley library, you can permanently set the mShouldCache variable to false in the Request class.

does not seem to be enough for GET requests. however, clearing the cache before adding to the queue seems to help

myRequestQueue.getCache().clear();

You can create your RequestQueue from constructor and pass a NoCache object as the first parameter. The second parameter is a network transport based on your choice of AndroidHttpClient or HttpURLConnection.

RequestQueue queue = new RequestQueue(new NoCache(), new BasicNetwork(new HurlStack()));
For more details see this documentation.

According to the documentation, BasicNetwork is Volley's default network implementation.
-------------------------------------------
참고 : https://stackoverflow.com/questions/25333351/disable-volley-cache-management






반응형

댓글을 달아 주세요

  1. ㅎㅎㅎㅎㅎ 2019.11.27 15:25

    갓대 열심히네?ㅎㅎㅎ