일반적으로 WAS서버만으로는 성능이 부족하므로,


웹+WAS 서버 결합해서 서비스를 하게 되는데,

에전에는 Apache+ Tomcat의 결합이 대세였다고 하면

요즘에는 nginx + SpringBoot(tomcat 포함)이 대세이다.



<nginx의 특징 및 주요 기능>


1. apache대비 빠르고 가볍다.

  - apache가 클라이언트별로 thread를 할당하는 구조인데 반해,

    nginx는 메시지방식(혹은 event-driven) 방식이다.  당연히 비동기식 메시징 방식이 훨씬 효율적이다.

    자세한 비교는  REF

  


2. reverse PROXY가 강력하다.

  - apace에서도 PROXY기능 외에 Reverse Proxy가 지원되었으나,

    nginx의 reverse Proxy는 ID/비번도 부여할 수 있고.. 더 안전/강력해 보인다. 


    참고: PROXY vs Reverse_PROXY   REF

      - proxy는 웹브라우저 쪽:  웹브라우저 여러개가 하나의 proxy를 이용해 cache된 데이터를 받도록 해서

      - reverse Proxy는 웹서버 쪽: 마치 L4처럼 하나의   ReverseProxy서버가 요청을 받아 여러 웹서버로 분리할 수 있다.



<nginx + springBoot 사용방식>


1. 기본설정    /nginx/conf/nginx.conf 를 편집하고 reload하면 됩니다.

   

   http { ... ... 

             server {

                          listen 80; 

                           ... ...

                          location / { 

                               proxy_pass http://localhost:8080; 

                          }

             ... }



2. nginx + 2개 springBoot (각각 다른포트)를 이용한  무중단 배포 방식: REF   gitREF



참고: 

  nginx를 centOs7에 설치하는 법 -  REF

  nginx를 이용한 reverseProxy 설정 - REF

Posted by yongary
,

엔터프라이즈 이더리움 dApp을 개발할 때, 많이 사용하는 개발툴은 다음과 같다.



1. truffle과 truffleContract를 이용한 solidity와 js 개발.

  => 특히 truffle5 부터 async/await을 훨씬 더 잘 지원함.


2. js개발은 ES6의 promise를 잘 쓰기 위하여 react를 추천.



3. MetaMask 로그인 plug-in


4. 그리고 백엔드(웹서버)로는 개인적으로는 express보다는 
   springBoot를 추천한다 - 엔터프라이즈급 백엔드가 가능하다.


  

React + SpringBoot + Truffle예제:  http://github.com/yongarykim


자세한 툴들 List:
https://github.com/ConsenSys/ethereum-developer-tools-list/blob/master/README.md


Posted by yongary
,

참고 사이트 https://brunch.co.kr/@bitcoin/10


PoW PoS BFT등 합의 알고리듬을 평가할 때 DCS삼각형을 사용해서 평가한다.


토큰이코노미에서 토큰 수량을 분석할때는 MV=PQ 공식을 사용한다.

Posted by yongary
,

이더리움(ethereum) 엔터프라이즈  dApp 개발환경인 truffle 5.0.0 beta가 출시되었다.

덩달아 web3.js의 1.0.0도 같이 나왔다.  

(하지만 2018년에는 아직까지는 비추이다.. 버그 존재)


https://github.com/trufflesuite/truffle/releases  (2018.8월 기준)



가장 큰 변화는 truffle test  시에 에러내역이 출시된다는 것이다. 


베타인 관계로 설치방법이 좀 다른데, 아래와 같이 설치가 가능하다.

npm uninstall -g truffle
npm install -g truffle@beta



그 외에 주요 변화는

1. truffle console에서도 await 이 된다. (async를 알아서 불러줘서, async없이도 await을 막 써도 되는 곳이 몇군데 있고) '

  심지어는 deploy에서도 await이 사용가능하다. 

 

 const One = artifacts.require("One");
 const Two = artifacts.require("Two");

 module.exports = async function(deployer) {
  await deployer.deploy(One);

  const one = await One.deployed();
  const value = await one.value();

  await deployer.deploy(Two, value);
};


2. BigNumber대신에 BN으로 대치되고, BN에서는 포맷설정이 가능하기 때문에 BigNumber도 계속 사용은 가능하다.


web3와 결합한 BN사용방식은 다음과 같다.

const stringBalance = await web3.eth.getBalance('0xabc..'); const bnBalance = web3.utils.toBN(stringBalance);




Posted by yongary
,

제목: truffle과 ES6 javascript를 이용한, 이더리움 dApp 개발환경 : 



Explanation - ENG

  • BaseContract (below) is base class when making javascript Contract using truffle & ES6-javascript
  • This can be used when using several ES6-javascript like Vue.js, Angular.js and react.js
  • When developing Ethereum dApp, this class helps to map functions between Solidity and Javascript, and helps web3 process of MetaMask and etc.
  • Detail using example can be found on https://github.com/yongarykim/RSTdAppDevEnv/tree/master/react-frontend/src/contracts


Explanation - KOR

  • truffle을 이용해서 javascript contract를 개발할 때, 상속받아서 쓰도록 만든 기본 class이다.
  • 각종 ES6-javascript language에서 아래 BaseContract를 상속을 받아서 이더리움 dApp개발시 사용할 수 있다.
  • 이더리움 dApp개발시 간단히 solidity의 함수와 js 함수간 매핑하도록 도와주며, metaMask 등의 web3처리를 자동으로 해준다. 자세한 사용예제는 https://github.com/yongarykim/RSTdAppDevEnv/tree/master/react-frontend/src/contracts 를 참고하도록 한다.


소스:  https://github.com/yongarykim/ES6-ContractBase


/**
* BaseContract - super class of all contracts (using Web3 & TruffleContract)
*
* <History>
* @author Yong Kim (yongary.kim@gmail.com)
* @date 2018.4.5 - first created
* @date 2018.6.11 - promisify & getMyAccount added
* @date 2018.6.13 - contractDeployed added.
*
*/

import Web3 from 'web3';
import TruffleContract from 'truffle-contract';
import $ from 'jquery';
import { Subject } from 'await-notify';

//web3함수를 Promise로 만들기 위한 공통모듈
const promisify = (inner) =>
new Promise((resolve, reject) =>
inner((err, res) => {
if (err) { reject(err) }
resolve(res);
})
);

export default class BaseContract {

constructor() {
this.contract = {};
this.initWeb3();
//contract.deployed()가 initContract 보다 빨리 호출되는 경우용
this.eventWaiting = new Subject();
}

initWeb3() {
// Metamask
console.log("this.web3:")
console.log(this.web3);
console.log("window.web3:")
console.log(window.web3);

if (typeof window.web3 !== 'undefined') {
this.web3 = new Web3(window.web3.currentProvider);
} else {
//LocalTest set the provider you want from Web3.providers
console.log("initWeb3: local");
this.web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:7545"));
}
console.log(this.web3);
}

/* new 이후에 호출 필요 */
initContract(contractJson) {

let self = this;
$.getJSON(contractJson, function (data) {
// Get the necessary contract artifact file and instantiate it with truffle-contract
let contractArtifact = data;

console.log('initContract:' + self.contract);

self.contract = TruffleContract(contractArtifact);
self.contract.setProvider(self.web3.currentProvider);

console.log('initContract out');
self.eventWaiting.notify(); //contractDeployed가 먼저 불렸을 경우 대비.
});
}

/* contract관련 check함수 */

/**
* contractDeployed
* - same as contract.deployed,
* But waiting initContract to finish
* - useful when doing something on screen loading..
* */
contractDeployed = async () => {

console.log('in contractDeployed:');
if (Object.keys(this.contract).length === 0 ) {//contract Empty = initContract수행중
console.log(this.contract);

while (true) {
await this.eventWaiting.wait();
console.log('initContract Done');
break; //exit when notified.
}
}
console.log('out contractDeployed:');
return this.contract.deployed().then((instance) => {return instance;});

}


/* web3/eth Basic함수들 - eth의 sync함수는 값을 못 읽어 오는 경우가 발생가능하므로 async함수를 써야 함. */

getMyAccount = async () => {
const accounts = await promisify(cb => this.web3.eth.getAccounts(cb));
return accounts[0]; //accounts Array의 0번째가 본인 account임.
}
}



부연설명


이더리움 dApp을 개발할 경우, truffle과 javascript를 이용해서 개발하는 게 편리하다.

특히, javascript를 ES6로 사용하면 상속을 쉽게 사용할 수 있기 때문에,

복잡한 dApp을 개발할 수 있는  밑거름이 된다. 

(아무 생각없이, html과 javascript, truffle을 섞어서 쓴다면 간단한 dApp을 만들고 나서

복잡한 dApp은 만들다가 포기하게 될 확률이 높아진다)

 

복잡하고 어려운 dApp을 개발하기 위해서는 건물의 기초공사부터 튼튼하게 해 놓듯이

ES6 javascript를 이용해서 javascript를 개발하기를 권장하고.

그래서, 그 때 도움이 될 class를 만들어 본것이다.


ES6- javascript의 상속을 사용할 수 있기 때문에 쉽게쉽게 truffle contract기반의

javascript contract가 생성이 가능하다. 


자세한 사용예제는  https://github.com/yongarykim/RSTdAppDevEnv/tree/master/react-frontend/src/contracts 



Posted by yongary
,

REF


private 이더리움 환경을 구성해서 mining을 시켜놓으면, 쉬지 않고 mining을 한다.


혹시 이게 부담된다면

transaction이 생겼을때만 mining하는 방법이 있다.


(하드디스크나 cpu부하를 줄이기에 용이하다.)




var mining_threads = 1

function checkWork() {
    if (eth.getBlock("pending").transactions.length > 0) {
        if (eth.mining) return;
        console.log("== Pending transactions! Mining...");
        miner.start(mining_threads);
    } else {
        miner.stop();
        console.log("== No transactions! Mining stopped.");
    }
}

eth.filter("latest", function(err, block) { checkWork(); });
eth.filter("pending", function(err, block) { checkWork(); });

checkWork();


pending블록이 있을때만 miner.start를 하고, 아닌경우 miner.stop을 하는 코드이다.


이걸 javascript로 별도저장해서 geth실행시 반영하려면

geth --preload "/path/to/mineWhenNeeded.js"  

와 같이 하면된다. 






Posted by yongary
,

solidity개발시 gas비용을 최소화하기 위해 연산을 줄이는 것이 중요하다.


그래서 가장 유용한 data Type은 mapping인데,

mapping안에 array를 넣는 방법도 꽤 추천할 만하지만


array의 길이가 길어진다면 O(n)의 서칭이 발생하므로,

데이타가 많다면 mapping안에 mapping을 또 넣는 방법이 좋을 것 같다.


일반적인 language용어로 말하자면, 

hashMap안에 또 hashMap을 넣는 것이다.


단, 이를 조회하려면 parentKey와 childKey모두 알아야 조회가 가능하다.


예제는 다음과 같다. 참고사이트: REF


contract SampleContract {

  struct ChildStruct {
    bool isPresent;
    bytes32 name;
  }

  struct ParentStruct {
    bool isPresent;
    bytes32 name;
    mapping (bytes32 => ChildStruct) childStructs; 
  }

  mapping(bytes32 => ParentStruct) public parentStructs;

  function insertData(
    bytes32 parentKey, 
    bytes32 parentName, 
    bytes32 childKey, 
    bytes32 childName)
    public 
    returns(bool success)
  {

    parentStructs[parentKey].isPresent = true;
    parentStructs[parentKey].name = parentName;
    parentStructs[parentKey].childStructs[childKey].isPresent = true;
    parentStructs[parentKey].childStructs[childKey].name = childName;
    return true;
  }

  function getChild(bytes32 parentKey, bytes32 childKey) public constant returns(bool isPresent, bytes32 name) {
    return (parentStructs[parentKey].childStructs[childKey].isPresent, parentStructs[parentKey].childStructs[childKey].name);
  }

}


Posted by yongary
,

React와 truffle로 dApp을 개발중이다. 

- 백엔드는 sprintBoot로 할 예정인데 모두 합해서 RST 개발환경으로 부르고 있다.
 (R: react-frontend ,  S: springboot-backend,   T: truffle )


dApp개발과 직접상관있는 부분은

  - R: dApp개발, 

  - T: solidity개발이다. (S: springboot-backend는 일단은 배포와 상관이 있다고 보면된다)



(UI와 web3가 섞이면서 콜백지옥에 빠질 수 있으므로,

RST개발환경과 같은 방식을 사용해 ES6 javascript를 사용해 이를 빠져나오길 추천한다.)



UI에서 promise를 활용해  web3.eth.getAccounts, 그 중에서도 accounts[0] 즉, 자기 account를 

가져오는 샘플코드는 다음과 같다.


<UI에서 호출하는 부분>

componentDidMount() {     console.log('componentDidMount start');     //getMyAccount     this.ATZToken.getMyAccount().then((account) => {         this.myAccountInput.value = account;         console.log('in componentDidMount:' + account);     }).catch((err) => {         console.log(err.message);     });     console.log('componentDidMount End'); }


<web3 연동 부분>  es6-promisify로는 잘안되는 부분이 있었는데, REF 를 보고 자체 promisify함수를 만들어 해결하였다. 

//web3함수를 Promise로 만들기 위한 공통모듈 const promisify = (inner) =>     new Promise((resolve, reject) =>         inner((err, res) => {             if (err) { reject(err) }             resolve(res);         })     );

 

/* Basic함수들 - eth sync함수는 값을 못 읽어 오는 경우가 발생가능하므로 async함수를 써야 함. */ getMyAccount = async () => {     const accounts = await promisify(cb => this.web3.eth.getAccounts(cb));     return accounts[0]//accounts Array 0번째가 본인 account. }


Posted by yongary
,

REF


라우저의 console에서도 web3로 contract를 실행할 수 있다.  

작년에 작성된 것이긴 하지만, ERC20 token을 전송하는 transfer를 아래와 같이 console로 수행가능하다.


 ==> 이를 응용하면 개발중인 화면에서, 유사방식으로 실행하면서 debugging이 가능하다. 


> const Web3 = require('web3');
undefined
> const web3 = new Web3('http://localhost:8545');
undefined
> web3.version
'1.0.0-beta.11'
> fs = require('fs');
<blah>
> const abi = fs.readFileSync('erc20_abi.json', 'utf-8');
undefined
> const contract = new web3.eth.Contract(JSON.parse(abi), '0xafb7b8a4d90c2df4ce640338029d54a55bedcfc4', { from: '0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1', gas: 100000});
undefined
> contract.methods.balanceOf('0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1').call().then(console.log).catch(console.error);
Promise {...}
> 99997
> contract.methods.transfer('0xffcf8fdee72ac11b5c542428b35eef5769c409f0', 1).send().then(console.log).catch(console.error);
Promise {...}
> { transactionHash: '0xf2d621ba5029a086e212d87fab83be31c3fa41fe47198c378f55c252e5b25d5b',
  transactionIndex: 0,
  blockHash: '0x0e806bf3e88f9335ee9be903303a2393c940ab22f4a73c7e28ca8d9a212ffa4e',
  blockNumber: 429,
  gasUsed: 35206,
  cumulativeGasUsed: 35206,
  contractAddress: null,
  events: 
   { Transfer: 
      { logIndex: 0,
        transactionIndex: 0,
        transactionHash: '0xf2d621ba5029a086e212d87fab83be31c3fa41fe47198c378f55c252e5b25d5b',
        blockHash: '0x0e806bf3e88f9335ee9be903303a2393c940ab22f4a73c7e28ca8d9a212ffa4e',
        blockNumber: 429,
        address: '0xAFB7b8A4d90C2Df4ce640338029d54A55BEDcfC4',
        type: 'mined',
        id: 'log_bfd297b0',
        returnValues: [Object],
        event: 'Transfer',
        signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        raw: [Object] } } }
> contract.methods.balanceOf('0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1').call().then(console.log).catch(console.error);
Promise {...}
> 99996


Posted by yongary
,

아래 세 군데의 Reference사이트를 참고하여, 간단하게 xls 다운로드 하는 spring기능을 만들어 보았다.

기존 3가지가 다 약간씩 장점이 있으나, 복잡한 면이 있어서 이들을 섞으니 간단하게 된다.



    참고0: https://okky.kr/article/421321

   참고1: http://pkbad.tistory.com/26

   참고2:  http://heowc.tistory.com/66 



1. xml 설정.


<!-- 엑셀 Download용 View  -->

<beans:bean class = "org.springframework.web.servlet.view.BeanNameViewResolver">

    <beans:property name = "order" value = "1" />

</beans:bean>

<!-- 엑셀 Download View는 컨트롤러 리턴시 다른것들보다 먼저 읽혀야 하기 때문에 order를 0과 1로 지정-->

<beans:bean class = "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

    <beans:property name = "order" value = "0" />

    <beans:property name = "defaultErrorView" value = "error" />

    <beans:property name = "exceptionMappings">

        <beans:props>

            <beans:prop key = "RuntimeException">error</beans:prop>

        </beans:props>

    </beans:property>

</beans:bean>

<beans:bean name="ExcelXlsView" class="my.common.view.ExcelXlsView" />



2.  ExcelXlsView class 작성.


@Component

public class ExcelXlsView extends AbstractXlsView {


@Override

protected Workbook createWorkbook(Map<String, Object> model, HttpServletRequest request) {

return (HSSFWorkbook)model.get("workbook");

}

 

@Override

protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) {


    response.setCharacterEncoding("UTF-8");

            response.setContentType("application/vnd.ms-excel");

            response.setHeader("Pragma","public");

            response.setHeader("Expires","0");

        

//file이름 및 확장자

response.setHeader("Content-Disposition",

                 "attachment; filename=\"" + model.get("fileName") +".xls" + "\"");

}

}



3. Controller에서 excel 다운로드 수행.


@RequestMapping(value="/ExcelDownload.do")

public ModelAndView ExcelDownload(ModelMap modelMap, @ModelAttribute("modelVO")myVo infoVO,  

HttpServletResponse response, HttpServletRequest request, HttpSession session) throws Exception {

//화면의 List와 똑같은 Query수행.

List<MyVo> resultList = service.selectList(infoVO);


//Excel 생성

Workbook xlsWb = new HSSFWorkbook();

Sheet sheet1 = xlsWb.createSheet("sheet1");

Row row = null;

Cell cell = null;

    //Header Row생성

    row = sheet1.createRow(0);

    

    //필드별 헤더설정.

    int k=0;

    

    cell = row.createCell(k++);   

    cell.setCellValue("번호");


    

    sheet1.setColumnWidth(k, 4000); //좀 넓은 column은 width설정. 4000정도하면 날짜가 여유있게 들어감.

    cell = row.createCell(k++);   

    cell.setCellValue("생년월일");

    

    

    

 //Data 출력

        for(int i = 0; i < resultList.size(); i++) {

        row = sheet1.createRow(i+1);

        JdnumBunJadonVo oneRow = resultList.get(i); 

        int j = 0;

        //필드별 값설정.

        cell = row.createCell(j++);

        cell.setCellValue(oneRow.getNo());

        

        cell = row.createCell(j++);

        cell.setCellValue(oneRow.getBirthday());

        }


//model에 data넣어서, modelAndView 리턴.

        modelMap.put("workbook", xlsWb);

        modelMap.put("fileName", "excelList_" + CommonUtil.getCurrentDate()); //파일네임 앞부분 설정(.xls는 자동추가됨)

        

return new ModelAndView("ExcelXlsView");


}




4. 마지막으로, jsp에서는 아래와 같이 실행.


      var url = '/ExcelDownload.do'; //+parameter  를 get방식으로 설정.

window.open(url, '_blank' ); 

Posted by yongary
,