먼저 typescript
, react
, react-dom
을 설치한다.
npm install typescript react react-dom
막상 .tsx, .ts
파일에 react 패키지를 사용하려하면 아래와 같이 에러가 발생한다.
그 이유는 타입이 없기 때문이다.
typescript 를 사용하기 위해서는 타입 정의가 필요한데 react, react-dom 패키지에는 타입이 없다.
따라서 아래와 같이 type을 정의한 패키지를 설치해야한다. 이때 devDependencies 설치한다.
왜냐하면 타입스크립트가 JS로 트랜스파일되어 파입 정의가 필요없기 때문이다.
npm install -D @types/react @types/react-dom
@types 패키지
다른 패키지의 타입을 정의해 둔 패키지이다.
@types 패키지는*.d.ts
파일들이 있는데*.d.ts
파일은 모듈의 외부 API를 설명하는 유형 정의 파일이다.
webpack과 그에 관련된 것들을 설치한다.
이들도 실제 애플리케이션 동작에는 상관이 없으므로 devDependencies 설치한다.
# webpack 패키지 및 cli를 사용하기 위해 설치
npm install -D webpack webpack-cli webpack-dev-server
# webpack 설정에 사용할 플러그인 설치
npm install -D autoprefixer css-loader html-webpack-plugin mini-css-extract-plugin postcss postcss-loader style-loader ts-loader
플러그인에 대한 설명은 아래와 같다.
플러그인 | 설명 |
---|---|
autoprefixer | PostCSS의 플러그인으로 CSS에서 vender-prfix 를 붙여준다. |
style-loader | CSS파일을 html 문서의 head 안에 <style> 요소로 변환한다. |
css-loader | js 파일에서 CSS 파일을 불러올 수 있게 해준다. |
html-webpack-plugin | 번들링시 html파일을 생성한다. |
mini-css-extract-plugin | CSS를 포함하는 js파일마다 CSS파일을 생성한다. |
postcss-loader | - postcss를 wepack에서 사용할 수 있게 한다. - postcss가 설치되어 있어야한다. |
ts-loader | typescript를 webpack에서 사용할 수 있게 한다. |
{
"compilerOptions": {
// 패키지 import를 Node처럼 한다.
"moduleResolution": "Node"
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"allowJs": true,
// import React from "react" 쓸 필요 없게하기
"jsx": "react-jsx",
"baseUrl": "./",
// 경로를 별칭으로 사용하게 한다.
// 코드에서 경로 src/app 은 ./src/app 가 된다.
"paths": {
"src/*": ["./src/*"],
"public/*": ["./public/*"]
}
// @types 에 대한 폴더 설정
"typeRoots": ["./node_modules/@types", "./src/@types"]
},
"include": ["./src", "public/testFolder"],
"exclude": ["./node_modules", "./dist", "./public"]
}
여기서 중요한 것은 paths
설정이 webpack
의 설정에서도 동일하게 이루어져야 하는 것이다.
왜냐하면 tsconfig가 path를 인식해도 webpack은 인식하지 못하기 때문이다.
이미지 파일을 import
하는 경우 에러가 발생한다.
이 이유도 typescript 가 해당 파일에 대해 타입 선언이 없기 때문이다.
이 경우 *.파일타입
에 대한 .d.ts
파일을 설정해주어야 한다.
// src/@types/import-ipg.d.ts
declare module "*.jpg" {
const content: string;
export default content;
}
.d.ts 파일?
- typescript 코드의 타입 추론을 돕는 파일이다.
- typescript는 전역 변수로 선언한 변수를 특정 파일에서 import 구문 없이 사용하는 경우 해당 변수를 인식하지 못해 에러가 난다.
- 이는 해당 변수를 선언해서 해결할 수 있다.
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const isProduction = process.env.NODE_ENV == "production";
const stylesHandler = isProduction
? MiniCssExtractPlugin.loader
: "style-loader";
const config = {
entry: "./src/index.tsx",
output: {
path: path.resolve(__dirname, "dist"),
// 이로 인해 clean-webpack-plugin 이 필요 없어졌다.
clean: true,
},
devServer: {
static: path.join(__dirname, "dist"),
open: true,
host: "localhost",
},
plugins: [
new HtmlWebpackPlugin({
// 번들링시 그대로 가져갈 html 파일
// 해당 html 파일에는 react 가 렌더링할 HTMLElement 가 있어야한다.
template: "./public/index.html",
}),
],
module: {
rules: [
{
// typscript를 webpack이 이해하게 한다.
test: /\.(ts|tsx)$/i,
loader: "ts-loader",
exclude: ["/node_modules/"],
},
{
// css에 대한 설정
test: /\.css$/i,
use: [stylesHandler, "css-loader", "postcss-loader"],
},
{
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
// 번들링시 파일을 base64로 인코딩 하지 않고 파일째로 나오게함
// "url-loader" 플러그인이 필요하지 않게 됨!
type: "asset/resource",
generator: {
// 번들링 된 파일을 이름 그대로 생성함
filename: "public/[name][ext]",
},
},
],
},
resolve: {
// 중요! tsconfig 와 경로가 같아야한다.
alias: {
src: path.resolve(__dirname, "src"),
public: path.resolve(__dirname, "public"),
},
extensions: [".tsx", ".ts", ".jsx", ".js", "..."],
},
};
// production 이냐 develop에 따라 동작이 달라진다.
module.exports = () => {
if (isProduction) {
config.mode = "production";
config.plugins.push(new MiniCssExtractPlugin());
} else {
config.mode = "development";
}
return config;
};
여기에서 중요한 것은 resolve.alias
이다.
이 옵션은 tsconfig와 같아야 webpack에서 제대로 된 경로를 찾을 수 있다.
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<!-- id를 root로 index.tsx에서 랜더링힐 것입니다. -->
<div id="root"></div>
</body>
</html>
html 파일에 대한 설정이다.
이 파일이 있어야 webpack이 번들링시 해당 html을 template 삼아 페이지를 생성한다.
만약
index.html
파일의 경로가public/index.html
가 아닌 경우webpack.config.js
의HtmlWebpackPlugin.template
을 바꿔야한다.
src/index.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "src/App";
const root = createRoot(document.querySelector("#root") as Element);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
src/App.tsx
export const App = () => {
return (
<div>
<h1>App</h1>
</div>
);
};
package.json은 아래와 같다.
{
...,
"scripts": {
"dev": "webpack serve",
"build": "webpack --mode=production --node-env=production",
"build:dev": "webpack --mode=development",
"build:prod": "webpack --mode=production --node-env=production",
"watch": "webpack --watch",
"serve": "webpack serve"
},
"dependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"autoprefixer": "^10.4.13",
"css-loader": "^6.7.3",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.7.2",
"postcss": "^8.4.20",
"postcss-loader": "^7.0.2",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
},
}
이로서 개발시 npm run dev
시 webpack-dev-server
가 실행된다.
repo를 참조
React-router 등을 이용하여 SPA(Single Page Application)을 구축시 URL을 통하여 페이지를 이동하면 아래와 같은 에러가 발생한다.
에러가 나는 이유는 Webpack 설정으로 인해 localhost:포트
의 주소만 웹을 띄우고 있기 때문에 locahost:포트/others
같은 URL은 없기 때문이다.
이 문제를 해결하기 위해 Webpack 설정을 아래와 같이 추가해야한다.
// webpack.config.js
const config = {
...
devServer: {
open: true,
host: "localhost",
port: process.env.PORT,
// 미지정 경로로 이동 및 새로고침 시 적절히 렌더링 여부
historyApiFallback: true,
},
...
historyApiFallback: true
시 미지정 경로로 이동하게 된다면 index.html
을 serving 한다.
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./src/App";
const root = createRoot(document.querySelector("#root") as Element);
// 개발 환경에서 2번 렌더됨!
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
react에서 <React.StrictMode>
를 사용한 경우 초기 render가 두번씩 된다. (facebook 에서 의도적인 기능이라고 한다. 부작용 등을 감지하려고 하는 것 같다.)
<React.StrictMode>
제거하면 이런 현상이 사라진다고 한다.// 1번 렌더됨
root.render(
<App />
);
tsconfig
webpack