fetch("/login/nicknameCheck",{
                    method : "post",
                    headers : {"Content-Type":"application/json"},
                    body : JSON.stringify(data)
                })
                .then(res => res.json())
                .then(result => {
                    console.log("result : ", result);
                    if(result == 1){
                        alert("닉네임 중복체크를 진행해주세요.")
                    }else{
                        register();
                    }
                })

front 에서 fetch() 를 사용하여 POST 방식으로 서버에 데이터를 요청

 

 

 

 

const process = {
    nicknameCheck : async (req, res) => {
            let inputNickname = req.body.nickname;
            let result = await service.duplicationCheck.nicknameCheck(inputNickname);
            // 아이디가 존재할 시 result : 1
            // 아이디가 존재하지 않을시 result : 0
            res.json(result);
        }
}

controller 에서 localhost:3000/login/nicknameCheck 로 요청이 들어오기는 하나 프론트에서 보내준 파라미터 값이 서버에서 req.body 로 받아지지 않는 현상 발생

 

 

 

 

 

- 해결방법 -

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

const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());

const router = require("./src/routers/root_router")(app);
app.use("/", router);


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

app.listen(3000, () => {
  console.log("3000 서버 구동");
});

최상단의 app.js 에서 app.use(bodyParser.json()); << 다음과 같이 json 형식의 파일을 bodyParser 미들웨어로 설정해 놓지 않아서 발생한 문제였다.

( post 형식의 데이터는 body-parser 를 사용하여 넘어오는 json 형식의 데이터를 변환시켜 사용해야 한다 )

 

위 설정 이후 console.log(req.body) 를 확인하니 정상적으로 값이 넘어온다.

 

오늘의 교훈 : POST 방식을 사용하면서 bodyParser 를 사용하지 않은 나는 바보이다....

728x90

 

 

 

 

 

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

 

 


 

게시글 상세보기 기능 추가

 

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

 

 

 

 

 

Node.js 파일에서 Oracle Cloud DB 에 접속하여 쿼리문을 실행시켜 볼 것이다

 

우선은 자신의 Oracle Cloud DB 에 접속하여 접속 가능한 IP 설정을 진행한다

엑세스 제어 목록 - 편집 클릭

 

값에 접속할 IP 를 설정해준다 ( 내 IP 주소 추가를 누르면 내 공인 IP 가 추가되며 내 아이피 외에 다른 아이피가 접속하려면 다른 공인 IP 를 기입해주면 된다 ) - 저장

 

mTLS 인증 옆의 편집 클릭

 

mTLS 인증 필요의 체크박스를 해제 후 저장 클릭

 

데이터 베이스 접속 클릭

 

접속 문자열에서 TLS 클릭

 

TNS 의 접속 문자열 중  high, low, medium 중 하나 복사 ( high, low, medium 은 성능을 나타냄 )

 

high - 최고 성능, 가장 빠른 응답 속도로 수행되지만 실행할 수 있는 동시 SQL 문 수가 3개

medium - high 보다 동시에 많은 SQL 문을 수행할 수 있으나 high 에 비해서는 낮은 수준의 성능을 제공

low - 동시에 가장 많은 SQL 문을 수행하며 각 SQL 문 마다 최소로 작은 수준의 리소스를 제공

 

const oracledb = require('oracledb');

// Oracle Cloud 자율운영 데이터베이스 연결 정보
const dbConfig = {
  user: 'admin',
  password: 'Oracle1234567',
  connectString: '!!!!Oracle DB의 접속 TLS 정보를 기입!!!!'
  // connectString 에 TLS 에서 복사한 정보를 기입
};

async function run() {
  let connection;

  try {
    // Oracle 데이터베이스에 연결
    connection = await oracledb.getConnection(dbConfig);

    // 연결 확인
    console.log('Connected to database!');

    // 여기에 쿼리 실행 또는 기타 작업 추가
    // 예를 들어:
    // const result = await connection.execute('SELECT * FROM my_table');
    // console.log(result);
    let result = await connection.execute("select * from test");
    console.log("result : ", result);
    
  } catch (err) {
    console.error('Error connecting to database:', err);
  } finally {
    // 연결 닫기
    if (connection) {
      try {
        await connection.close();
        console.log('Connection closed.');
      } catch (err) {
        console.error('Error closing connection:', err);
      }
    }
  }
}

run();

dbConfig 에서 connectString 부분에 복사한 oracleDB 의 TLS 주소를 넣어준다

 

테스트로 생성해둔 test 테이블의 결과를 출력했으며 응답은 다음과 같다.

rows 열에 있는 데이터를 꺼내 쓰면 될 듯 하다

 

지금은 배열 형식의 Object 를 사용하지만 key 로 값을 가져오는 Object 형식으로 변환하는 코드를 추가

 

const oracledb = require('oracledb');

oracledb.autoCommit = true;
// 쿼리문 작성 시 자동으로 커밋되는 기능 활성화
oracledb.outFormat = oracledb.OBJECT;
// 데이터 베이스로부터 데이터를 얻어올때 Object 자료형으로 가져온다
// 위 설정을 하면 [{}, {}, {}] 형식으로 들어오고
// 위 설정을 하지 않으면 [[], [], []] 형식으로 데이터가 들어온다
// 키와 값으로 사용하기 위해 해당 설정을 한다

// Oracle Cloud 자율운영 데이터베이스 연결 정보
const dbConfig = {
  user: 'admin',
  password: 'Oracle1234567',
  connectString: '(description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)(host=adb.us-chicago-1.oraclecloud.com))(connect_data=(service_name=g72f610f5f11b4c_orcl_high.adb.oraclecloud.com))(security=(ssl_server_dn_match=yes)))'
  // connectString 에 TLS 에서 복사한 정보를 기입
};

async function run() {
  let connection;

  try {
    // Oracle 데이터베이스에 연결
    connection = await oracledb.getConnection(dbConfig);

    // 연결 확인
    console.log('Connected to database!');

    // 여기에 쿼리 실행 또는 기타 작업 추가
    // 예를 들어:
    // const result = await connection.execute('SELECT * FROM my_table');
    // console.log(result);
    let result = await connection.execute("select * from test");
    console.log("result : ", result);
    
  } catch (err) {
    console.error('Error connecting to database:', err);
  } finally {
    // 연결 닫기
    if (connection) {
      try {
        await connection.close();
        console.log('Connection closed.');
      } catch (err) {
        console.error('Error closing connection:', err);
      }
    }
  }
}

run();

 

oracledb.autoCommit = true;
// 쿼리문 작성 시 자동으로 커밋되는 기능 활성화
oracledb.outFormat = oracledb.OBJECT;
// 데이터 베이스로부터 데이터를 얻어올때 Object 자료형으로 가져온다
// 위 설정을 하면 [{}, {}, {}] 형식으로 들어오고
// 위 설정을 하지 않으면 [[], [], []] 형식으로 데이터가 들어온다
// 키와 값으로 사용하기 위해 해당 설정을 한다

위 부분이 추가됐으며, 결과 값은 아래와 같다

 

기존 결과 값과 다르게 키, 값의 형식으로 가져오는 모습 여기서 키는 row 의 값으로 변환되어 나온다

 

이전에 DAO 를 사용하는 형식으로 사용될 때 db의 Config 파일만 고쳐주면 사용이 가능하다

기존에 connectString 의 값을 복사한 TLS 값으로 집어넣으면 됨

 

DAO 를 사용해서 DB 에 접근하는 부분은 아래 링크 참조

 

[Node.js] DB 연동(2) - DAO 로 쿼리문 실행, 네비게이션 bar 만들기

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','김개똥','개똥별'); in

youngho3358.tistory.com

728x90

 

 

 

 

 

Oracle Cloud DB 접속 방법

오라클 클라우드에 접속해서 본인의 데이터베이스 세부정보 > 데이터베이스 접속 클릭

 

인스턴스 전자 지갑 클릭 > 전자 지갑 다운로드 클릭

 

Name 에 Database 이름 기입 > 등록 계정 ID / PWD 기입 > 다운로드 받은 전자지갑 키를 구성파일 선택에서 등록 후 접속

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

+ Recent posts