NODE.JS 이미지 구현, 수정 및 삭제

history.back() : 이전 페이지로 돌아가기

const service = require("../../service/board/board_service");
const common = require("../../service/ser_common");

const board_views = {
  list: async (req, res) => {
    const list = await service.boardRead.list();
    res.render("board/list", { list });
  },
  writeForm: (req, res) => {
    const session = req.session;
    const msg = common.sessionCheck(session);
    if (msg != 0) {
      return res.send(msg);
    }
    res.render("board/write_form", { username: session.username });
  },
  data: async (req, res) => {
    const data = await service.boardRead.data(req.params.num);
    const username = req.session.username;
    res.render("board/data", { data, username });
  },
  modifyForm: async (req, res) => {
    const data = await service.boardRead.data(req.params.writeNo);
    res.render("board/modify_form", { data });
  },
};
const board_process = {
  write: async (req, res) => {
    const msg = await service.boardInsert.write(
      req.body,
      req.file,
      req.fileValidation
    );
    res.send(msg);
  },
  delete: (req, res) => {
    console.log("aaaaaaaaaaaaaaaaaaaaaaaaa");
    //데이터베이스 삭제 성공시 file 삭제
    file_process.delete(req.params.imgName);
    service.boardUpdate.delete(req.params.writeNo);
    res.redirect("/board/list");
  },
  modify: async (req, res) => {
    const deleteFile = req.body.change_file_name;
    const message = await service.boardUpdate.modify(req.body, req.file);
    if (req.file != undefined && message.result == 1) {
      file_process.delete(deleteFile);
    }
    res.send(message.msg);
  },
};
const fs = require("fs");
const file_process = {
  delete: (imgName) => {
    if (imgName != "nan") {
      fs.unlinkSync(`./upload_file/${imgName}`);
    }
  },
  download: async (req, res) => {
    const filePath = `./upload_file/${req.params.imgName}`;
    res.download(filePath);
  },
};
module.exports = { board_views, board_process, file_process };
const dao = require("../../database/board/board_dao");
const common = require("../ser_common");

boardUpdate = {
  upHit: (num) => {
    dao.boardUpdate.upHit(num);
  },
  delete: (writeNo) => {
    dao.boardUpdate.delete(writeNo);
  },
  modify: async (body, file) => {
    if (file != undefined) {
      body.origin_file_name = file.originalname;
      body.change_file_name = file.filename;
    }
    console.log("body => ", body);
    const result = await dao.boardUpdate.modify(body);
    console.log("result => ", result);
    let msg, url;
    let message = {};
    message.result = result.rowsAffected;
    if (message.result == 1) {
      msg = "수정 되었습니다";
      url = `/board/data/${body.write_no}`;
    } else {
      msg = "문제 발생 !!!";
      url = `/board/modify_form/${body.write_no}`;
    }
    message.msg = common.getMessage(msg, url);
    return message;
  }
};
const boardRead = {
  list: async () => {
    let list = await dao.boardRead.list();
    list = common.timeModify(list.rows);
    return list;
  },
  data: async (num) => {
    boardUpdate.upHit(num);
    let data = await dao.boardRead.data(num);
    data = common.timeModify(data.rows);
    return data[0];
  },
};
const boardInsert = {
  write: async (body, file, fileValidation) => {
    let msg, url;
    if (fileValidation) {
      msg = fileValidation;
      url = "/board/write_form";
      return common.getMessage(msg, url);
    }
    console.log("file : ", file);
    if (file != undefined) {
      body.origin_file_name = file.originalname;
      body.change_file_name = file.filename;
    } else {
      body.origin_file_name = "nan";
      body.change_file_name = "nan";
    }
    console.log("body : ", body);
    const result = await dao.boardInsert.write(body);
    if (result.rowsAffected === 1) {
      msg = "등록되었습니다!!!";
      url = "/board/list";
    } else {
      msg = "문제 발생!!!";
      url = "/board/write_form";
    }
    return common.getMessage(msg, url);
  },
};
module.exports = { boardRead, boardInsert, boardUpdate };
const con = require("../common_dao");
const boardRead = {
  list: async () => {
    const sql = "select * from board";
    console.log("con : ", con);
    const list = (await con).execute(sql);
    return list;
  },
  data: async (num) => {
    const sql = `select * from board where write_no='${num}'`;
    const data = (await con).execute(sql);
    return data;
  },
};
const boardInsert = {
  write: async (body) => {
    const sql = `insert into board(write_no, id, title, content, origin_file_name,
        change_file_name) values(board_seq.nextval, :id, :title, :content, 
        :origin_file_name, :change_file_name)`;
    const result = await (await con).execute(sql, body);
    console.log("result : ", result);
    return result;
  },
};
boardUpdate = {
  upHit: async (num) => {
    const sql = `update board set hit = hit + 1 where write_no='${num}'`;
    (await con).execute(sql);
  },
  delete: async (writeNo) => {
    const sql = `delete from board where write_no=${writeNo}`;
    (await con).execute(sql);
  },
  modify: async (body) => {
    const sql = `update board set title=:title, content=:content, 
        origin_file_name=:origin_file_name, 
        change_file_name=:change_file_name where write_no=:write_no`;
    let result = 0;
    try {
      result = (await con).execute(sql, body)
    } catch (err) {
      console.log(err);
    }
    return result;
  },
};
module.exports = { boardRead, boardInsert, boardUpdate };

 

list.ejs

<%- include ("../default/header") %>
<div class="content wrap">
    <table border="1" style="width:100%;">
        <tr>
            <th>번호</th> <th>id</th> <th>제목</th> <th>날짜</th>
            <th>조회수</th> <th>원본 이미지이름</th> <th>변경 이미지이름</th>
        </tr>
        <% if( list.length == 0){ %>
            <tr>
                <th colspan="7">등록된 글이 없습니다</th>
            </tr>
        <%}else{
            list.forEach(data=>{%>
            <tr>
                <td><%=data.WRITE_NO%></td><td><%=data.ID%></td>
                <td><a href="/board/data/<%= data.WRITE_NO %>"><%=data.TITLE%></a></td><td><%=data.SAVE_DATE%></td>
                <td><%=data.HIT%></td><td><%=data.ORIGIN_FILE_NAME%></td>
                <td><%=data.CHANGE_FILE_NAME%></td>
            </tr>
            <%})
        }%>
        <tr>
            <td colspan="7" align="right">
                <a href="/board/write_form">글 작성</a>
            </td>
        </tr>
    </table>
</div>

 

data.ejs

<%- include ("../default/header") %>
<div class="content wrap">
    <div style="width:600px; margin: 0 auto;">
    <h3 style="text-align: center; margin-bottom: 10px;"> - 개 인 정 보 - </h3>
    <table border="1" align="center">
        <tr>
            <th width="100">글 번호</th> <td width="200"><%= data.WRITE_NO %></td> 
            <th width="100">작성자</th>  <td width="200"><%= data.ID %></td>
        </tr>
        <tr>
            <th>제목</th> <td><%= data.TITLE %></td> 
            <th>등록일자</th> <td><%= data.SAVE_DATE %></td>
        </tr>
        <tr>
            <th>내용</th><td><%= data.CONTENT %></td> 
            <td colspan="2">
                <% if( data.ORIGIN_FILE_NAME === "nan"){%>
                    <b>이미지가 없습니다
                <%}else{%>
                    <b>이미지 : <%=data.ORIGIN_FILE_NAME%> </b>
                    <br>
                    <img src="/board/download/<%= data.CHANGE_FILE_NAME %>" width="100px" height="100px">
                <%}%>
            </td>
        </tr>
        <tr>
            <td colspan="4" align="center">
                <%if(username === data.ID){%>
                    <input type="button" onclick="location.href=
                    '/board/modify_form/<%= data.WRITE_NO %>'" value="수정하기"> 
                    <input type="button" onclick="location.href=
                    '/board/delete/<%=data.WRITE_NO%>/<%=data.CHANGE_FILE_NAME%>'"
                    value="삭제하기">
                <%}%>
                <input type="button" onclick="" value="답글달기"> 
                <input type="button" onclick="" value="리스트로 돌아가기">
            </td>
        </tr>
        </table>
    </div>
</div>

modify_form.ejs

<%- include ("../default/header") %>
<script src="/static/js/image_read.js"></script>
<div class="content wrap">
<div style="width:300px; margin: 0 auto;">
    <form action="/board/modify" enctype="multipart/form-data" method="post" >
        <input type="hidden" name="write_no" value="<%=data.WRITE_NO%>">
        <input type="hidden" name="change_file_name" value="<%=data.CHANGE_FILE_NAME%>">
        <input type="hidden" name="origin_file_name" value="<%=data.ORIGIN_FILE_NAME%>">
        제목  <input type="text" size="30" name="title" value="<%=data.TITLE%>"><hr>
        내용  <textarea rows="5" cols="30" name="content"><%=data.CONTENT%></textarea>
        <hr>
        <img width="200px" height="100px" id="img"
                        src="/board/download/<%=data.CHANGE_FILE_NAME%>">
        <input type="file" name="image_file_name" onchange="readURL(this)">
        <hr>
        <input type="submit" value="수정하기">
        <input type="button" onclick="history.back()" value="이전으로 돌아가기">
    </form>
</div>
</div>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 페이징 및 댓글 달기

 

 

reply.css 작성

#modal_wrap{
    display: none; position: fixed; z-index: 9;
    margin: 0 auto; top:0; left: 0; right: 0;
    width:100%; height:100%;
    background-color: rgba(0, 0, 0, 0.4);
}
#first{ 
    position: fixed; z-index: 10; margin: 0 auto;
    top:30px; left: 0; right: 0; display:none;
    background-color: rgba(212, 244, 250, 0.9);
    height:350px; width:300px;
}

reply.css 코드

 

reply.js 작성

function reply_form(){
    $("#first").slideDown('slow'); 
    $("#modal_wrap").show();
}
function reply_hide(){
    $("#first").slideUp('fast'); 
    $("#modal_wrap").hide();
}
function rep(){
    let form={}; 
    let arr = $("#frm").serializeArray();
    arr.forEach( d => { form[d.name] = d.value; })
    fetch("/boardrep/register", {
        method : "post",
        headers : {"Content-Type" : "application/json"},
        body : JSON.stringify( form )
    })
    .then(res => res.json() )
    .then( result => {
        if(result === 1)
            alert("답글이 달렸습니다!!")
        reply_hide();
    })
}

reply.js 코드

 

reply_form.ejs 작성

<link href="/static/css/reply.css" rel="stylesheet" >
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="/static/js/reply.js"></script>
<div id="modal_wrap">
    <div id="first">
        <div style="width:250px;margin: 0 auto;padding-top: 20px;">
            <form id="frm">
                <input type="hidden" name="write_no" value="<%= data.WRITE_NO %>">
                <input type="hidden" name="id" value="<%= username %>">
                <b>답글 작성 페이지</b> <hr>
                <b>작성자 : <%= username %></b> <hr>
                <b>제목</b><br><input type="text" size="30" name="title"><hr>
                <b>내용</b><br>
                <textarea name="content" rows="5" cols="30"></textarea>
                <hr>
                <button type="button" onclick="rep()">답글</button>
                <button type="button" onclick="reply_hide()">취소</button>
            </form>
        </div>
    </div>
</div>

reply_form.ejs 코드

 

data.ejs 위에 작성

 

data.ejs 에 추가 작성 ( 이 작업은 댓글 창을 따로 띄우는 것이 아닌 페이지 내에 띄우는 작업, 댓글 창 스타일에 따라 작성 )

 

답글 달기 버튼을 눌렀을때 텍스트 박스와 답변 버튼이 조그맣게 생기는 부분이 data.ejs 하단에 script 와 button, div, form 태그 추가한 부분

 

댓글을 달때 fetch 를 통해 넘어오는 json 형태의 데이터를 받기 위해 app.js 에 json 형태를 받을 수 있게 추가

 

기본 router.js 에서 답글 기능에 대한 라우터를 board_reply_router.js 에 위임

 

board_reply_router.js 에 경로 작성

 

board_reply_ctrl.js 에 로직 작성

 

board_reply_service.js 작성

 

board_reply_dao.js 작성

 

reply_view.js 작성

function init( groupNum ){
        fetch(`/boardrep/replyData/`+groupNum )
        .then( res => res.json() )
        .then( data => {
            let html = ""
            data.forEach((d)=>{
                html += "<div align='left'><b>아이디 : </b>"+d.ID+"님 / ";
                html += "<b>작성일</b> : "+d.SAVE_DATE+"<br>"
                html += "<b>제목</b> : "+d.TITLE+"<br>"
                html += "<b>내용</b> : "+d.CONTENT+"<hr></div>"
            });
            const content = document.getElementById("content");
            content.innerHTML = html;
        });
    }

reply_view.js 코드

 

data.ejs 에 내용 추가 ( td 추가한 부분은 댓글 부분 )

 

header.ejs 에 작성 ( header.ejs 가 onload 로 기본으로 실행될 때 init 함수가 실행되게 함

 

board_reply_router.js 작성

 

board_reply_ctrl.js 작성

 

board_reply_service.js 작성 ( 시간을 설정하기 위해 ser_common 의 기능을 호출하여 사용함 )

 

board_reply_dao.js 작성

 

 

 

현재는 답글 달면 새로고침 하기 전에 보이지 않는데 바로 댓글 입력한 데이터를 바로 출력하게끔 입력 받은 값을 덧붙여서 출력 ( 새로 고침을 하게 되면 DB 에서 끌고온 데이터가 출력되며, 이 방법은 일시적으로 데이터를 출력할 뿐 )

 

댓글을 바로 데이터로 덧붙여서 보여주게끔 설정, 위 코드는 일시적으로 댓글을 달면 자신이 단 댓글을 보여줄 뿐이며 실제 DB 에 적용된 데이터는 새로고침을 하면 출력되게 된다

 

실행 결과

 

 

 

'node.js' 카테고리의 다른 글

NODE.JS 활용 (7)  (0) 2024.04.08
NODE.JS 활용 (6)  (0) 2024.04.05
NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01

 

 

 

 

 

NODE.JS 페이징(게시판) (1)

oracledb.OBJECT : 불러온 db를 키, 값 형태로 가져오기

create sequence test_num : 1부터 1씩 증가시키겠다

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 페이징(게시판) (2)

 

const oracledb = require("oracledb");
const dbConfig = require("./../../config/db_config");

oracledb.outFormat = oracledb.OBJECT; //키, 값 형태로 가져오기
oracledb.autoCommit = true;

const daoRead = {
  list: async () => {
    const con = await oracledb.getConnection(dbConfig);
    let result = await con.execute("select * from paging");
    return result;
  },
  content: async (num) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `select * from paging where num='${num}'`;
    const data = await con.execute(sql);
    return data;
  }
};

const daoInsert = {
  write: async (body) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `insert into paging values(test_num.nextval,:title, sysdate, 0)`;
    let result = 0;
    try {
      result = await con.execute(sql, body);
    } catch (err) {
      console.log(err);
    }
  },
};

const daoUpdate = {
    upHit: async (num) => {
        const con = await oracledb.getConnection(dbConfig);
        const sql = `update paging set count=count+1 where num='${num}'`;
        await con.execute(sql);
    }
}
module.exports = { daoRead, daoInsert, daoUpdate };

 

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 페이징(게시판) (3)

* 한 페이지당 몇개의 글을 보여줄지 적용하는 방식

rownum : 글을 삭제하거나 추가하더라도, 개수만을 세어주는 역할이기 때문에 공백없이 잘 가져올 수 있다

 

Controller

const service = require("../service/page_service");
const views = {
  index: (req, res) => {
    res.render("index");
  },
  list: async (req, res) => {
    console.log("req.query.start : ", req.query.start);
    const totalContent = await service.pageRead.totalContent();

    const data = await service.pageRead.list(req.query.start, totalContent);

    //const list = await service.pageRead.list();
    console.log("data : ", data);
    console.log("data.list : ", data.list);
    console.log("data.start : ", data.start);
    res.render("list", { list: data.list, page: data.page, start: data.start, totalContent });
  },
  writeForm: (req, res) => {
    res.render("write_form");
  },
  content: async (req, res) => {
    const data = await service.pageRead.content(req.params.num);
    res.render("content", { data });
  },
};
const process = {
  write: async (req, res) => {
    const msg = await service.pageInsert.write(req.body);
    res.redirect("/page/list");
  },
};
module.exports = { views, process };

Service

const dao = require("../database/pageDAO");
const pageRead = {
  list: async (start, totalC) => {
    start = start && start > 1 ? Number(start) : 1;

    const page = pageOperation(start, totalC);

    const list = await dao.daoRead.list(page.startNum, page.endNum);
    console.log("service : ", list);

    data = {};
    data.page = page; 
    data.start = start;
    data.list = list.rows;

    return data;
  },
  content: async (num) => {
    await pageUpdate.upHit(num);

    const data = await dao.daoRead.content(num);
    return data.rows[0];
  },
  totalContent: async () => {
    const totalContent = await dao.daoRead.totalContent();
    console.log(totalContent);
    return totalContent.rows[0]["COUNT(*)"];
  },
};

const pageOperation = (start, totalContent) => {
  let page = {};
  const pageNum = 3; //하나의 페이지에 몇개의 게시물을 보여줄 지...
  const num = (totalContent % pageNum == 0) ? 0 : 1; 
  page.totPage = parseInt(totalContent / pageNum) + num;// 총 페이지 수
  page.startNum = (start - 1) * pageNum + 1; //start에 따라 페이지에 처음으로 보여줄 게시물
  page.endNum = start * pageNum;  //start에 따라 페이지에 마지막으로 보여줄 게시물
  return page;
};

const pageUpdate = {
  upHit: async (num) => {
    await dao.daoUpdate.upHit(num);
  },
};
const pageInsert = {
  write: async (body) => {
    const result = await dao.daoInsert.write(body);
  },
};
module.exports = { pageRead, pageInsert, pageOperation };

DAO

const oracledb = require("oracledb");
const dbConfig = require("./../../config/db_config");

oracledb.outFormat = oracledb.OBJECT; //키, 값 형태로 가져오기
oracledb.autoCommit = true;

const daoRead = {
  list: async (s, e) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `select B.* from
                    (select rownum rn, A.* from
                        (select * from paging order by num desc)A)B 
                            where rn between ${s} and ${e}`;
    let result = await con.execute(sql);
    return result;
  },
  content: async (num) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `select * from paging where num='${num}'`;
    const data = await con.execute(sql);
    return data;
  },
  totalContent: async () => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = "select count(*) from paging";
    const totalContent = await con.execute(sql);
    return totalContent;
  },
};

const daoInsert = {
  write: async (body) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `insert into paging values(test_num.nextval,:title, sysdate, 0)`;
    let result = 0;
    try {
      result = await con.execute(sql, body);
    } catch (err) {
      console.log(err);
    }
  },
};

const daoUpdate = {
  upHit: async (num) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `update paging set count=count+1 where num='${num}'`;
    await con.execute(sql);
  },
};
module.exports = { daoRead, daoInsert, daoUpdate };

ejs

<table border="1">
    <tr>
        <th>번호</th> <th>제목</th> <th>등록날짜</th> <th>조회수</th>
    </tr>
<% list.forEach( data => { %>
    <tr>
        <td><%= data.NUM %></td>  
        <td><a href="/page/content/<%= data.NUM %>"><%= data.TITLE %></a></td>
        <td><%= data.PDATE %></td> 
        <td><%= data.COUNT %></td>
    </tr>
<% });
if( list.length == 0 ){%>
    <tr><td colspan="4"><b>등록된 정보가 없습니다</b></td></tr>
<%} %>
    <tr><td colspan="4">
        <% if( start <= 1){ %>
            <button disabled>이전</button>
        <%}else{%>
            <button type="button" onclick="
                location.href='/page/list?start=<%=start - 1%>'">이전</button>
        <% }for(let i=1 ; i <= page.totPage ; i++ ){ %>
            <a href="/page/list?start=<%= i %>"><%= i %></a>
        <% }if( start < page.totPage ){%>
            <button type="button" onclick="
                location.href='/page/list?start=<%=start + 1%>'">다음</button>
        <% }else{ %>
            <button disabled>다음</button>
        <% } %>
            <br>
        start : <%= start %> / <%= page.totPage %> / <a href="/page/write_form">글 등록</a>
    </td></tr>
</table>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 페이징(게시판) (4)

quiz_dbconnect_board.zip
3.00MB

 

 

 

위 파일에 이어서 작업

 

<link href="/static/css/header.css" rel="stylesheet" />
<script>
  window.onload = () => {
    console.log(document.cookie);
    let msg = "<li><a href='/'>HOME</a></li>";

    msg += "<li><a href='/board/list'>BOARD</a></li>";

    if (document.cookie.indexOf("isLogin=true") !== -1) {
      msg += '<li><a href="/member/logout">로그아웃</a></li>';
      msg += '<li><a href="/member/list">회원정보</a></li>';
    } else {
      msg += '<li><a href="/member/login">로그인</a></li>';
      msg += '<li><a href="/member/login">회원정보</a></li>';
    }
    document.querySelector("#nav ul").innerHTML = msg;
  };
</script>
<div class="wrap">
  <div class="header">
    <h1 class="title">CARE LAB</h1>
  </div>
</div>
<div class="navdiv">
  <div class="wrap">
    <nav id="nav" class="nav">
      <ul></ul>
    </nav>
  </div>
</div>
<hr />
<%- include ("../default/header") %>
<script src="/static/js/image_read.js"></script>
<div class="content wrap">
    <div style="width: 400px; margin: 0 auto; ">
        <h1 style="text-align: center">글쓰기</h1>
        <form method="post" action="/board/write" 
                                        enctype="multipart/form-data">
            <b>작성자</b><br>   <!-- readonly : 읽기 전용 -->
            <input type="text" name="id" value="<%= username %>" readonly />
            <hr>
            <b>제목</b> <br> <input type="text" size="50" name="title" /><hr>
            <b>내용</b> <br>
            <textarea name="content" rows="10" cols="50"></textarea><hr>
            <b>이미지파일 첨부</b><br>
            <input type="file" name="image_file_name" onchange="readURL(this);" /> 
            <img id="img" src="#" width=100 height=100 alt="선택된 이미지가 없습니다" />
            <hr> 
            <input type="submit" value="글쓰기">
            <input type=button value="목록보기" onClick="location.href='/board/list'">
        </form>
    </div>
</div>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 페이징(게시판) (5)

const service = require("../../service/board/board_service");
const common = require("../../service/ser_common");

const board_views = {
  list: async (req, res) => {
    const list = await service.boardRead.list();
    res.render("board/list", { list });
  },
  writeForm: (req, res) => {
    const session = req.session;
    const msg = common.sessionCheck(session);
    if (msg != 0) {
      return res.send(msg);
    }
    res.render("board/write_form", { username: session.username });

  },
};
const board_process = {
    write : async (req, res) => {
        const msg = await service.boardInsert.write(
            req.body, req.file, req.fileValidation
        );
        res.send(msg);
    }
}
module.exports = { board_views, board_process };
const dao = require("../../database/board/board_dao");
const common = require("../ser_common");

const boardRead = {
  list: async () => {
    let list = await dao.boardRead.list();
    list = common.timeModify(list.rows);
    return list;
  },
};
const boardInsert = {
  write: async (body, file, fileValidation) => {
    let msg, url;
    if (fileValidation) {
      msg = fileValidation;
      url = "/board/write_form";
      return common.getMessage(msg, url);
    }
    console.log("file : ", file);
    if (file != undefined) {
      body.origin_file_name = file.originalname;
      body.change_file_name = file.filename;
    } else {
      body.origin_file_name = "nan";
      body.change_file_name = "nan";
    }
    console.log("body : ", body);
    const result = await dao.boardInsert.write(body);
    if (result.rowsAffected === 1) {
        msg = "등록되었습니다!!!";
        url = "/board/list"
    }else{
        msg = "문제 발생!!!";
        url = "/board/write_form";
    }
    return common.getMessage(msg, url)
  },
};
module.exports = { boardRead, boardInsert };
const con = require("../common_dao");
const boardRead = {
  list: async () => {
    const sql = "select * from board";
    console.log("con : ", con);
    const list = (await con).execute(sql);
    return list;
  },
};
const boardInsert = {
    write: async (body) => {
        const sql = `insert into board(write_no, id, title, content, origin_file_name,
        change_file_name) values(board_seq.nextval, :id, :title, :content, 
        :origin_file_name, :change_file_name)`;
        const result = await (await con).execute(sql, body);
        console.log("result : ", result);
        return result;
    }
}
module.exports = { boardRead, boardInsert };

<link href="/static/css/header.css" rel="stylesheet" />
<script>
  window.onload = () => {
    console.log(document.cookie);
    let msg = "<li><a href='/'>HOME</a></li>";

    msg += "<li><a href='/board/list'>BOARD</a></li>";

    if (document.cookie.indexOf("isLogin=true") !== -1) {
      msg += '<li><a href="/member/logout">로그아웃</a></li>';
      msg += '<li><a href="/member/list">회원정보</a></li>';
    } else {
      msg += '<li><a href="/member/login">로그인</a></li>';
      msg += '<li><a href="/member/login">회원정보</a></li>';
    }
    document.querySelector("#nav ul").innerHTML = msg;
  };
</script>
<div class="wrap">
  <div class="header">
    <h1 class="title">CARE LAB</h1>
  </div>
</div>
<div class="navdiv">
  <div class="wrap">
    <nav id="nav" class="nav">
      <ul></ul>
    </nav>
  </div>
</div>
<hr />
<%- include ("../default/header") %>
<script src="/static/js/image_read.js"></script>
<div class="content wrap">
    <div style="width: 400px; margin: 0 auto; ">
        <h1 style="text-align: center">글쓰기</h1>
        <form method="post" action="/board/write" 
                                        enctype="multipart/form-data">
            <b>작성자</b><br>   <!-- readonly : 읽기 전용 -->
            <input type="text" name="id" value="<%= username %>" readonly />
            <hr>
            <b>제목</b> <br> <input type="text" size="50" name="title" /><hr>
            <b>내용</b> <br>
            <textarea name="content" rows="10" cols="50"></textarea><hr>
            <b>이미지파일 첨부</b><br>
            <input type="file" name="image_file_name" onchange="readURL(this);" /> 
            <img id="img" src="#" width=100 height=100 alt="선택된 이미지가 없습니다" />
            <hr> 
            <input type="submit" value="글쓰기">
            <input type=button value="목록보기" onClick="location.href='/board/list'">
        </form>
    </div>
</div>
<%- include ("../default/header") %>
<div class="content wrap">
    <table border="1" style="width:100%;">
        <tr>
            <th>번호</th> <th>id</th> <th>제목</th> <th>날짜</th>
            <th>조회수</th> <th>원본 이미지이름</th> <th>변경 이미지이름</th>
        </tr>
        <% if( list.length == 0){ %>
            <tr>
                <th colspan="7">등록된 글이 없습니다</th>
            </tr>
        <%}else{
            list.forEach(data=>{%>
            <tr>
                <td><%=data.WRITE_NO%></td><td><%=data.ID%></td>
                <td><%=data.TITLE%></td><td><%=data.SAVE_DATE%></td>
                <td><%=data.HIT%></td><td><%=data.ORIGIN_FILE_NAME%></td>
                <td><%=data.CHANGE_FILE_NAME%></td>
            </tr>
            <%})
        }%>
        <tr>
            <td colspan="7" align="right">
                <a href="/board/write_form">글 작성</a>
            </td>
        </tr>
    </table>
</div>

 

실행 결과

 

'node.js' 카테고리의 다른 글

NODE.JS 활용 (8)  (0) 2024.04.09
NODE.JS 활용 (6)  (0) 2024.04.05
NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01

 

 

 

 

 

 

 

NODE.JS file (1)

enctype="multipart/form-data" : form태그 안에 파일을 보내는 형식 설정

* controller에서 module.exports 안에 양이 많아지는걸 대비해 ,

따로 오브젝트를 만들어서 여러 함수를 저장한 후, 

내보낼 때는 키 밸류 형식으로 내보낸다.

* 따라서, router에서 보낸때는 컨트롤러면.오브젝트명.함수명 형식으로 보낸다.

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS file (2)

npm install multer@1.4.4 --save : multer 기능 설치

req.file : 받아온 파일 전송 방법

require("multer") : 설치한 multer기능 불러오기

multer.diskStorage({

destination: (req, file, db) => { : cb : 파일이 들어왔을때 어떻게 처리할 지

cb(null, "upload_file") : 파일을 불러왔을때 어디에 저장할 지 지정

destination: (req, file, db) => { 

cb(null, file.originalname) : 넘겨올 떄 파일 이름을 원하는대로 설정(originalname : 원본 파일명)

Data.now() : 현재 시각

cb(null, true) : true면 저장

controller에게 보낼 때, upload.single("file_name") : 단일 파일 저장 목적 (괄호 안에는 ejs에서 널길 때 파일명과 동일)

const router = require("express").Router();
const multer = require("multer"); //설치한 multer기능 불러오기
//const upload = multer({ dest: "upload_file" }); // 사용자가 파일을 넘겼을 때 어디(폴더)에 만들지 지정
// dest : 목적지
const stg = multer.diskStorage({
  destination: (req, file, cb) => {
    // cb : 파일이 들어왔을때 어떻게 처리할 지
    console.log("req : ", req.body);
    console.log("file : ", file);
    console.log("cb : ", cb);
    cb(null, "upload_file");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname); // 넘겨올 때 파일 이름을 원하는대로 설정 (originalname = 원본 파일 명)
  }, // Date.now() : 현재 시각
});

const f_filter = (req, file, cb) => {
  console.log("f_filter file : ", file.mimetype.split("/"));
  const type = file.mimetype.split("/");
  // if (type[1] == "jpg" || type[1] == "jpeg" || type[1] == "png") {
  if (type[0] == "image") {
    cb(null, true); //true면 저장
  } else {
    req.fileValidation = "이미지만 저장하세요";
    cb(null, false);
  }
};

const upload = multer({ storage: stg, fileFilter: f_filter });
const fileCtrl = require("./../controller/file_controller");

router.get("/", fileCtrl.view.index);
router.post("/upload", upload.single("file_name"), fileCtrl.process.upload);
//단일 파일 저장 목적(괄호 안에는 ejs에서 넘길 떄 파일명과 동일하게)

module.exports = router;

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS file (3)

res.download(경로) : 경로에 있는 것을 다운로드 해주는 기능

thist = 본인 태그 자기 자신

FileReader() : 파일을 불러오는 기능

불러오 파일.onload() : 파일을 화면에 보여주는 기능

onchange="" : HTML요소에 변화가 일어났을 때

readAsDataURL() : 해당 객체를 URL표현으로 변환

<script>
  const readURL = (input) => {
    console.log(input);
    console.log(input.files[0]);
    const file = input.files[0];
    if (file != "") {
      let reader = new FileReader(); // 파일을 불러오는 기능
      reader.readAsDataURL(file);
      reader.onload = (e) => {
        // 파일을 화면에 보여주는 기능
        console.log(e.target.result);
        document.getElementById("img").src = e.target.result;
      };
    }
  };
</script>

<form action="/file/upload" method="post" enctype="multipart/form-data">
  <!-- 파일 보내는 형식 표시 -->
  <input type="text" name="title" /><br />
  <img id="img" src="" width="100" height="100" />
  <input type="file" name="file_name" onchange="readURL(this)" /><br />
  <input type="submit" value="전송" />
</form>
<hr />
<a href="/file/list">목록 확인</a>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS file (4)

fs.unlinkSync : 파일 삭제

* 기능이나 함수들은 script를 통해 받아온다

* css는 link를 통해 받아온다

app.use("/static", express.static("./public")) : public이라는 위치를 static이라는 명칭으로 사용

                                         그렇지 않으면 가져올 수 없다

const router = require("express").Router();
const multer = require("multer"); //설치한 multer기능 불러오기
//const upload = multer({ dest: "upload_file" }); // 사용자가 파일을 넘겼을 때 어디(폴더)에 만들지 지정
// dest : 목적지
const stg = multer.diskStorage({
  destination: (req, file, cb) => {
    // cb : 파일이 들어왔을때 어떻게 처리할 지
    console.log("req : ", req.body);
    console.log("file : ", file);
    console.log("cb : ", cb);
    cb(null, "upload_file");
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + "-" + file.originalname); // 넘겨올 때 파일 이름을 원하는대로 설정 (originalname = 원본 파일 명)
  }, // Date.now() : 현재 시각
});

const f_filter = (req, file, cb) => {
  console.log("f_filter file : ", file.mimetype.split("/"));
  const type = file.mimetype.split("/");
  // if (type[1] == "jpg" || type[1] == "jpeg" || type[1] == "png") {
  if (type[0] == "image") {
    cb(null, true); //true면 저장
  } else {
    req.fileValidation = "이미지만 저장하세요";
    cb(null, false);
  }
};

const upload = multer({ storage: stg, fileFilter: f_filter });
const fileCtrl = require("./../controller/file_controller");

router.get("/", fileCtrl.view.index);
router.post("/upload", upload.single("file_name"), fileCtrl.process.upload);
//단일 파일 저장 목적(괄호 안에는 ejs에서 넘길 떄 파일명과 동일하게)
router.get("/list", fileCtrl.view.list);
router.get("/download/:fileName", fileCtrl.view.download);
router.get("/deleteFile/:fileName", fileCtrl.process.deleteFile);
router.get("/modify_form/:fileName", fileCtrl.view.modifyForm);
router.post("/modify", upload.single("newFileName"), fileCtrl.process.modify)
module.exports = router;
const fs = require("fs");
const view = {
  // module.exportes안에 양이 많아지는걸 대비해 따로 오브젝트를 만들어서 여러 함수를 저장한 후,
  // 내보낼때는 키 밸류 형식으로 내보낸다.
  index: (req, res) => {
    console.log("controller index 연동");
    res.render("file_index");
  },
  list: (req, res) => {
    let fList;
    fList = fs.readdirSync("./upload_file");
    res.render("file_list", { list: fList });
  },
  download: (req, res) => {
    const path = `./upload_file/${req.params.fileName}`;
    res.download(path); //다운로드를 해주는 기능
  },
  modifyForm: (req, res) => {
    const fileName = req.params.fileName;
    res.render("modify_form", { fileName });
  },
};
const process = {
  upload: (req, res) => {
    console.log(req.file); // 파일 전송 방법
    console.log("========");
    console.log(req.body);

    console.log("validation : ", req.fileValidation);
    if (req.fileValidation) {
      return res.send(req.fileValidation);
    }
    res.send("연동");
  },
  deleteFile: (req, res) => {
    fs.unlinkSync("./upload_file/" + req.params.fileName); //파일 삭제
    res.redirect("/file/list");
  },
  modify: (req, res) => {
    console.log("req.file : ", req.file)
    if (req.file) { //파일을 변경했다면
        return res.redirect(`/file/deleteFile/${req.body.originFileName}`);
    }
    res.redirect("/file/list");
  }
};

module.exports = { view, process };

 

실행 결과

 

'node.js' 카테고리의 다른 글

NODE.JS 활용 (8)  (0) 2024.04.09
NODE.JS 활용 (7)  (0) 2024.04.08
NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01

 

 

 

 

 

NODE.JS 암호화

암호화 기능 설치 : npm install bcrypt --save

bcrypt.hashSync(pwd, 10) : pwd를 암호화하는 역할, 숫자가 높을수록 더 복잡하게 암호화

bcrypt.compareSync(pwd, 암호화한 변수) : 암호화된 값과 일반 값을 비교하는 역할 (true, false)

const express = require("express");
const bcrypt = require("bcrypt");
const app = express();

app.get("/", (req, res) => {
  const pwd = "test";
  //const changePwd = bcrypt.hash(pwd, 10);
  //console.log(changePwd);
  //changePwd.then((res) => console.log(res));
  const changePwd = bcrypt.hashSync(pwd, 10); // pwd를 암호화하는 역할, 숫자가 높을수록 더 복잡하게 암호화
  console.log(changePwd);
  const result = bcrypt.compareSync(pwd, changePwd); // 암호화된 값과 일반 값을 비교하는 역할
  console.log(result)
  res.send("/경로");
});

app.listen(3000, () => console.log("3000 실행 ..."));

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fetch

__dirname : 현재 파일이 속해있는 폴더까지의 절대경로를 알려주는 역할

* location은 전체 페이지를주고 받지만 fetch는 데이터만 주고 받는다.

* 따라서 페이지가 다시 로드되지는 않는다.

.json() : json형식으로 변환하는 역할

<script>
  function test() {
    //location.href = "fetch01";
    //fetch("/fetch01");
    /*
    const result = fetch("/get_count");
    console.log("result : ", result);
    result.then((res) => {
        console.log("res : ", res);
        return res.json();
    })
    .then((data) => {
        console.log("data : ", data);
        document.getElementById("cnt").innerHTML = data.cnt;
    });
    */
    fetch("/get_count")
      .then((res) => res.json())
      .then((data) => {
        console.log("data : ", data);
        document.getElementById("cnt").innerHTML = data.cnt;
      });
  }
  async function test2() {
    const result = await fetch("/get_count");
    console.log("result ; ", reuslt);
    const data = await reuslt.json();
    console.log("data : ", data);
    document.getElementById("cnt").innerHTML = data.cnt;
  }
</script>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
<h1></h1>
<button onclick="test()">클릭</button>
<button onclick="test2()">클릭2</button>
<span id="cnt"><%= count %></span>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fetch (2)

get : 데이터를 얻어올 때

post : 추가

put : 수정

delete : 삭제

rest.ejs
<script>
  let txt;
  window.onload = () => {
    txt = document.getElementById("text");
  };
  const getFunc = () => {
    fetch("/test", { method: "get" })
      .then((res) => res.json())
      .then((data) => (txt.innerHTML = data));
  };
  const postFunc = () => {
    fetch("/test", { method: "post" })
      .then((res) => res.json())
      .then((data) => (txt.innerHTML = data));
  };
  const putFunc = async () => {
    const res = await fetch("/test", {method : "put"});
    const data = await res.json();
    txt.innerHTML = data
  }
  const deleteFunc = async () => {
    const res = await fetch("/test", {method : "delete"});
    const data = await res.json();
    txt.innerHTML = data
  }
</script>
<span id="text">내용</span>
<hr />
<button type="button" onclick="getFunc()">getFunc</button>
<button type="button" onclick="postFunc()">postFunc</button>
<button type="button" onclick="putFunc()">putFunc</button>
<button type="button" onclick="deleteFunc()">deleteFunc</button>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fetch (3)

SPA(single page application) : 여러 기능들이 한 페이지에서 작동

const express = require("express");
const app = express();

app.set("views", __dirname + "/views");
app.set("view engine", "ejs");

let = members = [
  { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
  { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
  { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
];
app.get("/", (req, res) => {
  res.render("index");
});
app.get("/get_members", (req, res) => {
  res.json(members);
});

app.listen(3000, () => console.log("3000 실행 ..."));
<script>
  let members = [
    { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
    { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
    { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
  ];
  let content;
  window.onload = () => {
    content = document.getElementById("content");
    const getM = document.getElementById("getMembers");
    getM.addEventListener("click", getMembers);
    const regView = document.getElementById("regView");
    regView.addEventListener("click", registerView);
  };
  const getMembers = () => {
    fetch("/get_members")
      .then((res) => res.json())
      .then((m) => {
        let msg = "<table border='1'>";
        msg += "<tr><th>아이디</th> <th>이름</th> <th>주소</th></tr>";
        members.forEach((mem) => {
          msg += "<tr>";
          msg += `<td>${mem.id}</td><td>${mem.name}</td><td>${mem.addr}</td>`;
          msg += "</tr>";
        });
        msg += "</table>";
        content.innerHTML = msg;
      });
  };
  const registerView = () => {
    let msg = `<form id="form">
  <input type="text" name="id" placeholder="input id" /><br />
  <input type="text" name="pwd" placeholder="input pwd" /><br />
  <input type="text" name="name" placeholder="input name" /><br />
  <input type="text" name="addr" placeholder="input addr" /><br />
  <input type="button" onclick="register()" value="저장" />
</form>`;
    content.innerHTML = msg;
  };
</script>

<div id="content"></div>
<hr />
<button type="button" id="getMembers">목록보기</button>
<button type="button" id="regView">회원가입</button>

 

실행 결과 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fetch (4)

$(태그명).serializeArray() : 해당하는 태그에 name을 키값으로, 입력한 값을 밸류값으로 만들어주는 역할

JSON.stringify(데이터) : 해당하는 data를 JSON형식으로 변환

const express = require("express");
const app = express();

app.set("views", __dirname + "/views");
app.set("view engine", "ejs");

let = members = [
  { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
  { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
  { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
];
app.get("/", (req, res) => {
  res.render("index");
});
app.get("/get_members", (req, res) => {
  res.json(members);
});
const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.post("/register", (req, res) => {
  console.log("req.body : ", req.body);
  members = members.concat(req.body);
  res.json(1);
});
app.get("/search/:id", (req, res) => {
  console.log(req.params);
  const result = members.filter((mem) => mem.id === req.params.id);
  console.log("result : ", result);
  res.json(result[0]);
});

app.listen(3000, () => console.log("3000 실행 ..."));
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
  let members = [
    { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
    { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
    { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
  ];
  let content;
  window.onload = () => {
    content = document.getElementById("content");
    const getM = document.getElementById("getMembers");
    getM.addEventListener("click", getMembers);
    const regView = document.getElementById("regView");
    regView.addEventListener("click", registerView);
  };

  const getMembers = () => {
    fetch("/get_members")
      .then((res) => res.json())
      .then((m) => {
        let msg = "<table border='1'>";
        msg += "<tr><th>아이디</th> <th>이름</th> <th>주소</th></tr>";
        members.forEach((mem) => {
          msg += "<tr>";
          msg += `<td>${mem.id}</td><td style="cursor:pointer;" onclick="info('${mem.id}')">${mem.name}</td><td>${mem.addr}</td>`;
          msg += "</tr>";
        });
        msg += "</table>";
        content.innerHTML = msg;
      });
  };
  const register = () => {
    const arr = $("#form").serializeArray(); // 해당하는 태그에 name을 키값으로, 입력한 값을 밸류값으로 만들어주는 역할
    console.log("arr : ", arr);
    let data = {};
    arr.forEach((d) => (data[d.name] = d.value));
    console.log("data : ", data);
    fetch("/register", {
      method: "post",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data), // 해당하는 data를 JSON형식으로 변환
    })
      .then((res) => res.json())
      .then((result) => {
        console.log(result);
        if (result == 1) {
          alert("회원 가입 성공!!!");
          getMembers();
        }
      });
  };
  const registerView = () => {
    let msg = `<form id="form">
  <input type="text" name="id" placeholder="input id" /><br />
  <input type="text" name="pwd" placeholder="input pwd" /><br />
  <input type="text" name="name" placeholder="input name" /><br />
  <input type="text" name="addr" placeholder="input addr" /><br />
  <input type="button" onclick="register()" value="저장" />
</form>`;
    content.innerHTML = msg;
  };
  const info = (id) => {
    console.log("id : " + id);
    fetch("/search/" + id, { method: "get" })
      .then((res) => res.json())
      .then((data) => {
        console.log("data : ", data);
        content.innerHTML = `id : ${data.id}<br>
                pwd : ${data.pwd}<br>
                name : ${data.name}<br>
                addr : ${data.addr}<br>
                <span style=cursor:pointer; onclick="getMembers()">
                    목록으로 이동</span>`;
      });
  };
</script>

<div id="content"></div>
<hr />
<button type="button" id="getMembers">목록보기</button>
<button type="button" id="regView">회원가입</button>

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fetch (5)

addEventListner() : 괄호안에 함수를 넣어 줄 때 시작할 때 뜨지 않게 하려면 함수 뒤 ()를 생략해야한다

* 위 내용은 window.onload안에 있을 때 일이다

 

const express = require("express");
const app = express();

app.set("views", __dirname + "/views");
app.set("view engine", "ejs");

let = members = [
  { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
  { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
  { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
];
app.get("/", (req, res) => {
  res.render("index");
});
app.get("/get_members", (req, res) => {
  res.json(members);
});

const bodyParser = require("body-parser");
app.use(bodyParser.json());
app.post("/register", (req, res) => {
  console.log("req.body : ", req.body);
  members = members.concat(req.body);
  res.json(1);
});
app.get("/search/:id", (req, res) => {
  console.log(req.params);
  const result = members.filter((mem) => mem.id === req.params.id);
  console.log("result : ", result);
  res.json(result[0]);
});
app.put("/modify", (req, res) => {
  members = members.filter((mem) => mem.id !== req.body.id);
  members = members.concat(req.body);
  res.json(1)
});

app.delete("/delete", (req, res) => {
    members = members.filter(mem => mem.id !== req.body.id);
    res.json(1);
})

app.listen(3000, () => console.log("3000 실행 ..."));
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
  let members = [
    { id: "aaa", pwd: "aaa", name: "홍길동a", addr: "a산골짜기" },
    { id: "bbb", pwd: "bbb", name: "홍길동b", addr: "b산골짜기" },
    { id: "ccc", pwd: "ccc", name: "홍길동c", addr: "c산골짜기" },
  ];
  let content;
  window.onload = () => {
    content = document.getElementById("content");
    const getM = document.getElementById("getMembers");
    getM.addEventListener("click", getMembers);
    const regView = document.getElementById("regView");
    regView.addEventListener("click", registerView);
  };

  const getMembers = () => {
    fetch("/get_members")
      .then((res) => res.json())
      .then((m) => {
        let msg = "<table border='1'>";
        msg += "<tr><th>아이디</th> <th>이름</th> <th>주소</th></tr>";
        m.forEach((mem) => {
          msg += "<tr>";
          msg += `<td>${mem.id}</td><td style="cursor:pointer;" onclick="info('${mem.id}')">${mem.name}</td><td>${mem.addr}</td>`;
          msg += "</tr>";
        });
        msg += "</table>";
        content.innerHTML = msg;
      });
  };
  const register = () => {
    const arr = $("#form").serializeArray(); // 해당하는 태그에 name을 키값으로, 입력한 값을 밸류값으로 만들어주는 역할
    console.log("arr : ", arr);
    let data = {};
    arr.forEach((d) => (data[d.name] = d.value));
    console.log("data : ", data);
    fetch("/register", {
      method: "post",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data), // 해당하는 data를 JSON형식으로 변환
    })
      .then((res) => res.json())
      .then((result) => {
        console.log(result);
        if (result == 1) {
          alert("회원 가입 성공!!!");
          getMembers();
        }
      });
  };
  const registerView = () => {
    let msg = `<form id="form">
  <input type="text" name="id" id="id" placeholder="input id" /><br />
  <input type="text" name="pwd" id="pwd" placeholder="input pwd" /><br />
  <input type="text" name="name" id="name" placeholder="input name" /><br />
  <input type="text" name="addr" id="addr" placeholder="input addr" /><br />
  <input type="button" id="btn" value="저장" />
</form>`;
    content.innerHTML = msg;
  };
  const modify_form = async (id) => {
    registerView();
    $("#btn").click(modify);
    $("#btn").val("수정 완료");
    const res = await fetch("/search/" + id, { method: "get" });
    const data = await res.json();
    console.log("data : ", data);
    $("#id").val(data.id);
    $("#pwd").val(data.pwd);
    $("#name").val(data.name);
    $("#addr").val(data.addr);
  };
  const modify = () => {
    const arr = $("#form").serializeArray();
    let data = {};
    arr.forEach((d) => (data[d.name] = d.value));
    fetch("modify", {
      method: "put",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    })
      .then((res) => res.json())
      .then((result) => {
        alert("수정 성공");
        info(data.id);
      });
  };
  const deleteM = async (id) => {
    const res = await fetch("/delete", {
      method: "delete",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ id: id }),
    });
    const result = await res.json();
    if (result == 1) {
      alert("삭제 성공!!!");
      getMembers();
    }
  };

  const info = (id) => {
    console.log("id : " + id);
    fetch("/search/" + id, { method: "get" })
      .then((res) => res.json())
      .then((data) => {
        console.log("data : ", data);
        content.innerHTML = `id : ${data.id}<br>
                pwd : ${data.pwd}<br>
                name : ${data.name}<br>
                addr : ${data.addr}<br>
                <span style=cursor:pointer; onclick="getMembers()">
                    목록으로 이동</span>
                    &nbsp; / &nbsp;
                    <span style=cursor:pointer; onclick="modify_form('${id}')">
                    수정</span>
                    &nbsp; / &nbsp;
                    <span style=cursor:pointer; onclick="deleteM('${id}')">
                    삭제</span>`;
      });
  };
</script>

<div id="content"></div>
<hr />
<button type="button" id="getMembers">목록보기</button>
<button type="button" id="regView">회원가입</button>

 

실행 결과

'node.js' 카테고리의 다른 글

NODE.JS 활용 (7)  (0) 2024.04.08
NODE.JS 활용 (6)  (0) 2024.04.05
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01
NODE.JS 활용 (2)  (0) 2024.04.01

 

 

 

 

 

 

 

NODE.JS DB (1)

npm install oracledb@^5.5.0 --save : database 연결 기능 설치

* 동기 : 순서가 일정함, 비동기 : 순서가 일정하지 않음

promise() : 비동기로 동작하는 함수의 값을 보장해준다

resolve() : return 역할

setTimeout(함수, 시간) : 해당하는 함수를 지정한 시간 간격으로 계속 호출

async : 해당 함수 안에는 비동기로 동작하는 함수가 있다고 알려줌

await : 함수가 값을 가져올 때 까지 기다려주는 역할

.then : promise에 대한 값 설정

require("oracledb") : oracleDB 가져오기

dbConfig = {} : oracleDB정보 설정

oracleDB명.getConnection(oracleDB정보명) : DB정보 가져오기

const express = require("express");
const app = express();
let con;
app.get("/", (req, res) => {
  console.log("1.연동전..");
  con = connect();
  con.then((msg) => {
    //promise에 대한 값 설정
    console.log("3.연동 완료 후 특정 기능 사용..");
    res.send("con : " + msg);
  });
});

const connect = () => {
  let msg;
  return new Promise((resolve) => {
    //비동기로 동작하는 함수의 값을 보장해준다
    setTimeout(() => {
      msg = "연동 되었습니다";
      console.log("2.연동 하는 중..");
      resolve(msg); //retrun 역할
    }, 1000); //해당하는 함수를 지정한 시간 간격으로 계속 호출
  });
};

app.get("/async", async (req, res) => {
  // async 이 함수 안에는 비동기로 동작하는 함수가 있다고 알려줌
  console.log("1111 연동전 async");
  con = await connect(); // 함수가 값을 가져올 때 까지기다려주는 역할
  console.log("3333 받아온 객체 연산 async");
  res.send("con : " + con);
});

const oracleDB = require("oracledb"); //oracleDB가져오기
const dbConfig = {
  // oracleDB정보 설정
  user: "c##LTY",
  password: "1213",
  connectSring: "localhost:1521/orcl",
};
app.get("/connect", async (req, res) => {
  let con = await oracleDB.getConnection(dbConfig); //DB정보 가져오기
  console.log("con : ", con);
  //con.execute("sql 명령어");

  let con1 = oracleDB.getConnection(dbConfig);
  con1.then((res) => {
    console.log("res ; ", res);
    //res.execute("sql 명령어");
  });
  console.log("con11 : ", con1);
  res.send("con : " + con);
});

app.listen(3000, () => {
  console.log("3000");
});

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS DB (2)

require("라우터 경로")(app) : 해당하는 경로에 app을 전달하겠다는 의미

* app.js에서 받아온 app을 통해 좀더 간단하게 표현 가능

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS DB (3) select

* DB내용을 받아올 때 비동기 방식이므로 DAO, service, controller에 모두 비동기 처리를 해줘야 한다

비동기 처리 : async를 해당 함수에 적은 후, 해당되는 곳에 awiat를 적는다

db명.autoCommit = true : sql로 수정이나 삭제 등을 할 때 자동으로 commit해주는 역할

db명.outFormat = db명.OBJECT : 2차원 배열 대신, 키 배류 형식으로 보여주는 역할

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS DB (4) insert

con.excute(sql, body) : body안에 있는 정보를 가져와야 하기 때문에 .body를 쓴다

* sql문 작성할 때 :뒤에 나오는 이름은 컬럼명과 같게 한다

* 쿼리문에 오류가 생길 때를 처리하기 위해 try catch문을 활용한다

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS DB (5) delete

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS DB (6) modify

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS include

<%- include(경로) %> : 공통적으로 사용해야 하는 ejs가 있을때 활용

 

 

 

 

 

 

 

 

 

 

 

 

Quiz01

 

풀이 과정

const ser = require("../../service/member/member_service");
const login = (req, res) =>{
    res.render("member/login",{username : req.session.username});
}
const loginCheck = async (req, res) =>{
    const msgPack = await ser.loginCheck( req.body );
    if( msgPack.result === 0){
        req.session.username = req.body.id;
    }
    res.send( msgPack.msg );
}
const logout = (req, res) =>{
    req.session.destroy();
    res.clearCookie('isLogin');
    res.redirect("/");
}
const memberList = async (req, res) =>{
    console.log("username => ", req.session.username);
    console.log("isLogin => ", req.cookies.isLogin);
    
    if(!req.cookies.isLogin && !req.session.username){
        msg = "로그인 사용자만 접근 가능합니다!!!";
        url = '/member/login';
        return res.send( ser.getMessage(msg, url) );
    }

    const list = await ser.memberList();
    res.render("member/list",{ list : list });
}
    
const registerView = (req, res) =>{
    res.render("member/register_view");
}
const register = async (req, res) => {
    const msg = await ser.register( req.body );
    res.send( msg );
}

const getMember = async (req, res) =>{
    const member = await ser.getMember( req.params.id );
    res.render("member/info", { member, username:req.session.username });
}

const memberDelete = async (req, res) =>{
    const result = await ser.memberDelete( 
                req.params.id, req.session.username );
    if(result === 1){
        return res.redirect("/member/logout");
    }else if(result === 0 ){
        return res.redirect("/member/list");
    }
    msg = "삭제 실패!!!";
    url = '/member/list';
    return res.send( ser.getMessage(msg, url) );
}
const memberModifyView = async (req, res) =>{
    if(req.session.username === req.params.id ){
        const member = await ser.getMember(req.params.id);
        return res.render("member/modify_form", { member });
    }
    res.send( ser.getMessage("수정할 수 없음!!",
                        "/member/member_info/"+req.params.id) );
}

const memberModify = async (req, res) =>{
    const msg = await ser.memberModify(req.body);
    res.send(msg);
}
module.exports = { 
    login,loginCheck,logout,memberList,registerView 
    ,register,getMember,memberDelete,memberModifyView , memberModify
};
const memberDAO = require("../../database/member/member_dao");
const loginCheck = async ( body )=>{
    let member = await memberDAO.getMember( body.id );
    let msg="", url="", msgPack = {};
    if(member.rows.length == 1){
        member = member.rows[0];
        if( member.PWD === body.pwd ){
            msg = `${member.NAME}님 환영합니다^^`;
            url = "/";
            msgPack.result = 0;
        }else{
            msg = "비밀번호가 틀렸습니다!!!";
            url = '/member/login';
            msgPack.result = 1;
        }
    }else{
        msg = "존재하지 않는 아이디입니다!!";
        url = "/member/login";
        msgPack.result = 1;
    }
    msgPack.msg = getMessage(msg, url);
    return msgPack;
};
const  getMessage = (msg, url) =>{
    return `<script>alert('${msg}'); location.href="${url}";</script>`;
}
const memberList = () =>{
    return memberDAO.memberList();
}
    
const register = async ( body )=>{
    let result = await memberDAO.register( body );
    let msg="", url="";
    console.log("result : ", result);
    if(result !== undefined){      
        msg = `${body.name}님 회원가입 성공^^`;
        url = "/member/login";
    }else{
        msg = "문제가 발생했습니다!!!";
        url = '/member/register_view';
    }
    return getMessage(msg, url);
};
const getMember = async ( id ) =>{
    const list = await memberDAO.getMember( id );
    return list.rows[0];
}
const memberDelete = async (id, username) =>{
    const result = await memberDAO.memberDelete( id );
    //삭제 실패 rowsAffected : 0, 성공 : 1
    if( result.rowsAffected === 1 ){
        if(id === username){ //로그인 사용자와 삭제사용자 일치
            return 1;
        }
    }else{//삭제 실패
        return -1; 
    }
    return 0; //로그인 사용자와 다른 사용자 삭제
}
      
const memberModify = async (body) => {
    const result = await memberDAO.memberModify(body);
    let msg="", url="";
    if(result.rowsAffected == 1){
        msg = `${body.name}님 수정되었습니다^^`;
        url = `/member/member_info/${body.id}`;     
    }else{
        msg = "문제가 발생했습니다!!!";
        url = `/member/modify/${body.id}`;
    }
    return getMessage(msg, url);
}
module.exports = { loginCheck , memberList , register , getMessage
        , getMember , memberDelete , memberModify };
const oracledb = require("oracledb");
const dbConfig = require("../../../config/database/db_config");
oracledb.autoCommit = true;
oracledb.outFormat = oracledb.OBJECT;

const getMember = async ( id ) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `select * from members where id='${id}'`;
    let member;
    try{
        member = await con.execute(sql);
        console.log(id)
    }catch(err){
        console.log( err );
    }
    return member;
}

const memberList = async ( ) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = "select * from members";
    return ( await con.execute(sql) ).rows;
}
const register = async ( body ) =>{
    const con = await oracledb.getConnection(dbConfig);
    const sql = "insert into members values(:id,:pwd,:name,:addr)";
    let result;
    try{
        result = await con.execute(sql, body);
    }catch(err){
        console.log(err)
    }
    return result;
}   
const memberDelete = async ( id ) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql = `delete from members where id='${id}'`;
    let result;
    try{
        result = await con.execute(sql);
    }catch(err){
        console.log( err );
    }
    return result;
}
const memberModify = async (body) => {
    const con = await oracledb.getConnection(dbConfig);
    const sql =
"update members set pwd=:pwd, name=:name, addr=:addr where id=:id";
    let result;
    try{
        result = await con.execute(sql, body );
    }catch(err){
        console.log(err);
    }
    return result;
}
module.exports = { getMember , memberList , register 
    , getMember , memberDelete , memberModify };

 

실행 결과

 

 

'node.js' 카테고리의 다른 글

NODE.JS 활용 (6)  (0) 2024.04.05
NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (3)  (0) 2024.04.01
NODE.JS 활용 (2)  (0) 2024.04.01
NODE.JS 활용  (0) 2024.03.29

 

 

 

 

 

 

NODE.JS session (1)

session 이란? : 사용자가 접속하는 동안 매 요청마다 유지되는 데이터의 집합

session : 서버에서 관리

session 설치 방법 :npm install express-session --save

config 적용 방법 : const sessionConfig = {

secret : "암호화 키",

resave : false,

saveUninitialized : true,

}

const 세션명 = require("express-session"); : 세션 가져오기

req.session.키값 = 밸류값 : 세션의 키값에 밸류를 설정

delete req.session.키값 : 하나의 세션을 삭제할 때 사용(delete 명령어)

req.session.destroy(); : 모드 세션을 삭제할 때 사용(destory 명령어)

npm install body-parser --save : body-parser 설치 

body-parser : post방식의 데이터를 가져올 때 사용

router.post(); : post방식으로 보낼 때 사용

* query를 사용한 get방식과 달리 post는 .body를 사용한다

app.use(session(config.sessionConfig)) : session의 config정보 적용

app.use(bodyParser.urlencoded()) : bodyPaser를 적용하는 역할

* bodyParser적용 후 router적용해야 한다

const index = (req, res) => {
  res.render("session/index");
};

const setSession = (req, res) => {
  req.session.name = "홍길동"; //세션의 키(name)은 홍길동이다
  req.session.age = 20;
  res.render("session/set_session");
};

const getSession = (req, res) => {
  const sessionValue = {
    name: req.session.name,
    age: req.session.age,
  };
  res.render("session/get_session", sessionValue);
};

const delSession = (req, res) => {
  //delete req.session.name; // 하나의 세션을 삭제할 때 delete 명령어 사용
  req.session.destroy(); // 모든 세션 삭제할 때 destroy 사용
  res.render("session/del_session");
};

const login = (req, res) => {
  res.render("session/login");
};

const loginCheck = (req, res) => {
  console.log(req.body);
  console.log(req.body.id);
  console.log(req.body.pwd);
  res.send();
}

module.exports = { index, setSession, getSession, delSession, login, loginCheck };

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS session (2)

const index = (req, res) => {
  res.render("session/index");
};

const setSession = (req, res) => {
  req.session.name = "홍길동"; //세션의 키(name)은 홍길동이다
  req.session.age = 20;
  res.render("session/set_session");
};

const getSession = (req, res) => {
  const sessionValue = {
    name: req.session.name,
    age: req.session.age,
  };
  res.render("session/get_session", sessionValue);
};

const delSession = (req, res) => {
  //delete req.session.name; // 하나의 세션을 삭제할 때 delete 명령어 사용
  req.session.destroy(); // 모든 세션 삭제할 때 destroy 사용
  res.render("session/del_session");
};

const login = (req, res) => {
  res.render("session/login", { username: req.session.userName });
};

const loginCheck = (req, res) => {
  console.log(req.body);
  console.log(req.body.id);
  console.log(req.body.pwd);

  const DBId = "aaa",
    DBPwd = "aaa",
    DBNick = "홍길동";
  if (DBId === req.body.id && DBPwd === req.body.pwd) {
    req.session.userName = DBId;
    req.session.nick = DBNick;
    res.redirect("/session/success");
  } else {
    let msg = scriptMsg("로그인 실패", "/session/login");
    res.send(msg);
  }
};

const scriptMsg = (sMsg, sUrl) => {
  return `<script>
    alert("${sMsg}");
    location.href="${sUrl}";
  </script>`;
};
const success = (req, res) => {
  if (req.session.userName) {
    res.render("session/success", { nick: req.session.nick });
  } else {
    let msg = scriptMsg("로그인 먼저하세요", "/session/login");
    res.send(msg);
  }
};

const logout = (req, res) => {
  req.session.destroy(() => {
    console.log("모든 세션을 만료합니다");
  });
  res.redirect("/session/login");
};

module.exports = {
  index,
  setSession,
  getSession,
  delSession,
  login,
  loginCheck,
  success,
  logout,
};

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS fileStore

파일 형태로 데이터 저장하는 기능 설치 : npm install session-file-store

const  fileStore = require("session-file-store")(session);

config.sessionConfig.store = new fileStore();

'node.js' 카테고리의 다른 글

NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (2)  (0) 2024.04.01
NODE.JS 활용  (0) 2024.03.29
NODE.JS 기초  (0) 2024.03.28

 

 

 

 

NODE.JS cookie (1)

cookie란? : 웹에 저속할 때 서버에 의해 사용자의 컴퓨터에 저장되는 정보

cookie 사용 방법 : npm install cookie-parser --save

http 통신 : 비연결성

cookie : 사용자가 관리

쿠키 설정 확인 방법 : F12 => Application => Cookies

require("cookie-parser") : 쿠키 생성

app.use(쿠키명()) : cookie에서 동작하는 값을 middle ware에 부여 (쿠키라우터보다 먼저 생성)

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS cookie (2)

const 쿠키설정명 = { : 쿠키 설정 방법

httpOnlyt: true : 웹(http)통신만 적용

maxAge: 5000 : 쿠키 유지 시간 지정

const 쿠키밸류명 = req.cookies.쿠키 키값 : 사용자가 지정한 쿠키밸류값 설정

res.cookie(쿠키 키값, 쿠키 밸류값, 쿠키 설정명) : 쿠키값 설정 적용

res.render({쿠키밸류명}) : 쿠키값 넘겨주기

* window.open() : 새로운 창 표현 (괄호 안에 ""넣어야 새로운 창 표현 가능)

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS cookie (3)

location.href = "" : 서버로 다시 요청

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS cookie (4)

signed : true : 쿠키 밸류값을 암호화 처리

req.signedCookies.쿠키 키값  : 암호된 쿠키밸류값 설정 

* 경로의 내용을 받아올 때는 req.parms.변수명 으로 표현한다 (변수명은 router에서 설정한 변수명 사용)

* 경로의 내용을 방아올때는 라우터 경로에 /:변수명 을 붙여준다

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS cookie (5)

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Quiz01

 

풀이 과정

 

 

실행 결과

'node.js' 카테고리의 다른 글

NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01
NODE.JS 활용  (0) 2024.03.29
NODE.JS 기초  (0) 2024.03.28

 

 

 

 

 

 

NODE.JS router

* 새로운 프로젝트에 연결할 때 : package.json파일을 복.붙 하고, 해당 폴더의 TERMINAL창에 npm i 입력

router : 경로 연결 역할

 middle ware :특정 기능을 대신 처리하는 역할

express.Router(); : router 생성 방법

.use("/", router); : 미들 웨어를 등록 ("/"라는 경로가 들어오면 해당 router에서 처리)

라우터명.get("/경로', ()) : router경로 기준으로부터 경로 설정

*  package.json 패키지에 "scripts": {"start": " npx supervisor app.js" }

=> start를 입력하였을 때 npx supervisor app.js가 입력되는 효과

* TERMINAL창에 npm start  입력 = npx supervisor app.js 입력

module.exports = 파일명 : 다른 폴더에 db내용을 저장하고 외부로 내보내는 기능 

require("파일 경로"); : 내보낸 파일 경로를 입력하여 내보낸 데이터를 받아오는 기능

const express = require("express");
const DBMember = require("./db/memberDAO"); // 해당 경로에서 내보낸 데이터를 가져오는 기능

const app = express();

app.set("views", "./views");
app.set("view engine", "ejs");

const router = express.Router();
app.use("/", router);

router.get("/", (req, res) => {
  console.log("router 연동");
  console.log(DBMember);
  res.render("index");
});

const router2 = express.Router();
app.use("/member", router2);

router2.get("/", (req, res) => {
  res.send("member기본 페이지 입니다");
});

router2.get("/list", (req, res) => {
  res.send("member의 목록을 보여줍니다");
});

app.listen(3000, () => console.log("3000서버 실행!!!!"));

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 전체적인 흐름 

app : 설정 담당

router : 연결 담당

view : 사용자에게 보여주는 역할

service : 연산 담당

dao : DB접근 역할

 

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS router, controller 분할 

router 분할 : 다른 폴더에서 만든 라우터를 app페이지로 받아오는 방법

=> 분할된 라우터 폴더 : module.exports = router : 라우터를 외부로 보내기

=> require("라우터 경로") : 외부로 보낸 라우터 받아오기

 

controller 분할 : 다른 폴더에서 만들 컨트롤러를 router페이지로 받아오는 방법

=> 분할된 컨트롤러 폴더 : module.exprots = {키 : 밸류} (밸류는 함수도 가능) = 컨트롤러를 외부로 보내기

=> require("컨트롤러 경로") : 외부로 보낸 컨트롤러 받아오기

 

 

 

 

 

 

 

 

 

 

 

 

NODE.JS 흐름 마무리

 

 

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

Quiz01

 

풀이 과정

 

실행 결과

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Quiz02

풀이 과정

 

실행 결과

 

 

 

 

 

 

Quiz03 & Quiz04

 

풀이 과정

 

 

실행 결과

'node.js' 카테고리의 다른 글

NODE.JS 활용 (5)  (0) 2024.04.04
NODE.JS 활용 (4)  (0) 2024.04.02
NODE.JS 활용 (3)  (0) 2024.04.01
NODE.JS 활용 (2)  (0) 2024.04.01
NODE.JS 기초  (0) 2024.03.28

+ Recent posts