본문 바로가기
backend/Java

브라우저 캐시 관리

by 디 히 2023. 6. 22.

 프로젝트를 마치고 테스트운영중인데 운영사이트에 특정기능이 작동하지 않는다는 문의가 왔다. 알아보니 어제 저녁에 수정한 기능인데 정상작동하는지 확인하고 올렸음에도 문제가 있다는 것이었다. 근데 문제는 30여명의 이용자가 있는데 대부분 안된다고 하니 급히 문제를 찾기 시작했다. 당시 의심정황은 다음과 같았다.

 

  • 코드가 저장되지 않은 상태로 원격저장소에 올라갔다
  • 배포과정에서 실수가 있었다
  • 브라우저 캐시가 남아서 이전코드가 작동했다
  • 이용자들의 환경이 특수 환경이다

 

 하나씩 짚어나가기 시작했는데 로컬에서 정상작동하고 팀원들의 컴퓨터에서도 정상작동하는 것으로 보아 1번은 패스,. 동작하는 코드로 war파일을 재배포하여 운영에서 확인하여 정상작동하는 것을 확인. 2번도 패스. 3번은 고객사에 강력새로고침 방법을 알려드렸으나 따라하지 못해 확인불가. 그래서 4번으로 생각하고 현장에 나갈 준비를 하는 와중에 강력새로고침 단축키(ctrl + shift +R)를 알려주어 정상동작하는걸 확인했다. 우리쪽에서 전부 작동하던 이유는 개발자도구의 disable cache를 전부 켜뒀기 때문이였다.

 

 여기서 연쇄적으로 발생한 고민은 '수정하여 배포할 때마다 강력새로고침을 하라고 알려주는건 말이 안된다'였고 캐시를 컨트롤할 수 있는 방법을 찾기 시작했다. 요즘은 webpack에서 파일명에 hash를 붙여 다른파일로 인식하여 배포 시 다른파일로 인식되는 방식을 이용한다고 하는데, 진행한 프로젝트는 별도의 번들링이 없었기에 다른방법을 찾아야했다. 파일명에 쿼리스트링으로 버전을 기재하는 방식도 있었으나 각 파일마다 일일히 바꿔주는것은 문제였고, dotenv와같은 환경변수로 한번에 적용하면 되지 않을까도 생각했으나 쉽지 않았다. 참 뭔가 하기 어려운 환경이다.

 

 결국 java단에서 filter를 통해 header에 넣어주는 방식으로 결정했다.

처음엔 KST형식의 시간을 expires에 넣어주었는데, date에 GMT형식의 시간이 나오는 것을 보고 혹시 해서 GMT형식으로 바꿔주었더니 잘 작동하는걸 확인하였다. 아직은 수정사항이 빈번하기 때문에 6시간으로 설정해두었으나, 좀 더 안정기가 되면 시간을 늘려도 좋을 듯 하다.


// web.xml
<filter>
	<filter-name>CacheControlFilter</filter-name>
	<filter-class>com.filter.CacheControlFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>CacheControlFilter</filter-name>
	<url-pattern>*.css</url-pattern>
	<url-pattern>*.js</url-pattern>
	<url-pattern>*.png</url-pattern>
</filter-mapping>

// CacheControlFilter.java
public class CacheControlFilter implements Filter {

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		
        	// Cache Expires 설정
        	// SimpleDateFormat 객체를 생성하고 패턴을 설정합니다.
        	SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
        	dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));

        	// 현재 시간을 가져옵니다.
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.HOUR, 6); // 얼마 후에 Expires될 지 설정합니다.
		Date expiresDate = calendar.getTime();
        
        	// GMT 시간으로 변환하여 문자열로 출력합니다.
        	String gmtTimeString = dateFormat.format(expiresDate);
		httpResponse.setHeader("Expires", gmtTimeString);
		
		// Cache 사용하지 않게 설정
//		httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
//		httpResponse.setHeader("Pragma", "no-cache");
//		httpResponse.setHeader("Expires", "0");
		
		chain.doFilter(request, response);
	}
	
	@Override
	public void destroy() {}
}

'backend > Java' 카테고리의 다른 글

log4j2  (0) 2023.05.04