본문 바로가기
TIL

cra 없이 webpack 개발 환경 설정하기

by 은지:) 2024. 6. 23.
728x90
반응형

 

cra 없이 webpack 설정하는 법은 시중에 많이 나와있음

참고한 블로그 추가함

https://webpack.kr/concepts/ <= 진심 잘 나와 있어서 깜짝 놀람

 

https://velog.io/@tnehd1998/CRA%EC%97%86%EC%9D%B4-ReactTypeScript-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0

 


 

하고 싶은 거

1. react + ts + scss
2. webpack으로 development / product 서버 나누기
3. 코드 스플리팅 사용 <= 근데 처음부터 의도한 건 아니었음

 

 

만났던 오류

 

1. 간단하게 설정하고 서버 킬 때 나온 메세지 (실행 불가 상태는 x)

 

번들 파일 무거우니까 코드 스플리팅하라는 경고 메세지

근데 이제 서버 켰는데 왜 무거운건지... 이미 압축되어서 가벼워졌는데...

 

암튼 하라니까 일단 설정함

 

 

코드 스플리팅으로 가져오게 lazy, suspense 추가

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Suspense } from 'react';

const LazyRouter = React.lazy(() => import('./Router'));

const App: React.FC = () => (
	<Suspense fallback={<div>Loading...</div>}>
		<LazyRouter />
	</Suspense>
);

const root = createRoot(document.getElementById('root'));
root.render(<App />);

 

 

tsconfig 추가

// tsconfig.json

....
		"esModuleInterop": true,
		"moduleResolution": "node"

 

 

moduleResolution: 'node'

 

TypeScript가 모듈을 해석하는 방식이 Node.js의 모듈 해석 방식과 동일하게 만듭니다. 이는 다음과 같은 이유로 중요합니다:

 

  • 동적 임포트: 코드 스플리팅을 위해 사용되는 동적 임포트(import())는 ES 모듈 시스템의 일부입니다. Node.js의 모듈 해석 방식을 따르면 TypeScript가 이러한 동적 임포트를 제대로 해석할 수 있습니다.
  • 모듈 경로 확인: 동적 임포트를 사용할 때 모듈 경로를 올바르게 확인하는 것이 중요합니다. Node.js 방식의 모듈 해석을 따름으로써, TypeScript는 node_modules 디렉토리 및 프로젝트의 모듈을 올바르게 찾을 수 있습니다.

 

esModuleInterop: true

 

TypeScript가 CommonJS 모듈과 ES 모듈 간의 상호 운용성을 개선하는 데 도움이 됩니다

 

 

  • 호환성: 많은 Node.js 모듈이 CommonJS 형식으로 작성되어 있습니다. esModuleInterop 옵션을 사용하면 이러한 모듈을 ES 모듈처럼 임포트할 수 있으며, 이는 코드 스플리팅 시 동적 임포트 구문과의 호환성을 높입니다.
  • 간결한 구문: esModuleInterop 옵션이 활성화되면, CommonJS 모듈을 임포트할 때 더 간결한 ES 모듈 구문(import defaultExport from 'module')을 사용할 수 있습니다. 이는 코드 스플리팅 시 동적 임포트 구문을 사용할 때에도 동일하게 적용됩니다.

 

코드 스플리팅은 컴포넌트를 하나씩 가져오는 게 아니라

코드를 잘게 나누어서 가져오는 거임

그러다보니까 노드 가져오는 설정을 좀 더 딴딴히 해야했던 듯

 

 

 

 

 

2. dev, prod, common.js 나누다가 알게된 거

 

스크립트 파일

    "test": "jest",
    "dev": "webpack-dev-server --config webpack.dev.js", <= 개발용 서버
    "build": "webpack --config webpack.prod.js", <= 프로덕트용 빌드
    "serve": "node server.js" <= 프로덕트용 빌드 후 로컬 확인용

 

 

개발서버랑 프로덕트 서버랑 똑같은 기본 구조는 webpack.common.js에 넣음

 

// webpack.common.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.tsx',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
  },
  module: {
    rules: [
      //   바벨용 rules
      {
        test: /\.(js|jsx|ts|tsx)$/, // 해당 파일명으로 끝나면 babel-loader가 처리
        exclude: /node_modules/, // node_modules는 대상에서 제외
        loader: 'babel-loader', // 바벨 로더 추가
      },
      // CSS 처리 위한 설정
      {
        test: /\.scss$/, // SCSS 파일 처리
        use: ['style-loader', 'css-loader', 'sass-loader'], // 스타일, CSS, Sass 로더 사용
      },
    ],
  },
  // 다른 목적으로 플러그인을 여러 번 사용하도록 설정할 수 있으므로 new 연산자로 호출하여 플러그인의 인스턴스를 만들어야 합니다.
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html', // index.html 템플릿 설정
    }),
  ],

  resolve: {
    // resolve: import 할 때 확장자를 생략
    extensions: ['.jsx', '.js', '.tsx', '.ts'],
  },

  optimization: {
    minimize: true, // 번들 최소화 설정
    splitChunks: {
      minSize: 10000,
      maxSize: 250000,
    },
  },
};

 

빌드 환경은 js 파일만 인식하기 때문에 css 규칙 넣어줌

이때 바벨용 로더랑 css 로더 분리해줌... 하고 싶어서 한게 아니라 그게 규칙이라고 함

sass-loader: SCSS 파일을 CSS로 변환합니다.
css-loader: 변환된 CSS를 JavaScript 모듈로 해석합니다.
style-loader: JavaScript 모듈로 해석된 CSS를 <style> 태그로 삽입하여 브라우저에서 스타일을 적용합니다.

 

그리고 dev 파일

//dev

const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');

module.exports = merge(common, {
  mode: 'development',
  devServer: {
    static: {
      directory: path.join(__dirname, 'dist'),
    },
    compress: true, // Gzip 압축을 활성화
    hot: true,      // 핫 모듈 교체 활성화
    port: 9000,
  },
});

 

webpack-dev-server 모듈 받으면 쓸 수 있는 devServer를 열고 싶었던 이유는

빌드시간 오래 걸리는 거 + 저장할 때 바로바로 새로고침 이유로 핫로딩을 원했음 <= 빌드 파일을 정적으로 만드는 게 아니라 메모리에 두고 사용해서 빠름

장점들 밑에 정리함

 

 

 

  • 핫 모듈 교체 (HMR, Hot Module Replacement):
    • HMR이 없는 경우: 코드 변경 시 페이지 전체를 새로고침해야 합니다. 이는 개발 속도를 느리게 하고, 상태가 초기화될 수 있습니다.
    • HMR이 있는 경우: 코드 변경 시 변경된 모듈만 교체되므로, 페이지 전체를 새로고침하지 않아도 됩니다. 이는 빠른 피드백과 더 나은 개발 경험을 제공합니다.
  • 실시간 리로딩 (Live Reloading):
    • Live Reloading이 없는 경우: 파일을 수동으로 새로고침해야 합니다.
    • Live Reloading이 있는 경우: 파일이 변경될 때 자동으로 페이지를 새로고침합니다.
  • Gzip 압축:
    • 압축 없는 경우: 파일이 압축되지 않고 전송되므로, 파일 크기가 크다면 로딩 속도가 느려질 수 있습니다.
    • 압축 있는 경우: 파일이 Gzip 압축되어 전송되므로, 네트워크 전송량이 줄어들고 로딩 속도가 빨라집니다.

 

 

그리고 gzip 설정의 단점도 일단 가져옴

 

웹팩에서 Gzip 압축을 적용할 때, 일반적으로 텍스트 기반 파일(예: JavaScript, HTML, CSS 등)을 압축 대상으로 삼습니다
이미 압축된 파일 형식(예: 이미지, 비디오 등)은 추가적인 압축 효과가 적기 때문에 대상에서 제외하는 것이 좋습니다.
이를 위해 웹팩에서 compression-webpack-plugin을 사용할 수 있습니다.

- 이미지 파일 제외 이유
이미지 파일(예: JPEG, PNG 등)은 이미 압축된 형식이기 때문에
추가로 Gzip 압축을 적용해도 크기가 거의 줄어들지 않거나 오히려 더 커질 수 있습니다.
따라서 이미지 파일은 Gzip 압축 대상으로 지정하지 않는 것이 일반적입니다.

 

  plugins: [
    new CompressionPlugin({
      test: /\.(js|css|html)$/, // 압축할 파일 형식 지정
      filename: '[path][base].gz', // 출력 파일 이름 설정
      algorithm: 'gzip', // 압축 알고리즘 설정
      threshold: 10240, // 파일 크기가 10KB 이상일 때만 압축
      minRatio: 0.8, // 최소 압축 비율 설정
    }),
  ],

 

이런 설정도 가능하다고 함

정적으로 쓰는 이미지 엄청 무거우면 한번 해볼 법한...

 

 

아무튼 devServer 쓰면 빌드 파일 메모리에 두고 쓰는 건데 굳이

    static: {
      directory: path.join(__dirname, 'dist'),
    },

 

이 설정이 필요할까?

 

 

  • 정적 파일 제공: 프로젝트에는 JavaScript 번들 외에도 이미지, 폰트, HTML 파일 등 정적 파일이 있을 수 있습니다. 이러한 파일들을 static 옵션을 통해 제공할 수 있습니다.
  • 개발 편의성: 개발 중에 직접적으로 디스크에 저장된 파일을 확인해야 하는 경우가 있을 수 있습니다. 이때, static 디렉토리를 설정해 두면 편리하게 접근할 수 있습니다.
  • 정적 파일 서빙 테스트: 프로덕션 환경에서의 파일 서빙을 테스트하고 싶을 때, static 옵션을 통해 실제로 파일이 어떻게 제공되는지 확인할 수 있습니다.

 

라고 함

static 설정이 있다고 해서 HMR이 작동하지 않는 것은 아니라고함

 

그러니까

my-project/
├── public/
│   ├── images/
│   │   └── logo.png
│   └── index.html
├── src/
│   ├── index.tsx
│   └── App.tsx
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

 

개발 서버는 빌드 파일만 바라보고 있기 때문에

핫로딩을 쓴다면 이미지를 못 찾음

이럴 때 static설정으로 올려줘서 이미지 찾게 해준다는 거 같음

 

 

3.  스크립트 파일 이유

    "test": "jest",
    "dev": "webpack-dev-server --config webpack.dev.js", <= 개발용 서버
    "build": "webpack --config webpack.prod.js", <= 프로덕트용 빌드
    "serve": "node server.js" <= 프로덕트용 빌드 후 로컬 확인용

 

 

 

 

빌드 확인하려고... 따로 serve 넣음

다른 사람들 이렇게 안 쓰는 거 같긴한데;;;;

그래도 배포 전에 확인하는 게 좋은 거 아닌가...

 

그래서 프로덕트용 빌드 후에 빌드 파일을 실행시킬 수 있는 스크립트를 만듦

 

/** ==================================
프로덕션 빌드 확인용, npm run serve
================================== **/

const express = require('express');
const path = require('path');

const app = express();
const port = 3000;

// 정적 파일 제공
app.use(express.static(path.join(__dirname, 'dist')));

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

app.listen(port, () => {
  console.log(`Server is running at http://localhost:${port}`);
});

 

express로 서버 열어서 실행하도록...

개발 때만 써서 npm install express --save-dev 처리함

 

 

 

사실 jest 연습하려고 판 레포인데... ㅠ

다음 주말이나... 평일에 추가할 예정

 

 

 

https://github.com/Joeunji0119/testCode

 

GitHub - Joeunji0119/testCode: 리액트 테스트코드

리액트 테스트코드 . Contribute to Joeunji0119/testCode development by creating an account on GitHub.

github.com

 

 

728x90
반응형

'TIL' 카테고리의 다른 글

jest  (0) 2024.06.26
이미지 public에 넣기 vs src에 넣고 import하기 ++ 이미지 suspense로 감싸서 가져오기  (0) 2024.06.24
이산수학 필기노트  (0) 2024.06.10
컴퓨터의 이해 필기노트  (0) 2024.06.02
리액트 기초  (0) 2024.04.25

댓글