개발서버에 개발한것을 반영하였을 때, 모바일웹앱을 실행하면 이전 화면이 나타나지 않고 흰 화면이 나타나는 매혹적이고 치명적인 오류가 발생했다.
그래서 테스트를 하거나 앱을 시연할 때 앱 캐시를 한번 날리고 진행했다.
그렇게 연명하며 살던 도중 우리는 오류를 발견하였는데
Uncaught (in promise) TypeError: Failed to fetch dynamically imported module:
https://.../assets/atdrnModify-37776692.js
앱이 기능을 실행할 때 필요한 파일 atdrnModify-37776692.js 을 불러오려고 했는데, 그 파일이 서버에 없어서 실패한 상황이다.
Vue + Vite는 성능을 위해 화면 전환 시 필요한 기능만 동적으로 불러오는 방식(lazy loading) 을 사용한다.
새로운 버전이 배포되면, 기존에 있던 atdrnModify-37776692.js 같은 파일은 삭제되고,
대신 atdrnModify-11223344.js 처럼 새 이름의 파일이 생성된다.
예를 들어, A 사용자가 오전에 웹앱을 켜둔 채 3시간 뒤에 다시 화면을 눌렀을 때:
→ 이미 서버에는 옛날 파일이 사라졌고, 앱은 여전히 옛날 파일을 불러오려 해서 실패한다.
chunk 파일명을 고정시켜 삭제 문제 방지
단, 캐시 문제가 생기기 쉬워 권장되지 않음
서비스워커에서 캐시를 지우고 새 버전 강제 로드
이건 설정이 복잡하고 테스트가 어려워 (내가) 함부로 건들 수 있는 영역이 아니라 패스
에러가 나면 “새로고침 하시겠습니까?” 라는 안내창 표시
해당 방법은 기획과 이야기가 되지 않았고, 만약 사용자가 거절하면 문제가 지속되는 문제가 있음...
문제가 생기면 자동 새로고침해서 최신 버전을 로드
단, 사용자 경험이 조금 끊긴다
그래서 난 가장 해결이 간단하고 확실한 해결책을 사용하기로 했다.
사용자도 웹앱을 다시 껏다 켠 것 같은 느낌만 받을 뿐, 앱이 멈추거나 흰 화면이 노출되지는 않을테니까 (아마도)
main.js에 설정해 두었다
// Vite가 동적 파일을 못 불러오면
window.addEventListener('vite:preloadError', (event) => {
event.preventDefault(); // 기본 에러 막고
window.location.reload(); // 자동 새로고침
});
// Vue Router가 lazy import 실패 시
router.onError((error) => {
if (/Failed to fetch dynamically imported module/.test(error.message)) {
window.location.reload();
}
});
배포 후 확인해 보았을 때 아직까지 흰화면 발생하는 현상은 발견하지 못했다.
만약 추후에 발견될 경우 추가적으로 조치 예정
관련 문제들을 검색했을 때 Vue-Router에서 사용하는 URL 모드를 수정하라는 방법과, vite.config.js에 캐시제어를 하라는 방법들이 많이 나타났다.
import { createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(), // 히스토리 모드
routes,
})
📌 createWebHistory()
📌 createWebHashHistory()
즉, 라우팅 방식의 차이를 말한다.
rollupOptions: {
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: '[name].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]',
},
},
빌드된 파일명에 [hash]를 붙이면, 파일 내용이 바뀌면 이름도 바뀜
예: main.js → main.abc123.js
이는 브라우저 캐시를 무력화 시키기 위한 파일 캐싱 제어 방법이다.