본문 바로가기
Back-end/기초부터 따라하는 nest.js

시즌 2 #2. 기초부터 따라하는 Nest.js 2 : Express 배우기(1)

by hsloth 2024. 5. 11.

 

지난 시간에는 HTTP Method에 대해서 배웠습니다.

https://suloth.tistory.com/196

 

시즌 2 #1. 기초부터 따라하는 Nest.js 2 : HTTP 메소드

이전 시간에는 간단하게 서버와 클라이언트에 대해서 다뤄봤습니다.https://suloth.tistory.com/191 시즌 2 #0. 기초부터 따라하는 Nest.js 2 : Orientation조금 더 디테일하게 따라할 수 있는 Nest.js 튜토리얼

suloth.tistory.com

 

이번 시간에는 Nest.js를 배우기 전에, 간단하게 Express.js 라고 불리는 프레임워크에 대해서 배워보려고 합니다.


왜 Nest.js를 바로 안 배우고 Express.js를 배우나요?


먼저, 대부분의 사람들이 node.js 백엔드 개발이라고 하면 Express.js를 먼저 떠올립니다. 프레임워크 이름이 Express.js인줄도 모르고 node.js 백엔드라고 하면 당연하게 사용하는 것이 Express.js 이죠.

즉, Node.js = Express.js 라는 인식이 깔려있습니다.

그만큼 Express.js라는 프레임워크는 Node.js 생태계에서 오랫동안 사용되어 왔고, 여전히 인기있는 프레임워크입니다.

 

그리고 우리가 사용할 Nest.js 또한 Express.js 기반이기 때문에(fastify 기반 Nest.js도 있습니다) 기본적인 개념과 동작 방식에 대해 알아두는 것이 좋습니다.

또한 만약에 Node.js 분야로 일을 하게 된다면, 현재는 현업에서 Nest.js보다 Express.js의 코드를 마주칠 확률이 더 높습니다. (Nest.js로 바꾸는 추세긴합니다만, 아마 Nest.js 개발자를 뽑는 회사에 가서도 Express.js 코드를 Nest.js로 옮긴다거나 하는 등의 일이 필요할 수도 있습니다)

 


Express.js와 Nest.js의 차이점


간단하게 말하면 Express.js는 자유롭습니다. 자유롭게 코드를 작성할 수 있어서 러닝커브가 낮아 빠르게 배울 수 있습니다. 하지만, 너무 자유로워서 개발자마다 코드가 재각각이 될 수 있습니다. 프로젝트의 구조나 함수, 클래스의 사용이 다 재각각일 수 있다는 거죠. 그래서 남의 코드를 읽을 때 많은 시간이 걸리고 적응하는데 오랜 기간이 걸릴 수 있습니다.

그리고 단점을 하나 더 추가하자면, 개인적으로 타입스크립트 생태계와 잘 맞지 않습니다. Express.js는 res라는 객체를 통해 언제, 어디서든 Client에게 응답을 리턴할 수 있고, next() 라는 함수로 언제든지 미들웨어라는 것을 왔다갔다 할 수 있습니다. 그래서 타입스크립트로 오류를 방지하기에 조금 어려움이 있습니다. 이 말은 제 개인적인 견해일 뿐이니 흘려들어주세요.

 

Nest.js는 정형화된 틀이 있습니다. 코드를 작성하는 방식 자체를 Nest.js 자체에서 "이렇게 해라" 라고 이미 만들어져 있죠. 그래서 개발을 할 때, Nest.js가 미리 만들어놓은 여러가지 함수, 클래스를 사용해서 정형화된 틀안에서 코드를 작성하게 됩니다. 대신, 깊고 복잡한 이해가 필요한 코드의 경우 정형화된 틀을 부숴야할 수도 있을 텐데, 그럴 경우 다루기가 조금 복잡하다는 단점이 있습니다. 하지만 대부분의 경우는 이러한 틀을 벗어나지 않습니다.

Nest.js는 정형화된 만큼, 처음에 러닝커브가 높아서 배우기가 까다롭습니다. Nest.js가 정한 규칙과 사용 방법 등을 익혀야 하거든요. 대신 한 번 익혀두면 틀이 정해져 있어서 코드를 작성하기 수월하고, 다른 개발자들의 코드들도 비교적 쉽게 이해할 수 있다는 것이 장점입니다.

 

정리하자면,

Express.js

장점

  • 자유로움
  • 규칙에 얽매이지 않음
  • 빠른 학습 가능

단점

  • 너무 자유로워서 개발자 마다 코드가 재각각이 될 수 있음
  • 타인의 코드를 이해하는데 오랜 시간이 걸릴 수 있음
  • (개인적인 생각)타입스크립트 생태계와 맞지 않음

 

Nest.js

장점

  • 정형화된 틀이 있어서 한 번 배우면 코드를 작성하기 수월함
  • 타인의 코드를 비교적 쉽게 이해할 수 있음

단점

  • 정형화된 형식을 배워야하기 때문에 배우는데 시간이 오래 걸림
  • 정형화된 형식 외의 상황을 마주하면 처리하기가 힘듬

 


Express.js 시작


우리는 Express.js를 간단하게만 해보려고 합니다. 단순히 Express.js를 사용하는 법만을 알려주는 것이 아닌, 처음 무언가를 배울 때 어떤 자세로 임하고 어디서부터 시작해야하는지를 배우게 될 겁니다.

 

일단, 모든 배움의 시작은 "공식문서"입니다. 구글에 "express.js" 를 검색해서 express.js 공식문서로 들어가봅시다.

그리고 상단에 보면 "시작하기"라는 탭이 있을텐데 클릭해서 하나씩 해봅시다.

https://expressjs.com/ko/starter/installing.html

 

Express 설치

설치 Node.js가 이미 설치되었다고 가정한 상태에서, 애플리케이션을 보관할 디렉토리를 작성하고 그 디렉토리를 작업 디렉토리로 설정하십시오. $ mkdir myapp $ cd myapp npm init 명령을 이용하여 애플

expressjs.com

 

먼저 하나씩 따라해봅시다.

일단, 개발을 할 거니까 터미널은 필수겠죠?

각자 OS에 맞는 터미널을 열어줍시다. (윈도우는 terminal, 맥북은 terminal 혹은 iterm)

명령어를 잘 모르겠다구요? 아래 포스팅을 봐줍시다. 저는 맥북을 사용하지만, 최대한 윈도우 운영체제 입장에서 이해할 수 있도록 포스팅을 작성해보려고 합니다.

https://suloth.tistory.com/44

 

#0-1. 기초부터 따라하는 nest.js : 터미널 키는 법 + 터미널에서 작업 폴더 이동

윈도우 윈도우는 윈도우+R 버튼을 누른 후, cmd 를 입력하여 터미널을 킵니다. 혹은 윈도우 버튼을 눌러서 검색창에 cmd를 검색하면 터미널이 나올텐데 그걸 실행시켜주시면 됩니다. Mac OS Mac의 경

suloth.tistory.com

 

 

 

 

먼저 설치입니다.

터미널을 켜고 cd Desktop 명령어를 이용해서 바탕화면으로 이동해줍시다.

# mac
cd Desktop
# 혹은
cd ~/Desktop

# window
cd C:\사용자이름\Desktop
# 혹은
cd C:/사용자이름/Desktop

윈도우에서는 /를 쓰는 터미널도 있고, \를 사용하는 터미널도 있기 때문에 알아서 입력해주시기 바랍니다. 앞으로는 그냥 /로 통일하겠습니다.

 

여기서부터 Express.js 문서를 따라해줍시다.

# myapp 라는 폴더 생성
mkdir myapp

# myapp 폴더로 이동
cd myapp

 

myapp이라는 폴더를 만들어 myapp 폴더로 이동을 하고

 

# -y 옵션을 설정하면 부가적인 질문을 피할 수 있습니다
npm init -y

 

-y 옵션을 줘서 npm init 명령어를 실행합니다.

 

그리고 express를 설치해줍니다.

# express 설치
npm install express

 

 

마지막으로 vscode 까지 실행시켜줍시다.

# 해당 폴더에서 vscode 실행
code .

 

만약 위 명령어가 실행이 안된다면, vscode code 명령어 라고 구글에 검색해서 방법을 찾거나, 아니면 그냥 vscode를 실행해서 폴더를 선택해서 직접 열어주면 됩니다.

 


NPM 이란?


 

터미널에 명령어를 입력할 때, npm이라는 명령어를 사용해서 express를 다운로드 받았습니다. 그렇다면 npm은 뭘까요?

npm은 Node Package Manager라는 뜻으로 node.js에서 패키지(라이브러리)들을 관리해주는 녀석입니다.

node.js 생태계의 개발자들은 모두 npm에 자신만의 패키지(라이브러리)를 만들어서 업로드할 수 있으며, 해당 패키지를 누구나 npm에서 다운로드 받아서 이용할 수 있습니다.

 

npm init

npm init 명령을 입력하면, 해당 프로젝트는 이제 npm에서 패키지를 다운로드 받을 수 있는 상태가 됩니다.

그리고 package.json과 package-lock.json 파일이 생성되는데, 이는 패키지를 관리하기 위해서 존재하는 파일들입니다.

간단히 말하자면, package.json은 현재 당신이 개발중인 프로젝트 이름과 버전, 의존성(어떤 패키지, 라이브러리들이 사용되는지, 버전은 몇이 필요한지)을 명시하여 프로젝트를 보다 안정적으로 관리할 수 있게 해줍니다.

package-lock.json은 패키지들의 의존성 트리를 기록합니다. package.json 만으로는 부족한 패키지의 버전 정보들을 기록하여 의존성을 더 정확히 관리할 수 있게 해주는 녀석이라고 일단 생각해둡시다.

 

 


Hello World


이제 다시 Express.js 공식문서로 돌아와서, 시작하기 -> Hello world를 봐봅시다.

 

간단합니다. 글을 읽고 따라해봅시다.

 

먼저, myapp 폴더에 app.js라는 파일을 만든 뒤 아래 코드를 추가하라고 합니다. 따라합시다.

vscode에서 app.js 파일을 만든 뒤, 코드를 따라 쳐줍시다.

const express = require("express");
const app = express();
const port = 3000;

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

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

 

그리고 터미널에서 node app.js 명령을 실행시켜주면

node app.js

 

vscode 터미널에서 실행시켜도 좋습니다. (vscode를 켜고 ctrl+shift+` 을 누르면 터미널이 켜지는 걸로 알고 있습니다)

하지만... 개발자라면 그냥 터미널을 사용하는게... ㅎㅎ 일반적인 터미널을 이용하는게 보편적으로 많이 쓰이니까 더 좋은 것 같습니다.

 

이후, 브라우저에서 http://localhost:3000 을 주소창에 입력하면, 다음과 같은 화면이 뜰겁니다.

 

어때요. 나름 신기하죠?

 

그러면 코드를 해석해보겠습니다.

const express = require("express")

 

express 패키지를 express라는 변수에 담습니다.

만약에, require를 모른다면... 자바스크립트 문법을 다시 배우고 글을 읽는 것을 추천합니다.

 

const app = express()

 

express 서버를 실행(함수를 실행) 시키고 app이라는 변수에 담습니다. 즉, app = 실행중인 express 서버가 되는 겁니다.

 

개념이 살짝 헷갈릴 수 있지만 일단, 백엔드 서버 = 사용자 요청을 받기 위해 while문을 돌고 있는 코드 라고 생각합시다.

이래도 헷갈리면 그냥 app = express 서버 라고 생각만 해둡시다.

 

const port = 3000

서버를 열기 위한 포트를 지정해줍니다.

port = 항구 라는 뜻이죠.

컴퓨터는 모든 요청을 포트를 통해서 받을 수 있습니다. 컴퓨터 = 섬나라 라고 생각하면, 섬나라에서 다른 나라로 가기 위해서는 무조건 항구를 통해서 들어오고, 나갈 수 있죠? (배 외에 이동수단이 없다고 생각하면 말입니다 ㅎㅎ.. 비행기 같은건 생각하지 마시구요)

그래서 컴퓨터 또한 무조건 포트를 통해서 요청을 주거나 요청을 받을 수 있습니다. 그리고 이 포트에는 번호들이 있구요.

 

그래서 여기에서는 3000번 포트를 통해 요청을 받을 수 있도록 미리 port라는 변수이 3000이라는 수를 할당해둔 것입니다.

 

app.get("/", (req, res) => {
  res.send("Hello world");
}

 

해당 코드를 작성하면

/ 라는 경로로 get 요청이 들어오면 (req, res) => { res.send("Hello world"); } 라는 함수가 실행되도록 합니다.

get함수 이외에도 post, patch, put, delete 등 다양한 HTTP 메소드를 위한 함수를 사용 가능합니다.

 

백엔드 서버 = while문을 돌고 있는 코드 라고 아까 위에서 이야기 했죠? while문을 계속해서 돌면서, 사용자가 get 요청을 하면 바로 app.get("/",  ...)  이 함수를 실행하여 결과 값을 사용자에게 리턴해준다고 보면 됩니다. (태클 걸고 싶은 부분이 있을지도 모르지만, 일단은 이렇게 생각합시다)

 

여기서 req, res는 각각 request와 response를 말합니다.

req = 사용자 요청입니다. req라는 변수에는 사용자 요청에 대한 모든 것이 담겨있습니다.

res = 응답입니다. res라는 변수를 이용해서 사용자에게 응답을 전달해줄 수 있습니다.

따라서 res.send("Hello world") 라는 코드는, 사용자에게 "Hello world" 라는 문자열을 전달해주는 코드입니다.

 

그래서 우리가 http://127.0.0.1:3000/ 이라는 url 주소를 브라우저에 입력했을 때, Hello world가 브라우저에 나타났던 것입니다.

그렇다면 여기서 "어? 그렇다면 url 주소에 / 만 있는게 아니라 http://127.0.0.1:3000 도 있는데 그건 뭐죠?" 라고 생각이 들 수 있습니다. 

그냥 단순히 생각할 때, 서버는 포트번호까지 인식을 못한다고 보면 됩니다. 즉, http://127.0.0.1:3000까지는 서버에서 모르는 정보라는 것이고, 3000 뒤에 / 부터 인식한다고 생각합시다.

"어 그러면 맨 뒤에 / 안붙이고 http://127.0.0.1:3000 까지만 주소창에 입력해도 Hello world가 뜨던데 그건 어떻게 된 거죠? / 가 없잖아요" -> 이 부분은 그냥, 브라우저가 알아서 뒤에 / 를 붙여서 요청을 보낸다고 생각하시면 됩니다. 사용자 편의를 위해 굳이 맨 뒤에 / 를 붙이지 않아도 브라우저가 / 를 붙여서 요청을 보내준다고 생각합시다.

"그러면 우리가 평소에 사용하는 구글 같은 서버 주소는 포트번호가 없는데 어떻게 된 건가요?" -> 구글 같이 ip주소가 아닌 영어로된 url 주소가 들어가는 것은 포트가 감춰져 있습니다. 기본적으로는 80번 혹은 443번 포트를 사용합니다.

 

 

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

아까 지정해둔 3000번 포트로 서버를 열겠다라는 뜻입니다. 보통 서버가 요청을 듣는다. 라고 해서 listen이라는 함수를 사용합니다.

그리고 서버가 열리면, Example app listening on port 3000 이라는 문자열을 터미널에 출력합니다.

 

여기까지가 기본중의 기본입니다. 알고자 하는 기술이 있으면, 일단 공식문서에 들어가서 그 중에 기초가 되는 부분부터 하나씩 천천히 따라하는 것입니다. 그리고 챕터를 하나씩 따라가면 됩니다.

본 포스팅에서는 Express.js를 속성으로 가르치는 것을 목표로 하기 때문에, 간단하게만 공식문서 보고 따라해보았습니다.

여유가 된다면, "시작하기"의 "Express 생성기" 부분 부터는 직접 한 번 해보도록 합시다.


Router


Express에는 Router라는 개념이 있습니다. Express뿐만 아니라 다른 다양한 프레임워크들도 이러한 개념이 있죠.

Router의 사전적 정의는 "서로 다른 네트워크를 연결해주는 장치"입니다. 

우리가 url을 입력할 때, / 로 경로를 구분하는데 이런 경로들을 이어주는 녀석입니다.

 

조금 쉽게 이야기하면, Route라는 단어의 뜻은 "길"입니다.

서버에서 말하는 "길"은 url 경로를 의미합니다. (경로 = 길)

그리고 Router라는 녀석은 "길을 연결해주는 장치"라는 뜻으로 해석할 수 있습니다.

따라서 결론적으로 Router = url 경로를 연결해주는 장치입니다.

 

아직 잘 감이 안오죠? 코드를 보면서 하나하나 해봅시다.

일단, 다음과 같이 폴더구조를 만들어줍시다.

routers라는 폴더를 만들고, 그 아래에 post.router.jsuser.router.js 파일을 만들었습니다.

 

먼저, user.router.js 파일로 가서 아래와 같이 코드를 작성해봅시다.

우리는 /user 경로로 들어오면, user.router.js파일을 타도록 하려고 합니다. (정확히 말하면 user.router.js 파일의 router 변수)

// user.router.js
const router = require("express").Router();

router.get("/", (req, res) => {
  res.send("User Router GET /user");
});

module.exports = router;

 

express에서 Router를 불러와서 router라는 변수에 담습니다.

그리고 router를 이용해서 "GET /" 이라는 경로를 연결해줍니다.

이제, 해당 router로 들어오는 GET / 요청은 모두 User Router GET /user 라는 응답을 받게 됩니다.

그리고 module.exports = router 를 통해 router를 export 해줍시다.

 

하지만, 아직 router라는 변수가 express 서버와 연결 되지는 않았습니다.

연결해주려면 어떻게 해야될까요?

힌트는 app 변수입니다. 제가 아까 app이라는 변수 = express 서버 라고 했죠?

따라서, router를 app에 추가를 해주면 express 서버와 연결이 되는겁니다.

그러면, 어떤 파일에서 작업을 해야할까요? 바로 app.js 파일입니다. app.js 파일에 app 변수가 있잖아요.

 

app.js 파일에 다음 코드를 추가해줍니다. app.listen 위에 추가해야합니다. app.listen 다음에 추가하면 동작하지 않습니다.

const userRouter = require("./routers/user.router");

app.use("/user", userRouter);

 

require문을 통해서 user.router.js에서 정의한 라우터를 가져와 userRouter 변수에 담습니다.

그리고 app.use 함수를 통해서 /user 경로로 요청이 들어오면 userRouter를 거치도록 합니다.

따라서 /user/ 라는 주소로 요청이 들어오면 User Router GET /user 라는 문자열을 받을 수 있습니다.

 

그러면 한 번 봐볼까요? 터미널에서 서버를 실행시켜줍시다.

node app.js

 

그리고 브라우저 주소창에 http://127.0.0.1:3000/user 주소를 입력해봅시다.

그러면 위와 같은 화면을 볼 수 있습니다. 이게 라우터의 원리입니다.

 

간단히 말하면,

1. Router를 불러와서 get, post, put, patch, delete 등의 HTTP 메소드를 활용해서 함수를 작성하고, module.exports 구문으로 router를 내보낸다.

2. app.js에서 app.use를 사용하여 원하는 경로에 Router(길)를 붙여준다.

 

인겁니다. 쉽습니다.

Router에 Router를 붙일수도 있습니다. 그때는 app.use가 아닌 router.use를 사용해주면 됩니다. 길에다가 길을 연결할 수 있잖아요?

// 이 코드는 따라서 작성하지는 말고 이런게 있구나~ 하고 보기만 합시다.
const router = require("express").Router();
const anotherRouter = require("./another.router");

router.use("/something", anotherRouter);

router.get("/", (req, res) => {
  res.send("User Router GET /user");
});

module.exports = router;

그림으로 그리면 약간 위와같은 느낌이라고 생각하시면 될 것 같습니다. 그래도 이해가 안간다면 천천히 공부해가면서 감을 잡아가시면 좋을 것 같습니다.


Query, Param, Body


 

Query, Param, Body는 이전 포스팅에서 대략적으로 설명했습니다.

https://suloth.tistory.com/196

 

시즌 2 #1. 기초부터 따라하는 Nest.js 2 : HTTP 메소드

이전 시간에는 간단하게 서버와 클라이언트에 대해서 다뤄봤습니다.https://suloth.tistory.com/191 시즌 2 #0. 기초부터 따라하는 Nest.js 2 : Orientation조금 더 디테일하게 따라할 수 있는 Nest.js 튜토리얼

suloth.tistory.com

 

다시 한 번 설명하자면, query는 url 뒤에 ?를 입력하고 &를 구분자로하여 key=value 형식으로 데이터를 전달하는 방법을 이야기합니다.

param은 url 뒤에 / 를 구분자로 해서 데이터를 전달하는 방법을 이야기합니다.

body는 요청 시 서버로 전달해야하는 데이터의 양이 많은 경우 post, patch, put과 같은 메소드에 데이터를 담아 서버로 전달방법을 이야기합니다.

 

아까 만든 user.router.js를 토대로, nickname에 따라서 유저 정보를 다르게 가져오고 싶다고 생각해봅시다.

다음과 같이 코드를 작성해봅시다.

// user.router.ts
const router = require("express").Router();

// "/" 경로로 끝나는 GET 요청이 들어오면 함수를 실행한다.
router.get("/", (req, res) => { 
  // url의 문자열 ?뒤에 있는 nickname이라는 key값의 value를 가져와서 nickname 변수에 저장한다.
  const nickname = req.query.nickname;

  // nickname 변수를 사용자에게 리턴한다.
  res.json({
    userInfoForQuery: {
      nickname: nickname,
    },
  });
});

// "/:nickname" 경로로 끝나는 GET 요청이 들어오면 함수를 실행한다.
// 즉, "/user/suloth" 와 같이 suloth 라는 경로가 추가적으로 있으면
// suloth를 nickname이라는 param으로 인식한다.
router.get("/:nickname", (req, res) => {
  // params에서 nickname을 불러와 nickname 변수에 저장한다.
  const nickname = req.params.nickname;

  // nickname 변수를 사용자에게 리턴한다.
  res.json({
    userInfoForParam: {
      nickname: nickname,
    },
  });
});

module.exports = router;

 

위의 router.get("/") 함수를 사용하면 req.query에서 nickname을 읽어서 가져온 다음, json 형식의 데이터를 사용자에게 전달합니다.

아래의 router.get("/:nickname") 함수를 사용하면 req.param에서 nickname을 읽어서 가져온 다음, json 형식의 데이터를 사용자에게 전달합니다.

즉, param은 router.method("/:변수명") 형식으로 사용하면 됩니다.

 

여기서 res.json은 사용자에게 json 형식의 데이터를 전달한다는 의미입니다.

 

왼쪽 - query 사용 / 오른쪽 - param 사용

위 사진에서 url을 잘 봐주세요.

 

body는 조금 다릅니다. post, put, patch같은 특정 메소드에서만 사용할 수 있기 때문에 데이터 생성 혹은 수정에 대해서만 사용됩니다.

여기서는 맛만 간단하게 보겠습니다.

user.router.js에 다음과 같이 코드를 작성해서 추가해줍시다. (module.exports = router 보다 위에 추가해 줘야합니다)

Request body를 통해 데이터를 받으면, 해당 데이터를 그대로 사용자에게 전달하는 코드입니다.

// user.router.js
router.post("/", (req, res) => {
  const body = req.body;

  res.json(body);
});

 

그리고 app.js에 위쪽에 (require 구문 바로 아래쪽) 다음 코드를 추가해줍시다.

app.use(express.json());
app.use(express.urlencoded({ extended: false }));

 

app.use(express.json())은 express 서버가 요청으로 들어오는 body의 json을 읽을 수 있도록 해주는 녀석이고,

app.use(express.urlencoded({ extended: false })) 또한 마찬가지로 json이 아닌 다른 특정 타입을 읽을 수 있도록 해주는 녀석입니다.

 

 

이제 서버를 다시 실행시키고, postman을 켜줍시다. (없다면 설치해주세요)

그리고 포스트맨을 위와같이 설정해주고 Send를 누르면 아래족 Body에 그대로 데이터가 전달되는 것을 볼 수 있습니다.

여기서 주의할 점은 중간에서 Body에 raw -> JSON 으로 설정을 해주어야 한다는 점만 주의하면 됩니다.

 

전체 코드

// app.js
const express = require("express");
const app = express();
const port = 3000;
const userRouter = require("./routers/user.router");

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/user", userRouter);

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

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});


// routers/user.router.js
const router = require("express").Router();

router.get("/", (req, res) => {
  const nickname = req.query.nickname;

  res.json({
    userInfoForQuery: {
      nickname: nickname,
    },
  });
});

router.get("/:nickname", (req, res) => {
  const nickname = req.params.nickname;

  res.json({
    userInfoForParam: {
      nickname: nickname,
    },
  });
});

router.post("/", (req, res) => {
  const body = req.body;

  res.json(body);
});

module.exports = router;

 

 


Middleware


Express에서는 미들웨어라는 개념이 있습니다. (아마 이것도 마찬가지로 다른 프레임워크들에서도 많이 사용하는 용어입니다 ㅎㅎ)

Express 공식 문서에서는 미들웨어를 다음과 같이 정의하고 있습니다.

"미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트(res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수에 대한 액세스 권한을 갖는 함수입니다. 그 다음의 미들웨어 함수는 일반적으로 next라는 이름의 변수로 표시됩니다."

라고 적혀 있습니다.

그냥 간단하게 말해서 req ~ res 까지 거치는 모든 함수들을 미들웨어라고 합니다.

 

사용자에게 요청이 들어오면, app.js 의 맨위부터 코드를 한 줄 씩 읽게됩니다.

예를들어, GET /user 요청이 들어오면, app.use 코드들을 거쳐서, app.use("/user", userRouter) 코드를 거칩니다. 그렇게 해서 user.router.js 파일에 들어가서 router.get("/") 코드를 읽게되는 것이죠. 지금 이 과정에서 거친 모든 함수들이 미들웨어입니다.

 

그리고 모든 미들웨어함수는 next() 를 호출해서 다음 미들웨어 함수를 호출할 수 있습니다. (사용하지 않으면, 다음 미들웨어는 호출되지 않습니다. 따라서, next를 사용하든 res.json을 사용하든 해야합니다)

 

user.router.js 맨 위에(require 구문 아래) 아래 코드를 추가하고 다음 코드에서 미들웨어를 찾아봅시다.

router.get(
  "/middleware",
  (req, res, next) => {
    req.query["nickname"] = "suloth";
    next();
  },
  (req, res) => {
    res.json({
      nickname: req.query.nickname,
    });
  }
);

뭐가 미들웨어일까요? 정답은 router.get도 미들웨어이고 router.get의 콜백함수로 들어가는 (req, res, next) => {} 와 (req, res) => 등의 함수들도 미들웨어입니다.

위의 코드를 해석을 하자면, "/user/middleware" 경로로 GET 요청이 들어오면, (req,res,next) => {} 함수를 타는데, 여기서 req.query.nickname = suloth 라고 설정을 해주고, next() 함수를 통해 다음 미들웨어를 호출합니다.

그리고 다음 미들웨어인 (req, res) => {} 함수가 실행되어 req.query.nickname을 사용자에게 리턴합니다.

 

서버를 실행해서 "http://127.0.0.1:3000/user/middleware" 를 브라우저에 입력해주면 다음과 같은 화면이 나타납니다.

 

따로 url에서 query를 설정하지 않았는데도 suloth라고 나타나죠? 이렇게 미들웨어는 중간에서 객체(변수)를 직접 조작하는 일을 하게 됩니다.

이렇게 미들웨어를 통해서 에러처리, 응답 형식 통일, input/output validation 등의 일을 수행할 수 있습니다.

 

전체 코드

// user.router.js 전체 코드
const router = require("express").Router();

// 이 코드를 router.get("/:nickname") 보다 위로 올려야만 사용자 요청이 들어오면
// 위에서부터 코드를 읽기 때문에 router.get("/:nickname") 함수를 먼저 읽지 않고
// 이 함수부터 읽는다.
router.get(
  "/middleware",
  (req, res, next) => {
    req.query["nickname"] = "suloth";
    next();
  },
  (req, res) => {
    res.json({
      nickname: req.query.nickname,
    });
  }
);

router.get("/", (req, res) => {
  const nickname = req.query.nickname;

  res.json({
    userInfoForQuery: {
      nickname: nickname,
    },
  });
});

router.get("/:nickname", (req, res) => {
  const nickname = req.params.nickname;

  res.json({
    userInfoForParam: {
      nickname: nickname,
    },
  });
});

router.post("/", (req, res) => {
  const body = req.body;

  res.json(body);
});

module.exports = router;

router.get("/middleware") 함수를 router.get("/:nickname") 함수보다 위에 적는 이유는, 사용자 요청이 들어오면 코드를 위에서부터 읽기 때문에 middleware를 :nickname으로 먼저 인식해버리지 않기 위함입니다.

만약, routet.get("/:nickname") 이 위에 있으면, middleward라는 것을 nickname으로 인식해서 router.get("/middleware")함수가 아닌, router.get("/:nickname") 함수가 실행되게 됩니다.

 


Todo


 

간단한 과제가 있습니다. 다음 포스팅에 답이 있을텐데, 가급적 다음 포스팅은 보지마시고 해결해보시기 바랍니다.

저희가 아까 파일을 만들 때, user.router.js 말고도 post.router.js 파일도 만들었죠?

 

과제1

post.router.js 파일에 Router를 정의하고, /post 경로로 요청을 보내면 해당 Router를 탈 수 있도록 설정하기

 

과제2

/post 경로로 GET 요청을 하면, title과 content를 얻을 수 있도록 함수를 작성해주세요. 이때, title과 content는 query로 전달하는 방법과 param으로 전달하는 방법 두 가지를 모두 구현해주세요.

 

만약 위의 문제가 어떤 뜻인지 잘 모르시겠다면, 해당 포스팅을 한 번 더 읽어주세요.