[node js] 성경 db 연동

mysql 성경 db를 찾아서 데이터를 넣어준 이후 작업.


bible.js

var express = require('express')
var app = express()
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')




//성경 메인 화면
router.get("/bible",function(req,res){
console.log("메인화면 진행")
//console.log(searchBibleIndex("여호수아"))

fs.readFile('bible.html', 'utf-8', function (error, data) {
res.send(ejs.render(data, {
data: [],
title: {book:"",chapter:""}
}));
});
})


//메인 화면
router.post("/bible_search",function(req,res){
console.log("바이블 찾기 포스트 진행")
var maintext = req.body.maintext

//창세기이면 01, 출애굽기

maintext = searchBibleIndex(maintext)

var jang = req.body.jang
var jeol = req.body.jeol
var jeol2 = req.body.jeol2
console.log(maintext,jang,jeol,jeol2)

var queryString = 'select book, chapter,verse,content from bible_korHRV where book = ? and chapter = ? and verse between ? and ?'
fs.readFile('bible.html', 'utf-8', function (error, data) {
getConnection().query(queryString, [maintext,jang,jeol,jeol2], function (error, result) {
if (error) {
console.log("바이블 데이터 로드 에러" + error);
return
}
//var bookName = searchBibleName(result[0].book)

res.json(result)

// res.send(ejs.render(data, {
// data: result,
// bookname:bookName,
// chapter : result[0].chapter
// }))
})
});

})


function searchBibleName(index) {

var searchIndex = index - 1

var book= [ "창세기",
"출애굽기",
"레위기",
"민수기",
"신명기",
"여호수아",
"사사기",
"룻기",
"사무엘상",
"사무엘하",
"열왕기상",
"열왕기하",
"역대상",
"역대하",
"에스라",
"느헤미야",
"에스더",
"욥기",
"시편",
"잠언",
"전도서",
"아가서",
"이사야",
"예레미야",
"예레미야애가",
"에스겔",
"다니엘",
"호세아",
"요엘",
"아모스",
"오바댜",
"요나",
"미가",
"나훔",
"하박국",
"스바냐",
"학개",
"스가랴",
"말라기",
"마태복음",
"마가복음",
"누가복음",
"요한복음",
"사도행전",
"로마서",
"고린도전서",
"고린도후서",
"갈라디아서",
"에베소서",
"빌립보서",
"골로새서",
"데살로니가전서",
"데살로니가후서",
"디모데전서",
"디모데후서",
"디도서",
"빌레몬서",
"히브리서",
"야고보서",
"베드로전서",
"베드로후서",
"요한일서",
"요한이서",
"요한삼서",
"유다서",
"요한계시록"]


return book[searchIndex]
}

function searchBibleIndex(name) {

var book= [ "창세기",
"출애굽기",
"레위기",
"민수기",
"신명기",
"여호수아",
"사사기",
"룻기",
"사무엘상",
"사무엘하",
"열왕기상",
"열왕기하",
"역대상",
"역대하",
"에스라",
"느헤미야",
"에스더",
"욥기",
"시편",
"잠언",
"전도서",
"아가서",
"이사야",
"예레미야",
"예레미야애가",
"에스겔",
"다니엘",
"호세아",
"요엘",
"아모스",
"오바댜",
"요나",
"미가",
"나훔",
"하박국",
"스바냐",
"학개",
"스가랴",
"말라기",
"마태복음",
"마가복음",
"누가복음",
"요한복음",
"사도행전",
"로마서",
"고린도전서",
"고린도후서",
"갈라디아서",
"에베소서",
"빌립보서",
"골로새서",
"데살로니가전서",
"데살로니가후서",
"디모데전서",
"디모데후서",
"디도서",
"빌레몬서",
"히브리서",
"야고보서",
"베드로전서",
"베드로후서",
"요한일서",
"요한이서",
"요한삼서",
"유다서",
"요한계시록"]
var changeBookList =[];

for(var i = 1; i < 67; i++){
var bookChange = { value :book[i-1]}
changeBookList.push(bookChange)
bookChange = {}
}

var bookIndex = changeBookList.map(function (book) { return book.value; }).indexOf(name);

console.log(bookIndex);
console.log(bookIndex+1);

if(bookIndex === -1){
return "데이터를 다시 확인해주세요"
}else{
return bookIndex+1
}
}



var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})


//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


bible.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>성경 검색</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="jumbotron text-center">
<h1>성경검색</h1>
<p>찾을구절을 검색하세요</p>
</div>


<div class="container">
<h2>성경본문찾기</h2>
<p>찾으실 성경 본문을 입력해보세요</p>
<form class="form-inline" action="/bible_search" method="POST">
<div class="form-group">
<label for="maintext">성경본문</label>
<input type="text" class="form-control" id="maintext" placeholder="창세기" name="maintext">
</div>
<br><br><br>
<div class="form-group">
<input type="text" class="form-control" id="jang" placeholder="1장" name="jang">
<label for="jang"></label>
</div>
<div class="form-group">
<input type="text" class="form-control" id="jeol" placeholder="1절" name="jeol">
<label for="jeol"></label>
</div>
<br>
<br>
<h4>에서부터</h4>
<br>
<br>

<div class="form-group">
<input type="text" class="form-control" id="jeol2" placeholder="10절" name="jeol2">
<label for="jeol2"></label>
</div>
<br>
<br>
<h4>까지</h4>
<br>
<br>
<button type="submit" class="btn btn-default">찾기</button>
</form>
</div>

<br>
<br>

<div class="container">
<% var bookname = bookname %>
<% var chapter = chapter %>
<%
if(bookname && chapter){
%>
<h2><%= bookname %> 본문</h2>
<h3><%= chapter %> 장</h3>

<% }%>
<table class="table table-striped">
<thead>
<tr>
<th>구절</th>
<th>내용</th>
</tr>
</thead>
<tbody>

<% data.forEach(function (item,index){ %>
<tr>
<td>
<%= item.verse %>
</td>
<td>
<%= item.content %>
</td>
</tr>

<% })%>
</tbody>
</table>
</div>
</body>
</html>


결과화면




우분투 mysql에 외부에서 접속하기 


가비아에서 클라우드 서버를 쓰고 있는데 ip/phpmyadmin 들어가서 쿼리 작업하기가 많이 불편했다. 이참에 나의 맥북으로 원격으로 접속할 수 있게 설정을 해보았다. 사진위주로 정리해보았다.


1.나의 맥북 터미널로 ssh 서버 원격 접속!

2. etc/mysql/ 위치로 가서 vi my.cnf 열기~!

3.bind-address 부분 주석처리 #bind-address ! 이렇게!

4.sudo service mysql restart - 재시작!




5.  mysql -uroot -p 접속

6.select user, host from mysql.user  (유저와 호스트 확인)

7. % 이렇게 퍼센트로 기입된 host가 모든 host 허용이라는 뜻(나는 이미해놨음)

8.사진에 create ~~ 명령문 입력

9.명령문 다 입력후 flush privileges 를 꼭 적어준다.!!





아래는 3306 포트 열어주는 명령어 그리고 mysql 재시작





그리고 가비아 서버 매니저로 가서 방화벽정책에 mysql 3306 포트를 허용해줘야 한다. 

이것때문에 30분정도...삽질.




mysql워크벤치 확인~!




[node js] 파일전송


네비게이션



file.js

var express = require('express')
var app = express()
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')

//파일관련 모듈
var multer = require('multer')

//파일 저장위치와 파일이름 설정
var storage = multer.diskStorage({
destination: function (req, file, cb) {
//파일이 이미지 파일이면
if (file.mimetype == "image/jpeg" || file.mimetype == "image/jpg" || file.mimetype == "image/png") {
console.log("이미지 파일이네요")
cb(null, 'uploads/images')
//텍스트 파일이면
} else if (file.mimetype == "application/pdf" || file.mimetype == "application/txt" || file.mimetype == "application/octet-stream") {
console.log("텍스트 파일이네요")
cb(null, 'uploads/texts')
}
},
//파일이름 설정
filename: function (req, file, cb) {
cb(null, Date.now() + "-" + file.originalname)
}

})
//파일 업로드 모듈
var upload = multer({ storage: storage })


//파일 업로드 및 디비에 위치 저장
router.post('/upload_images', upload.single('fileupload'), function (req, res) {
console.log("post")
console.log(req.file)
console.log(req.file.path)
console.log(upload)
console.log(upload.storage.getFilename)

//파일 위치를 mysql 서버에 저장
getConnection().query('insert into myfile(name) values (?)', [req.file.path], function () {

res.redirect('/filepage');
});
});



//파일 페이지 보여주기
router.get("/filepage", function (req, res) {
console.log("파일 페이지 나와라")

//파일 가져올 위치
var path = __dirname + '/../' + 'uploads/images/'

fs.readFile('file.html', 'utf-8', function (error, data) {
var queryString = 'select * from myfile'
getConnection().query(queryString, function (error, result) {
if (error) {
console.log("파일가져올때 에러 발생" + error);
return
}
res.send(ejs.render(data, {
data: result
}));
});
})


})

//파일 다운로드 눌렀을 때 작동
router.get('/download/uploads/images/:name', function (req, res) {
var filename = req.params.name;

var file = __dirname + '/../uploads/images/' + filename
console.log(__dirname)
console.log(req.path)
console.log(file)
res.download(file); // Set disposition and send it.
});


var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})


//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


file.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>파일</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<div class="jumbotron">
<h1>파일업로드</h1>
<p> </p>
</div>
<p>.</p>
<a href="/main">게시판으로 이동</a>
</div>


<br>
<br>
<div class="container">
<form action="/upload_images" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleFormControlFile1">파일업로드</label>
<input type="file" class="form-control-file" id="fileupload" name="fileupload">
</div>
<input type="submit" value="올리기">
</form>
</div>

<br>
<br>
<br>
<h2>파일 다운로드</h2>
<ul>
<% data.forEach(function (item,index){ %>

<li><a href='/download/<%= item.name%>'><%= item.name%></a></li>

<% })%>
</ul>
</body>
</html>


결과화면


3년전인가 처음 게시판을 만들어보고 거의 처음인것 같다. 언제쯤이면 머릿속에 생각했던 내용들을 거침없이 만들 수 있는 날이 올까나. 암튼 게시판은 스스로 안보고 짤 수 있을 때까지 연습해보면 실력향상에 도움이 많이 되는 미니프로젝트긴하다. 나중에 게시판에 좀더 기능을 넣을 수도 있다. 예를 들면 db설계를 좀 해서 댓글 기능, 좋아요 기능, 조회기능 등을 넣어서 게시판을 확장 시킬 수 있다. 이번 포스팅에서는 아주 아주 심플한 게시판을 만들어서 페이징 기능의 핵심만 정리해본다.

네비게이션

아래는 파일 네비게이션이다. 우리가 사용할 파일들은 빨간점으로 표시해놓았다.


node js  기본셋팅(app.js)

app.js를 아래와 같이 작성한다. 게시판이랑 상관없는 로직도 있지만 지우지 않았다... var productRouter 부분 부터 자세히 보자. 우리가 만들 게시판과 직접 관련된 로직이다. 웹브라우저에 /main, /board 라고 적으면 ./routes/product.js에서 처리하게 해놓았다.그럼 products.js 페이지 부분으로 가보자

//모듈 임포트
var express = require('express');
var app = express()
var morgan = require('morgan') //로그 모듈 임포트
var mysql = require('mysql') //mysql 임포트
var bodyParser = require('body-parser');
//미들웨어 설정
app.use(morgan('short')) //로그 미들웨어
app.use(express.static('./public')) //기본 파일 폴더 위치 설정
app.use(bodyParser.urlencoded({extended:false}))
//라우트로 분리시켜주기
var userRouter = require('./routes/user.js')

//상품리스트 게시판~!
var productRouter = require('./routes/product.js')
app.use(userRouter)
app.use(productRouter)
//var PORT = process.env.PORT || 3003
//서버 가동
app.listen(3003,function(){
console.log("서버가동")
})


products.js (라우터)

가장 중요한 부분. 웹브라우저에서 넘어오는 url을 전부 이곳에서 받는다. 유심히 보아야 할 부분이 /main 부분과 /pasing/"cur 부분이다. 가장 처음으로 들어온 유저는 가장 첫페이지를 보여줘야 하기 때문에 기본값 1을 페이징 함수에 넘겼다. 나머지 설명은 주석에 적어 놓았다.

var express = require('express')
var router = express.Router()
var mysql = require('mysql')
var fs = require('fs')
var ejs = require('ejs')
var bodyParser = require('body-parser');


router.use(bodyParser.urlencoded({ extended: false }))


//게시판 페이징

router.get("/pasing/:cur", function (req, res) {

//페이지당 게시물 수 : 한 페이지 당 10개 게시물
var page_size = 10;
//페이지의 갯수 : 1 ~ 10개 페이지
var page_list_size = 10;
//limit 변수
var no = "";
//전체 게시물의 숫자
var totalPageCount = 0;

var queryString = 'select count(*) as cnt from products'
getConnection().query(queryString, function (error2, data) {
if (error2) {
console.log(error2 + "메인 화면 mysql 조회 실패");
return
}
//전체 게시물의 숫자
totalPageCount = data[0].cnt

//현제 페이지
var curPage = req.params.cur;

console.log("현재 페이지 : " + curPage, "전체 페이지 : " + totalPageCount);


//전체 페이지 갯수
if (totalPageCount < 0) {
totalPageCount = 0
}

var totalPage = Math.ceil(totalPageCount / page_size);// 전체 페이지수
var totalSet = Math.ceil(totalPage / page_list_size); //전체 세트수
var curSet = Math.ceil(curPage / page_list_size) // 현재 셋트 번호
var startPage = ((curSet - 1) * 10) + 1 //현재 세트내 출력될 시작 페이지
var endPage = (startPage + page_list_size) - 1; //현재 세트내 출력될 마지막 페이지


//현재페이지가 0 보다 작으면
if (curPage < 0) {
no = 0
} else {
//0보다 크면 limit 함수에 들어갈 첫번째 인자 값 구하기
no = (curPage - 1) * 10
}

console.log('[0] curPage : ' + curPage + ' | [1] page_list_size : ' + page_list_size + ' | [2] page_size : ' + page_size + ' | [3] totalPage : ' + totalPage + ' | [4] totalSet : ' + totalSet + ' | [5] curSet : ' + curSet + ' | [6] startPage : ' + startPage + ' | [7] endPage : ' + endPage)

var result2 = {
"curPage": curPage,
"page_list_size": page_list_size,
"page_size": page_size,
"totalPage": totalPage,
"totalSet": totalSet,
"curSet": curSet,
"startPage": startPage,
"endPage": endPage
};


fs.readFile('list.html', 'utf-8', function (error, data) {

if (error) {
console.log("ejs오류" + error);
return
}
console.log("몇번부터 몇번까지냐~~~~~~~" + no)

var queryString = 'select * from products order by id desc limit ?,?';
getConnection().query(queryString, [no, page_size], function (error, result) {
if (error) {
console.log("페이징 에러" + error);
return
}
res.send(ejs.render(data, {
data: result,
pasing: result2
}));
});
});


})

})


//메인화면
router.get("/main", function (req, res) {
console.log("메인화면")
//main 으로 들어오면 바로 페이징 처리
res.redirect('/pasing/' + 1)

});

//삭제
router.get("/delete/:id", function (req, res) {
console.log("삭제 진행")

getConnection().query('delete from products where id = ?', [req.params.id], function () {
res.redirect('/main')
});

})
//삽입 페이지
router.get("/insert", function (req, res) {
console.log("삽입 페이지 나와라")

fs.readFile('insert.html', 'utf-8', function (error, data) {
res.send(data)
})

})
//삽입 포스터 데이터
router.post("/insert", function (req, res) {
console.log("삽입 포스트 데이터 진행")
var body = req.body;
getConnection().query('insert into products(name,modelnumber,series) values (?,?,?)', [body.name, body.num, body.section], function () {
//응답
res.redirect('/main');
})

})
//수정 페이지
router.get("/edit/:id", function (req, res) {
console.log("수정 진행")

fs.readFile('edit.html', 'utf-8', function (error, data) {
getConnection().query('select * from products where id = ?', [req.params.id], function (error, result) {
res.send(ejs.render(data, {
data: result[0]
}))
})
});

})
//수정 포스터 데이터
router.post("/edit/:id", function (req, res) {
console.log("수정 포스트 진행")
var body = req.body;
getConnection().query('update products set name = ?, modelnumber = ?, series = ? where id = ?',
[body.name, body.num, body.section, req.params.id], function () {
res.redirect('/main')
})
})


//글상세보기
router.get("/detail/:id", function (req, res) {
console.log("수정 진행")

fs.readFile('detail.html', 'utf-8', function (error, data) {
getConnection().query('select * from products where id = ?', [req.params.id], function (error, result) {
res.send(ejs.render(data, {
data: result[0]
}))
})
});


})

//mysql db 연결 함수

var pool = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
user: 'root',
database: 'wow',
password: '1111'
})



//디비 연결 함수
function getConnection() {
return pool
}


module.exports = router


list.html(조회)

products.js에서 데이터를 처리한 후 뿌려주는 화면이 list.html이다. products.js 에서 게시물 조회 하는 로직 아래 

                res.send(ejs.render(data, {

                    data: result,

                    pasing: result2

                }));


이부분을 자세히 보자. mysql에서 가져온 데이터(result)와 페이징 관련 데이터 results2를 각각 data, pasing 키에 담았다! 이 키 값이 그대로 list.html로 넘어간다.


<!DOCTYPE html>
<html lang="ko">

<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>

<body>

<div class="container">
<h2>상품테이블</h2>

<a href="/insert">추가하기</a>
<br>
<br>
<p>상품테이블 목록입니다.</p>

<table class="table table-striped">
<thead>
<tr>
<th>아이디</th>
<th>상품명</th>
<th>넘버</th>
<th>분류</th>
<th>수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody>

<% data.forEach(function (item,index){ %>
<tr>
<td>
<%= item.id %>
</td>
<td>
<a href='/detail/<%= item.id %>'><%= item.name %></a>
</td>
<td>
<%= item.modelnumber %>
</td>
<td>
<%= item.series %>
</td>
<td>
<a href="/delete/<%= item.id %>">삭제</a>
</td>
<td>
<a href="/edit/<%= item.id %>">수정</a>
</td>
</tr>
<% })%>
</tbody>
</table>
</div>


<br>
<br>


<div class="container">
<ul class="pager">



<%
var curSet = pasing.curSet
var endPage = pasing.endPage
var startPage = pasing.startPage
var totalSet = pasing.totalSet
var totalPage = pasing.totalPage
var page_list_size = pasing.page_list_size
var page_size = pasing.page_size
var curPage = pasing.curPage
%>


<%
if(curSet > 1){
%>

<li value=(startPage -1) class="previous">
<a href='/pasing/<%= ( startPage - 1) %>'>이전페이지</a>
</li>

<%
}
%>


<%
for(var i = startPage; i <= endPage; i++){
if(i > totalPage){break;}

if(i == curPage){
%>
<li value=<%= i %> class='' >
<a href='/pasing/<%= i %>'>
<%= i %>
</a>
</li>

<%
}else{
%>

<li value=<%= i %> class='active' >
<a href='/pasing/<%= i %>'>
<%= i %>
</a>
</li>


<%
}
%>

<%
}
%>

<%
if(curSet < totalSet){
%>
<li value=( endPage + 1) class="next">
<a href='/pasing/<%= ( endPage + 1) %>'>다음페이지</a>
</li>
<%
}
%>

</ul>
</div>

<br>
<br>

</body>

</html>


결과확인


나머지 화면 페이지부분은 게시판 페이징 기능 구현과 관련이 거의 없어서(?) 추가로 설명을 생략하겠습니다.

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<div class="jumbotron">
<h1>메인화면</h1>
<p> </p>
</div>
<p>.</p>
<a href="/main">메인화면으로</a>
</div>

</body>
</html>


insert.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품관리</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품입력</h2>
<p>.</p>
<form action="/insert" method="POST">
<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name">
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num">
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section">
</div>
<button type="submit" class="btn btn-success">삽입하기</button>
</form>
</div>

</body>
</html>


edit.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품관리</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품수정</h2>
<p>나는 날마다 점점 더 좋아진다.</p>
<form method="POST">

<div class="form-group">
<label for="id">아이디:</label>
<input type="text" class="form-control" id="id" name= "id" value = "<%= data.id%>" disabled>
</div>

<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name" value = "<%= data.name%>">
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num" value = "<%= data.modelnumber%>">
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section" value = "<%= data.series%>">
</div>
<button type="submit" class="btn btn-success">수정하기</button>
</form>
</div>

</body>
</html>


detail.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>물품상제</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>

<div class="container">
<h2>상품상세보기</h2>
<p></p>
<form method="POST">

<div class="form-group">
<label for="id">아이디:</label>
<input type="text" class="form-control" id="id" name= "id" value = "<%= data.id%>" disabled>
</div>

<div class="form-group">
<label for="name">상품이름:</label>
<input type="text" class="form-control" id="name" name= "name" value = "<%= data.name%>" disabled>
</div>
<div class="form-group">
<label for="num">번호</label>
<input type="text" class="form-control" id="num" name="num" value = "<%= data.modelnumber%>" disabled>
</div>
<div class="form-group">
<label for="section">분류</label>
<input type="text" class="form-control" id="section" name="section" value = "<%= data.series%>" disabled>
</div>
<!-- <button type="submit" class="btn btn-success">수정하기</button> -->
</form>
</div>

</body>
</html>


자바스크립트로 블록체인 구현 강좌 11 - 나만의 비트코인 발행하기

지난 포스팅에서 하나의 네트워크에서 블록체인이 어떻게 작동이 되는지 살펴보았다. 이번포스팅에서는 여러개의 노드, 즉 컴퓨터들이 참여를 할때 블록체인 데이터가 어떻게 기록되는지 살펴보겠다.

순서

1. api.js 파일 이름을 networkNode.js 로 변경하자. 

2. networkNode.js 파일 윗 부분에 동적 포트관련 변수 선언해준다.

//동적 포트 package.json script 객체에서 2번째 방에 들어있는 데이터 3001,3002,3003 ...3005
var port = process.argv[2];

3. app.listen 을 아래와 같이 바꿔 준다.

//동적 포트
app.listen(port,function(){
console.log(`listening on port ${port}...`)
})

4. package.json 부분에서 script 부분을 아래와 같이 바꿔 준다.

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"node_1":"nodemon --watch dev -e js dev/networkNode.js 3001 http://localhost:3001",
"node_2":"nodemon --watch dev -e js dev/networkNode.js 3002 http://localhost:3002",
"node_3":"nodemon --watch dev -e js dev/networkNode.js 3003 http://localhost:3003",
"node_4":"nodemon --watch dev -e js dev/networkNode.js 3004 http://localhost:3004",
"node_5":"nodemon --watch dev -e js dev/networkNode.js 3005 http://localhost:3005"
},



이제 포트 3001 부터 3005번 까지의 node(서버)를 실행시킬 수 있다. 실행방법은 아래와 같다.

1. 터미널 열기 

2. 개발폴더에 위치하고

3. npm run node_1

4. 터미널 창 하나 더 뛰워서 npm run node_2  반복적으로 실행



blockchain.js로 이동

//sha256 모듈을 가져다 쓰겠다.
const sha256 = require('sha256');

//현재 노드의 url -- package.json script 객체에서 3번째 방에 들어있는 데이터 http://localhost:3001
const currentNodeUrl = process.argv[3];

//블록체인 데이터 구조
function Blockchain(){
this.chain = [];
this.pendingTransaction = [];
//현재 node url -- 이부분!!!
this.currentNodeUrl = currentNodeUrl;
this.networkNodes = [];

//제네시스 블락 - 임의의 인자값 넣어준다.
this.createNewBlock(100,'0','0')
}


sha 256모듈 변수 아래에 현재 노드의 url을 알 수 있는 코드를 작성하고 function blockchain(){} 함수 안에 //현재 node url 주석 아래 두 코드를 작성해준다. 이건 접속한 node의 url 데이터를 저장하겠다는 뜻.


새로운 노드가 기존의 네트워크에 진입하는과정 (탈중앙화된 네트워크 그림)

1. 새로운 노드가 기존의 노드 네트워크에 들어가기전에 등록을 거친 후 알린다.

2. 각 노드들에게 새로운 노드 정보를 등록시켜 준다.

3.기존의 모든 노드의 데이터를 새로운 노드에게 등록한다.


소스(networkNode.js에 작성)

//새로운 노드를 등록하고 전체 네트워크에 알림
app.post('/register-and-broadcast-node',function(req,res){

})
// 네트워크에 새로운 노드 등록
app.post('/register-node',function(req,res){

})
// 새로운 노드에 기존의 노드 정보 등록
app.post('/register-nodes-bulk',function(req,res){

})


npm promise api 설치 - js에서 비동기작업을 순차적으로 or 병렬로 처리가능하게 컨트롤하는 모듈


npm install request-promise --save

npm install request --save


둘다 입력!



promise 에 대한 자세한 설명

http://programmingsummaries.tistory.com/325

모듈 임포트

맨위쪽에 promise 모듈을 사용할 변수 설정

//request-promise 모듈
var rp = require('request-promise');


새로운 노드를 전체 노드에 등록하고 알리는 코드작성

//새로운 노드를 등록하고 전체 네트워크에 알림
app.post('/register-and-broadcast-node',function(req,res){
//새로 진입한 노드 주소
const newNodeUrl = req.body.newNodeUrl;
//비트코인 네트워크에 새로 진입한 노드의 주소가 없을 경우 추가
if(bitcoin.networkNodes.indexOf(newNodeUrl) == -1){
bitcoin.networkNodes.push(newNodeUrl);
}
const regNodesPromises = [];
//비트코인 네트워크에 등록된 네트워크에 새로운 노드 정보를 등록
bitcoin.networkNodes.forEach(networkNodes => {
//register - node
const requestOption = {
uri: networkNodesUrl + '/register-node',
method: 'POST',
body:{newNodeUrl:newNodeUrl},
json:true
};
//순차적으로 비동기를 실행하기 위해서 배열에 넣음
regNodesPromises.push(rp(requestOption))
}); //for 문 끝

//순차적으로 비동기 작업 처리
Promise.all(regNodesPromises)
.then(data => {
//새로운 노드안에 전체 네트워크에 대한 정보 한번에 입력해주기
const bulkRegisterOption = {
uri : newNodeUrl + '/register-nodes-bulk',
method : 'POST',
body : {allNetworkNodes : [...bitcoin.networkNodes,bitcoin.currentNodeUrl]},
json : true
};
return rq(bulkRegisterOption);
}).then(data => {
res.json({nodt: "새로운 노드가 전체 네트워크에 성공적으로 등록이 되었습니다."});

});
});


전체 네트워크에 새로운 노드 등록하는 코드

// 네트워크에 새로운 노드 등록
app.post('/register-node',function(req,res){
//새로운 노드 주소
const newNodeUrl = req.body.newNodeUrl;
//코인 네트워크에 새로운 노드의 주소가 없다면,
const nodeNotAlreadyPresent = bitcoin.networkNodes.indexOf(newNodeUrl) == -1;
//코인의 현재 url이 새로운 노드 주소가 아니라면, 즉 현재 접속한 주소와 중복되지 않는다면,
const notCurrentNode = bitcoin.currentNodeUrl !== newNodeUrl;

if(nodeNotAlreadyPresent&&notCurrentNode){
//코인 전체 네트워크에 새로운 주소 등록
bitcoin.networkNodes.push(newNodeUrl);
res.json({note: "새로운 노드가 등록되었습니다."})
}

})


결과확인

3001-3005번 서버를 다 작동시킨다.

웹브라우저에 http://localhost:3001/blockchain 입력후 확인


postman을 열어서 body로 json 데이터 전송

 

웹브라우저 다시 확인


마무리


다른 주소로도 여러번 테스트 해보자. 다음 포스팅에서 마저 정리하도록 하겠다.



  1. 기웃거리는 개발자 2018.06.14 18:03 신고

    한수 잘 배웠습니다.. 직접 코딩 해봐야 데이터 처리 흐름에 개념이 오네요.ㅎㅎ
    감사합니다~

자바스크립트로 블록체인 구현 강좌 10 - 나만의 비트코인 발행하기(소스코드)

이번 포스팅에서는 블록을 채굴할때, 즉 컴퓨팅 파워로 마이닝 할때 실행되는 app.get(/mine) 함수 부분을 작성해보도록 한다. 


순서

1.먼저 마지막 블록을 가져온다.

2.마지막 블럭의 타입은 객체 이기 때문에 key:value 형태다. 'hash' 키 값으로 해당 value(이전블럭해쉬)를 가져온다.

3.현재 블럭데이터는 이전의 발생한 거래내역을 저장해야 하기 때문에 bitcoin객체의 pendingTransaction 배열에 담긴 값을 가져와서 넣어주고, index는 마지막 블럭 index + 1을 해준다.

4. 이제 nonce, blockHash를 구할 수 있는 값들을 다 구했다. 

5. 그리고 위의 값들을 이용해서 createNewBlock()을 만든다.


// 웹브라우저에 get 방식으로 /mine 주소를 입력했을 때 실행
app.get('/mine', function (req, res) {
//마지막 블럭을 가져온다.
const lastBlock = bitcoin.getLastBlock();

//마지막 블럭의 해쉬 값, 즉 이전 블럭의 해쉬값
const previousBlockHash = lastBlock['hash'];

//현재 블락의 데이터 : 미완료된 거래내역 + 블락의 index 값
const currentBlockData = {
transactions:bitcoin.pendingTransaction,
index:lastBlock['index'] + 1
};

//이전블락해쉬, 현재블럭 데이터를 proofOfWork에 넣고 맞는 hash값(0000sfaff...)을 찾고 해당 nonce 값을 리턴.
const nonce = bitcoin.proofOfWork(previousBlockHash,currentBlockData);
//이전블락해쉬, 현재블럭 데이터, nonce 값을 넣고 현재 블락의 해쉬 값 리턴
const blockHash = bitcoin.hashBlock(previousBlockHash,currentBlockData,nonce);

//새로운 블락을 생성하려면 nonce,previousBlockHash,blockHash 값이 필요하다.
const newBlock = bitcoin.createNewBlock(nonce,previousBlockHash,blockHash);

res.json({
note: "새로운 블락이 성공적으로 만들어졌습니다.",
newBlock : newBlock
})

})


채굴자에게 보상 제공

블락을 채굴한 사람에게 보상을 해줘야 한다. 그러기 위해서는 채굴한 사람의 주소를 알아야 한다. 여기서는 npm 을 이용해서 네트워크 아이디를 임의로 지정해줘서 보상을 해주도록 하겠다.

터미널을 열고 -> 개발폴더에 위치하고 -> 명령어 입력 -> npm i uuid --save 


설치가 완료되었으면 api.js 윗부분에 아래 코드 입력

//나의 네트워크 고유 아이디 생성
var uuid = require('uuid/v1');
var nodeAddress = uuid().split('-').join('');


그리고 채굴에 대한 보상을 위한 코드 작성 - 주석으로 //채굴에 대한 보상

보상을 위해 지금 해당 비트코인 객체에 새로운 거래가 발생을 시키고, 보상으로 10비트코인을 bosang 이 nodeAddress 즉 나의 네트워크(내가 채굴했으니깐) 키에 지급한다.

// 웹브라우저에 get 방식으로 /mine 주소를 입력했을 때 실행
app.get('/mine', function (req, res) {
//마지막 블럭을 가져온다.
const lastBlock = bitcoin.getLastBlock();

//마지막 블럭의 해쉬 값, 즉 이전 블럭의 해쉬값
const previousBlockHash = lastBlock['hash'];

//현재 블락의 데이터 : 미완료된 거래내역 + 블락의 index 값
const currentBlockData = {
transactions:bitcoin.pendingTransaction,
index:lastBlock['index'] + 1
};

//이전블락해쉬, 현재블럭 데이터를 proofOfWork에 넣고 맞는 hash값(0000sfaff...)을 찾고 해당 nonce 값을 리턴.
const nonce = bitcoin.proofOfWork(previousBlockHash,currentBlockData);
//이전블락해쉬, 현재블럭 데이터, nonce 값을 넣고 현재 블락의 해쉬 값 리턴
const blockHash = bitcoin.hashBlock(previousBlockHash,currentBlockData,nonce);

//채굴에 대한 보상
bitcoin.createNewTransaction(10,"bosang0000",nodeAddress)

//새로운 블락을 생성하려면 nonce,previousBlockHash,blockHash 값이 필요하다.
const newBlock = bitcoin.createNewBlock(nonce,previousBlockHash,blockHash);

res.json({
note: "새로운 블락이 성공적으로 만들어졌습니다.",
newBlock : newBlock
})
})


테스트하기

먼저 터미널을 열고 개발 폴더에 위치하고 npm start로 서버를 실행한다. 그리고 웹브라우저에 차례대로 아래 주소를 적어본다. (꼭 차례대로 하나씩 따라해보세요)


http://localhost:3000/blockchain  - 제네시스 블럭 생성


http://localhost:3000/mine - 2번째 블록채굴 + 보상(transactoins 배열에 객체형태로 들어가 있음)


http://localhost:3000/blockchain - 2번째 블록 연결


http://localhost:3000/mine -  3번째 블록채굴 + 보상


http://localhost:3000/blockchain - 3번째 블록 연결


http://localhost:3000/transaction - 거래내역 발생


http://localhost:3000/blockchain - 거래내역은 pendingTransaction 배열에 존재



http://localhost:3000/mine - 4번째 블록 채굴 + 위의 거래내역 + 보상


http://localhost:3000/blockchain - pendingTransaction 배열 비움 + 거래내역과 보상이 담긴 4번째 블록 연결


마무리


일단 여기까지 따라오느라 정말 고생이 많으셨습니다. 여기까지 따라치면서 위와 같은 출력이 나왔다면 블록체인 기술에 대한 이해가 조금 더 생겼으리리라 생각이 됩니다. 여기서 한템포 쉬었다가 다음 포스팅 부터 또 이어서 진행하도록 하겠습니다.아래에 소스코드를 첨부했으니 참고하시면 될 것 같습니다.


소스코드

blockchain.zip


자바스크립트로 블록체인 구현 강좌 9 - 나만의 비트코인 발행하기


이번시간에는 api.js에 app.get() , app.post() 부분을 작성해주고, 개발에 조금 더 도움을 줄 node api를 설치 해보도록 하겠다. node js 에서 get, post이 부분이 이해가 안간다면...구글링을 부탁드린다..여기서는 조금이라도 알고 있다고 가정하고 쭉쭉 설명하겠다.


먼저 api.js 소스 코드다

var express = require('express')
var app = express()

// 웹브라우저에 get 방식으로 /blockchain 주소를 입력했을 때 실행
app.get('/blockchain', function (req, res) {
res.send('blockchain Start')
})

// 웹브라우저에 post 방식으로 /transaction 주소를 입력했을 때 실행
app.post('/transaction', function (req, res) {
res.send('transaction Start')
})


// 웹브라우저에 get 방식으로 /mine 주소를 입력했을 때 실행
app.get('/mine', function (req, res) {
res.send('mine Start')
})



app.listen(3000,function(){
console.log('listening on port 3000')
})

위에 소스코드 설명은 express js 기초지식에 해당되므로 여기서는 패스하고 간단하게 주석을 달아놓았다. 

서버 가동

브라우저 확인

nodemoe api 설치

nodemon  은 소스 수정시 자동으로 서버를 재실행시켜주는 api이다. 설치 안해도 무방하나 설치 해놓으면 엄청편하기 때문에 설치한다.

https://www.npmjs.com/package/nodemon

순서

터미널 열고 -> 개발폴더위치 -> npm i nodemon --save 입력 -> package.json 이동 -> script 부분에 아래 문자 추가  "start" : "nodemon --watch dev -e js dev/api.js" 

혹시나해서 나의 package.json 파일 전체를 올린다.

{
"name": "blockchain",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start" : "nodemon --watch dev -e js dev/api.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.3",
"nodemon": "^1.17.5",
"sha256": "^0.2.0"
}
}


그리고 다시 터미널 창으로 가서 개발 폴더에 위치하고 서버를 실행한다. 이번에는 npm start 명령으로 실행해보자.

확인 - 아래처럼 출력되면 성공이다.(안되도 크게 상관은 없다. 서버를 수동으로 재시작 해주면되니깐.)


body-parser api 설치

위에서 nodemon을 설치했던것 처럼 터미널 열기 -> 개발 폴더 위치 -> npm i body-parser --save 입력-> 엔터

이 모듈은 express js에서 post방식으로 파라미터를 넘길 수 있게 하는 모듈이다. 쉽게 말하면 웹주소를 이용해서 데이터를 넘기는 것을 도와주는 녀석이라고 생각하면 된다. 암튼 자세한건 구현하면 아~ 하고 이해가 될 것이다. 그리고 api.js 맨위쪽에 var app= express() 이 부분 아래 body paser를 사용하겠다는 코드를 작성해준다.

var express = require('express')
var app = express()
//bodyParser 임포트
var bodyParser = require('body-parser')

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}))



api.js 에서 post transaction 코드 작성

// 웹브라우저에 post 방식으로 /transaction 주소를 입력했을 때 실행
app.post('/transaction', function (req, res) {
console.log(req.body);
res.send(`거래 총액은 ${req.body.amount} 코인`);
})


postman에서 확인 해보기 (https://www.getpostman.com)

포스트맨은 웹상에서 데이터를 웹브라우저 주소로 넘길때 간단하게 테스트 해볼 수 있는 서비스다. 위의 홈페이지로 들어가서 각각 운영체제에 맞는 버젼을 다운로드 하자.


확인

포스트맨에 들어가서 맨위 붉은 줄 처럼 주소를 적고 body를 선택하고 raw , JSON 선택후 아래 문자를 작성한 뒤 Send 를 눌러보자. 그러면 거래 총액은 200 코인 으로 아래 소스 코드로 데이터가 넘어와서 출력된것을 알 수 가 있다.

// 웹브라우저에 post 방식으로 /transaction 주소를 입력했을 때 실행
app.post('/transaction', function (req, res) {
console.log(req.body);
res.send(`거래 총액은 ${req.body.amount} 코인`);
})


app.get /blockchain 작성 

웹브라우저에서 get 방식으로 /blockchain 주소가 들어왔을 경우. bodypaser 모듈을 임포트 하는 코드 아래에 밑의 코드 작성. 

//앞에서 만든 blockchain 모듈 임포트 -> 코인객체 생성
var Blockchain = require('./blockchain');
var bitcoin = new Blockchain();


app.get('blockchain') 부분 코드 작성

// 웹브라우저에 get 방식으로 /blockchain 주소를 입력했을 때 실행
app.get('/blockchain', function (req, res) {
res.send(bitcoin)
})


웹브라우저에서 테스트 - 제네시스 블락 정보가 json 형식으로 뜬다.

/transaction 부분을 담당하는 함수안 내용 수정 - 거래가 발생했을때 새로운 트랜잭션을 생성하기 때문에 발생하는 정보를 console.log 로 찍어주도록 하자.

// 웹브라우저에 post 방식으로 /transaction 주소를 입력했을 때 실행
app.post('/transaction', function (req, res) {

const blockIndex = bitcoin.createNewTransaction(req.body.amount,req.body.sender,req.body.recipient)
res.json({note : `트랜젝션은 ${blockIndex} 블락안으로 들어갈 예정입니다.`})

})



postman으로 확인 - 총 4번의 send를 누름 (4번의 거래 발생)



/blockchain 확인 -  위에서 발생한 트랜잭션 데이터가 pendingTransaction [] 에 들어가 있다. 저건 언제 index 1 블락으로 들어갈까? 이전전전 포스트에서 다뤘다. 새로운 블락이 생성될때. 그건언제인가? 컴퓨팅 파워를 통한 채굴. 마이닝!  그 작업을 통해서 맞는 hash 값을 찾았을 때 새로운 블락이 생성되면서 이전 거래 내역이 새로운 블락안으로 들어간다.  이번 포스팅은 여기서 마무리 하도록 하겠다.



자바스크립트로 블록체인 구현 강좌 8 - 나만의 비트코인 발행하기

이번 포스팅 부터는 앞에서 만든 blockchain을 서버에서 구현해보도록 하겠다. 우리는 서버 구축을 간단하게 도와줄 express api를 사용할것이다. 일단 구글로 가서 express npm을 검색하자. 아래 주소로 들어가도 된다.

https://www.npmjs.com/package/express

콘솔을 열어서 위에 install명령어를 자신의 개발 폴더 위치에서 입력해준다.


이제 express api를 설치 완료 했다. 

개발환경으로 가서 api.js 파일을 생성해주고 아래 소스 코드를 입력해주자 아래 소스코드는 express npm 문서에 있다. 간단하게 설명하면 app.get은 웹브라우저에 / 의 값이 들어오면 함수 아래를 실행하겠다는 뜻이다. /main, /home 등 다양한 주소를 입력해 줄 수 있다. 그러면 이 get 함수가 브라우저에 입력되는 문자열을 확인해서 함수를 실행한다. 

app.listen은 몇번 포트를 설정할것이다 라는뜻 3000이면 localhost:3000으로 입력을 해야 위에 get 함수가 실행이 된다. 설명이 너무 간단하고 추상적인데 이해가 안간다면 구글에 node js epxress 에 관한 기초지식을 쌓고 오는 것도 좋겠다.


var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('Hello World')
})
app.listen(3000)


네비게이션



서버실행

이제 터미널을 열고 node 경로/api.js 를 실행시켜준다. 그리고 웹브라우저에 localhost:3000을 입력- > helloworld 출력 확인



마무리

이제 blockchain을 구현할 서버작업을 마쳤다. 앞으로 이 서버가 어떻게 사용이될지 조금더 살펴보겠다.

자바스크립트로 블록체인 구현 강좌7 - 나만의 비트코인 발행하기


이번 포스팅에서는 블록체인 기술에서 핵심이라고 할 수 있는 작업증명 POW 개념을 알아보고 코드로 구현해 보자. 간단한 용어정리를 하고 넘어가자.


#용어정리

해쉬: 해쉬는 의미없는 데이터다. 컴퓨터에서 블럭의 길이를 맞추기 위해서 메모리에 작성된 의미없는 데이터다.

예)0000asdkflqwjkeljakldjflaj;

블럭: 해쉬를 가지고 있는 사람이 코인을 획득할 수 있는 블록을 의미

난이도: 블럭이 생성되는 시간을 일정하게 조정하기 위해 사용되는 개념

(전체 해쉬의 양 증가 -> 난이도 증가-> 블럭찾는데 더 많은 해쉬 필요 _> 블럭 생성시간 유지됨)

51% 공격: 전체 해쉬의 51% 이상 가진 집단이 존재할때 전체 코인의 이동정보, 즉 블록체인에 잘못된 정보가 전달 될 수 있다는 개념

pow: 컴퓨터간에 정보를 교환할때 통신방법에 대한 규칙이다. 많은 해쉬를 가진 사람이 코인을 획득할 수 있는 블럭을 더 많이 발견할 수 있는 시스템이다.



이전 포스팅에 이어서 blockchain.js에 작업증명을 담당하는 코드를 작성해보자.

#작업 순서도

    1. bitcoin.hashBlock(previousBlockHash,currentBlockData,nonce) - test.js에서 호출 할 것이다.

    2. 맞는 해쉬값(0000asldjflak) 찾을 까지 반복적으로 1. 작업을 실행한다

    3. 맞는 해쉬 값을 찾기 위해서 nonce 변경해 가면서 찾는다.

    4. 마지막으로 정확한 hash 값을 찾고 정확한 hash 값을 찾은 nonce 값을 리턴 한다.


//pow 작업 함수 - 이전블록의 해쉬, 현재 블록 데이터와 nonce 값을 사용한다.
Blockchain.prototype.proofOfWork = function(previousBlockHash,currentBlockData){

let nonce = 0;
let hash = this.hashBlock(previousBlockHash,currentBlockData,nonce);
while(hash.substring(0,4) != '0000'){
nonce++;
hash = this.hashBlock(previousBlockHash,currentBlockData,nonce)
}
return nonce;
}


이제 test.js에서 위에 blockchain.js에서 작성한 함수를 호출 해보자.


//blockchain.js 모듈을 이곳에서 가져다 쓰겠다.
const Blockchain = require('./blockchain');


//위에서 가져온 모듈의 객체를 만든다.
const bitcoin = new Blockchain();

const previousBlockHash = "abcdedferefa"
const currentBlockData = [
{
amount: 10,
sender: 'BACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 20,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 30,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

}
]

//pow 작업
console.log(bitcoin.proofOfWork(previousBlockHash,currentBlockData));


실행을 하면 

이 값이 나온다.!!

함수가 총 141237번 실행 한뒤 맞는 해쉬 값을 찾았다. 그러면 이 nonce 값을 이용해서 그 맞는 해쉬 값이 무엇인지 한번 살펴보자.


test.js에서 해쉬블락 함수에 위에서 찾은 값 141237값을 넣었다. 어떤 해쉬 함수가 나올까?

console.log(bitcoin.hashBlock(previousBlockHash,currentBlockData,141237))


결과


제네시스블록 생성

제네시스 블록을 생성해보자. 간단하다. blockchain.js 페이지에서 아래 소스 코드를 작성해준다. creatNewBlock 함수의 인자값은 임의로 넣어줘도 ok.

//블록체인 데이터 구조
function Blockchain(){
this.chain = [];
this.pendingTransaction = [];
//제네시스 블락 - 임의의 인자값 넣어준다.
this.createNewBlock(100,'0','0')
}


test.js 페이지로 와서 제네시스 블럭을 한번 찍어보자. 위에서 작성했던 noce 100 , hash 0 ,  previousBlockHash 0 값이 들어가있는 제네시스 블럭이 생성되었다.

//blockchain.js 모듈을 이곳에서 가져다 쓰겠다.
const Blockchain = require('./blockchain');


//위에서 가져온 모듈의 객체를 만든다.
const bitcoin = new Blockchain();

//제네시스 블락
console.log(bitcoin)


//제네시스 블락 !
// Blockchain {
// chain:
// [ { index: 1,
// timestamp: 1527475657742,
// transactions: [],
// nonce: 100,
// hash: '0',
// previousBlockHash: '0' } ],
// pendingTransaction: [] }


마무리

이제 pow 방식으로 채굴하는 방식이 조금 이해가 될것이다. 맞는 해쉬값을 찾을때까지 pow함수를 반복하고 그 과정에서 수많은 nonce 값들이 변경된다는 사실. 그리고 이 방식이 왜 더 좋은 컴퓨팅 파워가 필요한지도 좀더 이해가 됐으리라 생각한다.


소스코드

blockchain.zip

자바스크립트로 블록체인 구현 강좌6 - 나만의 비트코인 발행하기

저번 시간에 포스팅한 sha256 암호화 값 리턴 함수가 어떻게 사용되는지 보겠다. 일단 test.js 부분을 다 지운다. 

그리고 아래 코드를 넣어준다. bitcoin 객체의 hashBlock 함수는 3개의 인자값을 받는다. 1. 이전 블락의 hash 2.현재 블록의 거래 데이터 3.nonce 값


//blockchain.js 모듈을 이곳에서 가져다 쓰겠다.
const Blockchain = require('./blockchain');


//위에서 가져온 모듈의 객체를 만든다.
const bitcoin = new Blockchain();

const previousBlockHash = "abcdedferefa"
const currentBlockData = [
{
amount: 10,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 20,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 30,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

}
]

const nonce = 100;


console.log(bitcoin.hashBlock(previousBlockHash,currentBlockData,nonce))


//859096659fb3a8b0f053eb068c4b11ee24ba38fecaf6ea629ff301d14feeae84


blockchain.js에서 만든 hash 함수를 테스트 해보았다. console.log  를 찍어보면 해쉬 함수가 리턴 한 값을 볼 수가 있다. 여기서.

sender 보내는 사람이 PACK... 인데 여기서 P를 B로만 바꿔 보겠다. 그리고 실행해보자.


//blockchain.js 모듈을 이곳에서 가져다 쓰겠다.
const Blockchain = require('./blockchain');


//위에서 가져온 모듈의 객체를 만든다.
const bitcoin = new Blockchain();

const previousBlockHash = "abcdedferefa"
const currentBlockData = [
{
amount: 10,
sender: 'BACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 20,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

},
{
amount: 30,
sender: 'PACKadffaaf',
recipient: 'HONGllalflks'

}
]

const nonce = 100;


console.log(bitcoin.hashBlock(previousBlockHash,currentBlockData,nonce))


//ec90a89a8364d8762ec3d365f2c8c91b1547af493e068009c119356b41f9699b




문자가 하나만 달라졌는데도 해쉬 값은 완전히..전혀 다른 해쉬값이 리턴되는 것을 볼 수 있다.

+ Recent posts

티스토리 툴바