이번 프로젝트 QA 중에 IE11 브라우져에서만 데이터가 잘 안나온다는 피드백을 받았다. 오랜만에 윈도우즈를 켜고 인터넷 익스플로러를 열어 문제를 확인했는데 재현 시나리오는 이렇다.

  1. 목록 화면에서 상세 화면으로 들어가 내용을 수정한다.
  2. 수정 내용을 저장하고 목록화면으로 돌아온다.
  3. 수정 내용이 반영되지 않는다. (이전 목록 내용이 유지된다)

개발자 도구를 열어서 확인해 보았다. 수정 후 목록 데이터 조회하는 API가 브라우져 캐시 값을 사용하는 것이 원인이었다.

IE에서 캐쉬 이슈

이런 문제는 꽤 오래 전부터 있었던 모양이다. 해결하는 방법도 여러가지 인데 세 가지로 정리해 볼 수 있다.

  1. 쿼리스트링에 타임 스탬프를 전달하는 방법
  2. POST 메소드를 사용 (POST는 캐시하지 않는다)
  3. 캐시 컨트롤(Cache-Control) 헤더를 사용하는 방법

마지막 방법으로 문제를 해결할 수 있었다.

그런데 이슈를 조사하던 중에 발견한 스택 오버플로우 질문이 좀 흥미로웠다.

어떻게하면 IE11의 ajax 캐쉬를 피할수 있나요? 쿼리 문자열이나 POST 옵션 말구요. (How to avoid AJAX caching in Internet Explorer 11 when additional query string parameters or using POST are not an option)

질문자의 요지는 이렇다.

IE에서 캐쉬하는 방법은 정확하지도, 충분하지도 않다.

1번 방법

엄격한 검증 로직이 있는 API일 경우 정해지지 않는 쿼리 문자열 파라메터를 보내면 400이나 500 에러로 응답할 수 있다.

서버에서 체크하지 않는 쿼리 문자열을 사용하면 되지 않느냐라는 반박이 있을 수 있다. 하지만 쿼리문자열 충돌 이슈가 없는 것은 아니다.

좀 현실적이어야 한다고 본다. 제이쿼리는 사용하지 않는다는 의미의 언더스코어 문자를 쿼리스트링 키로 사용한다.

jQuery.ajax({cache: false})

캐쉬 옵션을 끄면 GET 메소드에 대해 _={timestampe} 형식의 쿼리 문자열을 추가하는 방식이다. (참고: https://api.jquery.com/jquery.ajax/)

2번 방법

행동을 의미하는 메소드를 사용하는 것은 모던 REST API에 맞지 않다

동감한다. 이런 식으로 모든 ajax 호출을 POST 메소드로 정의한 API 서버코드를 마주한 적이 있었다. 지금 생각해 보면 캐시 문제를 예방하기 위한 조치였을 수 있었겠다는 생각이 든다.

그러나 코드를 보면 잘 안읽힌다는 생각이 든다.

3번 방법

서버 캐시 정책이 적용되지 않는다

이건 브라우져 캐쉬 동작 원리를 조금 이해해야 한다.

캐시 대상: 브라우져가 캐시하는 대상은 GET 메소드에 한정한다. POST 따위의 다른 메소드는 매 호출마다 서버로 요청한다.

캐시 제어: HTTP 헤더를 이용해서 캐시하는 방법을 제어한다. 대표적인 것인 Cache-Control.

  • Cache-Control: no-cache: 캐시하지 않음
  • Cache-Control: max-age=<second>: 설정한 시간(초)만큼 캐시

응답 헤더에 no-cache를 전달하면 이를 받은 브라우져는 캐시하지 않는다.

응답 헤더에 max-age=3 으로 값을 전달하면 브라우져는 응답 받은 후 3초동안 이를 캐시한다. 가령 3초 내에 같은 요청을 서버로 보내면 브라우져는 캐시에 저장된 값을 사용하는 방식이다.

첫 요청을 서버에서 받는다. 응답 헤더에 Cache-Control: max-age=3을 받았다.

3초 내에 같은 요청을 하면 브라우져 캐시에 있는 값을 사용한다. Status Code 부분에 200 OK (from disk cache)라고 띄는걸 확인.

첫 응답을 반은 후 3초 지나서 다시 리소스를 요청하면 캐시가 아니라 서버에서 데이터를 다시 받아온다. (304는 서버가 응답한 상태코드로서 리소스 변동이 없다는 의미)

Cache-Control은 요청 헤더에도 추가할수 있는데 세 번재 방법이 바로 이것이다. 서버에서 캐시 정책을 알려주더라도 요청하는 브라우져가 이를 무시하고 다시 요청할 수 있다.

요청 헤더에 Cache-Control: no-cache를 담아 서버로 전송하면 브라우져는 캐시를 사용하지 않고 서버로 요청한다.

이런식으로 브라우져 단에서 캐시를 무력화하면 서버의 캐시 정책을 무시할수 있다는 것이 스택오버플로우 질문자의 주장이다.

이것도 동감하는 내용이다. 서버에서는 성능을 위해 캐시 정책을 정하고 서비스를 제공하는 브라우저에게 캐시 설정을 내릴 것이다. 브라우져가 이를 무시한다면 기대하는 성능을 얻을 수 없을수 있는 상황도 예측해 볼 수 있다.

정리

POST를 사용하는 두 번째 방법은 아닌 것 같다. 쿼리문자열이나 헤더를 조작하는 방법을 현실에 맞게 적용하는 것이 좋다고 생각한다.

쿼리 문자열을 쓴다면 관례적으로 사용하는 언더스코어를 쓰거나 유의미한 키를 사용하는것이 좋겠다. ?ignore_cache={timestamp} 정도면 이상할까?

요청 헤더를 쓴다면 일괄적으로 사용할 것이 아니라 성능을 위해 서버 정책을 따르자. 불가피한 부분에만 제한적으로 사용하는 것이 좋겠다.