express 프로젝트에 typescript 적용 시키기
2026. 01. 23.
왜 express.js에 typescript를 설정하면 좋을까?
express 프로젝트를 시작하면 기본적으로는 CommonJS 모듈을 사용한다.
const express = require("express")
위와 같이 모듈을 불러오는 것이 기본이 된다는 의미이다.
package.json에 type을 module로 지정하면 import express from "express" 형태로 사용할 수 있지만 타입스크립트 세팅을 해두면 더 익숙한 import 문 뿐만 아니라 최신문법도 사용가능하고 컴파일된 형태로 빌드 파일도 가질 수 있는 등 여러 가지 이점을 가질 수 있다. 물론 가장 중요한 점은 타입을 확인하며 에러를 줄일 수 있다는 것이다.
위와 같은 장점을 누리기 위해 express 5 버전에 타입스크립트를 세팅해보자.
세팅하기
우선 필요한 모듈들을 설치해보자.
npm i express
npm i typescript ts-node nodemon @types/node @types/express
설치한 버전을 확인하면 아래와 같다.
{
"dependencies": {
"express": "^5.2.1",
},
"devDependencies": {
"@types/express": "^5.0.6",
"@types/node": "^25.0.9",
"nodemon": "^3.1.11",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
}
}
- express: 프로젝트에 사용할 웹 프레임워크
- @types/express: express의 타입
- @types/node: 타입스크립트는 기본적으로 node의 타입을 내장하고 있지 않아서 노드의 타입을 사용할 때를 위해서 설치한다.(설치하지 않으면 __dirname 등 node의 전역 변수 등을 사용할 때 에러가 난다)
- nodemon: 서버가 실행중일 때 자동으로 재시작해준다.
- ts-node: 타입스크립트 파일을 빌드하지 않고도 실행 시점에 컴파일해서 node로 실행할 수 있게 해주는 런타임 도구
이제는 tsconfig.json을 생성해보자. tsconfig.json은 타입스크립트에 관한 설정들을 담고 있다. 컴파일 시 해당 설정을 참고해서 컴파일 하게 된다.
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration":
프로젝트에서 src 디렉토리를 생성한 후 거기에 타입스크립트 파일들을 작성할 예정이라 include와 rootDir를 src로 설정했다. 다른 구조를 생각한다면 해당 부분을 수정해주어야 한다.
또한 컴파일된 파일들은 dist라는 폴더가 만들어지고 거기 안에 넣어지길 원해서 "outDir"는 dist라는 값을 주었다.
"exclude"는 컴파일에 포함되면 안되는 폴더들을 나타내고, 컴파일 후의 결과물이 dist와 node_modules를 넣어서 제외시켰다.
기본적으로 nodejs는 commonjs이기 때문에 module엔 commonjs를 넣어주고, 컴파일 후 require 형태를 우선 찾으라고 moduleResolution에는 node 값을 주었다.
위와 같이 작성해주었다면 package.json도 수정해보자.
// package.json
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "nodemon --exec ts-node src/index.ts"
},
}
중요한 부분은 스크립트를 위와 같이 적어주는 것이다. dev 명령어를 통해서 nodemon과 ts-node를 이용하면 자동 재시작을 통해서 편하게 개발할 수 있고, build 명령어로 생긴 컴파일된 파일은 start 명령어를 통해서 실행할 수 있다.
node_modules/
src/
index.ts
package-lock.json
package.json
tsconfig.json
위와 같은 구조를 가지고 있고 아직 작성하지 않은 src/index.ts 파일을 아래처럼 작성해보자.
import express from "express";
const app = express();
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
npm run dev 명령어를 통해서 개발하다가 npm run build를 실행하면 루트 디렉토리에 dist 폴더가 생기고 거기에 컴파일 된 index.js를 확인할 수 있다.
추가 설정하기
기본적으로 타입스크립트는 자바스크립트로 된 파일들만 컴파일 한다. 그렇기 때문에 프로젝트에 정적파일 등을 제공하기 위해 미들웨어로 설정 해두었다면 추가적으로 설정이 필요하다.
import express from "express";
app.use("/static", express.static(path.join(__dirname, "public")));
나의 경우엔 src 폴더 밑에 public 폴더를 만들고 정적파일을 넣어주고 url에는 /static이라는 가상 url을 붙여서 제공하고 있다. 타입스크립트 컴파일 후 에 실행했을 때 정적파일도 제공하고 싶다면 추가적인 설정이 필요하다.
우선 루트 디렉토리에 public 폴더를 복사하는 코드를 넣어준다.
// scripts/copy-public.js
const fs = require("node:fs");
const path = require("node:path");
const src = path.join(process.cwd(), "src", "public");
const dest = path.join(process.cwd(), "dist", "public");
fs.rmSync(dest, { recursive: true, force: true });
fs.cpSync(src, dest, { recursive: true });
console.log("기존의 public 폴더를 삭제 후 새로 복사했습니다.");
여러 경우가 있을 수 있다고 생각해서 scripts라는 폴더를 만들고 안에 copy-public.js를 넣어두었다.
{
"scripts": {
"build": "tsc",
"postbuild": "node scripts/copy-public.js"
},
}
build 명령어 밑에 postbuild를 정의하자. package.json은 build가 실행된 후에 자동으로 postbuild 명령어를 실행한다. 해당 부분을 작성했으면 build를 실행해보자. 그러면 dist폴더에 public 폴더가 복사된 것을 확인할 수 있다.