Node.js + TypeScript — Express, Fastify 타입 안전한 서버
Node.js + TypeScript 서버에서는 요청/응답 타입, 미들웨어, 라우트 매개변수를 타입 안전하게 정의할 수 있습니다.
프로젝트 설정
# 기본 패키지 설치
npm install express
npm install --save-dev typescript @types/node @types/express ts-node
# tsconfig.json 생성
npx tsc --init
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
Express + TypeScript
기본 서버
import express, { Request, Response } from 'express';
const app = express();
app.use(express.json());
app.get('/', (req: Request, res: Response) => {
res.json({ message: '안녕하세요' });
});
app.listen(3000, () => {
console.log('서버 시작: http://localhost:3000');
});
요청/응답 타이핑
// 라우트 매개변수, 응답 본문, 요청 본문의 타입을 제네릭으로 지정
interface User {
id: number;
name: string;
email: string;
}
type CreateUserBody = Omit<User, 'id'>;
// Request<Params, ResBody, ReqBody, Query>
app.post(
'/users',
(req: Request<{}, User, CreateUserBody>, res: Response<User>) => {
const { name, email } = req.body; // 타입 안전
const user: User = { id: Date.now(), name, email };
res.json(user); // User 타입만 반환 가능
}
);
// 라우트 매개변수 타이핑
app.get(
'/users/:id',
(req: Request<{ id: string }>, res: Response<User | { error: string }>) => {
const { id } = req.params; // string
// ...
}
);
// 쿼리 파라미터 타이핑
app.get(
'/search',
(req: Request<{}, any, any, { q: string; page?: string }>, res: Response) => {
const { q, page } = req.query;
// q: string, page: string | undefined
}
);
미들웨어 타이핑
import { NextFunction } from 'express';
// 인증 미들웨어
interface AuthRequest extends Request {
user?: { id: number; role: string };
}
function authMiddleware(
req: AuthRequest,
res: Response,
next: NextFunction
) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: '인증 필요' });
}
// 토큰 검증 로직
req.user = { id: 1, role: 'admin' };
next();
}
app.get('/admin', authMiddleware, (req: AuthRequest, res: Response) => {
console.log(req.user?.role); // string | undefined
});
에러 핸들링 미들웨어
// 에러 핸들링 미들웨어는 매개변수가 4개
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).json({ error: err.message });
});
Fastify + TypeScript
Fastify는 TypeScript를 1급으로 지원 하며, @types/ 패키지가 필요 없습니다.
npm install fastify
import Fastify from 'fastify';
const server = Fastify({ logger: true });
// 스키마 기반 타이핑
server.get<{
Querystring: { page: string };
Reply: { users: User[]; total: number };
}>('/users', async (request, reply) => {
const { page } = request.query; // string
const users = await getUsers(parseInt(page));
return { users, total: users.length };
});
// 라우트 매개변수와 본문
server.post<{
Params: { id: string };
Body: { name: string; email: string };
Reply: User;
}>('/users/:id', async (request, reply) => {
const { id } = request.params;
const { name, email } = request.body;
// ...
});
server.listen({ port: 3000 });
Fastify + Zod
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
server.post('/users', async (request, reply) => {
const result = CreateUserSchema.safeParse(request.body);
if (!result.success) {
return reply.status(400).send({ error: result.error.issues });
}
const user = await createUser(result.data);
return reply.status(201).send(user);
});
Express vs Fastify 타이핑 비교
| 특성 | Express | Fastify |
|---|---|---|
| 타입 지원 | @types/express 필요 | 내장 |
| 제네릭 파라미터 | Request<Params, Res, Req, Query> | { Params, Body, Query, Reply } |
| 스키마 검증 | 별도 미들웨어 필요 | JSON Schema 내장 |
| 타입 추론 | 수동 타이핑 | 스키마에서 자동 추론 |
정리
- Express는
@types/express를 설치하고Request<Params, Res, Req, Query>제네릭으로 타이핑한다 - Fastify는 TypeScript를 1급 지원하며 제네릭 라우트 정의가 직관적이다
- 미들웨어에서
req를 확장하려면 인터페이스를 extends한다 - Zod와 결합하면 런타임 검증과 타입 안전성을 동시에 확보할 수 있다
- 새 프로젝트에서 TypeScript 지원이 중요하다면 Fastify를 고려하자
댓글 로딩 중...