728x90
반응형
1) Node.js
1-1) XMLHttpRequest, fetch, axios
1-2) 비동기 통신 - 댓글 CR(Create, Read)
1-3) 비동기 통신 - 댓글 UD(Update, Delete)
1) Node.js
"요청을 잘 보내고 잘 받는 것"에 숙달되는 것이 중요하다!!
"display: none;"된 element가 "template element"이다!!
어떤 요청을 보내면 어떻게 응답을 줄지(구체적으로 어떤 데이터 타입으로 줄지를 명시한 것을 의미함)를 정의한 것이 "API 문서"이다!!
1-1) XMLHttpRequest, fetch, axios
const request = ({ method, path, body }) => {
return new Promise((resolve, reject) => {
const host = "http://localhost:3000";
const xhr = new XMLHttpRequest();
xhr.open(method, `${host}${path}`);
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(JSON.stringify(body));
xhr.onload = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(`ERR`);
}
};
});
};
// "즉시 실행 함수"처럼 프로그램 시작 시 최초에 한 번만 실행하면 되는 함수를 "main 함수"라 한다!!
(async () => {
// XMLHttpRequest(Javascript 내장)
const ajax = await request({
method: "get",
path: "/comments",
body: null,
});
console.log(ajax);
// fetch(Javascript 내장)
const response = await fetch("http://localhost:3000/comments", {
method: "get",
headers: {
"Content-type": "application/json",
},
body: null,
});
// console.log(response); // response message를 객체화해서 보여줌!!(단, body는 확인 불가하다!!)
const body = await response.json();
console.log(body); // body를 확인하기 위해서는 위와 같이 await을 두 번 써야 한다!!
// axios(이는 외부에서 만든 것으로, XMLHttpRequest나 fetch에 비해 좀 더 편하게 쓰려 만든 것이다!!)
// 물론 axios 역시 XMLHttpRequest에 기반하여 만들어진 것이다!!
// <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
// 공식 문서 : https://axios-http.com/kr/docs/example
// "get"은 데이터를 가져오는 행위인 반면, "post"는 데이터를 넣는 행위이다!!
const res = await axios.get("http://localhost:3000/comments");
const res2 = await axios.post("http://localhost:3000/comments", {
content: "내용 잘 들어갔나~??",
});
console.log(res.data);
console.log(res2);
})();
// 오래전 Javascript에서 사용하던 것이 "XMLHttpRequest()"인데
// 이후 Javascript가 버전 업이 되면서 내장 객체를 새로 하나 만들었는데 이것이 바로 "fetch"이다!!
// 그 후 좀 더 편하게 쓰기 위해 외부 모듈 혹은 라이브러리가 등장하는데 이것이 바로 "axios"이다!
1-2) 비동기 통신 - 댓글 CR(Create, Read)
front
server.js
const express = require("express");
const nunjucks = require("nunjucks");
const app = express();
app.set("view engine", "html");
nunjucks.configure("views", {
express: app,
});
app.use(express.static("public"));
app.use(express.json());
app.get("/", (req, res, next) => {
res.render("index.html");
});
app.listen(3005, () => {
console.log("server start");
});
views/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link href="/css/index.css" rel="stylesheet" />
</head>
<body>
<div>
<ul class="comment">
<li class="comment-form">
<form id="commentFrm">
<h4>
댓글쓰기
<span></span>
</h4>
<span class="ps_box">
<input
type="text"
placeholder="댓글 내용을 입력해주세요."
class="int"
name="content"
/>
</span>
<input type="submit" class="btn" value="등록" />
</form>
</li>
<li id="comment-list"></li>
</ul>
</div>
<template id="commentRow">
<ul class="comment-row" data-index="1">
<li class="comment-id"></li>
<li class="comment-content"></li>
<li class="comment-date"></li>
</ul>
</template>
<template id="content-baisc">
<span class="comment-update-btn">입력입력입력~~~</span>
<span class="comment-delete-btn">❌</span>
</template>
<template id="content-update">
<span>
<input type="text" class="comment-update-input" data-value="" />
</span>
<span class="comment-delete-btn">❌</span>
</template>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="/js/comment.js" type="text/javascript"></script>
</body>
</html>
public/css/index.css
* {
margin: 0;
padding: 0;
}
ul,
li {
list-style: none;
}
.comment {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
padding: 30px;
width: 600px;
margin: 0 auto;
}
.comment > li {
margin-top: 20px;
}
.comment > li:nth-child(1) {
margin: 0px;
}
.comment-row {
display: flex;
justify-content: space-between;
flex-direction: row;
}
.comment-row {
margin-top: 20px;
width: 100%;
}
.comment-row > li:nth-child(2) {
flex-shrink: 0;
flex-grow: 1;
padding-left: 25px;
z-index: 1;
width: 100%;
}
.comment-row > li:nth-child(2) {
width: 85px;
}
.comment-form > form {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.comment-form > form > h4 {
width: 100%;
margin: 14px 0 14px 0;
}
.comment-content {
cursor: pointer;
word-break: break-all;
padding-right: 25px;
}
.ps_box {
display: block;
position: relative;
width: 80%;
height: 51px;
border: solid 1px #dadada;
padding: 10px 14px 10px 14px;
background: #fff;
box-sizing: border-box;
}
.ps_box > input {
outline: none;
}
.int {
display: block;
position: relative;
width: 100%;
height: 29px;
padding-right: 25px;
line-height: 29px;
border: none;
background: #fff;
font-size: 15px;
box-sizing: border-box;
z-index: 10;
}
.btn {
width: 18%;
padding: 18px 0 16px;
text-align: center;
box-sizing: border-box;
text-decoration: none;
border: none;
background: #333;
color: #fff;
font-size: 14px;
}
.comment-delete-btn {
display: inline-block;
margin-left: 7px;
cursor: pointer;
}
.comment-update-input {
border: none;
border-bottom: 1px solid #333;
font-size: 16px;
color: #666;
outline: none;
}
public/js/comment.js
const row = ({ id, userid, content, register }) => {
const template = document.querySelector("#commentRow");
// console.log(template.content);
const clone = document.importNode(template.content, true);
// console.log(clone);
const ul = clone.querySelector("ul");
const li = clone.querySelectorAll("li");
// console.log(ul);
// console.log(li);
ul.dataset.index = id;
li[0].innerHTML = userid;
li[1].innerHTML = content;
li[2].innerHTML = register;
// console.log(ul);
// console.log(ul.dataset);
return ul;
};
const drawing = (ul) => {
const commentElement = document.querySelector("#comment-list");
commentElement.append(ul);
const ulElement = document.querySelectorAll("#comment-list > ul");
totalRecord(ulElement.length);
};
const getCommentList = async () => {
try {
const res = await axios.get("http://localhost:3000/comments");
return res.data;
} catch (e) {
return null;
}
};
const addComment = async (content) => {
try {
const res = await axios.post("http://localhost:3000/comments", {
content,
});
return res.data; // 여기서 res.data의 return 값은 객체("{}")이다!!
} catch (e) {
return null;
}
};
const totalRecord = (count) => {
const countElement = document.querySelector("h4 > span");
countElement.innerHTML = `(${count})`;
};
// init : 요청을 때리고 데이터를 저장하는 역할을 함!
const init = async () => {
const list = await getCommentList(); // list의 데이터 타입은 배열("[]")이다!!
// console.log(list);
if (list === null) return;
// 아래 comment의 데이터 타입은 객체("{}")이다!!
for (const comment of list) {
const item = row(comment); // row 함수의 return 값은 "ul element"이다!!
drawing(item);
}
};
const events = ({ submitHandler }) => {
const form = document.querySelector("#commentFrm");
form.addEventListener("submit", submitHandler);
};
const submitHandler = async (e) => {
e.preventDefault();
const { content } = e.target;
// console.log(content.value);
const item = await addComment(content.value);
const ul = row(item);
drawing(ul);
e.target.reset();
content.focus();
};
// "즉시 실행 함수"처럼 프로그램 시작 시 최초에 한 번만 실행하면 되는 함수를 "main 함수"라 한다!!
(async () => {
// axios(이는 외부에서 만든 것으로, XMLHttpRequest나 fetch에 비해 좀 더 편하게 쓰려 만든 것이다!!)
// 물론 axios 역시 XMLHttpRequest에 기반하여 만들어진 것이다!!
// <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
// 공식 문서 : https://axios-http.com/kr/docs/example
// "get"은 데이터를 가져오는 행위인 반면, "post"는 데이터를 넣는 행위이다!!
// const res = await axios.get("http://localhost:3000/comments");
// const res2 = await axios.post("http://localhost:3000/comments", {
// content: "내용 잘 들어갔나~??",
// });
init();
events({
submitHandler,
});
})();
// 오래전 Javascript에서 사용하던 것이 "XMLHttpRequest()"인데
// 이후 Javascript가 버전 업이 되면서 내장 객체를 새로 하나 만들었는데 이것이 바로 "fetch"이다!!
// 그 후 좀 더 편하기 쓰기 위해 외부 모듈 혹은 라이브러리가 등장하는데 이것이 바로 "axios"이다!
back
server.js
const express = require("express");
const cors = require("cors");
const app = express();
const mysql = require("./models");
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cors());
/*
`id`
`userid`
`content`
`register`
*/
app.get("/comments", async (req, res, next) => {
try {
const [response] = await mysql.query(
`SELECT id, userid, content, DATE_FORMAT(register,'%Y-%m-%d') as register FROM Comment`
);
res.json(response);
} catch (e) {
next(e);
}
});
app.post("/comments", async (req, res, next) => {
try {
const userid = `web7722`;
const { content } = req.body;
if (!userid) throw new Error("userid가 없습니다.");
if (!content) throw new Error("content 없음!");
const sql = `INSERT INTO Comment(userid, content) VALUES('${userid}','${content}')`;
const [{ insertId }] = await mysql.query(sql);
const [[response]] = await mysql.query(
`SELECT id, userid, content, DATE_FORMAT(register,'%Y-%m-%d') as register FROM Comment WHERE id=${insertId}`
);
response.updated = false;
res.json(response);
} catch (e) {
next(e);
}
});
app.get("/comments/:id", (req, res, next) => {
try {
res.send("하나 게시물 가져오기");
} catch (e) {
next(e);
}
});
app.put("/comments/:id", (req, res, next) => {
try {
res.send("게시물 수정하기");
} catch (e) {
next(e);
}
});
app.delete("/comments/:id", (req, res, next) => {
try {
res.send("게시물 삭제");
} catch (e) {
next(e);
}
});
app.use((error, req, res, next) => {
console.log(error.message);
res.send(`${error}`);
});
app.listen(3000, () => {
console.log(`server start`);
});
models/index.js
const mysql = require("mysql2");
const pool = mysql
.createPool({
host: "127.0.0.1",
port: "3306",
user: "root",
password: "본인 MySQL 비밀번호",
database: "comments",
})
.promise();
module.exports = pool;
1-3) 비동기 통신 - 댓글 UD(Update, Delete)
※ 아래 내용의 경우, 위의 CR 구현 결과에서 내용이 추가로 바뀐 파일만 소개함!!
front
public/js/comment.js
const basicBox = (content) => {
const template = document.querySelector("#content-baisc");
const clone = document.importNode(template.content, true);
const span = clone.querySelector(".comment-update-btn");
span.innerHTML = content;
return clone;
};
const box = (updated, content) => {
if (updated) {
// updated가 true일 때는 수정 화면
} else {
// updated가 false일 때는 기본 화면
return basicBox(content);
}
};
const row = ({ id, userid, content, register, updated }) => {
/*
<row example>
content: "hello world~~"
id: 1
register: "2023-01-05"
updated: false
userid: "web7722"
*/
const template = document.querySelector("#commentRow");
// console.log(template.content);
const clone = document.importNode(template.content, true);
// console.log(clone);
const ul = clone.querySelector("ul");
const li = clone.querySelectorAll("li");
// console.log(ul);
// console.log(li);
ul.dataset.index = id;
li[0].innerHTML = userid;
li[1].append(box(updated, content)); // box 함수의 return 값은 "element"이다!!
li[2].innerHTML = register;
// console.log(ul);
// console.log(ul.dataset);
return ul;
};
const drawing = (ul) => {
const commentElement = document.querySelector("#comment-list");
commentElement.append(ul);
const ulElement = document.querySelectorAll("#comment-list > ul");
totalRecord(ulElement.length);
};
const getCommentList = async () => {
try {
const res = await axios.get("http://localhost:3000/comments");
return res.data;
} catch (e) {
return null;
}
};
const addComment = async (content) => {
try {
const res = await axios.post("http://localhost:3000/comments", {
content,
});
return res.data; // 여기서 res.data의 return 값은 객체("{}")이다!!
} catch (e) {
return null;
}
};
const totalRecord = (count) => {
const countElement = document.querySelector("h4 > span");
countElement.innerHTML = `(${count})`;
};
// init : 요청을 때리고 데이터를 저장하는 역할을 함!
const init = async () => {
const list = await getCommentList(); // list의 데이터 타입은 배열("[]")이다!!
// console.log(list);
if (list === null) return;
// 아래 comment의 데이터 타입은 객체("{}")이다!!
for (const comment of list) {
const item = row(comment); // row 함수의 return 값은 "ul element"이다!!
drawing(item);
}
};
const events = ({ submitHandler, clickHandler }) => {
const form = document.querySelector("#commentFrm");
const list = document.querySelector("#comment-list");
form.addEventListener("submit", submitHandler);
list.addEventListener("click", clickHandler);
};
const submitHandler = async (e) => {
e.preventDefault();
const { content } = e.target;
// console.log(content.value);
const item = await addComment(content.value);
const ul = row(item);
drawing(ul);
e.target.reset();
content.focus();
};
const clickHandler = (e) => {
console.log(e.target.parentNode.parentNode);
console.log(e.target.parentNode.parentNode.dataset.index);
if (e.target.className === "comment-update-btn") {
console.log("제목을 클릭했다~~~");
// id: DB 식별자
// id에 해당하는 화면을 바꿔야 함
// 내용을 입력하고,
// Enter 이벤트를 발동시키면
// put method(request method)를 써서 Web Server에 요청함
// 응답을 받고
// 그 후 element 내용을 수정한 다음 다시 updated의 값을 false로 바꿔줌!
} else if (e.target.className === "comment-delete-btn") {
console.log("삭제 버튼을 클릭했다~~~");
// id: DB 식별자
// delete method(request method)를 써서 Web Server에 요청함
// 응답을 받고
// 그 후 element를 삭제함!!
}
};
// "즉시 실행 함수"처럼 프로그램 시작 시 최초에 한 번만 실행하면 되는 함수를 "main 함수"라 한다!!
(async () => {
// axios(이는 외부에서 만든 것으로, XMLHttpRequest나 fetch에 비해 좀 더 편하게 쓰려 만든 것이다!!)
// 물론 axios 역시 XMLHttpRequest에 기반하여 만들어진 것이다!!
// <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
// 공식 문서 : https://axios-http.com/kr/docs/example
// "get"은 데이터를 가져오는 행위인 반면, "post"는 데이터를 넣는 행위이다!!
// const res = await axios.get("http://localhost:3000/comments");
// const res2 = await axios.post("http://localhost:3000/comments", {
// content: "내용 잘 들어갔나~??",
// });
init();
events({
submitHandler,
clickHandler,
});
})();
// 오래전 Javascript에서 사용하던 것이 "XMLHttpRequest()"인데
// 이후 Javascript가 버전 업이 되면서 내장 객체를 새로 하나 만들었는데 이것이 바로 "fetch"이다!!
// 그 후 좀 더 편하기 쓰기 위해 외부 모듈 혹은 라이브러리가 등장하는데 이것이 바로 "axios"이다!
back
server.js
const express = require("express");
const cors = require("cors");
const app = express();
const mysql = require("./models");
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cors());
/*
`id`
`userid`
`content`
`register`
*/
app.get("/comments", async (req, res, next) => {
try {
// "[response]"의 결과물은 배열("[]")이다!! --> 현재: [{id, userid, content, register}] >> 이렇게 바꾸려 함: [{id, userid, content, register, updated}]
const [response] = await mysql.query(
`SELECT id, userid, content, DATE_FORMAT(register,'%Y-%m-%d') as register FROM Comment`
);
const result = response.map((v) => {
return {
...v,
updated: false,
};
});
res.json(result);
} catch (e) {
next(e);
}
});
app.post("/comments", async (req, res, next) => {
try {
const userid = `web7722`;
const { content } = req.body;
if (!userid) throw new Error("userid가 없습니다.");
if (!content) throw new Error("content 없음!");
const sql = `INSERT INTO Comment(userid, content) VALUES('${userid}','${content}')`;
const [{ insertId }] = await mysql.query(sql);
const [[response]] = await mysql.query(
`SELECT id, userid, content, DATE_FORMAT(register,'%Y-%m-%d') as register FROM Comment WHERE id=${insertId}`
);
response.updated = false;
res.json(response);
} catch (e) {
next(e);
}
});
app.get("/comments/:id", (req, res, next) => {
try {
res.send("하나 게시물 가져오기");
} catch (e) {
next(e);
}
});
app.put("/comments/:id", (req, res, next) => {
try {
res.send("게시물 수정하기");
} catch (e) {
next(e);
}
});
app.delete("/comments/:id", (req, res, next) => {
try {
res.send("게시물 삭제");
} catch (e) {
next(e);
}
});
app.use((error, req, res, next) => {
console.log(error.message);
res.send(`${error}`);
});
app.listen(3000, () => {
console.log(`server start`);
});
'Node.js' 카테고리의 다른 글
Node.js(15) - Backend MVC 패턴 구현, class(상속 및 추상화)를 활용한 댓글 Front 화면 구현 (0) | 2023.01.09 |
---|---|
Node.js(14) - back 댓글 CRUD 구현, 모듈 및 class를 활용한 댓글 front 화면 구현 (0) | 2023.01.08 |
Node.js(12) - 비동기 통신 댓글 CRUD, Postman 및 Database 활용 (0) | 2023.01.04 |
Node.js(11) - AJAX 기본 포맷, server 분리(front/back) 구현 (0) | 2023.01.03 |
Node.js(10) - express를 활용한 게시판 구현(CRUD) 및 Router 나누기 (0) | 2022.12.20 |