미들웨어 (Middleware) 활용하기 🔗
미들웨어는 Express 애플리케이션의 "중간 처리 장치"와 같습니다. 클라이언트로부터 요청이 들어와서 라우트 핸들러가 그 요청을 처리하기 전까지, 또는 라우트 핸들러가 응답을 보낸 후에 거쳐가는 하나 또는 여러 개의 함수들을 말합니다.
마치 고속도로의 톨게이트처럼, 모든 차량(요청)은 목적지(라우트 핸들러)에 도달하기 전에 하나 이상의 톨게이트(미들웨어)를 통과해야 합니다. 각 톨게이트에서는 통행료를 징수하거나(로깅), 차량을 검문하고(인증), 화물을 싣는(데이터 추가) 등의 작업을 수행할 수 있습니다.
미들웨어 함수가 할 수 있는 일:
- 모든 코드를 실행할 수 있습니다.
- 요청(request) 및 응답(response) 객체를 변경할 수 있습니다.
- 요청-응답 주기를 종료하고 클라이언트에게 응답을 보낼 수 있습니다.
- 스택 내의 다음 미들웨어 함수를 호출할 수 있습니다.
1. 미들웨어의 기본 구조
미들웨어 함수는 기본적으로 req, res, 그리고 next라는 세 개의 매개변수를 가집니다.
function myMiddleware(req, res, next) {
// 어떤 작업 수행
console.log('미들웨어가 실행되었습니다.');
// 다음 미들웨어 또는 라우트 핸들러로 제어를 넘기려면 반드시 next()를 호출해야 합니다.
next();
}
- req: 요청 객체
- res: 응답 객체
- next: 다음 미들웨어 함수를 가리키는 콜백 함수. 만약 현재 미들웨어 함수가 요청-응답 주기를 종료하지 않는다면(예: res.send()를 호출하지 않는다면), 반드시 next()를 호출하여 다음 미들웨어로 제어를 전달해야 합니다. 그렇지 않으면 요청은 중간에 멈춰서 클라이언트는 아무런 응답도 받지 못하게 됩니다.
Java 개발자를 위한 비유: Express의 미들웨어는 Java 서블릿(Servlet) 환경의 **필터(Filter)**와 개념적으로 거의 동일합니다. 서블릿 필터의 doFilter(request, response, filterChain) 메소드에서 filterChain.doFilter(request, response)를 호출하여 다음 필터로 체인을 넘기는 것은, Express 미들웨어에서 next()를 호출하는 것과 완벽하게 같은 역할입니다. AOP(관점 지향 프로그래밍)의 Around 어드바이스와도 유사한 면이 있습니다.
2. 미들웨어의 종류 및 사용법
미들웨어는 적용되는 범위에 따라 여러 종류로 나눌 수 있습니다.
- 애플리케이션 레벨 미들웨어: app.use() 또는 app.METHOD()를 사용하여 Express 앱 객체에 바인딩됩니다. 모든 요청에 대해 실행되는 미들웨어를 등록할 때 주로 사용됩니다.위 코드에서 사용자가 어떤 경로로 요청하든, 항상 requestLogger가 먼저 실행되어 콘솔에 로그를 남깁니다.
-
JavaScript
const express = require('express'); const app = express(); // 간단한 로거(Logger) 미들웨어 const requestLogger = (req, res, next) => { console.log(`[${new Date().toLocaleString()}] ${req.method} ${req.url}`); next(); // 다음 미들웨어로 제어를 넘김 }; // 애플리케이션의 모든 요청에 대해 requestLogger 미들웨어를 사용 app.use(requestLogger); app.get('/', (req, res) => { res.send('Home Page'); }); app.listen(3000); - 라우터 레벨 미들웨어: express.Router() 인스턴스에 바인딩된다는 점을 제외하면 애플리케이션 레벨 미들웨어와 동일하게 작동합니다. 특정 라우터에 속한 경로들에만 미들웨어를 적용하고 싶을 때 유용합니다. (예: /admin 경로로 시작하는 모든 요청에 대해 관리자 인증 미들웨어 적용)
- 오류 처리 미들웨어: 이 미들웨어는 특별한 형태를 가집니다. (err, req, res, next)처럼 4개의 매개변수를 가집니다. err가 첫 번째 매개변수로 추가됩니다. Express는 4개의 매개변수를 가진 함수를 오류 처리 미들웨어로 인식하고, 다른 미들웨어에서 next(err)와 같이 에러 객체를 전달했을 때만 이 미들웨어를 호출합니다. 반드시 다른 app.use() 및 라우트 호출 다음에, 스택의 가장 마지막에 정의해야 합니다.
-
JavaScript
// ... 라우트 핸들러들 ... // 존재하지 않는 페이지에 대한 404 처리 미들웨어 app.use((req, res, next) => { res.status(404).send('Sorry, page not found!'); }); // 모든 서버 오류를 처리하는 미들웨어 (err 매개변수 존재) app.use((err, req, res, next) => { console.error(err.stack); // 에러 스택을 콘솔에 출력 res.status(500).send('Something broke!'); }); - 내장(Built-in) 미들웨어: Express에 기본적으로 포함된 미들웨어입니다. 가장 많이 사용되는 것들은 다음과 같습니다.
- express.json(): Content-Type: application/json 형태의 요청 본문(body)을 파싱하여 req.body에 담아줍니다.
- express.urlencoded({ extended: true }): Content-Type: application/x-www-form-urlencoded 형태의 요청 본문(HTML 폼 데이터)을 파싱하여 req.body에 담아줍니다.
- express.static('public'): public과 같은 특정 폴더에 있는 정적 파일(CSS, JS, 이미지 등)을 제공할 수 있게 해줍니다. 예를 들어, public/css/style.css 파일을 http://localhost:3000/css/style.css 주소로 접근할 수 있게 됩니다.
- 써드파티(Third-party) 미들웨어: NPM을 통해 설치하여 사용하는 외부 미들웨어입니다. 수많은 유용한 미들웨어들이 있습니다.
- morgan: HTTP 요청 로거 미들웨어. (위에서 직접 만든 requestLogger의 훨씬 강력한 버전)
- cors: CORS(Cross-Origin Resource Sharing) 문제를 해결해주는 미들웨어.
- helmet: 잘 알려진 웹 취약점으로부터 앱을 보호하기 위해 다양한 HTTP 헤더를 설정해주는 미들웨어.
- cookie-parser: 요청 쿠키를 파싱하여 req.cookies 객체에 담아주는 미들웨어.
내장 미들웨어 사용 예시:
const express = require('express');
const app = express();
// JSON 요청 본문을 파싱하기 위한 미들웨어
app.use(express.json());
// Form 데이터를 파싱하기 위한 미들웨어
app.use(express.urlencoded({ extended: true }));
app.post('/api/users', (req, res) => {
// 위 미들웨어 덕분에 req.body를 사용할 수 있습니다.
console.log('Received data:', req.body);
const username = req.body.username;
res.send(`User '${username}' created!`);
});
app.listen(3000);
express.json()과 같은 미들웨어를 사용하지 않으면, POST 요청의 본문(body)을 직접 스트림(stream)으로 받아 조각조각 합치는 복잡한 과정을 거쳐야 합니다. 미들웨어는 이 모든 과정을 추상화하여 개발을 매우 편리하게 만들어줍니다.
미들웨어는 Express의 심장과도 같은 개념입니다. 요청 처리 흐름에 원하는 기능을 자유롭게 끼워 넣을 수 있는 미들웨어의 특성을 잘 이해하고 활용하면, 코드의 재사용성을 높이고 애플리케이션을 매우 체계적으로 구조화할 수 있습니다.
---------------------------------------------------------404 미들웨어 내용 관련 추가 설명
Express.js는 **미들웨어(middleware)**를 순차적으로 실행하는 방식으로 동작합니다. 따라서 라우트 핸들러들 다음으로 404 처리 미들웨어가 위치하면, 앞선 어떤 라우트에도 해당하지 않는 모든 요청이 이 미들웨어로 도달하게 됩니다.
동작 원리
- 순차적 실행: 사용자가 서버에 요청을 보내면 Express는 코드가 작성된 순서대로 미들웨어와 라우트 핸들러를 하나씩 확인합니다.
- 라우트 매칭: app.get('/'), app.post('/users') 등과 같이 정의된 라우트(주소)와 요청된 주소가 일치하는지 검사합니다.
- 일치하는 라우트가 없을 경우: 만약 요청된 주소에 해당하는 라우트가 하나도 없다면, Express는 계속해서 다음 미들웨어로 제어권을 넘깁니다.
- 404 미들웨어 실행: 코드의 맨 마지막 부분에 위치한 app.use((req, res, next) => { ... });는 특정 경로가 지정되어 있지 않으므로, 위에서 처리되지 않은 모든 요청을 받게 됩니다. 이 미들웨어는 "catch-all" (모두 잡는) 역할을 하는 셈입니다.
- 404 응답 전송: 이 미들웨어는 res.status(404).send('Sorry, page not found!'); 코드를 실행하여 클라이언트에게 "페이지를 찾을 수 없음"을 의미하는 404 상태 코드와 메시지를 보냅니다.
코드 흐름 요약
// 1. /about 페이지 요청이 오면 이 핸들러가 실행됨
app.get('/about', (req, res, next) => {
res.send('About page');
});
// 2. /users 페이지 요청이 오면 이 핸들러가 실행됨
app.get('/users', (req, res, next) => {
res.send('Users page');
});
// ... 다른 라우트 핸들러들 ...
// 3. 만약 요청이 /about 이나 /users가 아닌,
// 예를 들어 /contact 와 같이 정의되지 않은 주소로 왔다면,
// 위의 모든 라우트를 통과하고 이 미들웨어에 도달함.
app.use((req, res, next) => {
// 4. 따라서 404 응답을 보냄.
res.status(404).send('Sorry, page not found!');
});
// 5. 서버 내부 오류 처리 미들웨어 (이전 미들웨어에서 next(err)가 호출될 때만 실행됨)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
결론적으로, 정의된 라우트 중 어느 것과도 일치하지 않는 요청이 순서에 따라 마지막에 위치한 404 처리 미들웨어로 "떨어지기(fall through)" 때문에 404 응답을 보낼 수 있는 것입니다.
'프로그래밍 > NODEJS 강좌 BY GEMINI' 카테고리의 다른 글
| 정적 파일 제공하기 (CSS, JavaScript, 이미지) 🖼️ (0) | 2025.06.23 |
|---|---|
| 템플릿 엔진 사용하기 (선택 사항) 🎨 (1) | 2025.06.18 |
| 라우팅 (Routing) 정의하기 🚦 (1) | 2025.06.12 |
| 5️⃣ Express.js로 강력한 웹 애플리케이션 구축하기 🌐 (1) | 2025.06.10 |
| Async/Await: 동기 코드처럼 비동기 코드 작성하기 MAGIC ✨ (1) | 2025.06.10 |