Javascript Tips

React - 이미지경로를 src에 바로 넣으면 안되는 이유

탐훈 2024. 11. 29. 18:00
728x90

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들이

삭제되든, 이름이 바뀌든 상관 없다.