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


결과화면


//

//  ShowBibleVC.swift

//  JooBo

//

//  Created by MacBookPro on 2018. 6. 12..

//  Copyright © 2018년 MacBookPro. All rights reserved.

//


import UIKit


class ShowBibleVC: UIViewController {

    

    //

    let commentTextView: UITextView = {

        let textView = UITextView()

        textView.backgroundColor = UIColor(red:0.92, green:0.92, blue:0.92, alpha:1.0)

        textView.translatesAutoresizingMaskIntoConstraints = false

        textView.text = "성경말씀 본문"

        textView.font = UIFont(name: "NanumMyeongjo-YetHangul", size: 14)

        //textView.textAlignment = .center

        textView.isEditable = false

        textView.layer.borderWidth = 0.3

        textView.layer.cornerRadius = 5

        textView.clipsToBounds = true

        //textView.isScrollEnabled = false

        return textView

    }()

    

    //취소 함수

    @objc func cancelAction(){

        self.dismiss(animated: true, completion: nil)

    }

    

    override func viewDidLoad() {

        super.viewDidLoad()

        self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "닫기", style: .plain, target: self, action:  #selector(cancelAction))

        view.backgroundColor = .yellow

        onPostShowBible()

        

        //onHttpRequest()

        

        

        view.addSubview(commentTextView)

        

        commentTextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        commentTextView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        commentTextView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

        commentTextView.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true

        

    }

    

    

    func onPostShowBible(){

        var bibletext = ""

        print("포스트 방식 데이터 가지러옴")

        // 1. 전송할 값 준비

        //2. JSON 객체로 변환할 딕셔너리 준비

        //let parameter = ["create_name" : "kkkkkkkk", "create_age" : "909090"]

        //let postString = "create_name=13&create_age=Jack"

        // 3. URL 객체 정의

        guard let url = URL(string: "http://169.254.232.102:3003/bible_search") else {return}

        

        // 3. URLRequest 객체 정의 및 요청 내용 담기

        var request = URLRequest(url: url)

        request.httpMethod = "POST"

        // 4. HTTP 메시지에 포함될 헤더 설정

        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

        //request.addValue("application/json", forHTTPHeaderField: "Content-Type")

        let body = "maintext=히브리서&jang=11&jeol=1&jeol2=10".data(using:String.Encoding.utf8, allowLossyConversion: false)

        request.httpBody = body

        

        

        // 5. URLSession 객체를 통해 전송 및 응답값 처리 로직 작성

        let session = URLSession.shared

        session.dataTask(with: request) { (data, response, error) in

            if let res = response{

                print(res)

            }

            

            if let data = data {

                

                do{

                    let json = try JSONSerialization.jsonObject(with: data, options: [])

                    print(json)


                   

                    guard let newValue = json as? Array<Any> else {

                        print("invalid format")

                        return

                    }

                    

                    for item in newValue{

                        

                        if let data = item as? [String:Any]{

                            

                            if let book = data["book"] , let chapter = data["chapter"] ,let verse = data["verse"] ,let content = data["content"]{

                                

                        print( String(describing: book) + String(describing: chapter) + String(describing: verse) + String(describing: content))

                                //print(book)

                               // print(chapter)

                               // print(verse)

                               // print(content)

                                 bibletext += String(describing: book) + String(describing: chapter) + String(describing: verse) + String(describing: content)

                            }

    

                        }

                    }

                    DispatchQueue.main.async {

                        

                        self.commentTextView.text = "\(bibletext)"

                    }

                    

                    // Check for the weather parameter as an array of dictionaries and than excess the first array's description

//                    if let verse = newValue["verse"], let chapter = newValue["chapter"],let book = newValue["book"],let content = newValue["content"] {

//                        print(verse)

//                        print(chapter)

//                        print(book)

//                        print(content)

//                    }

                    

                    

//                    let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers)

//                    if let items = jsonResult["items"] as! NSArray { for item in items { print(item["published"]) print(item["title"]) print(item["content"]) } }

                    

                    

                    //Convert to Data

                   // let jsonData = try JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions.prettyPrinted)

                    

                    //Convert back to string. Usually only do this for debugging

                    //if let JSONString = String(data: jsonData, encoding: String.Encoding.utf8) {

                    //    print(JSONString)

                    //}

                    

                    

                }catch{

                    print(error)

                }

            }

            // 6. POST 전송

            }.resume()

        

    }

    


    

   

    

    func onHttpRequest() {

        

        

        //URL생성

        guard let url = URL(string: "http://169.254.232.102:3003/users") else {return}

        

        var request = URLRequest(url: url)

        request.httpMethod = "get" //get : Get 방식, post : Post 방식

        

        let session = URLSession.shared

        let task = session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in

            //error 일경우 종료

            guard error == nil && data != nil else {

                if let err = error {

                    print(err.localizedDescription)

                }

                return

            }

            

            //data 가져오기

            if let _data = data {

                if let strData = NSString(data: _data, encoding: String.Encoding.utf8.rawValue) {

                    let str = String(strData)

                    print(str)

                    //메인쓰레드에서 출력하기 위해

                    DispatchQueue.main.async {

                        print(str)

                    }

                }

            }else{

                print("data nil")

            }

        })

        task.resume()

        

    }

    

    func onPostShowBible2(){

        

        print("포스트 방식 데이터 가지러옴")

        // 1. 전송할 값 준비

        //2. JSON 객체로 변환할 딕셔너리 준비

        let parameter = ["create_name" : "kkkkkkkk", "create_age" : "909090"]

        //let postString = "create_name=13&create_age=Jack"

        // 3. URL 객체 정의

        guard let url = URL(string: "http://169.254.251.88:3003/user_create") else {return}

        // 3. URLRequest 객체 정의 및 요청 내용 담기

        var request = URLRequest(url: url)

        request.httpMethod = "POST"

        // 4. HTTP 메시지에 포함될 헤더 설정

        

        request.addValue("application/json", forHTTPHeaderField: "Content-Type")

        let body = "create_name=mylove&create_age=1212".data(using:String.Encoding.ascii, allowLossyConversion: false)

        request.httpBody = body

        

        // 5. URLSession 객체를 통해 전송 및 응답값 처리 로직 작성

        let session = URLSession.shared

        session.dataTask(with: request) { (data, response, error) in

            if let res = response{

                print(res)

            }

            

            if let data = data {

                

                do{

                    let json = try JSONSerialization.jsonObject(with: data, options: [])

                    print(json)

                }catch{

                    print(error)

                }

            }

            // 6. POST 전송

            }.resume()

        

    }

    

}



'ios 뽀개기 > 스위프트 정리' 카테고리의 다른 글

swift http 통신 get방식 post 방식 - 일단 기록  (0) 2018.06.13
11. 클래스와 구조체  (0) 2017.12.04
10. 옵셔널  (0) 2017.12.04
9. 연산자의 종류  (0) 2017.11.15
8. 열거형  (0) 2017.11.15
7. 튜플 배열 딕셔너리 셋 (복습)  (0) 2017.11.14

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>


스위프트로 배우는 블록체인 - 탈중앙화 구현


노드모듈생성

지금까지는 하나의 네트워크에서 블록체인이 어떻게 작동하는지 알아보았습니다. 지금부터는 여러개의 노드를 만들어서 탈중앙화 개념을 구현해 보도록 하겠습니다. model.swift에 새로운 노드를 생성할 클래스를 아래와 같이 작성해 줍니다.

//새로운 노드 생성 클래스

class BlockchainNode : Codable {

    var address : String

    init(address : String) {

        self.address = address

    }

}


아래에 이이서 class Blockchain 부분에 아래에 위에서 만든 노드 데이터를 컨트롤할 변수와 추가할 함수를 만들어 준다.

//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

   

    //추가! 블록체인 데이터 구조에 블록체인 배열 값을 가지는 변수 생성

    private (set) var nodes : [BlockchainNode] = [BlockchainNode]()


    //추가! 블록체인 데이터 구조에 노드를 추가하는 함수

    func addNode(_ node : BlockchainNode){

        self.nodes.append(node)


}



마지막으로 이번에는 제일 위에서 만든 BlockchainNode 클래스에 아래와 같이 코드를 추가해 준다. 블록체인컨트롤.swift에서  웹브라우저로 넘어오는 request 데이터로 이 클래스를 초기화 해주기 위해서 아래 코드를 추가한다.

//새로운 노드 생성 클래스

class BlockchainNode : Codable {

    var address : String

    

    //BlockchainController.swift 에서 request 로 넘어오는 데이터로 초기화 하기 위한 초기화 함수

    init?(request : Request){

        

        guard let address = request.data["address"]?.string else {

            return nil

        }

        self.address = address

    }

    

    init(address : String) {

        self.address = address

    }

}


노드등록

노드를 생성하는 함수를 만들었으니 이번에는 생성된 노드를 등록하는 함수를 구현해보겠습니다. BlockchainService 클래스로 가서 아래 코드를 추가해준다. //추가! 부분이다.

class BlockchainService {

    

    //체인배열 변수

    private (set) var blockchain : Blockchain!

    

    //제네시스 블럭 초기화=

    init() {

        self.blockchain = Blockchain(genesisBlock: Block())

        

    }

    

    //추가! 생성한 노드를 블록체인에 추가하는 함수

    func registerNode(_ node : BlockchainNode){

            self.blockchain.addNode(node)

}


이어서 이제 BlockchainController.swift로 와서 func setupRoutes(){} 함수 부분에 웹브라우저를 통해서 노드를 등록하고, 등록한 노드를 확인해주는 로직을 작성해보겠다. 아래 소스 코드 참고.

    private func setupRoutes(){

        

        //노드 등록

        self.drop.post("/nodes/register"){ request in

            if let blockchainNode = BlockchainNode(request: request){

                self.blockchainService.registerNode(blockchainNode)

            }

            return try JSONEncoder().encode(["msg":"노드 등록 성공"])

        }

        

        //노드 확인

        self.drop.get("/nodes"){request in

            let nodes = self.blockchainService.blockchain.nodes

            return try JSONEncoder().encode(nodes)

}

테스트

위에 소스가 잘 작동을 하는지 포스트맨을 통해서 확인해보자~


등록된노드 확인 (8080,8088 등 여러개 등록해서 확인해보자)



여러 노드에서 마이닝 구현


vapor 프레임워크를 통해서 여러 포트의 서버를 등록해보겠다. 터미널을 열고 작업폴더에 위치한다음에 

vapor run --port=8080, vapor run --port=8090, vapor run --port=8010 등 여러 서버가 가동되게 한다. 그리고 반드시 웹브라우저에서 확인 해야 한다.


마지막 구현

모든 노드들이 데이터를 공유하게 하기 위한 코드를 작성해보자. 일단 Blockchain 클래스에 blocks 변수 앞에 private 키워드 제거해준다.

//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    var blocks : [Block] = [Block]()


service 부분에 아래 함수 추가

    func resolve(completion : @escaping (Blockchain) -> ()){

        

        let nodes = self.blockchain.nodes

        for node in nodes{

            let url = URL(string: "http://\(node.address)/blockchain")!

            URLSession.shared.dataTask(with: url){ data,_, _ in

                if let data = data{

                    let blockchain = try! JSONDecoder().decode(Blockchain.self, from: data)

                    if self.blockchain.blocks.count > blockchain.blocks.count{

                        completion(self.blockchain)

                    }else{

                        self.blockchain.blocks = blockchain.blocks

                        completion(blockchain)

                    }

                }

            }.resume()

        }

}

controller 부분에 아래 함수 추가

    private func setupRoutes(){

        self.drop.get("/nodes/resolve"){request in

            return try Response.async{

                portal in

                self.blockchainService.resolve(completion: { (blockchain) in

                    let blockchain = try! JSONEncoder().encode(blockchain)

                    portal.close(with: blockchain.makeResponse())

                })

            }

}


테스트

post맨을 열어서 한번더 테스트를 해보자.







스위프트로 배우는 블록체인 - 채굴구현


먼저 이전전 포스팅에서 다루었던 BlockChainSwift.playground를 vapor프로젝트에 App -> Model 안에 model.swift 라는 이름으로 새로 파일을 만들어서 복붙해준다.


//

//  models.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//

import Vapor

import Foundation

import Cocoa


//거래가 발생할때 거래 타입 선택 가능

enum TransactionType : String, Codable{

    case domestic

    case international

}


// 스마트 컨트랙드 프로토콜 함수 - 지원,신청하는 계약임

protocol SmartContract{

    func apply(transaction : Transaction)

}


//스마트 컨트랙트를 상속한 함수 구현

class TransactionTypeSmartContract : SmartContract{

    //스마트 컨트랙트 함수가 호출되면 apply 함수 실행

    func apply(transaction: Transaction) {

        

        var fees = 0.0

        //거래 타입에 따라 fees 계산해주기

        switch transaction.transactionType {

        case .domestic:

            fees = 0.02

        case .international:

            fees = 0.05

        }

        //거래 타입에 따라서 항목 계산 후 대입

        transaction.fees = transaction.amount * fees

        transaction.amount -= transaction.fees

    }

}




//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    

    //수수료

    var fees: Double = 0.0

    //거래 타입

    var transactionType : TransactionType

    

    //초기화

    init(from:String,to:String,amount:Double,transactionType:TransactionType) {

        self.from = from

        self.to = to

        self.amount = amount

        self.transactionType = transactionType

    }

}


//블록 구조체

class Block: Codable{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int

    

    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

            return String(self.index) + self.previousHash + String(self.nonce) + transactionsJSONString!

        }

    }

    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }

    

    //초기화

    init(){

        self.nonce = 0

    }

}


//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //스마트 계약 관련 데이터 배열 변수

    private (set) var smartContracts : [SmartContract] = [TransactionTypeSmartContract()]

    

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    private enum CodingKeys : CodingKey {

        case blocks

    }

    

    

    //블록체인 구조체에 블록을 추가하는 함수

    func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        

        // run the smart contracts

        self.smartContracts.forEach { contract in

            block.transactions.forEach { transaction in

                contract.apply(transaction: transaction)

            }

        }

        

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        var hash = block.key.sha1Hash()

        // 특정값 (앞 2자리가 00 인 hash 값 구하기

        while(!hash.hasPrefix("00")){

            //조건이 맞지 않을 경우 block의 noce 값 증가

            block.nonce += 1

            hash = block.key.sha1Hash()

            print(hash)

        }

        

        return hash

    }

    

    //새로운 블락 구하는 함수, 다음 블록 생성

    func getNextBlock(transactions:[Transaction]) ->Block{

        //새 블록 객체

        let block = Block()

        //새로운 블록이 생성될때는 이전 거래 내역이 배열로 들어간다.

        transactions.forEach { (transaction) in

            block.addTransaction(transaction: transaction)

        }

        //새로운 블록에는 이전 블록의 hash 값, 번호, 자신의 hash 값이 들어간다.

        let previousBlock = getPreviousBlock()

        block.index = self.blocks.count

        block.previousHash = previousBlock.hash

        block.hash = generateHash(for: block)

        return block

    }

    

    //이전 블럭 가져오기

    private func getPreviousBlock() -> Block {

        return self.blocks[self.blocks.count - 1]

    }

}


//제네시스 블록 생성

//let genesisBlock = Block()

//추가

//let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

//let transaction = Transaction(from: "kim", to: "hong", amount: 1000,transactionType:.international)


//print("--------------------------------------------------------")

//위에서 발생한 거래내역을 새로운 블럭을 생성하는 함수안에 넣어주었다.

//let block = blockchain.getNextBlock(transactions: [transaction])

//새로만든 블록을 넣어주고

//blockchain.addBlock(block)



//인코딩

//let data = try! JSONEncoder().encode(blockchain)

//json데이터를 string으로 출력

//let blockchainJSON = String(data: data, encoding: .utf8)


//print(blockchainJSON!)


//블록의 총 개수를 확인해보자.

//print(blockchain.blocks.count)



//let block1 = Block()

//block1.addTransaction(transaction: transaction)

//block1.key



// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}





BlockchainService.swift 작성

//

//  BlockchainService.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//


import Foundation


class BlockchainService {

    

    //체인배열 변수

    private (set) var blockchain : Blockchain!

    

    //제네시스 블럭 초기화=

    init() {

        self.blockchain = Blockchain(genesisBlock: Block())

        

    }

    

    //체인 반환 함수

    func getBlockchain() -> Blockchain{

        return self.blockchain

    }

    

}


BlockchainController.swift 작성

//

//  BlockchainController.swift

//  App

//

//  Created by MacBookPro on 2018. 5. 30..

//


import Foundation

import Vapor

import HTTP


class BlockchainController {

    private (set) var drop : Droplet

    private (set) var blockchainService : BlockchainService

    

    init(drop: Droplet) {

        self.drop = drop

        self.blockchainService = BlockchainService()

        setupRoutes()

    }

    

    //이부분 수정!

    private func setupRoutes(){

        //웹브라우저에서 /blockchain으로 진입했을 때 배열타입의 블록체인 json타입으로 반환

        self.drop.get("blockchain"){request in

            let blockchain = self.blockchainService.getBlockchain()

            return try! JSONEncoder().encode(blockchain)

        }

    }

    }


테스트



Model.swift에 class Transaction 부분 request 초기화 함수 추가

//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    


    //초기화

    init(from:String,to:String,amount:Double) {

        self.from = from

        self.to = to

        self.amount = amount

        

    }

    

    //request 초기화 함수

    init?(request : Request) {

        guard let from = request.data["from"]?.string,

        let to = request.data["to"]?.string,

        let amount = request.data["amount"]?.double

             else {

                return nil

        }

        //request로 받은 데이터 초기화 시켜줌

        self.from = from

        self.to = to

        self.amount = amount


    }

}

블록 마이닝을 위한 BlockchainController.swift 작성 - 주석확인

    private func setupRoutes(){

        

        //블록 마이닝을 위한 코드

        self.drop.post("mine"){request in

            if let transaction = Transaction(request:request){

                let block = self.blockchainService.getMinedBlock(transactions : [transaction])

                

                //block.addTransaction(transaction: transaction)

                // json 값으로 리턴

                return try JSONEncoder().encode(block)

            }

            return try JSONEncoder().encode(["msg":"뭔가 문제가..."])

        }

        

        //웹브라우저에서 /blockchain으로 진입했을 때 배열타입의 블록체인 json타입으로 반환

        self.drop.get("blockchain"){request in

            let blockchain = self.blockchainService.getBlockchain()

            return try! JSONEncoder().encode(blockchain)

        }

    }


블록 마이닝을 위한 BlockchainService.swift 작성 - 주석확인

import Foundation


class BlockchainService {

    

    //체인배열 변수

    private (set) var blockchain : Blockchain!

    

    //제네시스 블럭 초기화=

    init() {

        self.blockchain = Blockchain(genesisBlock: Block())

        

    }

    

    //체인 반환 함수

    func getBlockchain() -> Blockchain{

        return self.blockchain

    }

    //블록 마이닝 -> 새로운 블럭 생성

    func getMinedBlock(transactions: [Transaction]) -> Block{

         let block = self.blockchain.getNextBlock(transactions:transactions)

//채굴된 블락 추가

        self.blockchain.addBlock(block)

        return block

    }

    

}


확인







스위프트로 배우는 블록체인 - vapor프레임워크로 서버 만들기

개요

이번 포스팅에서는 vapor 프레임워크를 이용해서 나의 mac 에서 웹서버를 구축해보도록 하겠습니다. vapor란 웹서버를 구현하는데 도움을 주는 모듈이라고 생각하시면 될것 같습니다. 그럼 먼저 vapor 홈으로 가서 설치를 해보도록 하겠습니다. 설치환경도 있기때문에 자신의 xcode 버전과 swift 버젼을 잘확인하신후 설치를 하시기 바랍니다.

https://vapor.codes/


설치순서

1.터미널 열기

2. brew install vapor/tap/vapor 입력

3.작업 폴더 생성 vapor new Hello

4.작업 폴더로 이동 cd Hello

5.vapor프로젝트 실행 vapor xcode

6. 예시화면(BlockChain-Server 폴더를 만들고 이곳으로 이동해서 vapor xcode 실행)



설정 - run -> mac os -> 실행해보기

웹브라우저에 localhost:8080/hello 입력

확인

이런 결과가 나오는 이유는 App -> Routes 폴더에 Routes.swift 에보면 /hello 가 들어왔을 때 json 타입으로 hello:world를 출력하게 되어 있다.

BlockchainController 작성

App -> Controllers 밑에 BlockchainController.swift를 작성해주자

Droplet+Setup 작성

App -> Setup 밑에 Droplet+Setup를 작성해주자

BlockchainService.swift 작성

App -> Service 폴더를 만들고 그 밑에 BlockchainService.swift를 작성해주자


BlockchainController 에 BlockChainService 및 drop 관련 로직추가

import Foundation

import Vapor

import HTTP


class BlockchainController {

    private (set) var drop : Droplet

    private (set) var blockchainService : BlockchainService

    

    init(drop: Droplet) {

        self.drop = drop

        self.blockchainService = BlockchainService()

        setupRoutes()

    }

    

    private func setupRoutes(){

        self.drop.get("hello"){request in

            return "안녕 블록체인"

        }

    }

    

}


웹브라우저에 localhost:8080/hello 입력


스위프트로 배우는 블록체인 -스마트 컨트랙트


스마트컨트랙트 구현

물건을 산다거나 보험에 가입한다거나 할때 발생하는 거래데이터를 거래타입에 따라 결과값이 달라지는 스마트 컨트렉트 기능을 구현해보는시간을 가지겠습니다. 쉽게말해서 가입비가 일반회원이면 10만원이고, vip회원이면 20만원이 되는 스마트컨트랙트 거래기능을 구현해보겠습니다. 먼저 거래 타입 enum을 만들어 준다. 나중에 transaction을 발생시킬때 domestic인지 international인지 아래에서 선택할 수 있습니다.

//거래 타입

enum TransactionType : String, Codable{

    case domestic

    case international

}


거래 데이터 클래스를 수정해준다. fee, transactionType부분이 추가 되었다.

//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

   

    //수수료

    var fees: Double = 0.0

    //거래 타입

    var transactionType : TransactionType

    

    //초기화

    init(from:String,to:String,amount:Double,transactionType:TransactionType) {

        self.from = from

        self.to = to

        self.amount = amount

        self.transactionType = transactionType

    }

}


스마트컨트랙트 프로토콜 함수와 그것을 상속받은 TransactionTypeSmartContract 함수 구현

// 스마트 컨트랙드 프로토콜 함수 - 지원,신청하는 계약임

protocol SmartContract{

    func apply(transaction : Transaction)

}



//스마트 컨트랙트를 상속한 함수 구현

class TransactionTypeSmartContract : SmartContract{

    //스마트 컨트랙트 함수가 호출되면 apply 함수 실행

    func apply(transaction: Transaction) {

        

        var fees = 0.0

        //거래 타입에 따라 fees 계산해주기

        switch transaction.transactionType {

        case .domestic:

            fees = 0.02

        case .international:

            fees = 0.05

        }

        //거래 타입에 따라서 항목 계산 후 대입

        transaction.fees = transaction.amount * fees

        transaction.amount -= transaction.fees

    }

}


class Blockchain : Codable(){} 함수 안에 코드 작성

    //스마트 계약 관련 데이터 배열 변수


 private (set) var smartContracts : [SmartContract] = [TransactionTypeSmartContract()]



    private enum CodingKeys : CodingKey {

        case blocks

    }

위의 CodingKeys와 Codable은 swift4에 추가된 기능인데 이것들에 대한 자세한 설명은 http://blog.canapio.com/129 참고.


addBlock .. 블락을 추가할때 스마트컨트랙트 거래내역도 함께 추가

    //블록체인 구조체에 블록을 추가하는 함수

    func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        

        // 이부분! 스마트 컨트렉트 거래내역 추가

        self.smartContracts.forEach { contract in

            block.transactions.forEach { transaction in

                contract.apply(transaction: transaction)

            }

        }

        

        //삽입

        self.blocks.append(block)

 }


json으로 인코딩 -> String으로 출력

//인코딩

let data = try! JSONEncoder().encode(blockchain)

//json데이터를 string으로 출력

let blockchainJSON = String(data: data, encoding: .utf8)


print(blockchainJSON!)




결과



전체코드


import Cocoa


//거래가 발생할때 거래 타입 선택 가능

enum TransactionType : String, Codable{

    case domestic

    case international

}


// 스마트 컨트랙드 프로토콜 함수 - 지원,신청하는 계약임

protocol SmartContract{

    func apply(transaction : Transaction)

}


//스마트 컨트랙트를 상속한 함수 구현

class TransactionTypeSmartContract : SmartContract{

    //스마트 컨트랙트 함수가 호출되면 apply 함수 실행

    func apply(transaction: Transaction) {

        

        var fees = 0.0

        //거래 타입에 따라 fees 계산해주기

        switch transaction.transactionType {

        case .domestic:

            fees = 0.02

        case .international:

            fees = 0.05

        }

        //거래 타입에 따라서 항목 계산 후 대입

        transaction.fees = transaction.amount * fees

        transaction.amount -= transaction.fees

    }

}




//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    

    //수수료

    var fees: Double = 0.0

    //거래 타입

    var transactionType : TransactionType

    

    //초기화

    init(from:String,to:String,amount:Double,transactionType:TransactionType) {

        self.from = from

        self.to = to

        self.amount = amount

        self.transactionType = transactionType

    }

}


//블록 구조체

class Block: Codable{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int

    

    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

            return String(self.index) + self.previousHash + String(self.nonce) + transactionsJSONString!

        }

    }

    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }

    

    //초기화

    init(){

        self.nonce = 0

    }

}


//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //스마트 계약 관련 데이터 배열 변수

    private (set) var smartContracts : [SmartContract] = [TransactionTypeSmartContract()]

    

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    private enum CodingKeys : CodingKey {

        case blocks

    }

    


    //블록체인 구조체에 블록을 추가하는 함수

    func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        

        // run the smart contracts

        self.smartContracts.forEach { contract in

            block.transactions.forEach { transaction in

                contract.apply(transaction: transaction)

            }

        }

        

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        var hash = block.key.sha1Hash()

        // 특정값 (앞 2자리가 00 인 hash 값 구하기

        while(!hash.hasPrefix("00")){

            //조건이 맞지 않을 경우 block의 noce 값 증가

            block.nonce += 1

            hash = block.key.sha1Hash()

            print(hash)

        }

        

        return hash

    }

    

    //새로운 블락 구하는 함수, 다음 블록 생성

    func getNextBlock(transactions:[Transaction]) ->Block{

        //새 블록 객체

        let block = Block()

        //새로운 블록이 생성될때는 이전 거래 내역이 배열로 들어간다.

        transactions.forEach { (transaction) in

            block.addTransaction(transaction: transaction)

        }

        //새로운 블록에는 이전 블록의 hash 값, 번호, 자신의 hash 값이 들어간다.

        let previousBlock = getPreviousBlock()

        block.index = self.blocks.count

        block.previousHash = previousBlock.hash

        block.hash = generateHash(for: block)

        return block

    }

    

    //이전 블럭 가져오기

    private func getPreviousBlock() -> Block {

        return self.blocks[self.blocks.count - 1]

    }

}


//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

let transaction = Transaction(from: "kim", to: "hong", amount: 1000,transactionType:.international)


print("--------------------------------------------------------")

//위에서 발생한 거래내역을 새로운 블럭을 생성하는 함수안에 넣어주었다.

let block = blockchain.getNextBlock(transactions: [transaction])

//새로만든 블록을 넣어주고

blockchain.addBlock(block)



//인코딩

let data = try! JSONEncoder().encode(blockchain)

//json데이터를 string으로 출력

let blockchainJSON = String(data: data, encoding: .utf8)


print(blockchainJSON!)


//블록의 총 개수를 확인해보자.

//print(blockchain.blocks.count)



//let block1 = Block()

//block1.addTransaction(transaction: transaction)

//block1.key



// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}

전체코드

BlockChainSwift.playground.zip


스위프트로 배우는 블록체인 - 데이터구조


순서

xcode9열기 -> get Started with a playground -> 위쪽 탭에 mac os 선택 -> blank 선택


개요

이번 포스팅에서는 블록체인 기술의 뼈대가 되는 데이터 구조를 만들어 보겠습니다. 먼저 거래를 담당하는 transaction 구조체, 거래내역과 해쉬, 이전해쉬값, nonce 값을 담는 block 구조체를 만들고 그 블록을 담을 blockchain 구조체를 만들어 보겠습니다. 위에서 오픈한 mac os playground를 열고 아래 코드를 작성해주세요.


transaction 구조체

//거래 데이터 구조체

class Transaction{

    var from: String

    var to: String

    var amount: Double

    //초기화

    init(from:String,to:String,amount:Double) {

        self.from = from

        self.to = to

        self.amount = amount

    }

    }


block 구조체

//블록 구조체

class Block{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int

    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    init(){

        self.nonce = 0

    }

}


blockchain구조체

//체인 구조체

class BlockChain {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

}



key생성하기

먼저 class구조체를 json형식으로 사용할 수 있게 하기 위해서 class type을 Codable로 지정해줍니다. class Block : Codable{} , 모든class에 지정! 그리고 key를 Blok class의 getter 함수 key 를 만들어 주고, 그 아래 거래가 발생할때 거래 내역을 담아 줄 배열을 만들어 줍니다.

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

            return String(self.index) + self.previousHash + String(self.nonce) + transactionsJSONString!

        }

    }


    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }


그리고 맨 아래 부분에서 거래를 한번 발생시키고, 새로운 블럭과 key를 만들어 봅시다.

let transaction = Transaction(from: "kim", to: "hong", amount: 1000)

let block1 = Block()

block1.addTransaction(transaction: transaction)

block1.key


Blockchain 구조체 초기화

이 클래스에서는 blocks 배열에 아무런 블럭이 없으면 현재 블록개수는 0이기 때문에 제네시스 블럭, 즉 첫번째 블록을 만드는 소스를 추가하고, blocks 배열에 추가해 주는 로직을 작성합니다. 그리고 해쉬값을 생성해주는 함수를 만들어서호출 하면 해쉬값을 리턴시켜줍니다. 이때 mac os 내장 모듈인 sha1Hash를 사용합니다.

//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    //블록체인 구조체에 블록을 추가하는 함수

    private func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        let hash = block.key.sha1Hash()

        return hash

    }

}

sha1Hash 코드작성

// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}


제네시스 블록생성 후 체인에 추가

//제네시스 블록 생성

let genesisBlock = Block()


//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)



전체소스코드

//: Playground - noun: a place where people can play


/*

 

 

 

 */




import Cocoa


//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    //초기화

    init(from:String,to:String,amount:Double) {

        self.from = from

        self.to = to

        self.amount = amount

    }

    

}


//블록 구조체

class Block: Codable{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int


    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

            return String(self.index) + self.previousHash + String(self.nonce) + transactionsJSONString!

        }

    }

    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }


    //초기화

    init(){

        self.nonce = 0

    }

}


//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    //블록체인 구조체에 블록을 추가하는 함수

    private func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        let hash = block.key.sha1Hash()

        return hash

    }

}


//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)



let transaction = Transaction(from: "kim", to: "hong", amount: 1000)

let block1 = Block()

block1.addTransaction(transaction: transaction)

block1.key







// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}





특정값에 해당하는 hash값 리턴

위에서 block의 key값을 sha1Hash() 함수로 바꾸어서 임의의 문자열데이터로 만들어 주었다. 여기서 조건을 부여해서 해쉬 값이 특정조건에 맞으면 그 값을 리턴하게 하는 코드를 작성해 보겠습니다. class Blockchain (){} 함수 안에 있는 해쉬 생성 함수를 아래와 같이 변경 해줍니다.


    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        var hash = block.key.sha1Hash()

        // 특정값 (앞 2자리가 00 인 hash 값 구하기

        while(!hash.hasPrefix("00")){

            //조건이 맞지 않을 경우 block의 noce 값 증가

            block.nonce += 1

            hash = block.key.sha1Hash()

            print(hash)

        }

        

        return hash


}


새로운 블럭생성 함수

새로운 블럭에는 항상 이전 블럭의 해쉬값, 자신의 번호 , 자신의 해쉬 값이 들어갑니다. class Blockchain (){} 함수 안에 작성

    //새로운 블락 구하는 함수, 다음 블록 생성

    func getNextBlock(transactions:[Transaction]) ->Block{

        //새 블록 객체

        let block = Block()

        //새로운 블록이 생성될때는 이전 거래 내역이 배열로 들어간다.

        transactions.forEach { (transaction) in

            block.addTransaction(transaction: transaction)

        }

        //새로운 블록에는 이전 블록의 hash 값, 번호, 자신의 hash 값이 들어간다.

        let previousBlock = getPreviousBlock()

        block.index = self.blocks.count

        block.previousHash = previousBlock.hash

        block.hash = generateHash(for: block)

        return block

    }


이전 블럭가져오기

이전블록의 해쉬값을 가져오기 위해서 이전 블록을 가져오는 함수를 만들어보겠습니다.  위의 새로운 블럭 구하는 함수 코드 아래에 작성

    //이전 블럭 가져오기

    private func getPreviousBlock() -> Block {

        return self.blocks[self.blocks.count - 1]

    }



새로운 거래와 새로운 블록 생성

새로운 거래를 발생시키는 Transaction 함수를 이용해서 거래 내역을 만들고 이것을 다음에 생성될 새로운 블록안에 넣어주었다. 그리고 새로 생성된 블록은 blockchain 안에 있는 블록의 배열 안에 넣어주었다. 그리고 체인안에 존재하는 블록의 개수를 찍어 보았다. 총 2가 나온다. 처음만든 제네시스 블록 하나, 그리고 코드로 만들어준 블록 하나 총 2개다. 참고로 addBlock 함수에 접근하기 위해서 addBlock 함수 앞에 있는 private 키워드를 지워주자.

//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

let transaction = Transaction(from: "kim", to: "hong", amount: 1000)


print("--------------------------------------------------------")

//위에서 발생한 거래내역을 새로운 블럭을 생성하는 함수안에 넣어주었다.

let block = blockchain.getNextBlock(transactions: [transaction])

//새로만든 블록을 넣어주고

blockchain.addBlock(block)

//블록의 총 개수를 확인해보자.

print(blockchain.blocks.count)


결과확인


코드 정리




import Cocoa


//거래 데이터 구조체

class Transaction : Codable{

    var from: String

    var to: String

    var amount: Double

    //초기화

    init(from:String,to:String,amount:Double) {

        self.from = from

        self.to = to

        self.amount = amount

    }

    

}


//블록 구조체

class Block: Codable{

    //번호, 이전해쉬,해쉬,nonce를 가지고 있다.

    var index : Int = 0

    var previousHash : String = ""

    var hash: String!

    var nonce : Int


    //거래 내역을 가지고 있다.

    private (set) var transactions : [Transaction] = [Transaction]()

    

    //key 변수

    var key : String {

        get{

            //위 transaction 배열을 json 형식으로 변경

            let transactionsData = try! JSONEncoder().encode(self.transactions)

            // json형식의 transaction 배열을 String 값으로 변경

            let transactionsJSONString = String(data: transactionsData, encoding:.utf8)

            //번호, 이전해쉬, nonce + transactionString 값을 이용해서 고유 key 생성

            return String(self.index) + self.previousHash + String(self.nonce) + transactionsJSONString!

        }

    }

    //거래 내역을 담는 함수

    func addTransaction(transaction: Transaction){

        self.transactions.append(transaction)

    }


    //초기화

    init(){

        self.nonce = 0

    }

}


//체인 구조체

class Blockchain : Codable {

    //블록이 배열형태로 저장됨

    private (set) var blocks : [Block] = [Block]()

    

    //초기화

    init(genesisBlock : Block){

        addBlock(genesisBlock)

    }

    

    //블록체인 구조체에 블록을 추가하는 함수

     func addBlock(_ block : Block){

        //체인에 아무런 블록이 없으면 제네시스 블록 생성

        if self.blocks.isEmpty{

            //제네시스 블록의 이전 해쉬값 (임의지정)

            block.previousHash = "0000000000"

            //제네시스 블록의 해쉬값

            block.hash = generateHash(for :block)

        }

        //삽입

        self.blocks.append(block)

    }

    

    //해쉬 생성 함수

    func generateHash(for block : Block) -> String{

        //block의 key 값을 이용해서 hash 값을 만들어 낸다.

        var hash = block.key.sha1Hash()

        // 특정값 (앞 2자리가 00 인 hash 값 구하기

        while(!hash.hasPrefix("00")){

            //조건이 맞지 않을 경우 block의 noce 값 증가

            block.nonce += 1

            hash = block.key.sha1Hash()

            print(hash)

        }

        

        return hash

    }

    

    //새로운 블락 구하는 함수, 다음 블록 생성

    func getNextBlock(transactions:[Transaction]) ->Block{

        //새 블록 객체

        let block = Block()

        //새로운 블록이 생성될때는 이전 거래 내역이 배열로 들어간다.

        transactions.forEach { (transaction) in

            block.addTransaction(transaction: transaction)

        }

        //새로운 블록에는 이전 블록의 hash 값, 번호, 자신의 hash 값이 들어간다.

        let previousBlock = getPreviousBlock()

        block.index = self.blocks.count

        block.previousHash = previousBlock.hash

        block.hash = generateHash(for: block)

        return block

    }

    

    //이전 블럭 가져오기

    private func getPreviousBlock() -> Block {

        return self.blocks[self.blocks.count - 1]

    }

}


//제네시스 블록 생성

let genesisBlock = Block()

//추가

let blockchain = Blockchain(genesisBlock: genesisBlock)


//새로운 거래 발생

let transaction = Transaction(from: "kim", to: "hong", amount: 1000)


print("--------------------------------------------------------")

//위에서 발생한 거래내역을 새로운 블럭을 생성하는 함수안에 넣어주었다.

let block = blockchain.getNextBlock(transactions: [transaction])

//새로만든 블록을 넣어주고

blockchain.addBlock(block)

//블록의 총 개수를 확인해보자.

print(blockchain.blocks.count)



//let block1 = Block()

//block1.addTransaction(transaction: transaction)

//block1.key







// String 확장

extension String {

    

    func sha1Hash() -> String {

        

        let task = Process()

        task.launchPath = "/usr/bin/shasum"

        task.arguments = []

        

        let inputPipe = Pipe()

        

        inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!)

        

        inputPipe.fileHandleForWriting.closeFile()

        

        let outputPipe = Pipe()

        task.standardOutput = outputPipe

        task.standardInput = inputPipe

        task.launch()

        

        let data = outputPipe.fileHandleForReading.readDataToEndOfFile()

        let hash = String(data: data, encoding: String.Encoding.utf8)!

        return hash.replacingOccurrences(of: "  -\n", with: "")

    }

}

+ Recent posts

티스토리 툴바