[Node.js] 바닐라 Node.js, Express, NestJS 서버 및 코드 비교
Node.js는 JavaScript를 이용해 백엔드 서버를 구현할 수 있는 강력한 런타임 환경이다. 기본적으로 내장된 http 모듈만으로도 서버를 구축할 수 있지만, 현업에서는 Express나 NestJS 같은 프레임워크를
lifewithcoding.tistory.com
지난번 글에서는 Node.js로 서버를 구축하는 한 방법으로, 내장 API(http 등)를 활용해 외부 라이브러리나 프레임워크 없이 서버를 만드는 방식을 소개했다. 이러한 방식은 Node.js의 동작 원리와 기본 구조를 이해하는 데 유용하며, 학습 목적으로 적합하다. 그러나 실무에서는 이 방식으로 서버를 구축하는 경우가 거의 없다. 내장 API 방식은 요청 파싱과 라우팅 과정에서 복잡한 분기 처리를 직접 구현해야 하며, 기본적인 작업을 직접 작성해야 하는 비효율성이 있다. 예를 들어, JSON 데이터 파싱, 쿼리 파라미터 처리, 요청 경로와 HTTP 메서드에 따른 분기 같은 작업은 매 요청마다 반복적으로 작성해야 하므로 코드가 쉽게 복잡하고 난해해진다.
더욱이, 인증, 데이터 검증, 로깅, 보안 설정 등 실무에서 필수적인 기능 역시 직접 구현해야 한다. 이러한 기능을 직접 구현해야 하므로 개발자는 많은 시간을 소모하게 되고, 이는 코드의 중복과 비효율적인 설계로 이어진다. 결과적으로, 이러한 방식은 유지보수성을 크게 떨어뜨리며, 프로젝트 규모가 커질수록 구조적인 문제와 비효율성이 누적되어 협업 환경에서도 큰 어려움을 초래한다.
다음은 순수 Node.js 내장 API만을 사용하여 간단한 REST API 서버를 구현한 코드이다. 이 코드는 요청과 응답 처리, 라우팅, JSON 데이터 파싱을 모두 직접 구현한 예제다.
const http = require('http');
const url = require('url');
const PORT = 3000;
const resources = []; // 전역 인메모리 변수라고 가정
const server = http.createServer((request, response) => {
const parsedUrl = url.parse(request.url, true);
const pathname = parsedUrl.pathname;
const method = request.method;
if (pathname === '/api/resources' && method === 'POST') {
let body = '';
request.on('data', (chunk) => (body += chunk));
request.on('end', () => {
try {
const resource = JSON.parse(body);
if (resources.some((r) => r.id === resource.id)) {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Duplicate resource ID' }));
return;
}
resources.push(resource);
response.writeHead(201, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ message: 'Resource created', resource }));
} catch {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Invalid JSON format' }));
}
});
return;
}
if (pathname === '/api/resources' && method === 'GET') {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(resources));
return;
}
if (pathname.startsWith('/api/resources/') && method === 'DELETE') {
const id = pathname.split('/').pop();
if (!id) {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'ID is required' }));
return;
}
const index = resources.findIndex((r) => r.id === id);
if (index !== -1) {
resources.splice(index, 1);
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ message: 'Resource deleted' }));
} else {
response.writeHead(404, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Resource not found' }));
}
return;
}
response.writeHead(404, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Not Found' }));
});
server.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
이 코드는 요청 경로와 메서드에 따라 분기 처리를 직접 구현하고, JSON 데이터를 파싱하여 메모리 데이터베이스에 저장하거나 삭제하는 간단한 REST API 서버이다. 그러나 요청이 많아지고 라우팅 로직이 복잡해질수록 이러한 방식은 코드의 가독성을 저하시킨다.
또한, Node.js의 내장 API는 인증, 로깅, 데이터 검증, CORS 설정과 같은 실무에서 필수적인 기능들을 기본적으로 제공하지 않기 때문에, 이러한 기능을 직접 구현해야 한다. 이는 코드의 가독성을 저하시킬 뿐 아니라, 팀 협업과 확장성에도 부정적인 영향을 미친다. 반면, Express나 NestJS와 같은 프레임워크는 이미 검증된 구조를 제공하며, 인증이나 데이터 검증 같은 기능을 미들웨어로 간단히 추가할 수 있어 개발 생산성과 효율성을 높인다. 에러 처리를 체계적으로 관리하거나, 비즈니스 로직과 요청/응답 로직을 분리하는 데에도 프레임워크가 훨씬 유리하다.
그럼에도 불구하고 순수 Node.js API를 사용하는 서버는 특정 상황에서 활용될 수 있다. Node.js의 기본 동작 원리를 학습하거나, 간단한 프로토타입 서버를 구축할 때, 또는 도커 컨테이너를 테스트하는 용도로 유용하게 사용할 수 있다. 하지만 실무에서는 프레임워크를 활용하여 더 효율적이고 유지보수성이 높은 구조를 만드는 것이 일반적이다. 따라서 이 코드는 Node.js 내장 API로 서버를 만들어 보았다는 데 의의를 두고, 실제로 활용하기보다는 학습과 실험의 목적으로 참고하면 좋을 것이다.
'🕸웹 > 🟨JavaScript' 카테고리의 다른 글
[Node.js] 바닐라 Node.js, Express, NestJS 서버 및 코드 비교 (0) | 2024.12.11 |
---|---|
[React] JSX 문법 한 번에 정리하기 (4) | 2024.12.02 |
npm 주요 명령어 정리 (0) | 2024.11.28 |
리액트 라우터 개념과 설정 방법 (1) | 2024.11.25 |
[Node.js] Node.js 생태계의 필수 도구 npm, npx, yarn 비교 (2) | 2024.11.17 |