미들웨어

 

  • 미들웨어는 서버의 요청 ( Request ) - 응답 ( Response ) 과정에서 중간에 위치해 특정 기능을 수행하는 함수라고 볼 수 있다.
  • 예를 들어, 모든 요청에 대해 로그를 남기거나, 특정 사용자만 API를 접근하게 하고 싶을 경우에도 미들웨어를 사용한다.

 

Express.js의 미들웨어 

// 전역으로 미들웨어 등록
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
  • urlencoded : form-urlencoded 라는 규격의 body 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어
  • json : JSON 규격의 body 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어

 

Express.js에서 미들웨어 작성

app.use((req, res, next) => {
  // 필요한 코드
});

위 형태에서 req, res, next는 각자 역할을 지니고 있는 인자다

  • req : 요청 ( Request )에 대한 정보가 담겨있는 객체다. ( HTTP Headers, Query Parameters, URL 등 브라우저가 서버로 보내는 정보들이 담겨 있다. )
  • res : 응답 ( Response )을 위한 기능이 제공된다. ( 어떤 HTTP Status Code로 응답 할지, 어떤 데이터 형식으로 응답 할지, 헤더는 어떤 값을 넣어 응답 할지 다양한 기능을 제공한다. )
  • next : 다음 스택으로 정의된 미들웨어를 호출한다.

 

미들웨어 작성해보기

app.use((req, res, next) => {
    console.log('Request URL:', req.originalUrl, ' - ', new Date());
    next();
});
  • 위 코드를 작성하고 서버를 실행한 후 메인페이지로 접속하면 터미널에 로그가 남겨지는 것을 확인할 수 있다.

 

미들웨어 동작 방식

다음 미들웨어로 진행하지 않고 중간에 응답할 경우의 상황

 

  • 미들웨어는 어디에 위치시키느냐에 따라 다르게 동작한다. 일반적으로는 어플리케이션에 등록된 순서대로 실행된다.
  • 여러개의 미들웨어가 겹치는 경우, 이는 첫번째 미들웨어부터 순차적으로 진입한다.
app.use((req, res, next) => {
    console.log('첫번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('두번째 미들웨어');
    next();
});

app.use((req, res, next) => {
    console.log('세번째 미들웨어');
    next();
});

// print: 첫번째 미들웨어
// print: 두번째 미들웨어
// print: 세번째 미들웨어

 

예를 들어

  • 위 그림처럼 순차적으로 미들웨어를 통과하고 중간에 응답을해 종료가 되는 경우
  • 위 코드처럼 순차적으로 다음 미들웨어로 넘어가는 경우
  • 중간에 next()를 실행하지 않으면 다음 미들웨어는 실행되지 않는다.
  • 당연히 현재 미들웨어에서 응답을 보내는 경우, 즉 res.send() 나 res.json() 등의 메서드를 호출하는 경우 next()를 호출하면 안된다. 이렇게 하지 않으면 이미 요청이 종료된 상태에서 다른 미들웨어가 응답을 보내려고 해서 중복된 요청이 전달되는 문제가 발생한다.

 

라우터와 미들웨어 차이

  • 라우터와 미들웨어는 서로 다른 방식처럼 보이지만 라우터는 미들웨어 기반으로 구현된 객체라서 미들웨어와 동일한 방식으로 작동된다.
  • 라우터는 미들웨어 함수를 특정 경로에 바인딩하는 역할을 하고, 요청이 들어온 URL 경로에 따라 서로 다른 미들웨어를 실행시킬 수 있게 도와준다.
app.use(Middleware) : 모든 요청에서 미들웨어가 실행된다.
app.use('/api', Middleware) : /api로 시작하는 모든 요청에서 미들웨어를 실행한다.
app.post('/api', Middleware, (req,res)=>{}) : /api로 시작하는 POST 요청에서 미들웨어를 실행한다.

 

Request, Response

Request

  • Request란 클라이언트가 서버에게 전달하려는 정보나 메세지를 담은 객체를 의미한다.
  • Request의 세부 사항에는 URL, http method, 헤더( header ), 쿼리 파라미터 ( query parameter ), 바이 데이터 ( body data ) 등이 포함 된다.

 

Response

  • Response란 서버에서 클라이언트로 응답 메세지를 전송시켜주는 객체다.
  • Response의 세부 사항에는 상태 코드 ( status code ), 응답 데이터 ( response data ), 응답 헤더 ( response header ) 등이 포함된다.

서버 모듈

  • Node.js의 서버 모듈에는 대표적으로 http 모듈과 Express.js가 존재한다. ( http 모듈은 Node.js에서 기본 제공하는 http 서버 모듈이다 )
  • Express.js는 http 모듈을 확장해 제공한다.
  • Express.js는 기존 http 모듈의 메서드도 사용할 수 있지만, Express.js가 추가 제공하는 메서드나 속성들을 사용할 수 있다.

 

Express.js 통신 흐름

  • 클라이언트는 특정 URL과 데이터를 담은 요청 ( Request )을 서버에 전송한다.
  • 서버는 받은 데이터에 따라 필요한 비즈니스 로직을 수행한다.
  • 서버는 처리된 결과를 클라이언트에게 응답 ( Response )으로 보내준다.

 

예제 ) 

 

app.js

// app.js

import express from 'express';
import goodsRouter from './routes/goods.js';
import newsRouter from './routes/news.js';

const app = express();
const PORT = 3000; // 서버를 열 때 사용할 포트 번호

app.get('/', (req, res) => {
  res.send('Hello World!');
});

// 1. Express.js의 서버를 엽니다.
app.listen(PORT, () => {
  console.log(PORT, '포트로 서버가 열렸어요!');
});

// localhost:3000/api -> goodsRouter
// localhost:3000/api -> newsRouter
// 2. 라우터를 등록 합니다.
// app.use 미들웨어를 사용해 /api로 접속하면 goodsRouter와 newsRouter를 연결(등록)한다 
app.use('/api', [goodsRouter, newsRouter]);

 

1. 서버시작

 app.listen을 통해 3000 포트로 웹 서버를 연다.

 

2. 라우터 등록

 app.use 미들웨어를 사용해 /api 주소에 goodsRouter와 newRouter를 연결(등록)한다.

 

news.js

// routes/news.js


import express from 'express';

const router = express.Router();

// req : 클라이언트가 전달한 Path Params
// res : 서버가 클라에게 전달할 데이터 ( json 형태로 전달 )

/** 뉴스 목록 조회 API **/
// 3. HTTP Method와 URL을 지정한 API를 정의합니다.
// 만약, localhost:3000/api/news 라는 URL로 GET 요청이 들어온다면 해당 코드를 실행합니다.
router.get('/news', (req, res) => {
  // 4. 사용자의 요청에 맞는 데이터를 반환합니다.
  return res // Express.js의 res 객체를 반환합니다.
    .status(200) // API의 상태 코드를 200번으로 전달합니다.
    .send('뉴스 목록 조회 API 입니다.'); // API의 결과값을 '뉴스 목록 조회 API 입니다.'로 전달합니다.
});

/** 뉴스 세부 조회 API **/
// 3. HTTP Method와 URL을 지정한 API를 정의합니다.
// 만약, localhost:3000/api/news/:newsId 라는 URL로 GET 요청이 들어온다면 해당 코드를 실행합니다.
router.get('/news/:newsId', (req, res) => {
  // 클라이언트가 전달한 Path Params 데이터를 받아옵니다.
  const params = req.params;

  // Path Params 데이터 중 newsId를 추출합니다.
  const newsId = params.newsId;

  // 서버 콘솔에 클라이언트가 전달한 newsId를 출력합니다.
  console.log('클라이언트로 부터 전달받은 뉴스 ID:', newsId);

  // 4. 사용자의 요청에 맞는 데이터를 json 형태로 반환합니다.
  return res.status(200).json({
    data: '뉴스 세부 조회 API 입니다.',
  });
});

// Express 라우터를 외부로 전달합니다.
export default router;

 

3. API 정의 

등록된 각 라우터를 순서대로 검토하고, HTTP Method와 URL이 일치하는 함수를 실행한다.

 -URL /api/news로 GET 요청이 들어오면 router.get('/new' ~ )을 실행한다.

   

 -URL /api/news/:newsId로 GET 요청이 들어오면 router.get('/new/:newsId' ~)을 실행한다.

  • 여기서 중요한 부분은 :newId에 해당하는 경로 변수다. 이는 req.params을 통해 들어온 변수를 읽어들일 수 있다. 
  • 위 코드에서는 req.params을 통해 들어온 변수를 params에 저장하고 params.newsId를 통해 해당 변수를 읽어오고 출력해주는 형태다

4. 결과 반환

API에서 모든 로직 처리가 완료된 후, 클라이언트에게 결과를 전달한다.

 

 

 

'Javascript' 카테고리의 다른 글

[Javascript][Node.js] Mongo DB  (0) 2024.09.02
[Javascript][Node.js] Express.js - req, res 객체  (0) 2024.08.30
[Javascript] API Client ( Insomnia API Client )  (0) 2024.08.30
[Javascript] API  (0) 2024.08.29
[Javascript] Module  (0) 2024.08.29

Node.js 의 구조

 

Node.js는 "Javascript를 브라우저가 아닌 컴퓨터에서 브라우저 없이 실행하게 도와주는 환경"을 말한다.

이는 Core Library와 V8 Engine, 그리고 libuv라는 라이브러리를 통해 가능해진다.

 

V8 Engine은 구글이 개발하여 구글 크롬 브라우저에서 사용하는 Javascript Engine이다.

Node.js에서 이 Engine을 활용해서, 브라우저 환경 이외에서도 Javascript를 사용할 수 있게 되었다.

 

libuv는 Node.js가 비동기 I/O 작업을 수행할 수 있게 해주는 중요한 라이브러리다.

이 라이브러리 덕분에 Node.js는 논 블로킹 I/O 모델이라는 특징을 가지게 되었다.

 

이 두가지 주요 구성 요소인 V8 Engine과 libuv는 C와 C++ 언어로 작성되어 있어서, Node.js 사용자가 Javascript으로 작성하면 Node.js Bindings을 통해 c++로 변환되어 실행된다.

 

Node.js는 대표적으로 논 블로킹( Non-blocking ) I/O, 싱글 스레드, 이벤트 루프의 특성이 있다.

Node.js는 싱글 스레드로 동작하나, I/O 작업이 발생한 경우 이를 비동기적으로 처리해 여러 작업을 동시에 처리할 수 있게 한다.


호출 스택

콜스택, 이벤트 루프

 

Javascript는 코드를 실행하며 호출 스택 ( Call Stack )에 함수를 추가하고 함수가 완료되면 호출 스택에서 제거한다.

이는 비동기 작업에서 문제가 되는데, 특히 네트워크 요청과 같이 시간이 많이 걸리는 작업을 기다리는 동안

Javascript는 다른 어떠한 작업도 처리할 수 없게 된다. 이 문제를 해결하기 위해 Javascript는 이벤트 루프와 이벤트 큐를

사용하게 된다.

 

이벤트 루프

이벤트 루프

 

이벤트 루프는 여러 이벤트들과 같은 비동기 작업들을 모아서 관리하고, 어떤 순서대로 실행해야하는지 도와주는 도구다.

 - 이벤트 루프는 호출 스택과 이벤트 큐를 관찰하며, 호출 스택이 비어있고, 이벤트 큐에 작업이 있으면, 이벤트 큐의 작업을 호출 스택으로 이동하는 역할을 담당한다.

 - 이벤트 루프를 활용한다면, 자바스크립트는 시간이 오래 걸리는 작업을 이벤트 큐에 넣어 비동기적으로 처리하고, 그 동안 호출 스택에서 다른 작업들을 계속 처리할 수 있다.

 

이벤트 루프 동작 방식 코드로 살펴보기

function firstFunction() {
  console.log('firstFunction 입니다.');
  secondFunction();
}

function secondFunction() {
  // 2 초간 기다린다.
  setTimeout(function () {
    console.log('secondFunction 입니다.');
  }, 2000);
}

firstFunction();
console.log('전역 코드 실행 중!');

// print: firstFunction 입니다.
// print: 전역 코드 실행 중!
/** 2 초간 기다린다. **/
// print: secondFunction 입니다.

 

위 코드에서 firstFunction은 호출 스택에 추가되고, 실행되며 'firstFunction 입니다.'를 콘솔에 출력하고 secondFunction을 호출 한다. 그 다음 secondFunction은 호출 스택에 추가되고, setTimeout 함수를 실행한다.

setTimeout은 비동기 함수이므로, Javascript는 이 작업을 이벤트 큐에 넣고 secondFunction을 호출 스택에서 제거한다.

그 다음 firstFunction도 호출 스택에서 제거하고, '전역 코드 실행 중!'을 콘솔에 출력한다.

2초가 지난 후, 'secondFunction 입니다.'를 출력하는 작업이 이벤트 큐에서 호출 스택으로 이동하고 실행된다. 이때 호출 스택은 비어 있기 때문에 이벤트 루프가 이 작업을 호출 스택으로 이동시킬 수 있었다.

이처럼, 이벤트 루프와 이벤트 큐를 사용함으로써 Javascript는 비동기 작업을 처리할 수 있게 된다.

'Javascript' 카테고리의 다른 글

[Javascript] Module  (0) 2024.08.29
[Javascript] Express.js 프레임 워크  (0) 2024.08.28
[Javascript] 자료형  (0) 2024.08.24
[Javascript] 화살표함수  (0) 2024.08.22
[Javascript] 프로토타입 ( Prototype )  (0) 2024.08.22

+ Recent posts