Tempo Di Valse

[Electron] Build 한 후, net::ERR_FILE_NOT_FOUND 가 나는 경우. 본문

개발/Electron

[Electron] Build 한 후, net::ERR_FILE_NOT_FOUND 가 나는 경우.

TempoDiValse 2022. 6. 17. 17:31

내가 진행하고 있는 프로젝트의 경우에는 일반적인 프로젝트의 구성과는 약간 다르게 되어있다.

 

Electron 과 Vue 를 조합했지만 Electron 이 권하고 있는 main - renderer 프로세스는 아니며, 폴더의 구조도 Electron > Vue 가 아니라 Vue > Electron 이 되어있다.

 

"왜 그랬을까?" 하면 추가 확장성을 위해서 Vue 를 메인으로 생각했기 때문이라고 하고 싶은데..(어차피 Electron 은 껍데기니까.) Electron-vue 라는 Boilerplate 를 사용하지 않고 처음부터 만들어 보느라 갖가지 내 로직 상으로는 이해 안되는 일이 발생하곤 했다. 

 

그 중 대표적인 하나가 Webpack 을 통해서 Code Splitting 을 한 Chunk 애들이 파일은 있는데 net::ERR_FILE_NOT_FOUND 가 나는 것 이었다. Development 모드에서는 1도 에러나지 않던 아이들이 갑자기 어디로 갔는지 당황했다.

 

원인은 무엇일까... 하면서 크롬 개발자 콘솔을 확인해보는데 다음의 경우가 발생 되었다.

- 처음 로드 되는 애들은 상대경로로 접했어도 스크립트 태그로 박혀있기 때문에 절대경로로 잘 불러와진다. 처음 불러와진 아이들은 chunk 를 관리하는 스크립트와 index 관련 스크립트였다. 일단은 UI 가 보인다.
- 버튼을 누르면 해당 기능이 들어있는 스크립트를 불러온다. 이 때에는 상대 경로지만 '.' 이 빠지고 '/' 만 있어 루트 경로로 들어가버린다.

 

프로젝트 내에서 Production 과 Development 의 차이는 Electron 에서 BrowserWindow 를 실행할 때 loadFile 을 썼는가, loadURL 을 썼는가 의 차이이다. 물론 Development 는 즉각적인 변경을 알아보기 위해서 localhost 를 연결해 주었고, Production 은 Electron 자체 내 서버가 없으니 index 파일을 불러 실행을 시킨 것이다. 어차피 절대 주소를 사용하지 않아서 localhost 가 있으나 없으나 인 줄 알았더니...

 

이 문제는 나만 발생되는 문제는 아니었던 것 같다. React 를 사용하는 사람들도 Production 으로 실행하려니 에러가 생긴다는 사람들이 많이 보였다. 구글링에서 나오는 관련 페이지들도 전부 React 뿐 이었다. 대부분의 답은 webpack.config 에 publicPath 를 "./" 로 설정을 해보라는 것 이었다(나머지들은 너무 아니래서 스킵했다). 

 

그래서 일단 나는 React 가 아니기 때문에 vue.config.js 를 통해서 설정을 해 보았다. webpack 에 대해서 아직 잘 모르기 때문에 다른 값을 넣어서 어떤 모양이 나오는 지 확인해 보았다. 

그러고 돌려보니,

publicPath 는 앞에 붙는 아이들이구나! 알 수 있었다. 그러니 "./" 를 붙여서 상대경로로 사용할 수 있구나 생각했다. 그러나, 막상 입력하고 확인을 해보니..

똑같이 루트로 잡히는 것이다. 아오... 그렇다고 절대경로로 잡아버리면 Build 하고서 배포하게 되면 절대경로가 말썽이 되기 때문에 할 수 없는 방법이었다.

 

그러다가 문득 생각 났던 것이, Electron 상에서 로컬 데이터를 불러올 때에 일반적인 Path 가 아니라 Scheme 을 커스터마이즈하면 불러올 수 있다는 것이 떠올랐다. 관련된 포스팅은 다음과 같다.

 

[Electron] <img> 태그에 로컬 이미지 로드하기

웹 페이지에서 이미지를 보여주는 것을 만든다고 하면 를 통하여 이미지를 보여준다. 그래서, 형식처럼 URL 을 넘겨주면 알아서 URL 의 이미지를 불러오곤 한다. 여기서 URL 은 일반 웹상에서 돌아

tempodivalse.tistory.com

어차피 Production 은 로컬파일이고, 특정 Scheme 이 들어오면 path 조작이 용이하기 때문에 충분히 사용할 수 있는 로직이겠거니 생각하여 작업을 시작했다. 게다가 publicPath 라는 것이 Scheme 을 받을 수 있는 것으로 되어 있어서 이번에는 될 것 같았다.

 

먼저, Webpack 의 설정을 바꿨다. publicPath 를 특정 Scheme 으로 변경을 했다. Scheme 을 "build://" 라고 하자. 실행해보니 변경은 잘 되었다.

하지만 build:// 를 감지할 수 있는 무언가가 없기 때문에 Electron Protocol API 를 이용하여 build:// 를 감지 할 수 있는 로직을 만들어 준다.

const SCHEME = "build"

async function requestBuildProtocol(request, callback){
    const url = request.url
    
    const regex = new RegExp(`${SCHEME}:\/\/(?<path>.+)$`, 'g')
    const { groups: G } = Array.from(url.matchAll(regex))[0]
    
    // URL 값을 가져와서 정규식을 돌려 Path 부분만 가져온다.
    // 이 Path 부분은 상대 경로이기 때문에 app.getAppPath() 를 통해서 각 환경의 절대 경로로
    // 변경하도록 한다.
    
    callback({
        path: path.resolve(app.getAppPath(), G.path)
    })
}

protocol.registerFileProtocol(SCHEME, requestBuildProtocol)

이 상태에서 돌려보도록 하자.

 

index 페이지에서 초기에 불러오는 JS/CSS 는 물론이고, Code Splitting 을 통해서 분리된 Chunk 스크립트 들도 import 가 잘 되는 것을 확인할 수 있었다.

 

결론은 행복한 결말을 볼 수 있었다.

 

추가로, Webpack 의 Externals 를 통해 외부 스크립트 파일로 다른 라이브러리를 사용하려면 같은 방식으로 Scheme 방식을 사용하면 된다.

{
    externalsType: 'script',
    externals: {
        'lib1:' [ 'build://lib/lib1.js', 'lib1' ]
    }
}

더 간단한 좋은 방법이 있으면 좋겠는데, Webpack 을 통해서는 아직 찾을 수가 없었다. 이외에도 Code Splitting 기능을 사용하지 않으면 가능하지만, 혹시나 사용해야 하는 일이 생긴다면 Electron 환경 아래에서는 이 방법도 나쁘지 않을 것으로 생각이 된다.

반응형
Comments