익스프레스를 사용해 다양하게 라우팅하고 그걸 확인할 수 있는 썬더 클라이언트(thunder client)라는 vs 확장에 대해 알아보자.
지난번에 사용했던 get 요청 방식을 처리하는 코드를 보자.
const express = require("express");
const app = express();
app.get("/", (req,res)=>{
res.send("Hello Node");
});
app.get("/contacts", (req,res)=>{
res.send("Contacts Page");
});
app.listen(3000, () => {
console.log('서버 실행 중');
});
이외에 우리가 사용할 수 있는 메서드로는 post, put, delete가 있다.
GET : 서버에서 정보를 가져올 때 사용
POST : 서버로 정보를 보낼 때 사용
PUT : 기존 서버에 있던 자료를 수정할 때 사용
DELETE : 기존 서버에 있던 자료를 삭제할 때 사용
get, post, put, delete 4가지가 가장 많이 사용되는 요청 방식이다. 그리고 이 방식을 어떻게 처리할 것인지를 node.js에서 지정하게 된다.
post 를 넣어보자.
app.post("/contacts",(req,res)=>{
res.send("Create Contacts");
})
위에다가 이 코드를 추가하여 contacts 경로로 post 방식의 요청이 들어오면 다음과 같이 표시해주겠다.
그런데 브라우저 창에서 경로를 입력하고 enter를 누르면 get 방식으로 동작을 한다. post 방식을 테스트하려면 폼이 있어야 한다.
해당 동작을 쉽게 확인할 수 있게 하는것이 thunder client라고 하는 VS code 확장이다.

이렇게 설치를 하면

이렇게 아이콘이 생긴다.
어떻게 테스트를 하느냐.
저 아이콘을 누르고 New Request를 선택한다.

여기에 우리가 요청을 보내 볼 수 있는 것이다.
터미널 창에서 서버가 실행 중인지 우선 확인하고, thunder client 화면에서 요청 방식과 요청 경로를 지정해주면 된다.

잘 작동되는지 확인을 위해 contact 경로로 get 요청을 넣었고 성공적으로 작동했다.
이제 POST 방식을 확인해보자.

이렇게 Create Contacts 가 나온다. 같은 경로이지만 어떤 방식으로 요청하느냐에 따라 다른 결과가 출력된다. 즉, 우리가 작성한 이 라우트 코드가 정상적으로 동작한다는 것이다.
앞으로는 이렇게 thunder client를 이용해서 get이외의 방식을 테스트해보겠다.
이렇게 해서 연락처 관련한 페이지를 만들 때 생각해볼것이 2개가 있다. get 방식으로 입력하면 저장된 전체 연락처를 보여줄것이고, post를 하면 내가 새로 입력한 연락처가 서버에 저장이 될 것이다. 여기서 필요한 기능이 또 뭐가 있을까?
바로 특정 연락처만 가지고 오는 것이다. 이렇게 특정 부분만 가지고 오고 싶을 때 사용하는 것이 라우트 파라미터라고 하는 것이다.
특정 부분만 가지고 오고 싶을 때 사용하는 것으로, 요청 url 뒤에 :를 붙인 후 그 뒤에 어떤 조건을 붙이는 것이다.
/요청URL/:id
요청 할 때는 요청 url 뒤에 조건 값을 지정한다.
예를 들어, 연락처 정보 중에서 아이디가 1인것을 가져오려면 /contacts/1 처럼 쓰는 방식이다.
라우트 파라미터를 필요로 하는 방식은 다음과 같다. /contacts/:id라는 주소일 때,
GET : id에 맞는 연락처 가져오기
PUT : id에 맞는 연락처 수정하기
DELETE : id에 맞는 연락처 삭제하기
메서드가 코드에 어떻게 활용되는지 확인해보자.
// GET - 모든 연락처 가져오기
app.get("/contacts", (req,res)=>{
res.send("Contacts Page");
});
// POST - 새 연락처 추가
app.post("/contacts",(req,res)=>{
res.send("Create Contacts");
});
// GET - 연락처 1개 가져오기
app.get("/contacts/:id",(req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
});
// PUT - 연락처 수정하기
app.put("/contacts/:id",(req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
});
// DELETE - 연락처 삭제하기
app.delete("/contacts/:id",(req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
req.params.id는 id값이 담겨있는 변수이다.
결과를 한번 보도록 하자.


지정한 id값이 표시된다.

지정한 id값이 업데이트 된다.

지정한 id값이 삭제된다.
express에서 사용하는 요청(req) 객체에는 여러 속성들이 포함되어 있다.

위에서 진행했던 id값을 이용해서 라우트 파라미터를 사용했던 것은 req.params에 파라미터 정보들이 들어있다. 이렇게 요청 객체에 있는 속성을 활용하여 코딩할 수 있다.
express에서 사용하는 응답(res) 객체에는 여러 함수들이 존재한다.

우리가 방금 썼던건 res 객체 안의 send 함수였다 (res.send). send 함수 안의 내용을 브라우저 창에 표시를 해준 것이다.
익스프레스에 있는 큰 특징 중 하나로 요청과 응답 중간에 있으면서 요청을 처리하거나 원하는 형태로 응답을 수정하는 기능을 가진 함수이다. 모듈 형태들도 존재한다.
ex) 로그인 미들웨어
- 요청 안에 포함된 아이디와 비밀번호의 값을 애플리케이션에서 읽을 수 있는 형태로 변환(파싱)
- 아이디와 비밀번호의 값을 사용해서 사용자 인증
- 처리 결과를 다음 단계(ex 화면 렌더링)로 넘겨 줌
미들웨어의 역할은 크게 세 가지가 있다.

애플리케이션 단계에서 사용하는 미들웨어이다.
app.get("/contacts", (req,res) => {...});
app.get("/contacts/:di", (req,res) => {...});
오늘은 위에서 본 3가지 역할 중 라우팅 처리를 위한 미들웨어인 라우터 미들웨어에 대해 알아보도록 하겠다.
라우터 객체를 이용하여 라우트 코드를 읽고 관리하기 쉽게 만들어주는 미들웨어이다.
const express = require("express");
const app = express();
app.listen(3000, () => {
console.log('서버 실행 중');
});
app.get("/", (req,res)=>{
res.send("Hello Node");
});
// 모든 연락처 가져오기
app.get("/contacts", (req,res)=>{
res.send("Contacts Page");
});
// 새 연락처 추가
app.post("/contacts",(req,res)=>{
res.send("Create Contacts");
});
// 연락처 1개 가져오기
app.get("/contacts/:id",(req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
});
// 연락처 수정하기
app.put("/contacts/:id",(req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
});
// 연락처 삭제하기
app.delete("/contacts/:id",(req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
이렇게 우리가 앞서 작성했던 코드를 라우터 객체를 이용하여 수정해보자. 라우터 객체 안에는 라우팅을 쉽게 하도록 하는 여러 함수들이 포함되어 있다.
우선 라우터 객체를 만들어보자.
const router = express.Router();
이렇게 express의 라우터 객체를 새로 인스턴스로 하나 만들어준다. Router 부분이 라우터 객체를 가리키는 것이고, router 부분이 라우터 객체의 인스턴스 즉, 그 객체를 사용하기 위해 틀로 찍어낸 것이라고 생각하면 된다.
이제 연락처와 관련된 코드 부분을 수정해보겠다.
app.get("/contacts", (req,res)=>{
res.send("Contacts Page");
});
app.post("/contacts",(req,res)=>{
res.send("Create Contacts");
});
이 코드를
router.route(경로).요청방식(할 일).요청방식(할 일);
이런 형태로 작성할 것이다.
이렇게 이전 코드에서는 get 방식과 post 방식일 때 각각 다른 라우트 코드로 처리를 해줬지만 Router 객체를 사용하게 되면 경로 부분을 route로 묶고 처리할 방식을 뒤에 연달아서 작성해주면 된다. 이는 체이닝이라고 한다. router 후에 요청을 보낼 경로를 지정해준다. 여기서는 /contact이다. 그리고 같은 경로를 사용하는 게 2개가 있었기 때문에 .get()과 .post()를 모두 붙여준다.
그렇게 바꾼 코드의 형태는 다음과 같을 것이다.
router.route("/contacts")
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
res.send("Create Contacts");
});
이렇게 깔끔하게 바꿀 수 있다.
app.get("/contacts/:id",(req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
});
app.put("/contacts/:id",(req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
});
app.delete("/contacts/:id",(req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
이번에는 이 코드를 바꿔보자.
router.route("/contacts/:id")
.get((req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
})
.put((req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
})
.delete((req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
이렇게 라우터 객체를 사용하면 코드를 훨씬 간단하게 만들 수 있다. 여기서 주의할 것은 하나의 경로에 여러 개의 요청 방식을 나열하여 쓰고 있는데, 체이닝 중간에 ;을 붙이면 안된다.
이는 앞서 봤던
router.route(경로).요청방식(할 일).요청방식(할 일);
의 형태로 연결된 한 줄의 코드를 보기 쉽게 여러 줄로 표시한 것이기 때문이다.
우리가 작성한 위의 라우트 코드는 미들웨어를 이용한 것이기 때문에 이를 사용했다는 것을 애플리케이션에게 알려줘야 한다. 그 때 사용하는 것이 app.use 라는 함수이다.
app.use(router);
router라고 지정을 해 줘야지 미들웨어로서 동작하게 된다.
이렇게 작성 후 실행시키면


이렇게 잘 작동 되는것을 확인할 수 있다.
우리가 작성하고 있는 app.js 즉, 이 파일은 애플리케이션 전체를 시작시키는 시작 파일이다. 그렇기 때문에 애플리케이션과 관련된 여러 정보들이 저장되게 된다. 앞으로 작성을 하다 보면 이곳에 여러 정보가 들어가게 될 것이다.
그렇기 때문에 실제로는 이 라우트 코드를 별도의 파일로 저장해두고 모듈 형태로 가져다 사용한다. 이렇게 사용하는 방법을 알아보자.
우리는 연락처 관리 앱을 만들 것이기 때문에 contacts 경로가 붙은 부분의 라우트 코드를 별도로 저장해보겠다.
routes 폴더와 그 안에 contactRoutes.js 파일을 만들자.

이제 contact와 contacts/:id 경로가 있는 부분을 잘라서 붙여넣는 작업을 하겠다.
router.route("/contacts")
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
res.send("Create Contacts");
});
router.route("/contacts/:id")
.get((req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
})
.put((req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
})
.delete((req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
이 부분을 ctrl x로 잘라내고 contactRoutes.js 파일에 붙여넣자.
그리고 라우터 객체를 써야 하기 때문에 여기에 코드를 좀 추가해야 한다.
const express = require("express");
const router = express.Router();
router.route("/contacts")
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
res.send("Create Contacts");
});
router.route("/contacts/:id")
.get((req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
})
.put((req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
})
.delete((req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
module.exports = router; // 모듈로 사용하기 위함
이렇게 하면 라우트 코드를 별도의 파일로 성공적으로 저장한 것이다.
오려낸 부분, 즉 원래 라우트 코드가 있던 부분에다가는 이 모듈을 가지고 와서 쓸 수 있도록 해줘야 한다.
app.use("/", require("./routes/contactRoutes"));
이렇게 모듈이 있는 곳, 파일을 지정해주면 된다. routes폴더 안의 contactRoutes 파일을 가지고 와서 미들웨어로 사용하겠다는 뜻이다. 확장자 .js는 생략해 주면 된다.
정상적으로 동작하는지 확인해보자.



정상적으로 동작하는 걸 확인할 수 있다.
const express = require("express");
const app = express();
app.get("/", (req,res)=>{
res.send("Hello Node");
});
app.use("/", require("./routes/contactRoutes"));
app.listen(3000, () => {
console.log('서버 실행 중');
});
원래 파일에서 라우트 코드를 외부로 보내버렸기 때문에 애플리케이션 코드가 깔끔해졌다.
한 가지 더, 외부 파일을 할 때 라우트 코드에서 생각해 볼 것은 경로를 밖으로 빼 버릴 수 있다는 것이다. 그것이 무슨 말이냐?
const express = require("express");
const app = express();
app.get("/", (req,res)=>{
res.send("Hello Node");
});
app.use("/contacts", require("./routes/contactRoutes")); // 변경한 부분
app.listen(3000, () => {
console.log('서버 실행 중');
});
const express = require("express");
const router = express.Router();
router.route("/") // 변경한 부분
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
res.send("Create Contacts");
});
router.route("/:id") // 변경한 부분
.get((req,res)=>{
res.send(`View Contact for ID : ${req.params.id}`);
})
.put((req,res)=>{
res.send(`Update Contact for ID : ${req.params.id}`);
})
.delete((req,res)=>{
res.send(`Delete Contact for ID : ${req.params.id}`);
});
module.exports = router;
다음과 같이 contactsRoutes 파일에서 경로를 생략하고 app.js에서 경로를 작성할 수 있다. 이렇게 작성할 경우, 나중에 애플리케이션을 수정해야 할 때 경로를 바꾸고 싶다면 라우트 코드를 건드리지 않고 애플리케이션 코드만으로 변경할 수 있다.
app.use("/users", require("./routes/contactRoutes"));
예를 들어 경로를 이렇게 /contacts 에서 /users로 바꿔도

이런 식으로 잘 수행되는 것을 확인할 수 있다. 이렇게 하면 라우트 코드는 얼마든지 원하는 경로에서 재사용 할 수 있게 된다.
이번에는 express에서 자주 사용하는 바디파서 미들웨어에 대해 알아보자. 최신 버전의 express에서는 일부 바디파서 기능이 포함되어 있기 때문에 따로 모듈을 설치하지 않고도 사용할 수 있다.
바디파서 미들웨어 : 서버로 요청을 보낼 때 요청 본문에 담긴 것을 파싱하는 미들웨어
파싱 : 요청할 때 전송한 자료를 프로그램에서 사용할 수 있는 형식으로 변환하는 것
바디파서의 '바디'는 요청 본문을 말하고 '파서'는 파싱을 처리한다는 뜻이다.
바디파서는 요청 본문에 어떤 유형의 자료를 포함하는가에 따라 사용하는 함수가 달라진다.

- json 형식이나 urlencoded 형식을 처리할 경우 express 내장 함수를 사용.
app.use(express.json()); app.use(express.urlencoded({ extended: true }));이렇게 두 개를 미들웨어로 등록해 준다.
이 중 많이 사용하는 json과 urlencoded 함수는 express에 포함되어 있으나, raw와 text 함수를 사용하고 싶으면 바디파서를 설치 후 사용해야 한다.
여기서 urlencoded의 url로 인코딩되었다 라는것이 무슨 뜻인지 알아보자.
예를 들어 구글 검색창에 단어를 검색해보자.

그러면 브라우저의 주소 표시줄에 이런 형태로 나온다.

google.com 뒤에 search라는 경로가 있고, ?, q(query), 사용자가 넣은 검색어 순으로 표시된 것을 확인할 수 있는데, 이것이 query string 이다.
서버로 넘겨주는 값이 form 같은 것이 아니라 url을 통해 넘겨진다. 이 때 넘겨지는 자료를 어떻게 파싱할 것인지 지정하는 게 urlencoded라는 함수이고, 괄호 안에 extended:true와 extended:false라는 옵션을 사용할 수 있다. extended:true는 query string 이 중복된 개체인 것도 처리가 가능하다. 즉, 복잡한 자료 형태 처리에 사용된다. 단순한 자료 값이라면 extended:false라고 해도 차이가 없다. 보통은 extended:true 라고 넣고 사용한다.
우리가 앞서 작성한 코드에서 POST부분 즉, 자료를 추가하기 위해 넘겨주는 부분에 요청 본문을 한번 넣어보자.
router.route("/")
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
console.log(req.body); // 변경한 부분
res.send("Create Contacts");
});
어떻게 나오는지 확인해보자.

thunder client를 통해 이렇게 주소를 작성하고 post 방식으로 변경해준다. 그리고 [Body] 부분을 클릭해보자. JSON 형태로 보내보겠다.

이런 식으로 자료를 입력 해 보겠다. 이런 내용을 담고 있는 요청을 서버로 보내려는 것이다.

우선 send를 누르면 이렇게 제대로 동작하는 것을 확인 할 수 있다. 그리고 req.body 즉, 본문을 콘솔 창에 표시해 달라고 했는데

이렇게 undefined라고 나온다. 왜 그럴까?
req.body에 요청을 파싱해 주지 않았기 때문이다. 요청을 파싱해 줘야 json 값을 가져다가 프로그래밍에 사용할 수 있는 것이다. 그렇다면 우선 미들웨어를 등록해줘야 한다.
app.use(express.json());
app.use(express.urlencoded({extended: true}));
이 코드를 기존의 app.js 파일에 추가해주자. 파디 파서를 사용해 달라고 미들웨어를 등록한 것이다. 그 후 다시 send를 통해 전송해보자.

이번에는 이렇게 콘솔 창에 방금 입력한 json 내용이 잘 표시된 것을 확인 할 수 있다.
요청 본문에 있는 내용을 프로그래밍에 사용할 수 있도록 파싱하려면 바디파서 미들웨어를 꼭 등록해줘야 한다.
우리가 작성한 코드를 좀 더 활용해보자.
router.route("/")
.get((req,res)=>{
res.send("Contacts Page");
})
.post((req,res)=>{
console.log(req.body);
const {name, email, phone} = req.body; // 구조 분해 할당
if(!name || !email || !phone){
return res.send("필수 값이 입력되지 않았습니다.");
}
res.send("Create Contacts");
});
이 구문은 js에서 구조 분해 할당이라고 하는 문법이다.
req.body에 세 가지 변수가 들어가 있었는데, 그 각 내용을 프로그램 코드의 name, email, phone 변수로 할당 하는 것이다. 즉, req.name의 name의 내용은 name으로, email의 내용은 email로, phone의 내용은 phone으로 받는다.
요청 본문에서 세 가지 값을 가져오는데, 만일 하나라도 값이 없으면 "필수 값이 입력되지 않았습니다."라는 문장을 표시하도록 하는 것이다.

이렇게 본문을 비우고 send를 눌러보면 우리가 지정한 해당 문장이 표시된 것을 확인 할 수 있다.
바디파서 미들웨어를 등록해 주게 되면 요청 본문을 가지고 와서 필요한 변수로 할당 후 프로그램 코드에 사용할 수 있다.