• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    해결됨

개발환경에서 assets 파일 참조관련 질문

22.12.19 17:22 작성 조회수 712

0

요약


개발환경에서 src/assets/.... 에 있는 이미지 파일을 제대로 참조하는 방법이 궁금합니다.

 

구성요소


프로젝트의 구성요소는 아래와 같습니다.

  • public[index.html, favicon.ico]

  • src[assets[image0, image1...], index.js 등]

 

설치된 패키지는 아래와 같습니다.


    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.11.1"

// 본 강의에선 4.x.x 버전을 사용하지만...
// 5 version을 공부해야해서... 죄송합니다 😥

 

설명


dev server를 실행시켜 개발할 때,

js 파일을 수정하면 바로 반영이 되는 걸 확인했습니다.

 

그런데 이미지 파일의 경우 다른 파일을 참조하도록 하면 해당 파일을 불러오지 못합니다. 그리고 build된 파일을 참조합니다.

예로들어 정적 이미지 파일이 ./src/assets/image_0.jpg 라면,

dev server로 실행시켜 확인하면 HOST/dist/assets/images/[hash][ext][query].jpg 이렇게 되어있습니다. (경로가 다름)

그리고 build를 하면 분명 assets 디렉토리엔 다수의 이미지 파일이 존재함에도 불구하고 코드에서 사용된 이미지 파일만 build됩니다.

그러면 만약 코드내부에서 동적으로 다른 static image 파일을 참조하게 된다면 해당 이미지가 없기 때문에 오류가 날텐데 이런건 어떻게 처리해야하나요?

 

코드


const path = require('path');
const { BannerPlugin, DefinePlugin } = require('webpack');
const childProcess = require('child_process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const isDevMode = (process.env.NODE_ENV || 'development').trim() === 'development';

console.log('is DEV mode?', isDevMode);
console.log('__dirname: ', __dirname);

module.exports = {
  mode: isDevMode ? 'development' : 'production',
  // entry: webpack 시작되는 부분이라고 생각하면 된다.
  entry: {
    main: './src/index.js',
  },
  /**
   *  output
   * entry point를 기준으로
   * 모든 .js 파일을 합쳐서 하나의 bundle 파일로 만드는데,
   * 이걸 어디에 저장할 것인지 지정하는 option
   */
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: isDevMode ? '[name].js' : 'main.[contenthash].js',
    chunkFilename: '[id].chunk.js',
    assetModuleFilename: 'images/[hash][ext][query]',
    clean: true,
  },
  devServer: {
    port: 3000,
    hot: true,
    client: {
      overlay: {
        errors: true,
        warnings: false,
      },
    },
    // static: {
    //   directory: path.resolve(__dirname, './src/assets/'),
    // },
  },
  /**
   * module
   * test에 설정한 파일들을 inspect 하여,
   * 조건에 맞는 파일들에 대해 loader 들을 실행하여 해석함
   */
  module: {
    rules: [
      {
        test: /\.(sa|sc|c)ss$/i,
        exclude: [/node_modules/],
        use: [
          // creates 'style' nodes from JS strings
          isDevMode
            ? 'style-loader'
            : {
                loader: MiniCssExtractPlugin.loader,
                options: {
                  publicPath: '',
                },
              },
          // translates css into common JS
          'css-loader',
          'postcss-loader',
          // complies sass to css
          'sass-loader',
        ],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        exclude: [/node_modules/],
        type: 'asset/resource',
        parser: {
          dataUrlCondition: {
            // 크기가 8kb 미만인 파일은 inline 모듈로 처리되고 그렇지 않으면 resource 모듈로 처리됩니다.
            maxSize: 4 * 1042,
          },
        },
        // generator: {
        //   publicPath: './assets/',
        //   outputPath: './assets/',
        // },
      },
      {
        test: /\.js$/,
        exclude: [/node_modules/],
        loader: 'babel-loader',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        exclude: [/node_modules/],
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    /**
     * 개발할 때 API 서버주소,
     * 배포했을 때 API 서버주소를 설정하는 Plugin
     */
    // new DefinePlugin({
    //   NODE_ENV: 'development',
    // }),
    new BannerPlugin({
      banner: `Build Date: ${new Date().toLocaleString()}
      Commit Version: ${childProcess.execSync('git rev-parse --short HEAD')}
      Author: ${childProcess.execSync('git config user.name')}`,
    }),
    new HtmlWebpackPlugin({
      template: './public/index.html',
      templateParameters: {
        env: isDevMode ? '개발용' : '배포용',
      },
      minify: !isDevMode
        ? {
            collapseWhitespace: true,
            removeComments: true,
          }
        : false,
    }),
    ...(!isDevMode
      ? [
          new MiniCssExtractPlugin({
            filename: isDevMode ? '[name].css' : '[name].[contenthash].css',
            chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css',
          }),
        ]
      : []),
  ],
};

 

결론


즉 정리하자면,

  1. 개발모드일 때 정적 이미지 파일을 참조하도록 설정을 어떻게 해야하나요?

  2. 왜 build할 땐 이미지 파일이 코드에서 사용중인 것만 빌드 되나요?

 

답변 주시면 감사하겠습니다.

 

답변 1

답변을 작성해보세요.

1

혹시 이미지 파일을 사용하실때 어떻게 사용하신걸까요? 저는 import img from './assets/image.png' 처럼 모듈로 가져와서 사용했습니다.

참고: https://github.com/jeonghwan-kim/lecture-frontend-dev-env/issues/70

부연 설명을 드리자면, 웹팩에서는 모든 파일을 모듈로 다룹니다. 이미지 파일도 자바스크립트처럼 import/from 구문을 사용해 모듈로 가져와 사용합니다. 웹팩 개발 서버에서는 이미지를 모듈 형태로 가져와서 사용하면 되는 것이죠.

웹팩은 여러 개의 모듈이 거미줄처럼 엮여있습니다. 그 중 시작점을 엔트리포인트라고 하는데요. 여기서는 src/index.js 파일이 엔트리포인트 모듈이겠죠. 이것을 시작으로 import 한 모듈들을 모두 빌드합니다. src 폴더에 어떤 모듈이 있는데 이것을 사용하지 않는다면, import 하지 않는다면, 빌드하지 않아요. 질문자분 사례를 보면 모든 이미지 파일을 빌드하지 않고 사용한 이미지 파일을 빌드하는 것이 그 이유입니다.

taylous님의 프로필

taylous

질문자

2022.12.21

답변 감사합니다. 😀

 

Q. 혹시 이미지 파일을 사용하실때 어떻게 사용하신걸까요?


A. css로

background-image: url(./assets/background.jpg);

이렇게 사용하였습니다.

 

내용 중 질문자분 사례를 보면 모든 이미지 파일을 빌드하지 않고... 이런 문구가 있는데 여기서 한 가지 질문이 있습니다.

asset 폴더에 있는 모든 이미지를 올리고 싶은데 CopyWebpackPlugin을 사용하는 방법이 있는 것 같습니다.

하지만 생각해보면 static file의 경우 변경이 없으면 (ex: 로직에서 다른 이미지 참조) 바뀔일이 없을텐데 모두 다 올려야 할 필요가 있을까? 하는 생각이 듭니다.

 

그래서 제가 하고픈 말은,

  1. assets의 모든 file을 production 환경에 포함시키고 싶으면 CopyWebpackPlugin을 쓰는 방법밖엔 없나요?

  2. 현업의 정책에 따라 다르겠지만 static file을 모두 포함시켜 build 하는게 좋을까요?

뭔가 강의 내용이랑 다른 질문이 계속 되는 것 같은데,

여기까지만 질문드리고 그만하겠습니다... 죄송합니다 😥

두 가지 질문이 어서 각각 답변드리겠습니다.

--

질문1. assets의 모든 file을 production 환경에 포함시키고 싶으면 CopyWebpackPlugin을 쓰는 방법밖엔 없나요?

CopyWebpackPlugin을 사용하셔서 아웃풋 위치에 파일을 복사할수도 있습니다. 이런 경우는 쉘스크립트를 사용하시는 방법도 있습니다. 파일 복사 명령어(cp)를 사용하실수도 있고요, 자동화하고 싶으시다면 npm 스크립트에 웹팩명령어와 파일 복사 명령어를 같이 실행할수도 있습니다.

예를들면 package.json의 scripts 부분에 이렇게 추가할 수 있어요.

"my-build": "npx webpack && cp src/assets output/assets"

npm run my-build 명령어 하나로 웹팩도 빌드하고 에셋파일도 옮길수 있을 것입니다.

--

질문2. 현업의 정책에 따라 다르겠지만 static file을 모두 포함시켜 build 하는게 좋을까요?

좋지 않다고 생각합니다. 모든 정적파일을 빌드하면 하나의 파일로 합쳐지는데요. 용량이 너무 커지기 때문에 브라우져에서 로딩하는데 시간이 많이 걸려 성능 문제가 발생합니다. 단 질문1에서 다룬것 처럼 하나의 번들파일로 처리하지 않고 정적 파일을 아웃풋 폴더에 개별로 옮기는것은 상관 없습니다.