게시판 구현 ( 댓글 기능 추가, 페이징, 게시글 수정, 게시글 삭제, 이미지 수정 )

 

 


 

게시글 상세보기 기능 추가

 

list.ejs 에 댓글기능을 추가할 a 태그 추가

 

라우터에 경로 추가

 

board_ctrl.js 에도 data 함수 추가

 

board_service.js 에서 data 함수 추가

 

board_dao.js 에 data 함수 추가

 

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>
                <%}%>
            </td>
        </tr>
        <tr>
            <td colspan="4" align="center">
                <%if(username === data.ID){%>
                    <input type="button" onclick="" value="수정하기"> 
                    <input type="button" onclick="" value="삭제하기">
                <%}%>
                <input type="button" onclick="" value="답글달기"> 
                <input type="button" onclick="" value="리스트로 돌아가기">
            </td>
        </tr>
        </table>
    </div>
</div>

data.ejs 코드

 

게시글 상세보기 기능 구현 완료

 


 

이미지 출력 구현

 

파일 다운로드 경로 입력

 

board_router.js 에 경로 추가

 

board_ctrl.js 에 기능 추가

 

글에 들어가면 이미지 출력 구현 완료

 


 

글 조회수 기능 추가

 

 

board_dao.js 에 조회수 증가 기능 추가

 

조회수 증가 기능 구현 완료

 


 

글 삭제 기능 구현

 

이미지와 글을 모두 삭제해야 하므로 경로에 글 번호와 이미지를 모두 넘겨줌 ( data.ejs 에 작성 )

 

board_router.js 에 경로 추가

 

board_ctrl.js 에서 이미지 삭제 코드 구현

 

board_ctrl.js 에서 글 삭제 관련 코드 service 쪽으로 넘겨준 뒤 다시 글 목록으로 redirect 시킨다

 

board_service.js 에서는 받은 데이터를 dao 쪽으로 넘겨준다

 

board_dao.js 에서 삭제 부분 구현

 

글 삭제 구현 완료

 


 

글 수정 기능 구현

 

data.ejs 에서 수정하기 기능 클릭 시 경로 설정

 

board_router.js 에 경로 추가

 

board_ctrl.js 에 modifyForm 기능 추가

 

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>

modify_form.ejs 코드

 

수정하기 누르면 이동될 경로 board_router.js 에 설정

 

board_ctrl.js 에 modify 기능을 추가

 

board_service.js 에서 modify 기능을 추가

 

board_dao.js 에서 sql 문 작성 및 쿼리문 실행

 

게시글 수정 구현 완료

 


 

페이징 기능 구현

 

list.ejs 에서 코드 추가

    <% if( start <= 1){ %>
        <button disabled>이전</button>
    <%}else{%>
        <button type="button" onclick="
            location.href='/board/list?start=<%=start - 1%>'">이전</button>
    <% }for(let i=1 ; i <= totalPage ; i++ ){ %>
        <a href="/board/list?start=<%= i %>"><%= i %></a>
    <% }if( start < totalPage ){%>
        <button type="button" onclick="
            location.href='/board/list?start=<%=start + 1%>'">다음</button>
    <% }else{ %> <button disabled>다음</button> <% } %>
    <%= start %> / <%= totalPage %>

추가 코드

 

기존에 작성되어 있던 board_ctrl.js 에서 코드 수정

 

board_service.js 에 pageOperation 함수 추가 후 기존에 있던 list 함수 수정

 

board_dao.js 에서 list 의 sql 문 수정 및 totalContent 함수 추가

 

페이징 구현 완료

 


 

댓글 기능 구현

 

파일 추가 세팅

 

create table reply(
id varchar(20),
title varchar(50),
content varchar(300),
write_group number(10),
save_date date default sysdate,
constraint fk_test1 foreign key(write_group) references board(write_no) on delete cascade,
constraint fk_test2 foreign key(id) references members(id) on delete cascade
);

db에 sql 문 실행

 

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 에 적용된 데이터는 새로고침을 하면 출력되게 된다

 

댓글 기능 구현 완료

 


 

코드 실행

코드 실행
728x90

 

 

 

 

 

게시판 구현 ( 페이징, 답변 )

파일 세팅

 

create table paging(
num number not null,
title varchar2(30) not null,
pdate varchar2(10) not null,
count number not null,
primary key(num)
);
create sequence test_num;

DB 테이블 생성

 

npm install express --save
npm install ejs --save
npm install oracledb@5.5.0 --save
npm install supervisor --save
npm install body-parser --save

npm 설치

 


 

게시판 목록보기 구현

 

app.js 기본 세팅

 

router.js 기본세팅

 

page_router.js 기본 세팅

 

page_controller.js 기본 세팅

 

index.ejs 세팅

 

page_router 에 list 경로 추가

 

page_controller.js 작성 ( service 쪽에서 db 에 접근해서 작업할 것 이므로 async 처리 )

 

page_service.js 파일 작성

 

db_config.js 작성

 

pageDAO.js 작성

 

list.ejs 작성 ( DAO 에서 가져온 데이터로 테이블 작성 )

 

서버 실행

 

게시판 목록보기 구현 완료

 


 

글 작성기능 구현

 

page_router.js, page_controller.js 작성 ( /write_form 경로 )

 

write_form.ejs 작성 ( 글 작성 기능 )

 

글 작성시 이동되는 /write 경로 page_router.js 에 작성 ( post 방식 )

 

page_controller.js 에 async - await 을 통해 동기처리 하여 작성

 

page_service.js 에 pageInsert 부분 작성

 

pageDAO.js 에 글에 관련된 데이터 DB 에 저장하는 로직 작성 ( nextval : DB 에 sequence 로 생성해놓은 1씩 값이 증가하며 저장되는 부분을 의미 )

 

서버 실행

 

글 작성부분 구현 완료

 


 

글 상세보기 구현

 

list.ejs 에 글 타이틀에 글 번호를 경로로 하는 a 태그 작성

 

page_router.js 에 경로 작성 :num 으로 글 번호를 변수로 받음

 

page_controller.js 에 변수로 받은 num 을 service 로 넘겨주는 코드 작성

 

page_service.js 에 넘겨받은 글 번호를 dao 쪽으로 넘겨서 연산하는 코드 작성

 

dao 에 넘겨 받은 글 번호랑 일치하는 행을 찾는 코드를 작성 후 return 으로 해당 데이터를 service 로 반환

 

content.ejs 작성

 

 

글 상세보기 구현 완료

 


 

조회수 증가시키는 부분 구현

 

page_service.js 에 조회수 증가시키는 부분 로직 작성

 

dao 에 조회수 증가 로직 작성

 

서버 실행 ( 만약 조회수가 글을 눌러서 상세보기가 켜지자 마자 늘어나길 원한다면 조회수 늘어나는 로직에 async - await 을 적용해주면 된다 )

 

글 조회수 증가 구현 완료

 


 

 

 

 

 

페이징 처리

페이징 방식

 

글의 총 갯수를 가져오는 totalContent 를 page_controller.js 에 작성

 

page_service.js 에 글 전체 갯수 가져오는 로직 작성

 

pageDAO.js 에 DB 에 접근하여 전체 글 갯수 가져오는 로직 작성

 

list.ejs 에 totalContent 출력해 봄 ( 콘솔과 웹 출력 결과물 )

 

페이지를 확인할 수 있는 start 라는 변수를 생성하여 로직을 작성할 것, page_controller.js 작성

 

page_service.js 에서 로직 작성

 

start 값을 출력해 봄

 

이전을 누르면 start -1 값을 전달, 다음을 누르면 start +1 값을 전달, start 값이 1이면 이전 버튼을 비활성화, start 값이 totalContent 와 같아면 다음 버튼을 비활성화

 

rownum : 가상의 행의 수

( 의미는 없지만, 만약 글의 번호로 데이터를 처리한다고 가정했을 때 한 페이지에 글을 5개씩 출력하고자 할 때 2번째 작성 글이 삭제되었다면 오류가 발생하거나 첫번째 페이지에 1, 3, 4, 5 글 4개만 출력되게 된다..... 그래서 오라클에서 지원하는 가상의 행{결과 값이 출력되는 행만큼 임의의 번호를 붙여주는 rownum}인 rownum 을 사용하여 글이 삭제되었어도 빈 값이 없이 1, 3, 4, 5, 6 식으로 출력되게끔 설정한다 )

 

마지막 페이지 값을 담은 page 정보도 page_controller.js 에 작성

 

page_service.js 에서 pageNum 에 한 페이지 당 출력될 글의 갯수 설정 후 연산을 통해 toPage(총 페이지) 설정, startNum 과 endNum 은 DB 에 글의 rownum 연산을 할 때 필요한 값을 선언 ( start 는 현재 몇번째 페이지인지 나타냄 )

 

page_service.js 에서 페이지 정보를 DAO 에 전달하여 연산

 

list.ejs 에서 전달받은 page 의 totPage(토탈 페이지) 값으로 목록을 출력한다

 

서버 실행

 

페이징 처리 완료

 


 

 

 

 

 

댓글

quiz_dbconnect_board.zip
3.00MB

 

위 프로젝트 다운로드 받아서 위와 같이 세팅

 

create table board(
write_no number(10) primary key,
title varchar2(100),
content varchar2(300),
save_date date default sysdate,
hit number(10) default 0,
origin_file_name varchar(100),
change_file_name varchar(100),
id varchar(20) not null,
constraint fk_test foreign key(id) references members(id) on delete cascade
);
create sequence board_seq;

sql developer 에서 쿼리문 작성하여 테이블 생성

 

write no : 글 번호

title : 제목

content : 내용

save_date : 저장 날짜

id : 글 작성자

 

npm install multer@1.4.4 --save

multer npm 설치

( multer - 파일 업로드를 위해 사용되는 Node.js 의 미들웨어 )

 

header.css 작성

* { margin: 0; }
.wrap { width: 1000px; margin: auto; }
.header { width: 1000px; }
.navdiv { width: 100%; background-color: olive; }
.nav { background-color: olive; width: 1000px;}
.nav ul { list-style: none; display: flex;
        justify-content: end; }
.nav ul li { padding: 10px; }

.nav ul li a { text-decoration:none; color:white; }
.nav ul li a:hover{
color: orange; border-bottom: 2px solid black;
transition : all 0.25s; padding-bottom: 3px;
}
.title{ text-shadow: 10px 10px 15px black; 
    font-size: 70pt;
    text-align: center;
    margin-top: 0; 
    padding-bottom: 20px;
    color: burlywood;
    font-family: Gabriola;
}
.content { margin-top: 50px;  }

header.css 코드

 

css 적용을 위해 app.js 에서 /static 설정

 

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

header.ejs 코드 변경 ( link 로 외부 css 파일 적용 )

 

서버 실행 결과

 

board 항목 추가

 

router.js 에 /board 로 접속 시 사용될 boardRouter 선언

 

board_router.js , board_ctrl.js 작성

 

board_service.js 작성

 

DAO 에서 공통으로 사용하는 부분을 common_dao.js 를 생성하여 exports

 

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><%=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>

list.ejs 코드

 

list.ejs 에서 글 작성을 누르면 이동되는 경로인 /write_form 경로를 board_router.js 에 작성

 

세션을 체크하는 부분과 메세지를 출력하는 공통 기능을 ser_common.js 파일을 생성하여 작성

 

세션을 통해 로그인 정보를 확인한 뒤 로그인 상태라면 board/write_form 경로로 이동시킴

 

write_form.ejs 작성

<%- 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>

write_form.ejs 코드

 

public 폴더 하위에 js 폴더에 image_read.js 작성 ( 이미지 미리보기 부분 구현 )

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;
        }
    }
}

image_read.js 코드

 

서버 실행 ( 글 작성 진입 및 이미지 미리보기까지 구현 )

 

config - file - file_config.js 생성 및 작성

const multer = require("multer");
const stg = multer.diskStorage({
    destination : ( req , file, cb ) => {
        cb(null,  "upload_file")
    },
    filename : ( req, file, cb) => {
        cb(null, Date.now()+"-"+file.originalname );
    }
})
const f_filter = (req, file, cb)=>{
    const type = file.mimetype.split("/");
    if( type[0] == "image" ){
        cb(null, true);
    }else{
        req.fileValidation = "이미지만 저장하세요";
        cb(null, false);
    }
}
const upload = multer({storage : stg, fileFilter : f_filter })
module.exports = upload;

file_config.js 코드

 

board_router.js 에&nbsp; 이미지 업로드를 post 로 받을때 이동 될 /write 설정

 

이미지가 저장될 upload_file 폴더를 프로젝트 바로 하위에 생성

 

board_controller 작성

 

board_service.js 작성 ( 이미지 형식이 아닐 경우 alert 발생 )

 

board_dao.js 에 boardInsert 작성

 

코드 실행

 

ser_common.js 에서 시간을 지정하는 함수 생성

 

board_service.js 에 ser_common 을 require 했던 부분을 제일 상단으로 올려준 뒤 list 의 값을 timeModify 함수에 집어 넣어 변경 후 list 를 반환한다

 

시간 설정 완료

 

728x90

 

 

 

 

자바스크립트 파일과 css 파일을 하나의 파일로 분리하여 관리

중복된 스크립트 코드를 image_read.js 라는 파일에 옮겨놓음

 

스크립트가 들어있는 경로를 /static 으로 설정 후 file_index.ejs 파일과 modify_form.ejs 파일에 설정

 

공용으로 사용할 css 파일 작성 후 file_list.ejs 에 적용해봄

728x90

 

 

 

 

파일 업로드, 다운로드

파일 세팅

 

npm install express --save
npm install ejs --save
npm install supervisor --save
npm install body-parser --save
npm install multer@1.4.4 --save

npm 설치

 

세팅 완료

 

npm start 로 서버 구동되게끔 설정도 완료

 

기본 서버 구동 코드 작성

 

파일 업로드, 다운로드는 post 방식을 사용하기 때문에 body-parser 사용 router 위임

 

router.js 에서 app 을 위임받아 사용 후 바로 router 를 export

 

router 에서 /file 경로로 들어오는 부분은 fileRouter 를 변수로 생성하여 위임 후 file_router.js 에서는 /file 경로로 들어올때 내용을 처리하고 router 를 다시 export 로 반환 

 

file_router 에서는 세부 경로를 다시 file_controller 로 위임

 

file_controller 에서 Object 를 하나 변수로 선언하여 키와 값으로 경로들을 묶어서 export 하고 file_router.js 에서는 사용 하고자 하는 키로 값을 가져와 사용한다

 

controller 작성, file_index.ejs 작성 후 작동 결과

 

form 태그 내에서 파일을 전송하는 경우

enctype="multipart/form-data"

위 코드를 적용해줘야 함

 

file_router 에 파일 선택 후 업로드할때 출력될 페이지 경로 설정 ( 파일 전송이기 때문에 post 방식 )

 

단순히 화면을 출력하는 부분은 view 라는 Object 에 모아서 사용, process 라는 Object 에는 연산을 하는 부분을 넣어서 사용, file_router 에서도 코드 수정

 

controller 에서 기존처럼 사용하지 않는 이유는 결국 controller 에 세부 경로가 많아질 수록 코드가 쌓이게 되는데 코드에서 문제가 생길 시 단순 출력 부분과 연산 부분 중 어느 부분이 문제가 생긴지 파악하고 쉽게 고칠 수 있기 때문... 또한 Object 로 묶어서 사용하는 이유는 exports 의 코드가 너무 길어지기 때문이므로 Object 를 exports 하여 router 에서 Object.key 형식으로 함수를 사용한다

 

router 에 multer 설정 및 파일 업로드 부분에 multer 의 기능을 추가.... controller 에서 console 로 출력 코드 적고 파일 전송해봄

 

저장되는 파일의 이름을 원본 파일과 동일하게 저장하기 위해 코드를 수정

 

코드 실행

 

 

 

 

 

특정 파일 형식만 사용하고 싶은 경우

filter 를 따로 구성하여 multer 에 추가한다

 

사용할 수 없는 확장자를 올리는 경우 filter 에서 확장자가 일치하지 않는 경우 req.fileValidation 에 문구를 설정하고 req.fileValidation 은 controller 에서도 사용할 수 있기 때문에 컨트롤러에서 확인하여 분류한다

 

코드 동작 영상

 

 

 

 

 

 

 

 

 

 

다운로드 구현

router 와 controller 에 이미지가 저장된 디렉토리의 내용을 읽어오는 코드를 작성

 

읽어온 이미지를 /file/list 경로로 보내줘서 출력 후 a 태그를 걸어 다운로드 링크를 생성

 

라우터에 다운로드 경로 추가, 컨트롤러에 다운로드 기능 추가

res.download(경로) : 해당 경로를 다운로드

 

다운로드 기능 구현 완료

 

 

 

 

 

 

 

 

 

 

이미지 업로드 시 미리보기 기능 구현

이미지 미리보기 구현은 파일 올리는 부분의 ejs 에서만 처리해주면 된다

onchange="readURL(this)" : 만약 파일이 선택되면 태그에 변경이 일어나기 때문에 readURL() 함수를 실행시킨다, 이때 매개변수로 자기 자신 태그가 가지고 있는 파일 정보를 전송한다.

 

readURL 함수에서는 이 파일의 정보를 input 변수로 넘겨 받아서 해당 변수의 files 라는 키 안에 들어있는 파일의 저장 경로를 file 함수로 담아준다

> 만약 파일 경로가 비어있지 않다면 ( 파일이 정상적으로 올라왔다면 ) 이 파일의 정보를 FileReader 를 사용하여 읽어서 변수 e 로 만든다.

> 이후에 이 변수의 target 이라는 키 안에 result 키에 담겨있는 이미지에 대한 경로를 가져와서 html 코드의 img 경로를 변경해준다

 

 

 

 

나중에 쇼핑몰같은 홈페이지의 경우 썸네일을 모두 원본으로 사용해버리면 용량이 너무 커지기 때문에 썸네일용으로 따로 파일을 처리해서 사용해야 한다 ( 구선생님께 노드 썸네일 생성 으로 검색하면 나옴 )

 

 

 

 

 

 

 

 

 

 

파일 삭제 구현

ejs 작성, router 작성

 

controller 에 삭제 기능 구현

 

 

 

 

 

 

 

 

 

 

파일 수정

원본 파일을 삭제 후 저장

수정기능 경로로 가는 ejs 파일 작성, router 에 경로와 파일이름 같이 전송

 

controller 에 views 에 modify_form 경로로 가는 부분 작성

 

modify_form.ejs 작성

 

modify_form.ejs 에 hidden 으로 코드 추가

 

수정 부분 router 에 추가 ( 파일을 전송하기 때문에 post 방식 )

 

파일을 수정하면 원본으로 있던 파일을 삭제하는 코드를 controller 에 작성, 그리고 수정을 누르는 순간 router 에서 수정될 이미지는 upload 명령어를 사용해서 저장하기 때문에 따로 새로 수정될 이미지를 저장하는 코드는 작성하지 않음

 

 

 

 

 

 

 

 

 

 

자바스크립트 파일과 css 파일을 하나의 파일로 분리하여 관리

중복된 스크립트 코드를 image_read.js 라는 파일에 옮겨놓음

 

스크립트가 들어있는 경로를 /static 으로 설정 후 file_index.ejs 파일과 modify_form.ejs 파일에 설정

 

공용으로 사용할 css 파일 작성 후 file_list.ejs 에 적용해봄

728x90

 

 

 

 

get, post, put, delete

 

index.ejs, app03.js 생성

 

{
  "scripts": {
    "start": "npx supervisor app03.js"
  },
  "dependencies": {
    "bcrypt": "^5.1.1",
    "body-parser": "^1.20.2",
    "ejs": "^3.1.9",
    "express": "^4.19.2",
    "supervisor": "^0.12.0"
  }
}

package.json

 

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>

    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 register = () => {
        const arr = $("#form").serializeArray();
        // form 태그 안의 내용을 jquery 가 배열로 만들어준다
        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)
            // 보내는 데이터를 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 getMembers = () => {
        fetch("get_members", {method:"get"})
        .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 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}<hr>
                                <span style=cursor:pointer;
                                onclick="getMembers()"">
                                목록으로 이동
                                </span>
                                &nbsp; / &nbsp;
                                <span style=cursor:pointer;
                                onclick="modify_form('${data.id}')">
                                수정
                                </span>
                                &nbsp; / &nbsp;
                                <span style=cursor:pointer;
                                onclick="deleteM('${data.id}')">
                                삭제
                                </span>`;
        })
    }
    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();
        }
    }
</script>

<div id="content">

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

index.ejs

 

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());
// post 로 받아오는 데이터를 json 형식으로 받아온다는 의미
app.post("/register", (req, res) => {
    console.log("req.body : ", req.body);
    members = members.concat(req.body);
    res.json(1);
    // 프론트한테 정상적으로 받았기 때문에 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("3000server")})

app03.js

728x90

 

 

 

 

암호화

파일 app01.js 생성 및 npm 설치

 

암호화를 담당해주는 bcrypt 모듈도 설치

 

기본 서버 코드 작성

 

암호화 시

hash : 비동기적으로 작동 하므로 .then 이나 await 을 사용해줘야 함

hashSync : 동기적으로 작동하므로 따로 동기처리를 할 필요가 없다

 

DB 에 암호를 저장할때 암호화 과정을 진행한 값을 저장한다

 

암호화된 값을 비교할땐 compareSync(로그인 입력 비밀번호, DB에 저장된 비밀번호) 로 비교하여 결과 값이 true, false 로 나오는 것을 확인하여 사용한다.

 

 

 

 

 

 

 

 

 

 

fetch

fetch 를 사용하기 위해서 body-parser 모듈의 기능이 필요함

 

app02.js 파일 생성 후 코드 작성

 

보여질 폴더, 파일 추가

 

non_fetch.ejs 파일 작성

 

조회수 기능을 만들기 위해 count 함수 추가 후 전달

 

non_fetch.ejs 파일에 조회수 출력

 

fetch 를 사용하지 않고 자기 자신의 페이지로 가는 링크를 이동하면 웹페이지가 처음부터 끝까지 새로 갱신된다

( 즉, 웹을 처음부터 끝까지 다시 로딩한다 )

 

fetch 를 사용할 경로인 localhost:3000/fetch01 생성

 

fetch01.ejs 파일 생성하여 fetch 를 사용해서 자기 자신 경로를 다시 서버로 요청

 

fetch : 서버로 요청을 하는 것은 a 태그, location.href 와 동일하나 fetch 는 서버로 연동은 되지만 페이지를 요청하는 것이 아닌 데이터만 주고 받는다

 

.then 을 통해 동기처리, async - await 을 통해 동기 처리

 

fetch 를 사용해서 받아온 데이터는 비동기적으로 작동하기 때문에 동기적으로 작동하게끔 처리해줘야 하며, 전달 받은 데이터는 json 형식으로 변환해야 확인 할 수 있기 때문에 json() 을 사용하여 json 형식으로 변환시켜서 사용하며 이 과정도 비동기적으로 작동하기 때문에 동기적으로 작동하게끔 처리해줘야 한다

 

 

 

 

 

 

 

 

 

 

get : 데이터를 얻어올 때 사용 ( 파라미터를 get 방식으로 서버와 주고 받음 )

post : 데이터를 추가할 때 사용 ( 파라미터를 post 방식으로 서버와 주고 받음 )

put : 데이터를 수정할 때 사용

delete : 데이터를 삭제할 때 사용

app02.js 작성

 

rest.ejs 파일 생성하여 작성

 

 

 

 

 

다시 기본 웹 구동 파일 생성

 

<script>

    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 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 getMembers = () => {
        fetch("get_members", {method:"get"})
        .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>${mem.name}</td>
                        <td>${mem.addr}</td>`;
                msg += "</tr>";
            })
            msg += "</table>"
            content.innerHTML = msg;
        })
    }
</script>

<div id="content">

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

index.ejs 파일 작성

(app03.js 에서 json 형식으로 보내주는 members 변수를 fetch 를 통해 get 방식으로 가져와서 사용중.... )

 

코드 실행, SPA ( Single Page Application ) - 한 페이지에서 여러 기능이 다 작동

 

 

 

 

 

jquery 를 사용하여 프론트에서 백엔드로 데이터 넘겨주기

jquery 를 require

 

form 태그 안의 내용을 jquery 를 사용하여 배열로 만들어 arr 배열에 저장

> 해당 배열을 forEach 문을 사용해서 배열의 키를 우리가 사용할 키, 값으로 변경

arr : jquery 가 만들어준 데이터, data : jquery 가 만들어준 배열을 다시 생성

> fetch 를 통해 /register 경로를 서버에 요청하면서 post 방식으로 json 형식의 데이터를 보내는데 이 데이터는 body 안에 담겨서 보내지며 JSON.stringify() 를 통해 전송할 자료를 json 형식의 자료로 변환하여 보낸다

> .then 을 사용하여 서버로부터 돌아오는 응답을 json() 을 사용하여 json 형식으로 받는다

> .then 을 이용하여 받아온 json 데이터를 result 라는 변수에 담아서 사용한다

 

app03.js (백) 는 /register 경로로 넘어온 프론트에서 넘어온 데이터를 post 형식으로 받는다

> 이때 body-parser 가 필요하며, app.use(bodyParser.json()) 을 선언하여 받는 데이터를 json 형태로 받는다고 설정한다

> 이후 데이터를 처리하는 코드를 작성하며 res.json() 을 사용하여 json 형태로 프론트에 응답한다

728x90

 

 

 

 

DB 연동

파일 세팅

 

create table members(
id varchar2(20) primary key,
pwd varchar2(100),
name varchar2(20),
addr varchar2(100)
);
insert into members values('aaa','aaa','홍길동','산골짜기');
insert into members values('bbb','bbb','김개똥','개똥별');
insert into members values('ccc','ccc','고길똥','마포구');
commit;

DB 에 데이터 생성

 

npm install express --save
npm install ejs --save
npm install oracledb@^5.5.0 --save
npm install supervisor --save
npm install body-parser --save

npm 도 설치

 

app.js 기본 설정

이제는 router 경로를 최초 root 경로만 app.js 에서 설정한 뒤 세부 주소는 router.js 에서 나눠준다

 

router.js 에서는 app.js 에서 전달받은 app 을 사용한 뒤 return 으로 라우터를 반환하고 바로 module.exports 를 시켜버린다, index.ejs 작성

 

회원정보 클릭시 이동되는 /member/list 경로를 member_router.js 에 작성, router.js 에서는 경로가 /member 로 오면 member_router.js 가 경로를 관리하게끔 설정 - app.use("/member", [require 를 통해 받아온 member_router 의 이름] )

 

이제 member_router 에서 controller 로 연동할 것

/member/list 경로로 들어오면 컨트롤러에서 처리하도록 함 ( 좌 : member_router.js , 우 : member_ctrl.js )

 

db_config.js 에 db 에 접속하기 위한 기본 정보를 작성 후 exports

 

member_controller 에서 db 를 호출.... 하지만 DB 는 비동기적으로 연결되기 때문에 DB 에 연결되기 전에 console.log 구문이 먼저 실행되기 때문에 con 의 결과 값을 promise 로 가져온다

 

비동기적으로 돌아가는 함수 앞에 await 을 붙이고 이 전체 함수엔 비동기 함수가 들어있다는 것을 표현하는 async 을 넣은 뒤 코드를 실행하니 정상적으로 DB 에서 값을 가져오며 Object 자료형으로 가져오는 것을 확인

 

DB 에 접근해서 데이터를 가져오는 기능은 controller 에서 하지 않으므로 member_service 쪽으로 해당 기능을 옮긴다

 

service 에서는 DB 에 직접 접근하지 않으니 해당 기능을 또 DAO 로 넘겨준다

 

member_dao 에서 DB 에 접근

 

DAO 를 호출해서 사용하는 service 와 service를 호출해서 사용하는 controller 에도 async 와 await 을 걸어서 비동기로 작동하는 코드를 처리해줘야 promise 가 아닌 정상적인 연산을 끝낸 결과 값을 받을 수 있다

 

controller, service, dao

 

controller : service 와 연동하여 처리된 데이터를 ejs 와 스크립트 등에 전달하여 세부경로로 들어왔을때 클라이언트에게 보여줄 내용을 컨트롤한다

service : 기능을 담당하며 dao 에서 DB 에 접근해서 가져온 데이터를 가공해서 다시 controller 에 전송한다

dao : DB 에 접근하여 쿼리문을 처리한 뒤 DB 에서 돌아온 리턴 값을 그대로 service 에게 전달한다

 

member_index.ejs 에서 전달받은 데이터를 출력

 

데이터 추가 기능을 만들 것임

 

router 와 controller 에 register 경로로 들어올 때 기능 추가

 

연결될 register_form.ejs 생성 및 작성

 

정상적으로 연동, 회원가입을 누르면 /member/register 로 값들을 전달

 

회원가입 버튼 누르면 post 방식으로 들어오는 데이터를 처리할 /member/register 를 작성

 

controller 에서 post 방식으로 받은 데이터를 req.body 를 사용하여 출력해봄 - 정상 출력

 

service 에 insert 기능을 추가해서 거기에 입력 값을 전달

 

service 에서는 DAO 에 입력 값을 다시 전달하여 반환 값을 받고 반환 값에 따라 메세지가 출력되는 부분을 다시 리턴해준다

 

DAO 에서는 데이터를 전달 받아서 쿼리문을 실행시키고 쿼리문이 정상적으로 동작하면 result 에 객체 값이 전달되어 리턴된다, 만약 쿼리문 사용에 오류가 생기면 catch 로 코드가 진행되고 종료되기 때문에 result 는 0 으로 반환된다

sql 문에서 사용된 :id, :pwd, :name, :addr 은 키에 대한 값을 의미한다.

:id > id 키에 대한 값으로 변환

:pwd > pwd 키에 대한 값으로 변환

:name > name 키에 대한 값으로 변환

:addr > addr 키에 대한 값으로 변환

 

코드 실행 ( id 값이 db 에서는 primary key 로 설정되어 있으므로 동일한 zzz 아이디를 두 개 생성할 수 없어 문제 발생 )

 

member_index 를 볼때 이름에 a 태그로 회원 정보를 보는 기능을 추가 ( 하나는 경로로 아이디 값을 보여줌, 하나는 아이디 값으로 아이디를 보이지 않고 전달함 )

 

member_router 에 기능 추가

 

controller 에 기능 추가

 

service 는 controller 로 부터 전달 받은 id 정보를 DAO 에 전달

 

DAO 에서는 sql query 문 실행 후 리턴 값을 service 에 전달 ( sql 문을 작성할때 위 코드에서 DB 에 있는 id 열과 매개변수의 이름을 동일하게 id 로 맞춰줘야 한다 ), 위 코드에서 :id > mId.Id 를 의미

 

service 에서는 전달 받은 값의 rows 키 안에 들어있는 내용의 0번째 index 에 데이터가 들어있기 때문에 해당 값을 리턴

 

controller 에서는 해당 값을 member 라는 변수로 받아서 member_view.ejs 를 출력할때 값을 전달

 

member_view.ejs 생성 후 전달 받은 값으로 내용을 작성

 

회원 삭제 버튼을 누를 시 /member/delete 경로로 이동될 때 처리될 내용을 router 와 controller 에 작성

 

서비스에서도 전달 받은 아이디 값으로 DAO 에 전달하여 리턴을 받아 사용

 

DAO 에서는 해당 값을 전달 받고 쿼리문을 실행한 뒤 반환

 

삭제 기능 구현 완료

 

 

 

 

 

회원 수정 기능 구현

router 에 회원 수정 기능 경로 설정

( 경로 오타 : /modify_form 으로 수정해야 함 )

 

controller 에서 우선적으로 기존에 사용하던 멤버 보는 기능을 구현했던 service 의 함수를 재활용 하여 출력할 것임

 

modify_form.ejs 작성 후 수정 버튼 누르면 modify로 가기 때문에 modify 기능 추가

 

router 와 controller 작성
service 작성

 

DAO 작성, 쿼리문 결과 값 return

 

회원 수정 기능 구현 완료

 

 

 

 

 

네비게이션 bar 설정하기

( 모든 페이지에서 상단에 같은 ejs 파일을 적용하기 )

test.ejs 생성 및 네비게이션 bar 로 설정 및 적용

 

localhost:3000/ 으로 접속 시 나오는 페이지 상단에 test.ejs 파일의 내용이 출력되는 모습

 

- 네비게이션 bar 로 설정할때 주의점 -

<%- include("네비게이션 bar 로 사용할 파일의 경로")%>

위 코드로 적용할때 경로는 상대경로로 적용하는 경우 현재 파일 위치를 기준으로 변경해줘야 한다

728x90

'BE > Node.js' 카테고리의 다른 글

[Node.js] get, post, put, delete  (0) 2024.04.05
[Node.js] 암호화, fetch, spa  (1) 2024.04.04
[Node.js] DB 연동(1)  (0) 2024.04.02
[Node.js] 세션, post 방식 전달  (1) 2024.04.02
[Node.js] 쿠키  (0) 2024.04.01

 

 

 

 

DB 연동(1)

db_connect 폴더 생성 후 해당 폴더로 터미널 디렉토리 이동하여 필요한 모듈 설치


module 다운로드

npm install express --save
npm install ejs --save
npm install oracledb@^5.5.0 --save
npm install supervisor --save
npm install body-parser --save

 

app01.js 파일 생성

 

app01.js 간단히 작성

위 코드는 동기 방식 ( 순차적으로 코드를 읽는다 )

 

 

 

 

 

DB 를 연결하는 방식은 비동기 방식이다.

비동기 방식이므로 코드가 순차적으로 읽히지 않고 DB 에서 데이터를 읽어오지 못한 경우에도 DB 에서 가져온 데이터를 가지고 연산해야 하는 코드들이 실행되게 된다.... 이때 비동기 방식의 코드가 처리된 이후 코드를 실행하게 설정해 줄 필요가 있다.

비동기 방식의 함수인 connect 를 선언하고 해당 함수가 실행 된 뒤에 다른 코드를 실행하게끔 설정

 

1. 함수를 변수로 선언 [ con = connect(); 와 같이 ] 한 뒤 con.then 으로 con 함수가 실행되면 다음 함수를 실행하게끔 설정

2. 함수 내에 비동기 함수가 있음을 async 을 통해 선언 후 await 으로 비동기 함수가 동작한 뒤 다음 내용을 실행할 수 있도록 설정 

 

 

 

 

 

DB 를 연결 할때 비동기 방식을 처리한 뒤 연결하게끔 설정

비동기 처리 방식 2가지 ( .then / async - await 두 가지를 모두 사용해봄 )

728x90

+ Recent posts