Node.js + Express + TypeScript 게시판 만들기 #1 – 개발 환경 구성
Node.js + Express + TypeScript 게시판 만들기 #2 – nodemon 설정과 라우터 분리
자 지난 글에서는 nodemon 설정과 라우터 분리를 통해 기본 세팅을 이어갔었다.
이번 글에서는 다음을 진행해보려 한다.
- dotenv 를 이용한 환경변수 관리
- Controller / Service 계층 분리
- 에러 처리 미들웨어 추가
1. dotenv 설치

> npm install dotenv

> npm install -D @types/dotenv

그 후 .env 파일 생성.
나중에 개발, 운영, 로컬 다 따로 나눌 수 있지만 우선은 하나로 가고 여기에 디비정보라던지 그런 환경변수들을 넣어준다.
기본적인 PORT 먼저 3000으로 설정했다.
그리고 index.ts에서 dotenv를 로드한다.

import express from 'express';
import indexRouter from './routes/index_routes';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const port = Number(process.env.PORT) || 3000;
app.use('/', indexRouter);
app.listen(port, () => {
console.log('server start!!!');
console.log("PORT : ", port);
});
process.env.PORT로 아까 작성했던 포트를 가져온다.

서버 시작을 해서 콘솔로 확인할 수 있다.
2. Controller 분리
기존에는 라우터에서 바로 응답을 보냈지만 이제 Controller에서 요청/응답을 담당하도록 분리한다.
src/controller/home_controller.ts 생성

import { Request, Response } from 'express';
export const home = (req: Request, res: Response) => {
res.send('hello controller');
}
3. Router에서 Controller 연결
index_routes.ts 수정

import { Router } from 'express';
import { home } from '../controller/home_controller';
const router = Router();
router.get('/', home);
export default router;
기존에 라우터에서 바로 응답을 처리해줬던 것을 컨트롤러로 보내서 처리해준다.
http://localhost:3000 호출

잘 출력된다.
4. Service 계층 추가
controller에 로직을 다 넣으면 복잡해지므로 비즈니스 로직은 Service로 분기한다.
뭔가 자바에서 mvc패턴으로 개발했던 구조와 비슷해서 좋다.
src/service 디렉토리를 만들고 home_service.ts 를 생성한다.
참고로 index_routes, home_service 이렇게 스네이크식으로 네이밍을 짓는 이유는 나중에 프론트엔드 또한 node.js로 붙일건데 구조도 비슷하기에 네이밍에 구별을 두려고 했다.
프론트는 homeIndex 이렇게 카멜스타일로 하려고 한다.
src/service/home_service.ts

export const getHomeMessage = () => {
return 'hello service';
}
앞서 작성한 home_controller.ts 수정한다.

import { Request, Response } from 'express';
import { getHomeMessage } from '../service/home_service';
export const home = (req: Request, res: Response) => {
res.send(getHomeMessage());
}
컨트롤러에서 처리하던것을 서비스의 함수호출로 처리한다.
이제 Request, Response 구조는 아래처럼 진행된다.
Router -> Controller -> Service

localhost:3000 으로 확인해보면 service 까지 잘 온 것을 알 수 있다.
5. 에러 처리 미들웨어 추가
src/middleware 디렉토리 생성 error_middleware.ts 생성

import { Request, Response, NextFunction} from 'express';
export const errorHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction
) => {
console.error(err);
res.status(500).json({ message: 'Internal Server Error'});
}
에러 처리 전용 미들웨어를 작성했다.
그리고 index.ts를 수정한다.

...
import { errorHandler } from './middleware/error_middleware';
...
...
app.use(errorHandler);
...
위 두 줄을 추가해준다.
그리고 index_routes.ts에도 테스트용 에러 라우트도 추가해준다.

router.get('/err', () => {
throw new Error('test error');
});
방금 추가한 테스트용 라우트를 호출해본다.


localhost:3000/err 로 호출하면 화면에 응답과 콘솔에 로그를 확인할 수 있다.
6. 404(Not Found) 미들웨어 추가
존재하지 않는 경로에 대한 처리를 위해 404 미들웨어도 추가한다.
src/middleware/notfnd_middleware.ts 생성

import { Request, Response, NextFunction } from 'express';
export const notFound = (req: Request, res: Response, next: NextFunction) => {
res.status(404).json({ message: 'Not Found'});
}
그리고 역시 index.ts 모든 라우터 뒤에 등록한다.

...
import { notFound } from './middleware/notfnd_middleware';
...
...
app.use(notFound);
...
그리고 아무 url이나 입력해서 호출해보면

잘 확인할 수 있다.
7. 현재까지 전체 구조 정리
src
├─ controller
│ └─ home_controller.ts
├─ service
│ └─ home_service.ts
├─ routes
│ └─ index_routes.ts
├─ middleware
│ ├─ error_middleware.ts
│ └─ notfnd_middleware.ts
├─ index.ts
이제 기본적인 백엔드 뼈대는 어느정도 완성이고 이제 저 구조에서 필요한 기능마다 붙여가며 개발하면 된다.
다음 글에서는
- morgan 으로 HTTP 요청 로그 관리
- helme 으로 기본 보안 HTTP 헤더 자동 적용
- cors 설정으로 프론트엔드와 API 서버 분리 대비
환경세팅 거의 다 온 것 같다… 이제 조금만 더..