[배경]
당시에 프론트 단의 js validation 로직을 구현하고 서버에 적용을 시켰는데,
적용된 파일이 제대로 작동하지 않는 현상이 발생했다!
일일이 서버의 파일 수정 -> 빌드 -> 재배포 하면서 계속 디버깅해본 결과,,
static file 이 브라우저에 의해 캐시되고 있어서 변경된 js 파일이 적용되지 않았던 것이다.
파일 이름은 동일하니까, 브라우저 입장에서는 해당 파일이 변경되었는지 알 수 없다.
[가능한 대처 방안]
이 경우에는
- 사용자가 직접 쿠키를 지우거나, ctrl+f5 눌러서 새로 정적파일을 받아오거나
- 파일 이름에 해시값을 붙여, 배포 시에 변경된 파일 이름만 해시값을 변경해주는 방식
- 파일 이름에 태그를 붙이는 방식인데, naver 메인페이지 html 헤더 보니까, 태그에 수정날짜를 적어 관리했다.
- 또는 cache-control 과 같은 response 헤더를 사용해, 브라우저가 캐시 하지 않도록 하는 방식도 있는데,,,
1번과 4번은 사실 사용자의 경험을 봤을 때 정말 별로인 방법이고,
3번을 사용하자면, 해당 js 파일이 사용되는 곳이 지엽적이거나, 담당자가 확실히 정해져서 관리를 하고 있어야 편한데,, 지금과 같이 두세명이 프로젝트 전체의 js 파일을 동시에 관여하고 있고
하나의 js 파일이 여러군데 많이 사용되고 있어서, 일일이 네임태그를 사용하기에는 무리가 있다고 판단했다.
그래서 일괄적으로 빌드할 때 변경된 정적파일 이름의 해시값만 변경하는 방식을 선택하기로 했다.
하지만 나는,,, 백엔드,, 담당인데,,, 내가 왜,, 이런 걸,, 고민해..?
rest api 로 분리되어있지 않아서 django 가 템플릿까지 다 렌더링하고 있는 상황이었기 때문.. 허허
[구현 코드]
Django 구현 소스코드
처음에는 CompressedManifestStaticFilesStorage 를 이용해서 파일 압축도 되게끔 했는데, 빌드할때마다 모든 정적파일을 다시 압축하길래.. 너무 느리고 비효율적이라 판단해서 ManifestStaticFilesStorage 를 선택했다.
settings.py 에 이렇게만 추가해주면 되었다.
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
근데,, 다 좋은데,, ValueError: Missing staticfiles manifest entry for '' 이런 오류가 계속 났다.
정적파일을 찾을 수 없을 때 ValueError 를 발생시키는데,, 요청도 안하는 '' 이런 이상한 제목을 찾을 수 없다고,,
레퍼런스 읽어보니까 아래처럼 설정하면 된다했는데,,,
# settings.py
STATICFILES_STORAGE = 'IBAS.storage.StaticFilesMd5HashingStorage'
# StaticFilesMd5HashingStorage.py
class StaticFilesMd5HashingStorage(ManifestStaticFilesStorage):
manifest_strict = False
안된다..
뭘해도 안돼 으으으으..
그래서 결국 ValueError 를 일으키는 소스코드를 들여다 보았고, 아래와 같이 변경했다!
hashed_name() 함수를 오버라이딩해서 재정의했는데, ValueError 를 일으키는 소스코드만 주석처리 했다.
(그래도 성능 상 이슈가 있는 부분은 아닌듯해서)
# static 못 찾으면 500 오류 뱉어내는거 수정
class StaticFilesMd5HashingStorage(ManifestStaticFilesStorage):
manifest_strict = False
def hashed_name(self, name, content=None, filename=None):
# `filename` is the name of file to hash if `content` isn't given.
# `name` is the base name to construct the new hashed filename from.
parsed_name = urlsplit(unquote(name))
clean_name = parsed_name.path.strip()
filename = (filename and urlsplit(unquote(filename)).path.strip()) or clean_name
opened = content is None
if opened:
if not self.exists(filename):
# raise ValueError("The file '%s' could not be found with %r." % (filename, self))
return name
try:
content = self.open(filename)
except OSError:
# Handle directory paths and fragments
return name
try:
file_hash = self.file_hash(clean_name, content)
finally:
if opened:
content.close()
path, filename = os.path.split(clean_name)
root, ext = os.path.splitext(filename)
file_hash = ('.%s' % file_hash) if file_hash else ''
hashed_name = os.path.join(path, "%s%s%s" %
(root, file_hash, ext))
unparsed_name = list(parsed_name)
unparsed_name[2] = hashed_name
# Special casing for a @font-face hack, like url(myfont.eot?#iefix")
# http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax
if '?#' in name and not unparsed_name[3]:
unparsed_name[2] += '?'
return urlunsplit(unparsed_name)
[당시 이슈]
Static files hashing & caching · Issue #4 · InhaBas/Inhabas.com
클라이언트 브라우저에 정적 파일들 잘 업데이트 되도록!
github.com
[추가]
cdn 서비스를 이용하는 방법도 있을 것 같다...
'웹 프로젝트 (IBAS) > Django 레거시' 카테고리의 다른 글
[Django 웹 프로젝트] 7. 유지 보수를 위한 새로운 인가인증 체계 고민 (2021-10-31) (0) | 2022.03.01 |
---|---|
[Django 웹 프로젝트] 6. 유지 보수를 위한 새로운 아키텍처 고민 (2021-10-21) (0) | 2022.03.01 |
[Django 웹 프로젝트] 4. 댓글(vue.js)을 django 에 붙이기 (2021-08-03) (0) | 2022.02.28 |
[Django 웹 프로젝트] 3. 파일 관리 시스템 개선 (2021-04-30) (0) | 2021.07.09 |
[Django 웹 프로젝트] 2. 장고 폼(forms) 도입 => 코드 간결화 (2021-04-28) (0) | 2021.07.09 |