typescript를 이용하여 개발하기 위한 환경을 구성하는 과정이다.
Installation TypeScript
아래와 같은 의존성 모듈들을 설치한다.
yarn add -D typescript @types/express @types/node
- @types/express : 앞의 과정에서 express를 설치해주어 typescript 사용을 위해 추가로 설치
그리고 VSCode를 사용하는 경우, 아래와 같은 명령어를 수행한다.
yarn dlx @yarnpkg/sdks vscode
PnP(플러그앤플레이) 설정을 위해 .yarnrc.yml 파일 상단에 다음을 추가해준다. (해당 과정은 생략해도 된다)
# .yarnrc.yml
nodeLinker: pnp
yarnPath: ".yarn/releases/yarn-berry.cjs"
typescript 관련 plugin을 주입하기 위해 아래와 같은 명령어를 수행해준다.
(이 과정 또한 선택 사항이지만, @types/
관련 패키지를 package.json에 추가해주므로 유용하다.)
yarn plugin import typescript
Configure tsconfig.json
다음과 같은 커맨드를 실행하여 tsconfig.json
을 생성한다.
npx tsc --init
생성된 tsconfig.json
파일을 아래와 같이 수정하여 설정해주었다.
outDir
의 경우에는 기존에 작성한 html과 css 파일까지는 현재 빌드 산출물 경로에 복사하도록 구성을 하지 않아 임의로 주석처리를 하여 ts 파일과 동일한 경로에 생성되도록 설정하였다.
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Language and Environment */
"target": "es6" /* 사용할 특정 ECMAScript 버전 설정: 'ES3' (기본), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 혹은 'ESNEXT'. */,
"jsx": "react" /* JSX 코드 생성 설정: 'preserve', 'react-native', 혹은 'react'. */,
/* Modules */
"module": "esnext" /* 모듈을 위한 코드 생성 설정: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"moduleResolution": "node" /* 모듈 해석 방법 설정: 'node' (Node.js) 혹은 'classic' (TypeScript pre-1.6). */,
"baseUrl": "./" /* non-absolute한 모듈 이름을 처리할 기준 디렉토리 */,
"resolveJsonModule": true /* Enable importing .json files */,
/* JavaScript Support */
"allowJs": true /* 자바스크립트 파일 컴파일 허용 여부 */,
/* Emit */
"sourceMap": true /* 소스 위치 대신 디버거가 알아야 할 TypeScript 파일이 위치할 곳 */,
// "outDir": "./dist" /* 해당 디렉토리로 결과 구조를 보냅니다. */,
"noEmit": false /* 결과 파일 내보낼지 여부 */,
"downlevelIteration": true /* 타겟이 'ES5', 'ES3'일 때에도 'for-of', spread 그리고 destructuring 문법 모두 지원 */,
/* Interop Constraints */
"esModuleInterop": true /* 모든 imports에 대한 namespace 생성을 통해 CommonJS와 ES Modules 간의 상호 운용성이 생기게할 지 여부, 'allowSyntheticDefaultImports'를 암시적으로 승인합니다. */,
"forceConsistentCasingInFileNames": true /* 같은 파일에 대한 일관되지 않은 참조를 허용하지 않을 지 여부 */,
/* Type Checking */
"strict": true /* 모든 엄격한 타입-체킹 옵션 활성화 여부 */,
/* Completeness */
"skipLibCheck": true /* 정의 파일의 타입 확인을 건너 뛸 지 여부 */
},
"include": ["src"]
}
Modify src/server.js
to src/server.ts
앞에서 작성한 <project_root_dir>/src/server.js
을 ts 파일로 수정한다.
// server.ts
import express from 'express';
import path from 'path';
const app = express();
const resolve = (dir) => path.resolve(__dirname, 'frontend', dir);
/* Ensure any requests prefixed with /static will serve our "src/static" directory */
app.use('/static', express.static(resolve('static')));
/* Redirect all routes to our (soon to exist) "index.html" file */
app.get('/*', (req, res) => {
res.sendFile(resolve('index.html'));
});
app.listen(process.env.PORT || 3000, () => {
console.log('Server running...');
});
Modify package.json
script
에 아래와 같이 추가해준다.
"start": "yarn build;node src/server.js",
"build": "tsc -p .",
Run server
yarn start
를 실행하여 서버가 정상적으로 실행이 되고, 웹 브라우저로 접속이 되는 지 확인해본다.
이제 다음번에는 webpack
을 이용하여 빌드된 산출물을 경로를 분리하고, 코드 수정 시에 수정된 코드를 반영하여 재컴파일이 될 수 있도록 구성해보고자 한다.
Trouble Shootings
Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
node dist/server.js
와 같이 실행하였을 때에 아래와 같은 에러가 발생하였다.
(node:53574) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
해결방법은 package.json
에 type: "module"을 추가해준다.
{
"name": "cocoa-clone",
"version": "1.0.0",
"packageManager": "yarn@3.2.0",
//...SKIP...
"license": "MIT",
"type": "module"
//...SKIP...
}
ReferenceError: __dirname is not defined
commonjs에서 사용하던 __dirname
변수가 ES 모듈에는 없으므로 발생하는 에러이다.
file:///Users/user/Documents/mio/cocoa-clone/dist/server.js:4
var resolve = function (dir) { return path.resolve(__dirname, "frontend", dir); };
^
ReferenceError: __dirname is not defined
at resolve (file:///Users/user/Documents/mio/cocoa-clone/dist/server.js:4:52)
at file:///Users/user/Documents/mio/cocoa-clone/dist/server.js:7:35
at ModuleJob.run (node:internal/modules/esm/module_job:154:23)
at async Loader.import (node:internal/modules/esm/loader:177:24)
at async Object.loadESM (node:internal/process/esm_loader:68:5)
아래와 같이 수정하여 해결해주었다.
import path from 'path';
import { fileURLToPath } from 'url';
const app = express();
const resolve = (dir: string) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
return path.resolve(__dirname, 'frontend', dir);
};