2편 : React + TS boilerplate 제작기 - 설치 패키지 & npx
현재 react 17버전부터 가능해진 import React from 'react' 제거 작업을 진행중입니다. ts-loader에게 모든걸 맡긴 상태라 현재 아래 글에는 고려되지 않았습니다.
조만간 수정할 예정이니 참고만해주세요.
React를 처음 접할 때 대부분의 강의는 create-react-app(이하 CRA)을 사용합니다. 저 또한 React 프로젝트를 시작할 때 마다 일단 CRA를 실행하고, 추가적으로 필요한 의존성을 추가한 후 개발을 시작하곤 했습니다.
CRA는 webpack, babel, eslint 등 복잡한 환경 설정을 신경쓰지 않아도 되며, autoprefixer 를 지원하는 등 개발을 매우 편리하게, 시간을 절약해주는 아주 유용한 툴입니다.
🤦♂️ 다만, 그만큼 단점도 존재하죠.
즉, CRA는 React는 라이브러리가 아닌 프레임워크라 부르게 만드는 이유 중 하나입니다. 이미 촘촘하게 짜여진 환경에 따라야하며, 사용자가 커스텀하기엔 큰 부담이 따르거든요.
👉 하지만 언젠가는 직접 환경설정을 해야하는 날이 올 것이기 때문에, CRA에만 의존해서는 안 되겠다는 생각이 들었습니다.
아래 설명 부분부터는 말투가 좀 바뀔겁니다..
사실 보일러플레이트가 뭐 별거 있겠나(사실 별거임). 이미 수많은 블로그에 다양한 정보가 넘쳐난다.
하지만 블로그마다 환경 설정이 조금씩 다르다. 근데 다 제대로 동작한다.
🤦♂️ 이 미묘한 차이가 초보자 입장에서 아주 골치다.
의문이 들지만 명확한 설명은 없다. 보통의 환경 설정을 설명하는 블로그 글은 대부분 이 파일은 이렇게 작성하세요에서 그친다.. 다 그렇다는건 아니지만!
일단 한 번 따라해봐! 그럼, 짠! 동작해!
👉 가능하면 각종 설정 파일들(package.json, tsconfig ..)의 각 속성들이 어떤 의미인지 최대한 설명하겠다.. 이후 글에는 내가 진행하면서 궁금하고 찾아본 사소한(?) 내용들은 ❓로 표시하고 찾아낸 내용을 기술할 예정이다. 그러다보니 뭐 이런거까지 언급하냐 싶은 내용이 많을 수 있으니 참고해주시길..
npm init -y
❓ -y 는 뭐지?
npm i react react-dom
npm i -D typescript @types/react @types/react-dom
❓ 왜 typescript 관련 설치는 -D를 붙이나?
이 파일이 존재하는 경로가 TypeScript 프로젝트의 root 경로가 된다.
compilerOptions, files, include, exclude, extends, compileOnSave 등의 속성을 작성할 수 있다. 이에 대한 설명은 간단하게만 작성하고, 자세한 내용은 검색!
TIP
컴파일 대상 경로를 정의하는 속성의 우선 순위
files > include = exclude // exclude에 있더라도 files에 지정된 파일은 컴파일 대상이 된다.
TIP2
exclude에 node_modules를 지정하더라도 @types 폴더는 컴파일에 포함한다!
프로젝트 root 폴더에 tsconfig.json 파일을 만들어준다.
타입스크립트가 글로벌로 설치되어 있다면 tsc --init 명령어로 만들어도 되지만 그러면 모든 속성값이 적힌 tsconfig.json파일이 생성된다. 궁금하면 해당 명령어로 생성해 보자.
이 글에서는 직접 tsconfig.json 파일을 생성하고, 내용을 추가하는 방식을 진행한다.
{
"compilerOptions": {
"target": "es5", // 컴파일 된 결과물이 어느 버전의 ECMAScript를 따를 것인지
"lib": ["DOM", "DOM.Iterable", "ESNext"], // 컴파일 시 포함시켜야하는 javascript 내장 API들의 타입 정의에 대한 정보들
"module": "esnext", // 프로그램에서 사용할 모듈 시스템. import/export 코드가 어떤 방식의 코드로 컴파일 될지 결정한다
"allowJs": true, // js files를 허용할 것인가
"jsx": "react", // jsx 코드를 어떻게 컴파일 할 것인가
"baseUrl": "./", // 비상대적 import 모듈 해석시 기준이 되는 경로
"moduleResolution": "Node", // 모듈 해석 전략. 웬만해선 node로 고정할 것
"sourceMap": true, // map 파일을 생성할 것인가
"esModuleInterop": true, // es module 사용시 컴파일 단계에서 헬퍼 함수를 사용할 것인가
"strict": true, // strict family 속성 전부를 true로 할 것인가
"noImplicitAny": false, // any 타입으로 구현된 표현식 혹은 정의를 에러처리 할 것인가
"isolatedModules": true, // 각 파일을 분리된 모듈로 트랜스파일링할 것인가
"forceConsistentCasingInFileNames": true, // 사용할 파일의 이름을 대소문자까지 정확하게 작성하도록 강제할 것인가
"declaration": false, // d.ts 파일을 생성할 것인가
"removeComments": true, // 컴파일시 주석을 제거할 것인가
"pretty": true, // 에러와 메세지를 색, 컨텍스트를 사용해서 스타일을 지정할 것인가
"strictFunctionTypes": true, // 함수, 메소드의 인자 타입을 더 정확히 추론할 것인가
"skipLibCheck": true, // 사용하는 라이브러리의 타입 검사를 skip할 것인가
"noImplicitThis": true, // any 타입으로 암시한 this 표현식에 오류를 보고할 것인가
"noFallthroughCasesInSwitch": true, // switch문에서 fallthrough case가 발견되면 에러를 발생시킬 것인가
"noImplicitReturns": true, // void가 아닌 함수가 리턴을 제대로 하지 않는 경우가 있다면 에러 발생
"noEmit": true, // 컴파일러가 js 파일 등 출력 결과물을 만들지 않을 것인가
"noEmitOnError": true, // 에러 발생시 js 소스코드, source map, declaration 등이 생성되지 않는다
//
"noUnusedLocals": false,
"downlevelIteration": true
// 사용되지 않는 지역 변수에 대해 에러를 발생시킬 것인가
},
"include": ["src"],
"exclude": ["node_modules"]
}
❓ 이 많은 옵션이 다 필요한건가?
참고 링크
여기 상당히 잘 정리된 4개의 문서가 있다. 각자 공부해보면서 자신만의 tsconfig.json 파일을 만들어보자
이제 모든 프로젝트를 번들링해줄 웹팩 설정을 해보자
npm i -D webpack webpack-cli webpack-dev-server
npm i -D ts-loader css-loader style-loader file-loader html-webpack-plugin
npm i dotenv
❓ loader와 plugin은 무슨 차이일까?
단순히 비교하면 번들이 생성 되기 전(빌드 전)에 쓰이냐, 후에 쓰이냐로 구분할 수 있다
loader는 webpack으로 빌드를 진행하기 전/진행 중에 개별 파일들(빌드 과정에 포함되는 각 파일들)에게 적용하기 위해 사용된다
plugin은 번들이 생성 된 후 결과 파일에 적용하기 위해 사용된다. 번들된 js 난독화, 압축, 복사, 특정 텍스트 추출, 별칭 등 부가 작업 등등 후처리를 한다.
이것들 모두 프로덕션 배포 이전 빌드 단계에서만 사용되기 때문에 -D 옵션으로 설치한다.
require("dotenv").config(); // ❓
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const isProd = process.env.NODE_ENV === "production"; // ❓
const PORT = process.env.PORT || 3000;
module.exports = {
mode: isProd ? "production" : "development",
devtool: isProd ? "hidden-source-map" : "source-map", // development 환경에서만 source-map을 만든다.
entry: "./src/index.tsx",
output: {
filename: "[name].js", // [name]은 청크의 이름을 사용한다. ❓
path: path.join(__dirname, "/dist"), // ❓
},
resolve: {
modules: ["node_modules"],
extensions: [".js", ".jsx", ".ts", ".tsx"], // ❓
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
loader: "ts-loader",
options: {
transpileOnly: isProd ? false : true, // ❓
},
},
{
test: /\.css?$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(webp|jpg|png|jpeg)$/,
loader: "file-loader",
options: {
name: "[name].[ext]?[hash]",
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "public", "index.html"),
hash: true,
}), // ❓
],
devServer: {
contentBase: path.resolve(__dirname, "public"),
host: "localhost",
port: PORT,
open: true,
hot: true,
compress: true,
historyApiFallback: true,
overlay: true,
stats: "errors-only",
},
};
❓ dotenv는 뭘까?
❓ NODE_ENV는 뭘까?
❓ Chunk(청크)는 뭘까?
❓ path.resolve vs path.join
❓ resolve 필드는 어떤 역할을 할까?
import Calender from "@jjunyjjuny/react-calendar"
import a from 'src/App'
❓ transpileOnly는 어떤 속성일까
fork-ts-checker-webpack-plugin
라는 플러그인으로 타입 체크를 한다.❓ HtmlWebpackPlugin는 어떤 역할을 할까?
npm i -D cross-env
webpack 실행시 NODE_ENV 값을 지정하기 위해 보통 --mode production/development 옵션을 추가해주는데, 이는 Mac에서만 동작한다
윈도우에서 NODE_ENV값을 변경하기 위해서는 cross-env 라이브러리를 설치, 사용해야한다. 서러워서 윈도우 쓰겠나..
{
"name": "", // 실제로 공백은 불가능하다.
"version": "0.1.0",
"description": "",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack serve --open --hot --progress",
"start": "cross-env NODE_ENV=development webpack --progress",
"build": "cross-env NODE_ENV=production webpack --progress"
},
"keywords": [], // npm 검색 키워드
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^17.0.9",
"@types/react-dom": "^17.0.6",
"cross-env": "^7.0.3",
"css-loader": "^5.2.6",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.1",
"styled-loader": "*",
"ts-loader": "^9.2.3",
"typescript": "^4.3.2",
"url-loader": "^4.1.1",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"dotenv": "^10.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"homepage": "", // git 저장소 주소 or 홈페이지
"repository": {
"type": "git",
"url": "" // git 저장소 주소
}
}
❓ scripts
node_modules
/dist
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
// src/App.tsx
import React from "react";
export default function App() {
return <div>sample</div>;
}
// src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Title</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
├── node_modules
├── package-lock.json
├── package.json
├── public
| └── index.html
├── src
| ├── App.tsx
| └── index.tsx
├── tsconfig.json
└── webpack.config.js
가능한 이건 뭐지? 싶은 내용을 최대한 설명하려고 노력했으나 여전히 부족한 점이 많습니다. 사실 의구심이 들면 스스로 찾아보면서 공부해가는게 더 개발자다운 방식이라고 생각하기도 합니다. 하지만 초보자 입장에서 환경 설정은 너무나 복잡하고 이해하기 힘든 영역이라 쪼금 더 설명을 덧붙였습니다.
다음은 이렇게 제작한 보일러플레이트를 CRA처럼 npx를 사용해 설치하는 방법에 대해 포스팅하겠습니다.
위 내용에서 오류를 발견하시면 댓글 부탁드립니다!
참고한 아티클들
mode를 development로 실행하면 해당글의 devServer 속성은
devServer: {
contentBase: path.resolve(__dirname, "public"),
host: "localhost",
port: PORT,
open: true,
hot: true,
compress: true,
historyApiFallback: true,
overlay: true,
stats: "errors-only",
},
처럼 되어있는데 webpack devServer 속성의 업데이트 때문인건지 약간 다르게 바꿔줘야하네용,
stats: "errors-only",
devServer: {
static: {
directory: path.resolve(__dirname, "public"),
},
port: PORT,
open: true,
client: {
overlay: true,
},
hot: true,
host: "localhost",
historyApiFallback: true,
compress: true,
},
stats속성은 따로 빼주어야하고, contentBase속성은 static > directory 속성으로, overlay는 client > overlay로 빼주니 되었습니다.
The Top 10 Big Ass Porn Actresses often collaborate, creating memorable scenes that push the boundaries of creativity in adult films. Their partnerships frequently lead to iconic performances loved by fans.
똑같이 따라했는데 "Typescript emitted no output for [경로] at makeSourceMapAndFinish, successLoader,Object.loader" 라는 에러를 뱉어서 tsconfig.json에 "noEmit" 속성을 true에서 false로 바꿔줌으로써 해결했습니다.
** transpileOnly속성을 true로 주고 plugin에 ForkTsCheckerWebpackPlugin을 넣어줘도 해결이 되네요 :)