끄적이고 기록하는 나의 블로그

[Vuejs 트러블슈팅] Uncaught SyntaxError: Unexpected token ‘<’ (at app~….js:1:1) console 오류 본문

카테고리 없음

[Vuejs 트러블슈팅] Uncaught SyntaxError: Unexpected token ‘<’ (at app~….js:1:1) console 오류

toeun 2023. 12. 21. 21:26

 

 

⚠️ 발생 이슈

Vuejs로 구현된 SPA 환경이고, 브라우저에서 도메인 접속 시도하면 화면이 뜨지 않고 console 오류가 발생된다.

local에서 또는 개발서버에서는 화면이 정상적으로 뜨는데, 운영서버에서만 화면이 뜨지않고 console 오류가 발생되고 있다.

오류가 발생되는 script를 보면 정상적으로 구현된 소스를 볼 수 있다. 다만, <!DOCTYPE html> 부분부터 코드 인식이 안되는 것으로 보여진다.

 

 

 

🤔 원인 분석

해당 오류가 발생되는 원인은 아래 세가지의 경우로 볼 수 있다.(from chatGPT)

  1. 배포된 파일 경로가 올바르지 않은 경우
  2. 서버 설정이나 라우팅 문제로 파일이 올바르게 제공되지 않는 경우
  3. 개발 모드에서는 동작하지만 프로덕션 모드에서 문제가 발생된다면, Vue CLI의 환결 설정이 잘못된 경우(빌드 프로세스)

 

크롬 개발자 도구에서 오류발생되는 script를 볼 수 있고, 코딩된 내용이 담겨있는 것으로 보아 1번은 아닌 것으로 판단된다.

하여 다시 원인을 찾아보았다. 체크 포인트는 하기와 같다.(from chatGPT)

  1. 서버 응답을 확인하고 정상적인 HTML이 반환되고 있는지?
  2. 서버 측에서 제공되는 Content-Type 헤더를 확인하여 올바른 유형의 파일이 전송되고 있는지?
  3. 서버 설정에서 정적 파일(JavaScript 파일 등)의 MIME 유형이 올바르게 설정되어 있는지?

 

확인 결과, 개발 서버에서와 운영 서버에서의 script 로드 요청 header의 Content-Type이 상이하였다.(Thank you chatGPT)

 

&rarr; 개발 서버에서의 크롬 개발자도구 network 추적 화면

 

&rarr; 운영 서버에서의 Whale 개발자도구 network 추적 화면

 

 

운영서버에서도 Content-Type이 개발서버와 같이 application/javascript가 되도록 설정하면 될 것 같다고 생각하였고 Apache 설정으로 해결하는 방법을 찾아보았다.

.htaccess 파일 작성을 통해 해결이 가능하다고 하는데, 작성 내용이 생소하였고 그동안 운영했던 솔루션의 해결책으로 나오지 않았던 점이 뭔가 멀리 돌아가는 느낌이 들었다.

 

.htaccess 파일이란?

Apache 웹 서버에서 디렉터리 수준에서 구성을 지정하는데에 사용되는 설정 파일이다. 이 파일은 해당 디렉터리 및 하위 디렉터리에 있는 파일에 대한 서버 구성을 변경하는데 사용된다.

또한 보안 및 구성의 편의성을 제공하며, 서버의 부하를 줄이기 위해 매우 자주 사용된다.

    • 주요기능
      • 디렉토리별 구성 : .htaccess 파일은 해당 디렉터리에 속한 파일 및 하위 디렉터리에 대한 서버 구성을 제어한다.
      • 인증 및 권한 부여 : 사용자 인증 및 디렉터리 기반의 권한 부여를 설정할 수 있다.
      • URL 리다이렉션 : URL 리다이렉션 및 리라이팅을 통해 URL 구조를 변경하거나 특정 규칙에 따라 동작하도록 구성할 수 있다.
      • 캐시 및 압축 : 파일 캐싱, 압축, 브라우저 캐시 제어 등과 같은 성능 관련 설정을 할 수 있다.
      • 특정 파일 형식에 대한 처리 : 특정 파일 형식에 대한 MIME 타입 또는 서버에서의 처리 방법을 지정할 수 있다.
      • 사용자 정의 에러 페이지 : 서버 에러에 대한 사용자 정의 에러 페이지를 설정할 수 있다.
    • 영향범위
      • .htaccess 파일은 해당 파일이 위치한 디렉토리와 그 하위 디렉토리에 대해서만 영향을 미치게 된다. 하여 같은 서버의 다른 웹 프로젝트에 직접적인 영향은 미치지 않는다.
      • 다만, 만약 서버 설정에서 상위 디렉토리에 있는 .htaccess 파일이 하위 디렉토리에 영향을 미칠 수 있도록 허용되어 있다면 주의가 필요하다. 일반적으로 서버 구성에서는 하위 디렉토리의 .htaccess 파일이 우선 순위가 높아 해당 디렉토리에 속한 설정이 우선으로 적용된다.

 

 

그러다가 같은 운영서버에 배포된 다른 웹 디렉토리를 보니, 모두 .htaccess 파일이 있는 것이 아니었다. 하여 운영 서버가 이중화 되어 있으므로, 해당 script 파일이 동일하게 배포되어 있는지 확인해 보았다.

 

&rarr; 웹서버1의 /dist/js 디렉토리

 

&rarr; 웹서버2의 /dist/js 디렉토리

 

우선, 두 서버에 배포된 파일명이 불일치하였다.

또한 번들링된 많은 js를 로드할때, 두 웹서버의 Apache access_log 모니터링하며 확인해보니 하나의 서버에서만 응답을 내려주지 않고, 두 서버를 번갈아가며 응답하거나 두 서버 모두에서 하거나였다.(LB의 밸런싱이 하나의 클라이언트에 대해 하나의 서버에서 응답하는 결과가 아닌 것은 이미 알고 있었던 사실이긴 하다.)

 

 

하여 하기와 같은 작업으로 해결해볼 수 있겠다.

더보기

두 서버 모두 같은 파일명으로 배포되도록 vue configuration 변경

 

 

 

🏁 이슈 해결

이중화 되어있는 두 서버에 같은 파일명으로 js 번들링되도록 수정하였다.

 

vue.config.js 기존

const baseConfig = function() {
	let settingArgument;

  if(process.env.NODE_ENV === 'production') {
    settingArgument = {
      publicPath: '/',
      runtimeCompiler: undefined,
      productionSourceMap: undefined,
      parallel: undefined,
      css: undefined,
      lintOnSave: false,
      configureWebpack:{
        optimization: {
          splitChunks: {
            minSize: 10000,
            maxSize: 250000
          }
        },
      },
      chainWebpack: (config) => {
        console.log("========================== PRODUCTION MODE ==========================")
        config.resolve.alias.set('vue$', 'vue/dist/vue.esm.js')
      }
    }
  }
}

 

vue.config.js 변경

const baseConfig = function() {
	let settingArgument;

  if(process.env.NODE_ENV === 'production') {
    settingArgument = {
      publicPath: '/',
      runtimeCompiler: undefined,
      productionSourceMap: false,
      parallel: undefined,
      css: undefined,
      lintOnSave: false,
			 // 추가한 부분
      filenameHashing:false,
      configureWebpack: (config) => {
        config.output.filename = "js/[name].js?v=[hash]";
        config.output.chunkFilename = "js/[name].js?v=[hash]";
        config.optimization = {
          splitChunks: {
            minSize: 10000,
            maxSize: 250000
          }
        }
      },
      chainWebpack: (config) => {
        console.log("========================== PRODUCTION MODE ==========================")
        config.resolve.alias.set('vue$', 'vue/dist/vue.esm.js')
      }
    }
  }
}

→ 기존 configureWebpack 옵션 객체를 함수형으로 변환하였다.

 

 

이렇게 수정하고 배포하면 정상 구동된다.