아마 13까지 진행하면 typescript의 기본적인 건 거진 끝내는 것 같다!
항상 그냥 만들어주는 CRA라던지 그런 걸 사용해서 웹팩을 직접적으로 다루지 않았었는데 한번 잡고 가야 되긴 하는 것 같다
webpack은 의존성을 가진 수십, 수백 개의 파일로 구성된 복잡한 애플리케이션 처리를 돕는다
해당 파일들은 내보내기, 가져오기나 다른 파일로 나누기가 이루어지고 제 3자 라이브러리를 갖는다
Webpack은 이 모든 걸 모아서 번들을 만든다
모두 합치고 압축해서 브라우저에 넣을 수 있는 작은 번들로 만드는 것
가면 갈수록 javascript의 맡는 영역이 넓어지는데 백여개의 각기 다른 스크립트를 어떻게 모두 불러와서 올바른 순서대로 정렬해야 할까?
아니면 가능한 한 작게 번들로 만들어서 최대한 적은 요청을 보내는 게 나을까?
Webpack은 각기 다른 에셋 처리를 도와줌으로 우리는 TS파일과 JS파일만 다루면 된다
다양한 에셋을 모아 정적 에셋으로 번들링 해준다
수많은 라이브러리를 포함하는 백여개의 Javascript 파일이 하나로 합쳐져서 한 스크립트로 불러올 수 잇는 번들 파일이 된다
이게 Webpack의 기본 개념이다
연습할 디렉토리를 만든 후
tsc --init
입력하고 npm init -y
입력이후 tsconfig.json
파일로 이동한다
include
조건과 outDir
조건으로 컴파일을 포함할 폴더랑 컴파일 된 파일이 나올 폴더를 구분
target
은 es2015 또는 es6지정, module
또한 ES로 맞춰준다
이후 소스 폴더에 index.ts
와 utils.ts
를 만들고 함수를 작성해준다
// utils.ts
export function add(x: number, y: number) {
return x + y;
}
export function multiply(x: number, y: number) {
return x * y;
}
export function divide(x: number, y: number) {
return x / y;
}
이 파일은 연산 함수들을 내보내기 한 곳
그리고 Dog.ts
란 파일을 만들어서 클래스를 하나 만들어준다
//Dog.ts
export default class Dog {
constructor(public name:string, public breed:string, public age:number){}
bark(){
console.log("WOOF WOOF !!!")
}
}
이번엔 Dog
클래스를 상속받는 ShelterDog
를 만들어보자
// ShelterDog.ts
import Dog from "./Dog"
export default class ShelterDog extends Dog {
constructor(
name: string,
breed: string,
age: number,
public shelter: string
) {
super(name, breed, age);
}
}
이제 클래스와 연산함수들을 index.ts
파일에서 가져오기를 해보면
//index.ts
import Dog from "./Dog"
import ShelterDog from "./ShelterDog"
import {add, multiply, divide} from "./utils"
const elton = new Dog("Elton","Aussie",0.5)
elton.bark(); // "WOOF WOOF !!!"
console.log(add(4,2)) // 6
console.log(multiply(4,2)) // 8
console.log(divide(4,2)) // 2
const buff = new ShelterDog("Buffy","Pitbull",5,"Desert Springs Shelter")
이로써 서로서로의 의존성이 만들어졋다
이제 index.html
을 만든 후 타입 모듈로 스크립트 파일을 연결시켜준다
이후 실행을해보면 정상 작동하는 것을 알 수 있는데 Network 탭을 확인해보면 각 파일이 전부 개별 요청에 해당된다
하나하나가 로딩 되었어야 하는 개별 스크립트인 것이다
만약 파일이 여러개이고 제 3자 라이브러리까지 추가된다면 엄청나게 많은 파일들이 로딩 될 것이다
webpack을 사용하기 위해선 패키지 몇 개와 webpack을 설치해야 하는데
npm install --save-dev webpack webpack-cli typescript ts-loader
이것만 먼저 설치를 해보자
일단 Webpack은 모듈 번들러이지만 명령줄에서 사용하거나 package.json파일 내에서 호출하려면 webpack-cli가 필요하다.(둘은 함께 사용됨)
webpack-cli는 webpack의 명령줄 인터페이스로 webpack의 없이는 사용할 수 없으나 webpack의에 포함이 되어 있지는 않는 개별 패키지인 것
여기서 의문 Typescript가 이미 설치되어 있는데도 왜 다운로드를 할까?
이는 package.json에 Typescript를 포함시키는 것이 올바르기 때문이다. 작업하고 있는 버전을 보여주기 때문에 누군가가 이 파일을 다운로드 하게 되면 package.json을 통해 typescript가 필요하다는 사실을 확실히 알려주게 된다
이미 Typescript가 설치되어 있을 거라 추측하는 것보다 안전하게 가능하다
ts-loader 즉, Typescript 로더는 Typescript와 Webpack 사이의 중간자 역할을 한다.
비교적 작은 패키지로 Typescript를 호출해서 이를 모두 번들링하게 될 Webpack으로 전달하는 역할을 한다
먼저 webpack.config.js
란 파일을 만든다(json아니라js다)
이후 module.exports={}
안에 엄청 많은 것들을 적을건데 webpack의 공식문서에서 typescript 섹션을 확인해서 예제 파일을 확인하자
하나하나 한번 따져보며 파고들자면
1.가장 먼저 할 작업은 엔트리 포인트의 설정이다
엔트리 포인트는 Webpack에게 번들링을 시작할 애플리케이션의 시작점을 지정해준다
위에서 만든 dog부터 연산함수까지 src
폴더를 사용했기에
entry:"./src/index.ts"
를 적어준다
2.module프로퍼티추가
해당 프로퍼티는 객체인데 배열이 될 rules
프로퍼티가 포함되며, 여러 개의 규칙을 추가할 수 있다
현재는 Typescript 규칙 하나만 포함하면 되지만, Typescript 혹은 Webpack이 sass파일이나 일반 javascript파일, CSS 파일이나 정적 에셋을 만났을 때의 규칙을 정해줄 수 있다
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
};
module
은 객체고 rules
는 배열이다. 먼저 우리가 설치한 ts-loader를 사용하라는 규칙을 만든다
.ts
, 혹은 .tsx
로 끝나는 파일이 있을시를 표기해준다
/\.tsx?$/
정규식에서 $
는 이 패턴이 파일의 맨 마지막에 와야 한다는 의미이며 파일은 ts
로 끝나거나, 혹은 뒤에 선택적으로 tsx
가 올 수 있다는 의미이다(?
가 선택적이기에 x
는 선택적)
그럼 test를 통해 파일 형식을 정규식으로 전달했고 webpack이 해당 파일을 찾게 되면 무엇을 사용할 지 지시한다
여기선 당연 ts-loader
를 사용하라고 지시하고 마지막으로 exclude
를 추가하는데 이역시 정규식으로/node_modules
입력하면 해당 디렉터리는 건드리지 말라고 얘기하는 것이다
다른 로더를 사용하는 경우 다른 규칙을 추가할 수 있다
3.resolve작성
module 다음에 resolve가 나오고 확장자 목록이 들어간다
프로퍼티중 extensions
에는 Webpack이 리졸브 할 수 있는 확장자의 리스트가 들어간다
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
};
4.output작성
webpack으로 생성하려는 출력이다
const path = require("path");
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
};
bundle.js
를 생성해야 할 건데 파일네임을 그대로 적어주자 이후 path
가 등장하는데 공식 문서 예시를 보면 상단에서 require
로 path
에서 가져온 path.resolve
메서드를 사용하고 있다
이 resolve
메서드는 하드코딩 되어 있지 않은 path name
을 구축할 수 있도록 해주는 메서드로 다른 디렉터리에서 실행을 해도 작동이 되도록 도와준다!
그래서 사용하는 것이다
__dirname
으로 지정되어 있고 'dist'
로 첨부를 하는 것5.package.json에서 스크립트 추가하기
"script"
부분에 "build"
라고 하고 "build":"webpack"
을 입력해서 cli에 build를 입력하면 webpack을 실행시키도록 한다
npm run build
를 호출해 보면 오류가 발생하지만 Webpack은 정상적으로 실행이 된다
위에서 만든 Dog
클래스등이 있는 파일들을 그대로 사용했는데 먼저 import
에 있는 파일 확장자 .js
를 모두 지웠다
그러니 정상적으로 실행되었고 모든 의존성이 있는 bundle.js
파일이 생겼다
이후 index.html
파일로 가서 원래 index.js
로 받아오고 있던 스크립트를 bundle.js
로 가져오게 변경한다. (모듈타입도 필요없다)
이후 서버를 실행해보면 정상 동작을 확인해볼 수 있다
소스 맵은 압축되어 있는 번들 코드를 디버깅하고 이해하는 데 도움을 준다
역매핑을 통해 빌드 전의 상태를 보여줌으로써 번들을 구성하고 있는 코드가 어디서 오는지를 보여준다
sourceMap
이 true
로 설정되어야 한다고 지시한다typescript는 json파일 안에서 주석으로 들어가 있으니 찾기를 이용해 검색하면 해당 프로퍼티를 찾아서 바꿔주면 된다
webpakc에서는 하나를 추가해주면 되는데 devtool: "inline-source-map"
를 추가해주자
const path = require("path");
module.exports = {
entry: "./src/index.ts",
devtool: "inline-source-map", //추가한 코드
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
},
};
이제 다시 번들링을 하면 브라우저에 소스 맵이 작동한다
개발자 도구의 소스에 들어가서 구름 모양 아이콘의 webpack_ts를 확인하면 각 파일들을 확인해 볼 수 있다
여기까지 했으면 추가적으로 볼게 build
를 할때 WARNING
을 확인할 수 있는데
빨간 줄로the 'mode' option has not been set,...
으로 mode 옵션을 설정하지 않았으므로 webpack이 값을 프로덕션으로 풀백 하겠다는 뜻이다
우리는 개발을 해야하니 개발을 설정해야한다
webpack.config.js
파일로 가서 또 하나를 추가해준다 mode: "development"
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.ts",
devtool: "inline-source-map", // 추가 코드
//...some code
};
그러면 개발 단계에 있기 때문에 bundle 파일은 경량화 되지 않는다. 이는 프로덕션과의 차이점이다
그러면 현재 lite-server
로 서버를 사용하는데 Webpack Dev
서버로 바꾸고 싶다
먼저 webpack-dev-server를 설치해주자
npm install --save-dev webpack-dev-server
그리고 package.json
에서 스크립트를 바꿔주자 "serve": "webpack serve"
로
그 다음으로 webpack.config.json
으로 돌아와 output
내에 publicPath프로퍼티를 추가하고 /dist로 설정한다
const path = require("path");
module.exports = {
//...some code
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/dist", //추가 코드
},
};
webpack 서버로 작동시킬 경우 bundle.js
를 파일로 계속 만드는 것이 아닌 메모리에 저장해서 사용한다. 즉 서버에는 있고 dist
라는 폴더에는 존재하지 않는다
이후 서버를 중단하고 build
를 하면 webpack은 번들파일을 만들고dist
폴더에 집어 넣는다
현재는 항상 개발 모드로 설정되어 있고 항상 devServer를 사용한다
다만 개발 모드로 설정하면 프로덕션 모드일 때 가능한 경량화가 되지 않는다
두 옵션 모두 사용하고 싶다면 어떻게 할까?
webpack.config.js
를 만들때 dev구성 파일과 prod 구성파일을 만든 다음 병합하면 된다.
위 예시에서 계속 그대로 써보자면
webpack.prod.js
로 만들어보자)//webpack.prod.js
const path = require("path");
module.exports = {
mode: "production", // production으로 교체
entry: "./src/index.ts",
devtool: "inline-source-map",
//...some code
};
이후 package.json
으로 간다
"build"
스크립트를 실행할 때 --config
를 사용해 방금 생성한webpack.prod.js
를 가리키면 해당 구성 파일을 사용하게 된다
"build" : "webpack --config webpack.prod.js"
webpack.config.js
를 찾는 게 기본값이지만 해당 옵션을 사용하면 원하는 파일을 가리킬 수 있다
※여기서 자주 사용하는 게 있는데 현재는 build
를 할때마다 bundle.js파일이 생성되기 때문에 파일이 쌓이지만 해당 파일들을 새롭게 build
할 때마다 지워주는 플러그인이 있다
평군적으로 webpack.config.js
에서 output
쪽에 filename
프로퍼티의 값을
[contenthash].bundle.js
식으로 만들어서 해시가 되고 webpack이 자동으로 파일이름에 추가하게 된다
이 방법을 많이 사용하는 이유는 내용이 변경된 다른 파일이란 것을 브라우저가 인식하도록 돕기 때문이다. 캐싱 문제를 피할 수 있다
이러한 문제를 CleanWebpackPlugin
으로 해결할 수 있는데 npm
으로 설치하고 플러그인을 사용하라고 지시하면 된다
npm install --save-dev clean-webpack-plugin
으로 설치한다
이후 번들 파일은 프로덕션 때만 생성되니 webpack.prod.js
파일로 가서 마지막에 플러그인을 추가한다
//webpack.prod.js
const path = require("path");
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: "production",
entry: "./src/index.ts",
devtool: "inline-source-map",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js"],
},
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "dist"),
publicPath: "/dist",
},
plugins: [new CleanWebpackPlugin()] //추가코드
};
이렇게 작성하면 가장 최신 번들만 남게 된다.(혼선방지를 위해 [contenthash]
는 삭제)
이후 마지막으로 파일 구성 이름을 통일하게 위해 webpack.prod.js
와 동일하게 webpack.config.json
의 파일이름을 webpack.dev.json
으로 변경하여 각각 프로덕션일때 개발일때 구성파일로 사람이 파일 이름만 읽어도 확인되게끔 해준다.
(이때 변경할 때 package.json
파일로가서 --config
로 구성파일을 특정하는 것 까먹지 말자! dev로 바꾼 파일도 마찬가지로 연결해줘야 한다)