오늘은 어제 배웠던 백엔드부분을 다시 복습하고 보다 좀 더 디테일하게 백엔드의 기초적인 부분을 배웠다. 그리고 배운 부분들을 사용하여 Todolist를 만들어보는 시간을 가졌다. 다행히도 백엔드부분은 직관적으로 코드를 작성했기 때문에 이해하는데 있어서 이전에 React를 했을때 보다는 좀 더 쉬웠고, 특히 오후에 진행했던 Todolist 만드는 수업은 생각외로 재밌어서 놀랐다. 일단은 어제 배웠던 부분들에 대해서 정리못했던 부분을 강사님의 교안을 바탕으로 간단하게 정리를 하고 오늘 수업에서 진행했던 코드를 리뷰해보도록 하겠다.
Rest API란 무엇인가?
Rest API의 특징.
자원의 이름과 전달 방식만으로 해당역할을 추론할 수 있다.
HTTP Methods를 통해서 해당 자원에 대한 CRUD Operation을 적용한다.
Rest API의 특징 및 장, 단점
Rest API의 특징
Server - Client 구조로 되어 있다.
자윈을 가지고 있는 쪽이 서버, 제공받는 쪽이 클라이언트가 된다.
Stateless(무상태)다.
HTTP 프로토콜을 사용하므로 HTTP 프로토콜처럼 무상태성을 가지고 있다.
클라이언트의 context를 서버에 저장하지 않는다. (서버는 신경쓸 것 없이 본연에 업무에만 집중하면 된다.)
서버는 각각에 요청에 대한 응답만 하면 된다.
Rest API의 장, 단점!
Rest API의 적용
실제 Get요청, Post요청을 통해 Rest API를 사용할 수 있다.
AXIOS는 HTTP 통신을 위한 라이브러리다.
Insomnia(Postman)는 REST API를 테스트 할 수 있는 툴이다.
React Icons는 리액트 진영에서 사용하는 아이콘 라이브러리다.
How to use
npm i react-icons
프론트엔드는 백엔드 서버와의 통신을 위해서라도 비동기 처리를 해야되는 상황이 발생한다.
비동기 처리를 위해서 사용하는 문법인 async / await에 대해서 살펴보도록 하겠다.
Async/Await(Promise)
Code
async function checkPromise() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("완료!"), 1000); }); // Promise가 있는 경우 없는 경우 둘 다 실행해보세요. // let result = promise; let result = await promise; console.log(result); } checkPromise();
await 키워드를 붙였을 때는 정상적으로 나타낸다.
await 키워드를 사용하기 위해서는 함수를 반드시 async(비동기)로 만들어야 한다.
await를 사용하지 않은 promise는 pending 상태를 출력하게 된다.
try/catch
Code & Result
NodeJS의 등장으로 자바스크립트는 프론트엔드 영역을 넘어, 백엔드 영역까지 확장하게 되었다.
그 중에서도 ExpressJS를 공부하여 백 엔드는 어떻게 구성되어 있고, 백엔드와 프론트엔드는 어떻게 소통이 이루어지는지 살펴보도록 하겠다.
ExpressJS 설치
npm init
해당 이미지에 맞게
터미널에서 작성진행
npm i express (npm install express --save)
(추가) app.js 생성.
Code & Result.
백엔드 서버가 구동되기 시작하면 listen이 실행된다.
웹 브라우저에서 3010을 접속하게 되면 Get요청을 받을 수 있다.
ExpressJS를 사용하여 CRUD 구현하기
모듈시스템 이란?
Module System이란?
모듈 시스템 등장 이전의 자바스크립트 개발은 <script src=”…”>
로 불러와야 했다.
모듈 시스템에 등장으로 보기에 직관적이고, 효율성 있는 개발이 가능하게 됐다.
ES6
ES - ECMAScript의 약어이며, 자바스크립트의 표준을 나타내는 용어다.
ES6 - 2015년에 등장한 자바스크립트 표준이다.
import/export를 사용해서 모듈 시스템을 구현한다.
CommonJS
2009년에 자바스크립트의 표준을 만들기 위해서 등장하였다.
주로 서버 사이드(백엔드)에서 사용되는 모듈 시스템이다.
require 사용하여 모듈시스템을 구현한다.
CommonJS 모듈 시스템의 사용
<a.js> function goModule() { console.log("Hello, Module!"); } module.exports = goModule;
<b.js> const goModule = require("./a"); goModule();
CommonJS의 모듈 시스템은 module.exports를 통해 export 한다.
require를 통해 import 할 수 있다.
많은 API 요청이 생긴다면 app.js의 내용이 길어지게 된다. 이때 ExpressJS의 라우팅 기능을 활용하면 효율적으로 API를 관리할 수 있다.
Code & Result
<app.js>
const express = require("express");
const userRouter = require("./routes/user");
const tweetRouter = require("./routes/tweet");
const app = express();
const port = 3010;
app.use("/user", userRouter);
app.use("/tweet", tweetRouter);
app.get("/", (req, res) => {
res.send("Hello, Express!");
});
app.listen(port, () => {
console.log(`Server listening on port: ${port} 🚀🚀🚀`);
});
- 해당 코드는 Node.js와 Express.js를 사용하여 서버를 구축하는 코드다. 간단하게 REST API를 생성하는 것이 목적이다.
const express = require("express");
const userRouter = require("./routes/user");
const tweetRouter = require("./routes/tweet");
const app = express();
const port = 3010;
app.use("/user", userRouter);
app.use("/tweet", tweetRouter);
app.get("/", (req, res) => {
res.send("Hello, Express!");
});
app.listen(port, () => {
console.log(Server listening on port: ${port} 🚀🚀🚀
);
});
<user.js>
const express = require("express");
const router = express.Router();
router.get("/:id", (req, res) => {
res.send("유저 조회");
});
router.post("/", (req, res) => {
res.send("신규 유저 생성");
});
router.put("/:id", (req, res) => {
res.send("유저 정보 수정");
});
router.delete("/:id", (req, res) => {
res.send("유저 삭제");
});
module.exports = router;
- 해당 코드는 Express.js의 라우팅 기능을 사용하여 User CRUD(Create, Read, Update, Delete) API를 구현하는 코드다.
const express = require("express");
const router = express.Router();
router.get("/:id", (req, res) => {
res.send("유저 조회");
});
router.post("/", (req, res) => {
res.send("신규 유저 생성");
});
router.put("/:id", (req, res) => {
res.send("유저 정보 수정");
});
router.delete("/:id", (req, res) => {
res.send("유저 삭제");
});
module.exports = router;
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send("전체 트윗 조회");
});
router.get("/:id", (req, res) => {
console.log(req.params);
res.send("특정 트윗 조회");
});
router.post("/", (req, res) => {
res.send("신규 트윗 생성");
});
router.put("/:id", (req, res) => {
res.send("특정 트윗 게시물 수정");
});
router.delete("/:id", (req, res) => {
res.send("특정 트윗 게시물 삭제");
});
module.exports = router;
- 해당코드는 Express.js의 라우팅 기능을 사용하여 Tweet CRUD(Create, Read, Update, Delete) API를 구현하는 코드입니다.
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send("전체 트윗 조회");
});
router.get("/:id", (req, res) => {
console.log(req.params);
res.send("특정 트윗 조회");
});
router.post("/", (req, res) => {
res.send("신규 트윗 생성");
});
router.put("/:id", (req, res) => {
res.send("특정 트윗 게시물 수정");
});
router.delete("/:id", (req, res) => {
res.send("특정 트윗 게시물 삭제");
});
module.exports = router;
자바스크립트 필터 함수를 사용하면 특정 조건에 맞는 요소로만 새로운 배열을 구성할 수 있다.
필터함수 사용(1)
v.length >= 5;
return할때 어떤 조건을 입력하는지에 따라서 바뀐다.
필터함수 사용(2)
v.type === "fruit";
map함수 vs filter함수
filter함수를 사용하면 조건에 부합하지 않으면 값을 저장하지 않음.
반면, map함수를 사용하면 조건에 부합하지 않아도 값을 저장할 수 있음.
정확한 값을 지정하기 위해선 filter함수를 사용하는게 좋음.
지금까지 배웠던 내용을 기반으로 todolist를 만들어보았다.
<app.js>
const express = require("express");
const todoRouter = require("./routes/todo");
const app = express();
const port = 3010;
app.use(express.json());
app.use("/todo", todoRouter);
app.get("/", (req, res) => {
res.send("Hello, My World!!");
});
app.listen(port, () => {
console.log(`Server listening on port: ${port}🦁 `);
});
- 해당코드는 Node.js를 사용하여 Todo List 웹 애플리케이션을 작성하는데 필요한 코드다.
해당코드는 Express.js를 사용하여 간단한 RESTful API 서버를 구축하는 코드입니다.
const express = require("express");
const todoRouter = require("./routes/todo");
const app = express();
const port = 3010;
app.use(express.json());
JSON 형식의 요청 본문을 구문 분석하기 위해 express.json() 미들웨어를 사용
(미들웨어: 서로 다른 애플리케이션이 서로 통신하는 데 사용되는 소프트웨어)
app.use("/todo", todoRouter);
app.get("/", (req, res) => {
res.send("Hello, My World!!");
});
app.listen(port, () => {
console.log(Server listening on port: ${port}🦁
);
});
<todo.js>
const express = require("express");
let todoData = require("../todoData.json");
const router = express.Router();
router.get("/", (req, res) => {
console.log(todoData);
res.json(todoData);
});
router.get("/:id", (req, res) => {
const { id } = req.params;
if (parseInt(id) >= todoData.length) {
return res.status(400).json({ error: "존재하지 않는 ID입니다." });
}
res.json(todoData[parseInt(id)]);
});
router.post("/", (req, res) => {
const { title, desc } = req.body;
if (!title || !desc) {
return res
.status(400)
.json({ error: "타이틀이나 설명 중에 하나의 값은 입력해야 합니다." });
}
todoData.push({ title, desc, isDone: false });
res.json(todoData);
});
router.put("/:id", (req, res) => {
const { id } = req.params;
const { title, desc } = req.body;
if (parseInt(id) >= todoData.length) {
return res.status(400).json({ erroe: "존재하지 않는 ID입니다." });
}
if (!title && !desc) {
return res
.status(400)
.json({ error: "타이틀이나 설명 중에 하나의 값은 입력해야 합니다." });
}
todoData[parseInt(id)] = {
title: title ? title : todoData[parseInt(id)].title,
desc: desc ? desc : todoData[parseInt(id)].desc,
isDone: todoData[parseInt(id)].isDone,
};
res.json(todoData);
});
router.put("/done/:id", (req, res) => {
const { id } = req.params;
if (parseInt(id) >= todoData.length) {
return res.status(400).json({ erroe: "존재하지 않는 ID입니다." });
}
todoData[parseInt(id)] = {
title: todoData[parseInt(id)].title,
desc: todoData[parseInt(id)].desc,
isDone: !todoData[parseInt(id)].isDone,
};
res.json(todoData);
});
router.delete("/:id", (req, res) => {
const { id } = req.params;
if (parseInt(id) >= todoData.length) {
return res.status(400).json({ erroe: "존재하지 않는 ID입니다." });
}
todoData = todoData.filter((v, i) => {
return parseInt(id) !== i;
});
res.json(todoData);
});
module.exports = router;
- 해당코드는 Node.js 기반의 Express 프레임워크를 사용하여 작성된 RESTful API의 라우팅을 처리하는 코드다.
해당코드는 길기 때문에 최대한 축약해서 작성해보도록 하겠다.
먼저 express 모듈을 가져오고, todoData라는 변수에 "../todoData.json" 파일의 내용을 할당한다. 이 파일에는 현재 Todo 앱에 등록된 Todo 항목들의 정보가 저장되어 있다.
router라는 객체를 생성하고, HTTP 요청의 메서드(GET, POST, PUT, DELETE)에 따라 다른 함수를 실행시키도록 한다. 전체 투두리스트를 조회할 수 있다.
GET /:id 요청을 받았을 때, 해당 id에 해당하는 Todo 항목을 반환한다. 만약 요청한 id가 todoData의 길이보다 크거나 같으면 "존재하지 않는 ID입니다."라는 에러를 반환한다.
기존 DB에 있는 todo를 가져오고 DB에 없으면 error 가 뜨도록 만든 코드다.
특정한 값을 얻기 위해서 parseInt(id) 를 추가함.
특정 투두리스트를 조회할 수 있다.
POST / 요청을 받았을 때, 요청 본문에서 title과 desc를 추출하여 새로운 Todo 항목을 생성한다. 만약 title과 desc 중 하나라도 입력하지 않았다면 "타이틀이나 설명 중에 하나의 값은 입력해야 합니다."라는 에러를 반환한다.
기존 내용에 내가 원하는 내용을 집어넣는 코드.
if문으로 둘 중 하나라도 없다는 조건이 만족할 때, 결과적으로 둘 중 하나라도 없을때의 조건이 성립할때 error 내용일 출력되도록 한다.
todoData.push({ title, desc, isDone: false }); 를 작성해 todoData DB에 push를 해주면 DB에 추가가 되도록 함.
PUT /:id 요청을 받았을 때, 해당 id에 해당하는 Todo 항목의 title과 desc 값을 수정한다. 만약 요청한 id가 todoData의 길이보다 크거나 같으면 "존재하지 않는 ID입니다."라는 에러를 반환한다. 또한, title과 desc 모두 입력하지 않았다면 "타이틀이나 설명 중에 하나의 값은 입력해야 합니다."라는 에러를 반환한다.
특정 저장된 내용을 내가 원하는 내용으로 전체 변경하는 코드.
const { id } = req.params; 코드는 params로 todo를 받음. params는 하나만 받아올 수 있어서 body와는 다르다.
const { title, desc } = req.body; 코드는 json 형식으로 todo를 받아와야 해서 body를 사용.
해당 코드에는 두개의 if문이 있는데 첫번째 if문의 조건은 요청한 id가 todoData의 길이보다 크거나 같으면이고, 두번째 if문의 조건은 둘 다 타이들이 없다는 조건을 만족할 때 이다.
title과 desc에 삼항연산자를 사용함으로서 title이나 desc중 원하는 값만 바꾸고 싶을때 사용한다. 기존에 값이 있으면 기존값이 사용되도록 한다.
PUT /done/:id 요청을 받았을 때, 해당 id에 해당하는 Todo 항목의 isDone 값을 반대로 전환한다. 만약 요청한 id가 todoData의 길이보다 크거나 같으면 "존재하지 않는 ID입니다."라는 에러를 반환한다.
저장되어 있는 결과값(done)을 바꾸는 코드(업데이트).
isDone: !todoData[parseInt(id)].isDone,
DELETE /:id 요청을 받았을 때, 해당 id에 해당하는 Todo 항목을 삭제한다. 만약 요청한 id가 todoData의 길이보다 크거나 같으면 "존재하지 않는 ID입니다."라는 에러를 반환한다.
저장되어 있는 내용 중 지우고 싶은 내용을 지울 수 있는 코드.
위 모드 코드에 있는 status(400)는 text뿐만 아니라 코드의 오류를 알리기 위해 작성됨.
return parseInt(id) !== i;
드디어 다 정리했다... 어제 못했던 부분까지 정리하려니
시간이 정말.. 휴...
그래도 다 정리하고 나니 뿌듯하긴 하다.
이것말고도 할일이 많은데 급하지 않은건 주말에 해야겠다.
오늘 강현님이 내 벨로그를 수업중에 얘기하셔서 너무 당황했다; 부족한 벨로그를 누군가 볼까봐 후다닥 비공개를 해버렸는데 막상 비공개 한것도 웃기기도 하고..
내가 틀릴지언정 내 노력이 부끄러운건 아니니까
다시 공개를 했다. 나의 블체스 마지막 벨로그는 지금보다 훨씬 완벽하겠지... 싶다?