2시간 동안의 사투 끝에 드디어 해결했네요!

Radit 이나 여러 여러 여러 사이트들을 보면 저와 동일한 에러에 직면한 케이스가 많더라구요.

저처럼 Expo SDK 52NativeWind v4 조합으로 앱을 개발하다가 EAS Build에서 막힌 분들을 위해, 그리고 나중에 또 같은 실수를 반복하지 않을 저를 위해 삽질 기록을 정리해 봅니다.

에러사진1
expo build fail image


■ 문제 상황: "분명 로컬에선 됐는데, 빌드만 하면 터진다?"

만보기 기능을 구현하려고 expo-sensors를 달고, 스타일링을 위해 NativeWind v4를 세팅했습니다. 로컬에서는 아주 예쁘게 잘 돌아갔죠. 그런데 eas build 명령어만 날리면 아래와 같은 무시무시한 에러와 함께 빌드가 중단되었습니다.

SyntaxError: index.js: [BABEL] ... Cannot find module 'react-native-worklets/plugin'

분명 react-native-worklets-core를 설치했는데도 바벨(Babel)은 자꾸 이름이 미묘하게 다른 react-native-worklets/plugin이 없다고 징징대는 상황이었습니다.

 

■ 원인 분석: 범인은 ^ (캐럿) 하나였다

2시간 동안 구글링과 문서 분석 끝에 찾아낸 원인은 NativeWind의 과한 의욕 때문이었습니다.

  1. 버전 자동 업데이트의 함정: package.json에 "nativewind": "^4.1.23"라고 적어두면, npm은 설치 시점에 4.1.23보다 높은 최신 버전(4.2.x)을 가져옵니다.
  2. NativeWind 4.2.x의 변화: 최신 4.2.x 버전은 Reanimated 4에 대응하기 위해 내부적으로 react-native-worklets/plugin을 강제로 호출합니다.
  3. 이름 불일치: 하지만 현재 Expo SDK 52의 표준은 react-native-worklets-core입니다. 바벨은 -core가 붙지 않은 정확한 이름을 찾으려다 보니 모듈을 찾지 못하고 뻗어버린 것이죠.

 

해결 방법: 버전을 강제로 묶어라 (Pinning)

결국 해결책은 똑똑한 척하는 업데이트 기능을 끄고, 우리 프로젝트와 가장 잘 맞는 안정적인 버전으로 강제 고정하는 것이었습니다.

 

1. package.json 수정

버전 앞의 ^ 기호를 제거하여 딱 4.1.23 버전만 쓰도록 못 박았습니다.

"dependencies": {
  "nativewind": "4.1.23",  // ^ 제거!
  "react-native-css-interop": "0.2.3",
  "react-native-reanimated": "~3.16.1"
}

2. 찌꺼기 제거 및 재설치

이전 버전의 캐시가 남지 않도록 과감하게 밀고 다시 깔았습니다.

rm -rf node_modules package-lock.json
npm install

3. babel.config.js 정리

불필요한 플러그인 호출을 제거하고 NativeWind v4 전용 설정을 적용했습니다.

module.exports = function (api) {
  api.cache(true);
  return {
    presets: [
      ["babel-preset-expo", { jsxImportSource: "nativewind" }],
      "nativewind/babel",
    ],
    plugins: ["react-native-reanimated/plugin"],
  };
};

▶️ 최신이 항상 정답은 아니다

이번 삽질을 통해 다시 한번 깨달았습니다. 라이브러리 간의 의존성이 복잡하게 얽힌 리액트 네이티브 환경에서는 캐럿(^) 하나가 전체 빌드를 무너뜨릴 수 있다는 것을요.

특히 NativeWind처럼 과도기에 있는 라이브러리는 안정적인 버전을 확인하고 강제로 고정해서 쓰는 습관이 정신 건강에 이로운 것 같습니다. 이제 만보기 데이터가 올라가는 걸 보니 속이 다 시원하네요!

저와 같은 에러로 고통받는 분들이 계신다면, 지금 당장 package.json에서 ^부터 지워보세요.

 


Expo 에서 build fail 한 내용입니다.

Starting Metro Bundler
Android Bundling failed 735ms index.js (1 module)
SyntaxError: index.js: [BABEL] /home/expo/workingdir/build/index.js: Cannot find module 'react-native-worklets/plugin'
Require stack:
- /home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js
- /home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/index.js
- /home/expo/workingdir/build/node_modules/@babel/core/lib/index.js
- /home/expo/workingdir/build/node_modules/metro-transform-worker/src/index.js
- /home/expo/workingdir/build/node_modules/react-native-css-interop/dist/metro/transformer.js
- /home/expo/workingdir/build/node_modules/metro/src/DeltaBundler/Worker.flow.js
- /home/expo/workingdir/build/node_modules/metro/src/DeltaBundler/Worker.js
- /home/expo/workingdir/build/node_modules/jest-worker/build/workers/processChild.js

Make sure that all the Babel plugins and presets you are using
are defined as dependencies or devDependencies in your package.json
file. It's possible that the missing plugin is loaded by a preset
you are using that forgot to add the plugin to its dependencies: you
can workaround this problem by explicitly adding the missing package
to your top-level package.json.

Error: [BABEL] /home/expo/workingdir/build/index.js: Cannot find module 'react-native-worklets/plugin'
Require stack:
- /home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js
- /home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/index.js
- /home/expo/workingdir/build/node_modules/@babel/core/lib/index.js
- /home/expo/workingdir/build/node_modules/metro-transform-worker/src/index.js
- /home/expo/workingdir/build/node_modules/react-native-css-interop/dist/metro/transformer.js
- /home/expo/workingdir/build/node_modules/metro/src/DeltaBundler/Worker.flow.js
- /home/expo/workingdir/build/node_modules/metro/src/DeltaBundler/Worker.js
- /home/expo/workingdir/build/node_modules/jest-worker/build/workers/processChild.js

Make sure that all the Babel plugins and presets you are using
are defined as dependencies or devDependencies in your package.json
file. It's possible that the missing plugin is loaded by a preset
you are using that forgot to add the plugin to its dependencies: you
can workaround this problem by explicitly adding the missing package
to your top-level package.json.

    at Module._resolveFilename (node:internal/modules/cjs/loader:1212:15)
    at resolve (node:internal/modules/helpers:193:19)
    at tryRequireResolve (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js:128:11)
    at resolveStandardizedNameForRequire (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js:162:19)
    at resolveStandardizedName (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js:183:12)
    at loadPlugin (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/files/plugins.js:56:7)
    at loadPlugin.next (<anonymous>)
    at createDescriptor (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-descriptors.js:140:16)
    at createDescriptor.next (<anonymous>)
    at evaluateSync (/home/expo/workingdir/build/node_modules/gensync/index.js:251:28)
    at /home/expo/workingdir/build/node_modules/gensync/index.js:31:34
    at Array.map (<anonymous>)
    at Function.sync (/home/expo/workingdir/build/node_modules/gensync/index.js:31:22)
    at Function.all (/home/expo/workingdir/build/node_modules/gensync/index.js:210:24)
    at Generator.next (<anonymous>)
    at createDescriptors (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-descriptors.js:102:41)
    at createDescriptors.next (<anonymous>)
    at createPluginDescriptors (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-descriptors.js:99:17)
    at createPluginDescriptors.next (<anonymous>)
    at /home/expo/workingdir/build/node_modules/@babel/core/lib/gensync-utils/functional.js:22:27
    at Generator.next (<anonymous>)
    at mergeChainOpts (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-chain.js:349:34)
    at mergeChainOpts.next (<anonymous>)
    at chainWalker (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-chain.js:316:14)
    at chainWalker.next (<anonymous>)
    at buildPresetChain (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/config-chain.js:33:24)
    at buildPresetChain.next (<anonymous>)
    at loadPresetDescriptor (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/full.js:295:53)
    at loadPresetDescriptor.next (<anonymous>)
    at recursePresetDescriptors (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/full.js:77:31)
    at recursePresetDescriptors.next (<anonymous>)
    at /home/expo/workingdir/build/node_modules/@babel/core/lib/config/full.js:156:21
    at Generator.next (<anonymous>)
    at loadFullConfig (/home/expo/workingdir/build/node_modules/@babel/core/lib/config/full.js:113:5)
    at loadFullConfig.next (<anonymous>)
    at transform (/home/expo/workingdir/build/node_modules/@babel/core/lib/transform.js:20:44)
    at transform.next (<anonymous>)
    at evaluateSync (/home/expo/workingdir/build/node_modules/gensync/index.js:251:28)
    at sync (/home/expo/workingdir/build/node_modules/gensync/index.js:89:14)
    at stopHiding - secret - don't use this - v1 (/home/expo/workingdir/build/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js:47:12)
    at Object.transformSync (/home/expo/workingdir/build/node_modules/@babel/core/lib/transform.js:40:76)
    at parseWithBabel (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/transformSync.js:65:18)
    at transformSync (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/transformSync.js:54:12)
    at Object.transform (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/babel-transformer.js:115:58)
    at transformJSWithBabel (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/transform-worker/metro-transform-worker.js:440:47)
    at Object.transform (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/transform-worker/metro-transform-worker.js:549:12)
    at transform (/home/expo/workingdir/build/node_modules/@expo/metro-config/build/transform-worker/transform-worker.js:117:23)
    at Object.transform (/home/expo/workingdir/build/node_modules/react-native-css-interop/dist/metro/transformer.js:15:16)
    at transformFile (/home/expo/workingdir/build/node_modules/metro/src/DeltaBundler/Worker.flow.js:54:36)

반응형

 

 

Jenkins Build & Deploy shell

Jenkins 를 통해 FrontEndBackEnd의 CI/CD 를 구축 중에 있었다.

이런식으로 FrontEnd 소스를 빌드&배포하는 작업을 처리해놓았고, 실제로 수동 빌드가 동작하는 것까지 확인을 하였다.

branch를 main으로 잡아놓고, 해당 브랜치가 업데이트 되었을 때 배포하도록 설정해놨다.
그런데 아무리 기다려봐도 동작이 없었다.

그래서 원인이 뭔가를 확인해봤더니 Trigger 가 문제였다.

Trigger

Poll SCM

트리거를 특별히 지정해놓지 않았더니 수동 배포 동작을 눌러주기까지 계속 기다리고 있던 것.
그래서 Trigger(빌드유발) > Poll SCM 을 추가해주었다.

다들 ChatGPT를 확인해서 알겠지만, 위 내용은
평일 오전9시-오후6시에 5분마다 변경사항을 체크하고 배포한다는 내용이다.

그랬더니 정상적으로 build & deploy 가 되는것을 확인하였다.

 

반응형

안녕하세요 상훈입니다.

 

해당 프로젝트는 Vue.js, React.js 등 SPA 의 공통적인 요소이기 때문에,
SPA 프로젝트를 빌드하여 같은 오류가 난다면 마찬가지로 진행해주셔도 무방합니다.

 

React.js 프로젝트를 build -> nginx server 에 배포하였습니다.

그런데, nginx 서버에서 index.html 파일을 정상적으로 불러왔는데, 새로고침했을 때 404에러가 떠버렸습니다.

왜 404 Not Found 가 뜨냐고!

 

 

기본적인 빌드 환경은 다음과 같습니다.

/var/www/html/build/index.html 

그렇다. build 라는 디렉터리가 또 껴있는 것이다. 

그래서 error.log 를 줄기차게 테스트해보면서 확인해봤다.

이렇게 경로를 읽어오고 있었던 것.

이제 에러를 확인했으니, 고쳐야지..

*참고 : error.log 는 /var/log/niginx/error.log  에 있다.

일단 Ubuntu, Nginx 를 이용하는 입장에서만 서술하도록 하겠습니다.

 /etc/nginx/sites-enabled/default  를 수정하면 되는데, 아래 이미지처럼 수정하면 된다.

server {
	root /var/www/html/build/;
    
    index index.html
    
    #... 이하생략
    
    location / {
    	#이부분을 수정해주시면 됩니다.
    	try_files $uri $uri/ /index.html;
    }   
}

저는 저 try_files 부분에서 index.html 로만 작성이 되어져 있었기에
build/index.html 이 아닌 buildindex.html 로 uri 호출이 되었더라구요.

수정내역

 

그래서 위와 같이 수정해주고, (sudo nano default) 저장. 그리고 서버 재시작

 sudo service nginx restart 

 

그리고.....

감덩...👍

새로고침 시 해당 페이지가 다시 호출되어 화면에 렌더링 되는 것을 볼 수 있습니다.

 

이게 뭐라고 계속 이렇게 끙끙 앓았다니 속상하네요.

반응형
gradlew Permission Denied

에러 발생

내용인즉슨 권한이 없어 거부됨

chmod 명령어로 권한 할당

chmod +x gradlew

반응형

Webpack 이란?

  • html, css, javascript 등 많은 파일을 하나의 .js파일로 변환 해주는 도구
  • 코드 관리 용이, 로딩 속도 증가, 버전 관리 용이 등의 장점이 있음.

 

필요조건

  • node, npm

 

구현 내용

  • 수학문제 프로그램
  • create 에서 build 까지 기술
  • build 후에는 구동되고 있는 웹서버에 올리고 index.html 파일을 실행하는 것으로 사용이 가능함

 

결과 이미지
build 후 dist 폴더 생성

전체 구현내용 흐름

  1. 프로젝트 폴더 생성, 이동
  2. npm init : package.json 생성
  3. npm i vue : npm module 로 vue 설치
  4. npm webpack webpack-cli -D : npm module로 webpack과 cli 를 developer모드로 설치
  5. npm i vue-loader -D : npm module로 vue-loader를 설치
  6. npm i vue-template-compiler -D : npm module로 vue-temnplate-compiler 설치
  7. npm i css-loader : css-loader 설치
  8. npm i vue-style-loader : vue-style-loader 설치
  9. npm run build : 8번 이후 코드를 작성 후 프로덕션 프로젝트 생성

 

Codes

// main.js

import Vue from 'vue'
import app from './App.vue'

new Vue(app).$mount('#root')
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
    <script src="./dist/app.js"></script>
</body>
</html>
// package.json : build 명령어 추가

"scripts": {
    "build": "webpack --watch",
  },
// webpack.config.js

// entry객체 내에 app의 path.join 지정  
const path = require('path')        
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
    mode: 'production',
    entry: {    // app => 합쳐질 파일명, main.js => webpack으로 build 할 파일
        app: path.join(__dirname, 'main.js') 
    },
    module: {    // rules를 통해 '.vue'로 끝나는 파일은 vue-loader를 사용하겠다고 정의
        rules:   // 이유: webpack은 기본적으로 javascript를 처리 App.vue 파일은 javascript 파일이 아니기 때문에, vue-loader가 처리하게 만듬
				[{ 
            test: /\.vue$/,
            use: 'vue-loader'
        },
        {
            test: /\.css$/,
            use: ['vue-style-loader', 'css-loader']
        }
    ]},
    plugins: [  // vueloader plugin을 사용하기 위해 vue-loader plugin 모듈을 가져옴
        new VueLoaderPlugin()
    ],
    output: {   // build결과물에 대한 정보. 
        filename: 'app.js', // build 파일명 
        path: path.join(__dirname, 'dist')  // 파일이 저장될 디렉터리 지정
    }
}

 

 

 

 

 


기타

node.js 와 vue.js 에서 모듈들을 삽입하는 방식의 차이. require, import...

// In node.js
 require, module.export

// In Vue.js
 import, export default

이렇게 직접 하나씩 module들을 다운로드 하고 삽입하는 것은 vue-cli가 잘나와서 굳이 필요하다고는 생각치 않는다.
하지만 프로젝트 기본을 설정하는 webpack.config.js 를 어떻게 구조화하는지에 따라 명령어에 따라 에러가 발생할 수도있다는 것을 한 번 더 느끼게 해주었고, vue의 config가 어떤 식으로 돌아가는 지 이해가 더 잘되었다.

그간 프로젝트 만들고 에러 해결해 나가는 것에 급급해 했지만, 하나씩 기초를 다질 때가 된 것 같아 이렇게 webpack에 대해 알아보았다.

예전에 bootstrap css-framework를 사용하면서 dist가 무엇인가? 에 대해 의문을 가졌지만 그 때에는 그것보다 구현하는데에 급급했기에 넘어갔지만, 이번 기회에 dist가 왜 발생하고, 어떤 식으로 프로젝트를 구성하는지에 대해 좀 더 잘 알 수 있었다.

 

 

반응형

 

Vue.js 프로젝트를 webpack을 이용해 

npm run build

를 하였을 때 나타나는 경고 문구

WARNING in configuration The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment. You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

 

webpack.config.js 에 어떤 모드로 프로젝트를 설정한것인지 기재를 안해놓았기 때문이다.

그렇기에 mode: 'production'  혹은 'development' 를 추가해주면 된다.

 

 

반응형

+ Recent posts