React - 이미지경로를 src에 바로 넣으면 안되는 이유
local에서 돌리던 static 파일들은
이미지를 잘 불러왔다
<img src="/image1.png" />
그런데 빌드만 하면
이미지를 불러오지 못하는데
그 이유는 아래 사진을 보면 알 수 있다.
빌드 후에는
파일명들이 해시값으로 변한다.
따라서 Img에 선언한 파일명은
build time 이후에도
상대경로로
해당 파일명을 찾으려고 한다.
build를 하게 되면
dist 디렉토리 하위에 빌드 파일들이 모인다.
아래 태그를 보면
<img src="/image.png"/>
dist 디렉토리에서 image.png를 찾으려고 한다.
빌드 후에는
file-loader에 의해 파일명이 해시값으로 바뀌어서
파일명이 같지 않다.
추가로 위의 img태그는
public 디렉토리 아래에 있는 이미지를 바라보고 있다.
빌드 시점 이후에
해당 이미지가 사라지거나
파일명이 바뀐다면 찾을 수 없게 될 수 있다.
따라서
빌드시점에 해당 이미지를 가져와
웹팩이 함께 묶어서 번들링으로 관리해야 한다.
한번 테스트 해보자
1. webpack 라이브러리 가져오기
npm install webpack-cli
[package.json]
{
"dependencies": {
"webpack-cli": "^5.1.4"
}
}
2. 테스트 파일 셋팅
public 디렉토리 만드시고
이미지 암거나 넣으세요
[index.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- react 라이브러리 1-->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<!-- react 라이브러리 2-->
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<!-- 바벨 라이브러리 (node 패키지의 babel/preset-env, babel/preset-react 두개 모두 포함되어 있음)-->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<!-- text/babel은 해당 스크립트를 바벨로 먼저 transpile 하겠다고 브라우저에게 알려줌 -->
<script type="text/babel" src="/app.js"></script>
</body>
</html>
4. index.js 소스 작성
[app.js]
const Test = () => {
return (
<img src="public/error-img.png" alt="" />
)
}
const App = () => {
return <>
<Test/>
</>
};
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
5. npm run build 해보기
빌드하면 이런 에러 나온다.
스크립트를 선언 안해서 나온 것
6. package.json에 웹팩으로 빌드 하는 스크립트 추가
[packaghe.json]
{
"scripts": {
"build": "webpack"
},
"dependencies": {
"webpack-cli": "^5.1.4"
}
}
7. 로더 추가 후 웹팩이 로더 사용할 수 있도록 설정 추가
바벨설치)
-> preset-react: 리액트를 js로 transpile
-> preset-env: javascript 구버전으로 transpile
npm install --save-dev @babel/preset-env @babel/preset-react
파일로더설치)
npm install --save-dev file-loader
웹팩이 파일을 확인하여
번들링해서 가공하여 가져옴.
[.babelrc]
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
[webpack.config.js]
const path = require('path');
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource',
generator: {
filename: 'images/[name][hash][ext][query]',
},
},
],
},
resolve: {
extensions: ['.js'],
},
mode: 'development',
};
설정은 끝났으니
확인해보자
[확인1. index.html 열어보기]
이미지가 잘 나오고
소스탭에서 해당 이미지는
public에서 가져오고 있다.
[확인2. 빌드해서 확인해보기]
빌드 전
node package install 하자
1. babel-loader
-> JavaScript 파일을 babel에게 전달해준다.
-> 바벨은 jsx파일을 transpile 해줌
-> 바벨은 babel-loader 설치시 내장되어서 설치됨
npm install babel-loader --save-dev
2. babel preset
-> babel-loader가 preset을 보고 맞춰서 transpile 해준다.
npm install @babel/preset-env @babel/preset-react --save-dev
3. file loader
npm install file-loader --save-dev
[build.html]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- react 라이브러리 1-->
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<!-- react 라이브러리 2-->
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
</head>
<body>
<div id="root"></div>
<script src="/dist/bundle.js"></script>
</body>
</html>
index.html에서는
babel.min.js를 cdn으로 갖고 왔지만
빌드 후에는 필요가 없다.
node package의 바벨로
transpile해놨기 때문이다.
번들한 javascript 파일만 넣어주고
react 관련 cdn만 넣어서 켜보자
잘 나온다.
그런데 public 내에 있는
해당 png 파일명이
어떤 이유로 바뀌었다고 가정해보자.
[확인2. 빌드해서 확인해보기 - public에 있는 이미지명 변경]
build.html을 켜보면
이미지 로드가 안된다.
이미 번들링된
bundle.js 파일에
src 경로가 박혀버려서 수정할 수 없다.
따라서
webpack이 이미지를 접근해서
따로 번들링하여 가져갈 수 있도록 수정해보자
[확인3. 이미지를 웹팩이 접근해 함께 번들링시키기]
[app.js]
const testImg = require('/public/test-img.png');
const Test1 = () => {
return (
<img src={testImg}></img>
)
}
const App = () => {
return (
<>
<Test1/>
</>
)
}
const rootElement = document.querySelector('#root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App/>);
수정 후
빋르하기
npm run build
build.html 열어보기
번들링한 이미지 경로를 가리키고 있다.
public 아래에 있는
이미지명을 바꿔보자
다시 build.html 열어보면
이전과 달리
잘 가져온다.
요약
public 디렉토리에 있는 이미지를
img 태그 src 속성에 경로를 넣으면 안된다.
해당 이미지를
node의 require를 통해 가져 온 뒤
웹팩이
해당 이미지를 가공하여
한꺼번에 번들링하여 사용 할 수 있도록
file-loader 패키지를 가져오고
webpack.config.js에 셋팅해준다.
이렇게 하면 퍼블릭 하위에 있는
static resource들이
삭제되든, 이름이 바뀌든 상관 없다.