본문 바로가기
시작/TIL(Today I Learned)

230104 -JavaScript DB를 이용한 댓글 기능 만들기

by 백씨네 2023. 1. 5.

오늘 내가 배운 것

1. MySQL, CSR 방식을 이용한 댓글 기능 구현

 

기본 틀은 기존에 만들었던 코드를 이용해서 만든다.

https://baekspace.tistory.com/61

 

221116 실전예제 - try_catch(), 데이터셋(dataset, data- ), CRUD 댓글창 만들기

오늘 내가 배운 것 1. try-catch() 2. 데이터셋 3. CRUD - update 영역, delete 영역 코드 진행 순서 1. Creat : 댓글을 입력할 수 있다 - 댓글 입력 폼에 내용을 입력 한 뒤 `submit` 을 누르면 리스트에 추가된다.

baekspace.tistory.com

 

 

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)​

 

 

 

 


 

댓글