Cute Apple
본문 바로가기
개발/Node.js

Node.js 갤러리 게시판

by 미댕댕 2021. 4. 27.

폴더 구성

필요한 도메인 LIST

-게시판 폼

-게시판 등록

-게시판 목록

-게시판 상세보기

-게시판 수정

-게시판 삭제

 

 

 


server 작업 

 

1. 서버 준비 및 모듈 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
var http = require("http");
//클라이언트가 업로드한 바이너리 데이터 처리를 위한 모듈
var multer=require("multer"); //외부모듈
// var oracledb=require("oracledb");//외부
var mysql=require("mysql");
var mymodule=require("./lib/mymodule.js");
var fs=require("fs"); //내장 모듈
var ejs=require("ejs"); //외부모듈
var path=require("path"); //파일의 경로와 관련되어 유용한 기능을 보유한 모듈, 확장자를 추출하는 기능 포함
var express = require("express");
 
var app = express(); //express객체 생성
 
//필요한 각종 미들웨어 적용
app.use(express.static(__dirname+"/static"));
 
//업로드 모듈을 이용한 업로드 처리 storage:저장할 곳, filename:저장할 이름
//노드 js 뿐만이 아니라, asp,php, jsp 등등은 일단 업로드 컴포넌트를 사용할 경우
//모든 post는 이 업로드 컴포넌트를 통해 처리된다.
var upload=multer({
    storage: multer.diskStorage({
        destination:function(request, file, cb){
            cb(null, __dirname+"/static/upload");
        },
        filename:function(request, file, cb){
            // console.log("file is", file);
            //업로드한 파일에 따라서 파일 확장자는 틀려진다. 프로그래밍적으로 정보를 추출할것!
            //path.extname(originalname) 의 결과는 jpg.png
            console.log("업로드된 파일의 확장자는", path.extname(file.originalname));
            cb(nullnew Date().valueOf()+path.extname(file.originalname));
        }
    })
});
 
//mysql 접속 정보 
var conStr={
    url:"localhost:3306",
    user:"root",
    password:"1234",
    database:"nodejs"
};
 
 
 
 
//여기에 기능 구현
 
 
 
 
var server = http.createServer(app); //기몬 모듈에 express 모듈 연결
server.listen(9999function(){
    console.log("Gallery Server is running at 9999 port....");
});
cs

 

 ❓ Multer 란 ❓

* 파일 업로드를 위해 사용되는 multipart/form-data를 다루기 위한 미들웨어

 

※ 여기서 upload 되는 파일이름은 시간을 16진수로 바꾼 숫자와 path.extname(file.originalname) 에 의해 추출된 확장자명이다.

 

<파일 정보>

 

 

2. gallery/regist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//글등록 요청 처리 
app.post("/gallery/regist", upload.single("pic") ,function(request , response){
    //파라미터 받기
    var title=request.body.title;
    var writer=request.body.writer;
    var content=request.body.content;
    var filename=request.file.filename;//multer를 이용했기 떄문에 기존의 request객체에 추가된 것
    console.log("filename 은", filename);
    // console.log("request 객체는", request);
 
    var con=mysql.createConnection(conStr);
    var sql="insert into gallery(title, writer, content, filename) values(?, ?, ?, ?)";
    con.query(sql, [title, writer, content, filename], function(err, fields){
        if(err){
            console.log(err);
        }else{
            response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
            response.end(mymodule.getMsgUrl("등록완료","/gallery/list"));
        }
        con.end();
    });
});
cs

 

3. gallery/list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//글목록 요청 처리
app.get("/gallery/list"function(request, response){
    var con=mysql.createConnection(conStr);
    var sql="select * from gallery order by gallery_id desc"//내림차순
    con.query(sql, function(err, result, fields){
        if(err){
            console.log(err);
        }else{
            //ejs 렌더링
            fs.readFile("./gallery/list.ejs""utf8"function(error, data){
                if(error){
                    console.log(error)
                }else{
                    response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
                    response.end(ejs.render(data,{
                        galleryList:result,
                        lib:mymodule
                    }));
                }
                con.end();
            });
        }
    });
});
cs

 

 

4. gallery/detail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//상세보기
app.get("/gallery/detail"function(request, response){
    var con=mysql.createConnection(conStr);
    var gallery_id=request.query.gallery_id;
 
    var sql="select * from gallery where gallery_id="+gallery_id;
    //쿼리문 수행
    con.query(sql,function(err, result, fields){
        if(err){
            console.log(err)
        }else{
            //상세페이지 보여주기
            fs.readFile("./gallery/detail.ejs""utf8"function(error, data){
                if(error){
                    console.log(error)
                }else{
                    var d=ejs.render(data, {
                        gallery:result[0]
                    });
                    response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
                    response.end(d);
                }
                con.end();
            });
        }
    });
});
cs

 

5. gallery/del

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//삭제요청 처리(DB삭제+이미지 삭제)
//파일을 업로드 하건, 안하건 일단 multer를 사용하게 되면, 모든 post방식에 관여하게 되어있다
//(node.js 의 특징)
app.post("/gallery/del",upload.single("pic"), function(request, response){
    var gallery_id=request.body.gallery_id;
    var filename=request.body.filename
 
    console.log("gallery_id", gallery_id);
 
    fs.unlink(__dirname+"/static/upload/"+filename, function(err){
        if(err){
            console.log("삭제실패", err);
        }else{
            var sql="delete from gallery where gallery_id="+gallery_id;
            var con=mysql.createConnection(conStr); //접속 및 커넥션 객체 반환
            con.query(sql, function(error, fields){
                if(error){
                    console.log("삭제실패", error);
                }else{
                    //목록요청
                    response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
                    response.end(mymodule.getMsgUrl("삭제완료","/gallery/list"));
                }
            })
        }
        con.end();
    });
});
cs

 

6. gallery/edit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
app.post("/gallery/edit", upload.single("pic"), function(request, response){
    var title=request.body.title;
    var writer=request.body.writer;
    var content=request.body.content;
    var filename=request.body.filename;
    var gallery_id=request.body.gallery_id;
 
    //업로드시, request객체의 json 속성 중 file 이라는 속성이 판단대상
    // console.log(request.file);
 
    if(request.file != undefined){//업로드를 원하는것(사진교체)
        console.log("사진을 교체합니다");
        //사진 지우기+db수정
        fs.unlink(__dirname+"/static/upload/"+filename, function(err){
            if(err){
                console.log("수정실패", err);
            }else{
                filename=request.file.filename; //새롭게 업로드된 파일명으로 교체
                var sql="update gallery set title=?, writer=?, content=?, filename=? where gallery_id=?";
                var con=mysql.createConnection(conStr); //mysql접속
                con.query(sql, [title, writer, content, filename, gallery_id], function(error, fields){
                    if(error){
                        console.log("수정실패", error);
                    }else{
                        response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
                        response.end(mymodule.getMsgUrl("수정완료","/gallery/detail?gallery_id="+gallery_id));
                    }
                    con.end();
                });
            }
        });
    }else{//(사진유지)
        var sql="update gallery set title=?, writer=?, content=? where gallery_id=?";
        var con=mysql.createConnection(conStr); //mysql접속
        con.query(sql, [title, writer, content, gallery_id], function(error, fields){
            if(error){
                console.log("수정실패",error)
            }else{
                response.writeHead(200, {"Content-Type":"text/html;charset=utf-8"});
                        response.end(mymodule.getMsgUrl("수정완료","/gallery/detail?gallery_id="+gallery_id));
            }
            con.end();
        });
 
    }
 
    //db만 변경(사진 유지)
});
cs

 

 


client 작업

 

1. regist_form.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 파비콘.. 윈도우 상 아이콘이다. -->
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
 
<style>
body {font-family: Arial, Helvetica, sans-serif;}
{box-sizing: border-box;}
 
input[type=text],textarea, input[type=file]{
  width: 100%;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  margin-top: 6px;
  margin-bottom: 16px;
  resize: vertical;
}
textarea{
  height: 200px;
}
#preview{
  width: 300px;
}
 
input[type=button] {
  background-color: #4CAF50;
  color: white;
  padding: 12px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
 
input[type=submit]:hover {
  background-color: #45a049;
}
 
.container {
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.ckeditor.com/4.16.0/standard/ckeditor.js"></script>
<script>
/*JQuery 의 문법 형시 (누구를).어떻게, 누구자리에 올수있는 요소?
CSS의 selector가 올수 있다.*/
$(function(){ //onLoad되면
  //편집기 입히기
  CKEDITOR.replace("content");
 
  $($("input[type='button']")[0]).click(function(){//등록버튼
    regist();
  });
 
  $($("input[type='button']")[1]).click(function(){//목록버튼
    getList();
  });
 
  //이미지 선택시
  $("input[type='file']").on("change"function(){
    //preview라는 이미지 scr값을 선택한 값을 변경
    // console.log($(this).val());
    var filename=$(this).val().substring($(this).val().lastIndexOf("\\")+1, $(this).val().length);
    console.log("추출된 파일명", filename);
    $("#preview").attr("src","http://localhost:9999/images/"+filename);
  });
 
});
 
function regist(){
  $("form").attr("action""/gallery/regist");
  $("form").attr("method""post");
  $("form").attr("enctype""multipart/form-data");//텍스트 아닌 바이너리 형식도 포함되어있다
  //만일 이 속성을 지정하지 않으면, 절대로 바이너리 파일 전송은 불가능하다
  $("form").submit(); //전송행위가 발생
 
}
 
function getList(){
  location.href="/notice/list";
}
</script>
</head>
<body>
 
<h3>Contact Form</h3>
 
<div class="container">
    <!-- 폼 태그의 속성중 action은 이 폼양식을 전송할 대상 서버의 url을 명시할 수 있다. -->
    <!-- 
      id와 name 공통점과 차이점
      공통점)문서내의 요소를 식별하기 위함
      차이점)id-유일해야함, name-중복허용(배열로 인식)
                name은 폼 전송시 전송 파라미터 역할을 한다, 즉 변수 역할을 한다
                이때 전송 파라미터로서의 name의 이름은 주로 db의 테이블의 컬럼명과 일치시키는 규칙
     -->
  <form>
    <input type="text" name="title" placeholder="Your title..">
    <input type="text" name="writer" placeholder="Your name..">
    <textarea name="content" placeholder="content.."></textarea>
    <input type="file" name="pic">
    <img src="#"  alt="" id="preview">
 
    <!-- input 태그의 type 중 submit은 디폴트로 전송기능이 포함되어 있기 때문에
    클릭만으로도, 전송이 발생함. 따라서 일반 버튼화 시켜놓자 -->
    <input type="button" value="등록">
    <input type="button" value="목록">
  </form>
</div>
 
</body>
</html>
 
cs

 

2. list.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
table {
  border-collapse: collapse;
  border-spacing: 0;
  width: 100%;
  border: 1px solid #ddd;
}
 
th, td {
  text-align: left;
  padding: 16px;
}
 
tr:nth-child(even) {
  background-color: #f2f2f2;
}
</style>
</head>
<body>
 
<h2>게시물의 수는 <%=galleryList.length%></h2>
 
<table>
  <tr>
    <th>No</th>
    <th>이미지</th>
    <th>제목</th>
    <th>작성자</th>
    <th>작성일</th>
    <th>조회수</th>
  </tr>
  <%var totalRecord=galleryList.length; //총 게시물 수를 담자%>
  <%for(var i=0; i<galleryList.length; i++){%>
  <%var gallery=galleryList[i]; //배열에서 json 하나 꺼내기%>
  <tr>
    <td><%=(totalRecord--)%></td>
    <td><img src="/upload/<%=gallery.filename%>" width="45px"></td>
    <td><a href="/gallery/detail?gallery_id=<%=gallery.gallery_id%>"><%=gallery.title%></a></td>
    <td><%=gallery.writer%></td>
    <%var d=new Date(gallery.regdate);%>
    <td>
      <%=d.getFullYear()%>-<%=lib.getZeroString(d.getMonth()+1)%>-<%=lib.getZeroString(d.getDate())%>
    </td>
    <td><%=gallery.hit%></td>
  </tr>
  <%}%>
  <tr>
    <td colspan="5">
      <button onClick="location.href='/gallery/regist_form.html'">글등록</button>
    </td>
  </tr>
</table>
 
</body>
</html>
 
cs

 

3. detail.ejs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 파비콘.. 윈도우 상 아이콘이다. -->
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
 
<style>
body {font-family: Arial, Helvetica, sans-serif;}
{box-sizing: border-box;}
 
input[type=text],textarea, input[type=file]{
  width: 100%;
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
  margin-top: 6px;
  margin-bottom: 16px;
  resize: vertical;
}
textarea{
  height: 200px;
}
#preview{
  width: 300px;
}
 
input[type=button] {
  background-color: #4CAF50;
  color: white;
  padding: 12px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
 
input[type=submit]:hover {
  background-color: #45a049;
}
 
.container {
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.ckeditor.com/4.16.0/standard/ckeditor.js"></script>
<script>
/*JQuery 의 문법 형시 (누구를).어떻게, 누구자리에 올수있는 요소?
CSS의 selector가 올수 있다.*/
$(function(){ //onLoad되면
  //편집기 입히기
  CKEDITOR.replace("content");
 
  $($("input[type='button']")[0]).click(function(){//등록버튼
    regist();
  });
 
  $($("input[type='button']")[1]).click(function(){//목록버튼
    getList();
  });
 
  $($("input[type='button']")[2]).click(function(){//수정버튼
    edit();
  });
 
  $($("input[type='button']")[3]).click(function(){//삭제버튼
    del();
  });
 
  //이미지 선택시
  $("input[type='file']").on("change"function(){
    //preview라는 이미지 scr값을 선택한 값을 변경
    // console.log($(this).val());
    var filename=$(this).val().substring($(this).val().lastIndexOf("\\")+1, $(this).val().length);
    console.log("추출된 파일명", filename);
    $("#preview").attr("src","http://localhost:9999/images/"+filename);
  });
 
});
 
function regist(){
  $("form").attr("action""/gallery/regist");
  $("form").attr("method""post");
  $("form").attr("enctype""multipart/form-data");//텍스트 아닌 바이너리 형식도 포함되어있다
  //만일 이 속성을 지정하지 않으면, 절대로 바이너리 파일 전송은 불가능하다
  $("form").submit(); //전송행위가 발생
 
}
 
function getList(){
  location.href="/gallery/list";
}
 
function del(){
  if(confirm("삭제하실래요?")){
    $("form").attr("action""/gallery/del");
    $("form").attr("method""post");
    $("form").attr("enctype""multipart/form-data");//텍스트 아닌 바이너리 형식도 포함되어있다
    $("form").submit(); //전송행위가 발생
 
    //location.href="/gallery/del?gallery_id=<%=gallery.gallery_id%>&filename=<%=gallery.filename%>"; GET방식
  }
}
//수정요청(데이터 양도 많고, 바이너리 파일도 포함될 수 있으므로  post 방식)
function edit(){
  if(confirm("수정하실래요?")){
    $("form").attr("action""/gallery/edit");
    $("form").attr("method""post");
    $("form").attr("enctype""multipart/form-data");//텍스트 아닌 바이너리 형식도 포함되어있다
    $("form").submit(); //전송행위가 발생
  }
}
</script>
</head>
<body>
 
<h3>Contact Form</h3>
 
<div class="container">
    <!-- 폼 태그의 속성중 action은 이 폼양식을 전송할 대상 서버의 url을 명시할 수 있다. -->
    <!-- 
      id와 name 공통점과 차이점
      공통점)문서내의 요소를 식별하기 위함
      차이점)id-유일해야함, name-중복허용(배열로 인식)
                name은 폼 전송시 전송 파라미터 역할을 한다, 즉 변수 역할을 한다
                이때 전송 파라미터로서의 name의 이름은 주로 db의 테이블의 컬럼명과 일치시키는 규칙
     -->
  <form>
    <!-- 수정, 삭제시 사용할 gallery_id primary key를 히든으로 숨겨놓자 -->
    <input type="hidden" name="gallery_id" value="<%=gallery.gallery_id%>">
    <input type="hidden" name="filename" value="<%=gallery.filename%>">
 
    <input type="text" name="title" value="<%=gallery.title%>">
    <input type="text" name="writer" value="<%=gallery.writer%>">
    <textarea name="content" ><%=gallery.content%></textarea>
    <input type="file" name="pic">
    <img src="/upload/<%=gallery.filename%>"  alt="" id="preview">
    <p></p>
 
    <!-- input 태그의 type 중 submit은 디폴트로 전송기능이 포함되어 있기 때문에
    클릭만으로도, 전송이 발생함. 따라서 일반 버튼화 시켜놓자 -->
    <input type="button" value="등록">
    <input type="button" value="목록">
    <input type="button" value="수정">
    <input type="button" value="삭제">
  </form>
</div>
 
</body>
</html>
 
cs

 

 

반응형

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

Node.js 댓글 게시판(ajax사용/Oracle 사용)  (0) 2021.04.13
Node.js 게시판(express 사용/Mysql 사용)  (0) 2021.04.08
Node.js express 없이 서버 구축  (0) 2021.04.06
Node.js 기초  (0) 2021.04.01

댓글