오늘 내가 배운 것
1. MySQL, CSR 방식을 이용한 댓글 기능 구현
기본 틀은 기존에 만들었던 코드를 이용해서 만든다.
https://baekspace.tistory.com/61
CSR 방식이기 때문에 AJAX를 이용한다.
코드 순서는
1. front 와 back 서버 분리
각 npm, 외장 모듈 설치
- front
- express
- nunjucks
- back
- express
- cors
- mysql2
2. 각 서버 열기
다른 프로세스로 돌아가기 때문에 포트를 2개 사용해서 각자 서버를 열어야한다.
back : 3000, front : 3005 사용할 예정
- front : index.html, css 연결, nunjucks
- back : bodyparser()
3. 라우터 작성
(REST, RESTful)
GET /comments
POST /comments
GET /comments/:id
PUT /comments/:id
DELETE /comments/:id
라우터를 만들 때 에러 컨트롤을 위해 next를 이용하여 다음 라우터를 사용할 수 있도록 한다.
그리고 그 에러를 에러처리 라우터에서 받을 수 있게 try-catch 문법을 이용하여 작성한다.
4. mysql 데이터베이스 및 테이블 만들기
CREATE database commednts;
use comments;
CREATE table Comment(
`id` INT(11) PRIMARY KEY AUTO_INCREMENT,
`userid` VARCHAR(30) NOT NULL,
`content` TEXT NOT NULL,
`register` DATETIME NOT NULL DEFAULT now()
);
5. back server에서 db 연결하기
/back/models/index.js
사용할 mysql정보를 담아서 모듈로 만든다.
필요한 내용 : host, port, user, password, database
mysql의 데이터를 가져오기 위해서 연결하는 것이니 mysql설치하면서 만든 정보 및 4번에서 만든 database이름을 적어준다.
6. HTML, CSS 연결하기
댓글 생성을 위한 HTML, CSS 파일 레이아웃 만들어서 연결하기.
7. JS 연결하기
6번에서 만든 HTML, CSS를 JavaScript에서 조작해서 CRUD를 할 수 있게 한다.
코드 확인하기
HTML이나 CSS는 그 위에 링크 내용이랑 비슷하니 생략한다.
Front - server.js
const express = require("express")
const nunjucks = require("nunjucks")
const app = express()
app.use(express.static("public"))
nunjucks.configure("views", {
express:app
})
app.get("/", (req, res)=>{
res.render("index.html")
})
app.listen(3005, ()=>{
console.log("front server open")
})
Back - server.js (오늘은 Create 부분만 했다.)
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())
/*
GET /comments
POST /comments
GET /comments/:id
PUT /comments/:id
DELETE /comments/:id
*/
// PUT
app.get("/comments" , (req, res, next)=>{
try{
res.send("전체 게시물 가져오기")
}catch(e){
next(e)
}
})
app.post("/comments", async(req, res, next)=>{
try{
const userid = "wb72"
const {content} = req.body
console.log(req.body)
//userid가 없으면... 논리연산자를 붙이면 T||F로 값이 바뀜
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)
//DATE_FORMAT(register,'%Y-%m-%d') as resister
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) //e = new Error("userid가 없습니다.")
}
})
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)
res.send(`ERROR ${error}`)
})
app.listen(3000, ()=>{
console.log("back server open")
})
CSS
const commentFrm = document.querySelector("#commentFrm")
const commentList = document.querySelector("#comment-list")
const state = []
const request = ({ method, path, body } ) =>{
return new Promise((resovle, reject)=>{
const host =`http://127.0.0.1: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 ){
resovle(JSON.parse(xhr.response))
}else{
reject("에러발생~")
}
}
})
}
const setTotalRecord = () => {
const span = document.querySelector("h4 > span")
span.innerHTML = `(${state.length})`
}
const getBox = (flag, content) => (!flag ? createContentBox(content) : createUpdateBox(content))
//일반적인 글 작성
function createContentBox(content) {
// const selector = "#" + "content-baisc"
const template = document.querySelector("#content-baisc")
const clone = document.importNode(template.content, true)
console.log(template)
console.log(clone)
const span = clone.querySelectorAll("span")
span[0].innerHTML = content
return clone
}
// 제목을 눌렀을 때 글 수정
function createUpdateBox(content) {
const template = document.querySelector("#content-update")
const clone = document.importNode(template.content, true)
const input = clone.querySelector("span > input")
input.addEventListener("keyup", enterHandler)
input.value = content
return clone
}
function enterHandler(e) {
if (e.keyCode !== 13) return
try {
const { index } = e.target.parentNode.parentNode.parentNode.dataset
const { value } = e.target
state[index].Content = value
state[index].updated = !state[index].updated
// 수정
drawing()
} catch (e) {
alert(e.message)
}
}
function createRow(index) {
const template = document.querySelector("#commentRow")
const clone = document.importNode(template.content, true)
const ul = clone.querySelector("ul")
const li = clone.querySelectorAll("li")
const item = state[index]
ul.dataset.index = index
li[0].innerHTML = item.userid
li[1].innerHTML = ""
li[1].append(getBox(item.updated, item.content))
li[2].innerHTML = item.register
return ul
}
function drawing() {
commentList.innerHTML = ""
for (let i = state.length - 1; i >= 0; i--) {
const row = createRow(i)
commentList.append(row)
}
}
async function addState(value) {
try {
const content = value
const instance = await request({method:"post", path:"/comments", body:{content}})
state.push(instance)
setTotalRecord()
drawing()
} catch (e) {
alert(e.message)
}
}
function submitHandler(e) {
e.preventDefault()
const { content } = e.target
const { value } = content
addState(value)
content.focus()
this.reset()
}
function clickHandler(e) {
const contentNode = e.target.parentNode
const { index } = contentNode.parentNode.dataset
switch (e.target.className) {
case "comment-delete-btn":
const flag = confirm("삭제 할꺼야?~")
if (!flag) return
state.splice(index, 1)
drawing()
break
case "comment-update-btn":
state[index].updated = !state[index].updated
const content = e.target.innerHTML
contentNode.innerHTML = ""
const item = getBox(state[index].updated, content)
contentNode.append(item)
break
}
}
setTotalRecord()
commentList.addEventListener("click", clickHandler)
commentFrm.addEventListener("submit", submitHandler)
'시작 > TIL(Today I Learned)' 카테고리의 다른 글
230106 - JavaScript - module, 모듈화 하기 (0) | 2023.01.09 |
---|---|
230105 - JavaScript - AJAX, FETCH, AXIOS를 이용한 비동기 통신하기 (0) | 2023.01.05 |
MySQL 명령어, 테이블 구조 (0) | 2023.01.03 |
Network - HTTP 통신, REST API (0) | 2023.01.03 |
230103 - SSR,CSR 비교 / AJAX / 프론트엔드 서버와 백엔드서버 / CORS (0) | 2023.01.03 |
댓글