우분투 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>


결과화면


  1. 2018.10.24 12:58

    비밀댓글입니다

  2. it취준생 2018.11.11 23:11

    한가지 궁금증이 있습니다.
    ejs(html)에서 파일뿐아니라 게시글제목, 작성자, 게시글내용까지 같이 작성한뒤에 mysql에 저장하려고 합니다.
    제목,작성자,게시글,파일까지 하나의 form내부에 작성한뒤 submit하도록 하였고 form타입은 enctype="multipart/form-data" 로 지정하였습니다.
    일부 html코드를 덧붙입니다.
    <td>작성자</td>
    <td><input type="text" name="brdwriter" size="20" maxlength="20" value="<%=row.BRDWRITER%>"></td>
    <input type="file" name="imgFile" value="<%=row.BRDIMAGE%>">

    이렇게 넘겨준 상태에서
    router의 파트에서
    router.post('/save', uqload.single("imgFile"), function(req,res,next){
    var data = [req.body.brdtitle, req.body.brdmemo, req.body.brdwriter, req.body.brdno, req.file.imgFile];
    ~~~ sql = "INSERT INTO TBL_BOARD(BRDTITLE, BRDMEMO, BRDWRITER, BRDIMAGE, BRDDATE) VALUES(?,?,?,?, NOW())";
    connection.query(sql, data, function (err, rows)
    이렇게 사용하면 될거라고 생각하고 코드를 작성하였으나, 애초에 data자체가 비어버리더군요..
    어떻게 가능하게 만들수 있는지 알수 있을까요???

    p.s 따로 이미지첨부관련 기능을 싹 빼버리면 잘 작동하며, 본문대로 이미지만 처리하는식으로 코드를 작성해도 잘 작동합니다.
    이미지와 게시판을 동시에 db에 저장하는 과정에서 문제가 발생합니다.

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>


  1. 2019.07.22 18:34

    비밀댓글입니다

  2. 2019.09.30 16:20

    비밀댓글입니다

  3. 노드 개발자 2019.10.07 16:37

    저 혹시 public 폴더안에 있는 form.html이랑 routes 폴더안에 들어있는 user.js 파일은 따로 코딩 올려주실수 없을까요 ㅠ

자바스크립트로 블록체인 구현 강좌 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번 서버를 다 작동시킨다.

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


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

 

웹브라우저 다시 확인


마무리


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



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

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

  2. kebi300 2018.06.29 13:53

    안녕하세요. 덕분에 많은 것을 배우고 가요.

    근데 register-and-broadcast-node 부분은 아직 작동안하는데, 다음 포스트에서 하시나요?

    그리고 forEach 구문에서 networkNodes=> 이 아니라 networkNodesUrl=>

    아닌가요? 오타인듯 해서요.

    좋은 포스팅 감사합니다.


    bitcoin.networkNodes.forEach(networkNodes => {
    //register - node
    const requestOption = {
    uri: networkNodesUrl + '/register-node',
    method: 'POST',
    body:{newNodeUrl:newNodeUrl},
    json:true
    };
    //순차적으로 비동기를 실행하기 위해서 배열에 넣음
    regNodesPromises.push(rp(requestOption))
    })

  3. ㅇㅁㄴ 2018.08.09 16:55

    TREBIT

    코인거래소
    Trebit 이 원화마켓을 오픈합니다 :)
    8월 8일 18시 기준 원화마켓이 오픈이 되었습니다 !

    원화(KRW)마켓 오픈과 함께
    옵저버(OBSR)와 아이온(ION) 8월10일 상장!!!

    특히 옵저버(OBSR)는 전 세계 최초로 트래빗 거래소에 상장되는 코인으로,

    일반 암호화폐와 다르게
    ICO를 진행하지 않고 상장되는 종목입니다.

    원화(KRW)마켓 오픈 기념으로
    거래수수료 0% 이벤트를 진행 중이니
    수수료 부담 없이 거래하시고,
    무려 3억원 상당의
    에어드랍 이벤트의 주인공에 도전해 보세요.

    트래빗고객센터 : 1600-5433
    운영시간 : 평일 09:00 ~ 18:00
    이용문의 : cs@trebit.com

    *옵저버 더 알아보기: http://www.obsr.org/index
    *아이온 더 알아보기: https://ionomy.com/games

  4. 코더 2018.08.28 22:25

    포스팅 정말 감사드립니다 자바스크립트로 코인 만들어보고 싶었는데 이렇게 이해하기 쉽게 올려주셔서ㅜㅜ

  5. 굿굿 2019.03.11 14:05

    다 따라해보면서 하는데도 아리송한 부분도 있고

    덕분에 깨우친 부분도 있었습니다

    하지만 js로 이정도 퀼리티로 설명해주는곳은 얼마 없으며

    저를 이해시키다니!! 대단하십니당ㅎㅎㅎ

    강좌 12강을 기다리고 있습니다ㅠㅠㅠ

    아니면 제가 배운것을 응용해서 만들어보는것도 나쁘지 않겠지요

    잘 배웠습니다.!!

    • ㅇㅇ 2019.03.11 19:51

      11번쪽은 예제를 두세번 따라하고 오타를 전부 잡았음에도 불구하고 Postman 결과값이 Cannot POST /blockchain/register-node 이나오네요 ...

      왜일까요 마지막인데 속상하네요ㅠㅠ

자바스크립트로 블록체인 구현 강좌 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로 서버를 실행한다. 그리고 웹브라우저에 차례대로 아래 주소를 적어본다. (꼭 차례대로 하나씩 따라해보세요)


https://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


  1. 김도원 2018.07.07 12:11

    안녕하세요 ㅎㅎ 글 잘 보고 있는데 혹시 적어주신 글을 제가 따로 정리해서 저의 깃허브에 올려도 될까요? ㅜㅜ

자바스크립트로 블록체인 구현 강좌 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




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

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


이번 포스팅에서는 sha256 해시생성기와 npm을 이용해서 sha256 모듈을 설치해서 사용해보도록하겠다.

일단.

sha256 은 암호화 알고리즘의 하나이다. 음..엄청쉽게 말해서 '어떤 입력값이든 고정된 길이의 임의의 문자열값으로 출력한다' 라고 알고 있으면 쉽다.

예를 들면 abc 값을 sha256 알고리즘에 넣고 돌리면

BA7816BF8F 01CFEA4141 40DE5DAE22 23B00361A3 96177A9CB4 10FF61F200 15AD

이값이 생성이된다. 길이를 보자.

abcdef를 넣으면?

BEF57EC7F5 3A6D40BEB6 40A780A639 C83BC29AC8 A9816F1FC6 C5C6DCD93C 4721

같은 길이의 임의의값이 출력이 된다.

 

https://passwordsgenerator.net/sha256-hash-generator

이곳에 들어가서 직접 값을 넣어봐도 된다.


그리고 한번 암호화 한건 다시 되돌리기 힘들다는 것이다. 어려운 말로 복호화가 어렵다고 하는데 그냥 아 한번 암호화 해버리면 다시 원래 값으로 되돌리기가 거의 빌게이츠가 삼와머니에서 돈빌릴 확률 보다 낮다고만 알아두자.


그러면 계속해서 코드를 작성해보자. 이번에는 인자값을 받으면 그것을 이용해서 암호화 한다음 리턴해주는 함수를 작성해보도록 한다. 


1.구글에서 npm sha256 모듈 검색 후 다운로드 하기

https://www.npmjs.com/package/sha256 여기 들어가면 npm i sha256 --save 이렇게 생긴 녀석을 볼 수 있다


2.터미널 열고 자신의 개발폴더(blockchain)로 가서 위의 명령어를 쳐준다.



3.확인해보기


3.모듈을 변수에 할당

blockchain.js 맨위에 작성

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



4.Blockchain.prototype 함수들 이어서 맨아래 작성

//해쉬 값 리턴 함수
Blockchain.prototype.hashBlock = function(previousBlockHash,currentBlockData,nonce){
const dataAsString = previousBlockHash + nonce.toString() + JSON.stringify(currentBlockData);
const hash = sha256(dataAsString);
return hash
}


다음 포스팅에서 작성한 해쉬함수를 어떻게 사용하는지 테스트 해보도록하자.





+ Recent posts