일단 아이디어가 어느정도 구체화 되었다면, 해당 아이디어에 대한 시장 가능성과 실제 아이디어의 실행을 위한 방법들을 수립해야 하며 크게 상황분석 => 전략 수립 => 전략 실행 의 흐름으로 이어진다.
시장과 경쟁자에 대한 명확한 분석을 통해 독보적인 차별점을 도출하자
여기서 가장 먼저 해야 할 일은 바로 기업의 내부적 정보와 외부적 정보를 수집하는 것이다. 가령 우리 스타트업이 가장 잘하는 것은 무엇인지 또 할 수 있는것과 할 수 없는 것은 무엇인지 우리가 다른 스타트업에 비해 뛰어나게 잘하는 것과 부족한 것이 무엇인지를 냉철하게 판단해야 한다. 이를 위해서는 하고자 하는 비즈니스에 끊임없는 고민이 필요하며, 시장에 어떤 경쟁자들이 있고, 그들의 강점과 약점이 무엇인지를 명확하게 이해하는 것이 중요하다. 대부분의 성공한 스타트업들은 이미 시장에 자리잡은 경쟁자가 있음에도 불구하고 명확한 차별점을 내세우고 기존 시장에 참여한 기업들이다 즉, 시장을 분석하고 내가 가장 잘 하는 강점을 무기로 삼아 시정을 침투해야하며, 이를 위한 정보의 수집은 매우 중요하다.
가장 간단하게는 다음과 같은 질문을 해볼 수 있다.
나와 우리 팀이 가장 잘하는 것은 무엇이고 그 근거는 무엇인가?
이미 시장에 있는 경쟁자들이 잘하는 것은 무엇이고 우리가 그들보자 나은점이 무엇인가?
시장의 고객들은 기존 경쟁자들의 어떤 차별점에 반응하고, 그들의 체워지지 않은 니즈는 무엇인가?
현상분석
다음으로 조사해야할 부분은 시장의 상황과 트렌드 그리고 다양한 이슈들을 말미암아 스타트업의 성공가능성과 실패 가능성을 점치는 것이다. 가령 커피를 판다고 하면 시장에 어떤 커피들이 인기를 끌고 있으며, 전체 커피 시장과 고객의 니즈는 향후 어떤 방향으로 나아갈 것인가에 대한 분석이 이것이다.
고객의 선호와 취향이 변모해 나가는 트렌드를 이해하고 하고자 하는 비즈니스가 그 흐름에 부합하는 지를 숙고하자.
비즈니스 전략의 수립
내가 시장의 다른 경쟁자들에 비해 무엇을 잘하고, 또 경쟁력을 가지지 못하는 약점을 명확하게 인지하였다면, 기업의 모든 자원을 기업이 가진 강점에 쏟아부어야 한다. 가량 다른 기업이 이미 잘하고 있고, 그들이 가진 강점의 나와 우리 팀이 아무런 근거 없이 ‘더 잘할 수 있다’ 라는 마인드 셋으로 전략을 세우는 것은 많은 스타트업이 하는 실수이다.
여기서 세운 전략을 기업을 운영함에 있어 잊지 말아야 할 본질이고, 나의 모든 자원을 합리적으로 분배할 근거가 되기에 이 내용을 바탕으로 말미암아 기업의 사명 선언문과 단기적 프로젝트의 목표를 규정한다.
때문에 부정적인 이미지를 주지 않기 위해 warm introduction 을 하는 것이 매우 중요하다.
가장 많이 하는 실수중 하나는 여러 명의.사람들에게 무작위 cold email 을 보내는 것이다. 이는 personalized 되지도 않았고 무작위로 copy and paste 되기 일쑤이다.
이는 전혀 효과적이지 않다.
수신자는 당신의 프로모션에 관심이 없고, 당신을 만나야 할 이유가 명확하게 5초 이내에 설득되지 않으면 당신의 이메일은 쓰레기통으로 들어갈 것이다.
주제문단이 가장 가장 가장 가장 중요하다.
다른 수많은 이메일과 독보적인 차이를 보이지 못하면 바로 삭제될 것이다
기본 포맷
첫문단에서는 간단한 인사를 건내고 그 사람의 신상정보에 대해 아는 것이 있다면 작성해라. SNS 나 링크드인에서 찾은 사람이 있다면 언급해주자.
두번째 문단에서는 제품에 대한 피칭을 진행한다.3~4 줄로
세번째 문단에서는 해당 제품이 해당 기업에 어떤 가치를 제공할지에 대해 이야기 하라 어떤 기회를 가져다주고 해당 기업을 조사하여 personalize 해야한다. 만약 가격을 제시한다면 이곳이 적당하다.
마지막 문단에서는 인사를 하고 전화나 이메일로 컨택을 요청한다.
제일 중요한 것은 진정성이다.
피칭 메일을 작성하는 사람의 머릿속에 드는 가장 큰 생각은 바로 어떻게 내 제품을 사게 만들것인가이다. 하지만, 진정으로 수신자의 마음을 움직이기 위해서는 진정으로 그 사람을 이해하고 우리가 그들에게 제공할 수 있는 가치가 무엇인지를 계속 고민해야 한다.
만약, 메일을 쓰는 내내 그들의 입맛ㅇ르 당기게 하고, 제품만 팔고자 하는 마음을 가지고 있다면, 정말 진정성 있는 제안서가 나오기 힘들다. 특히 이런저런 이메일 피칭법을 보고 다른 피칭 메일을 참조하면서 정형화된 형식의 메일만이 나올뿐이며, 그 메일은 아마도 수신자의 마음을 움직이지는 못할 것이다.
피칭을 하는 과정은 내 제품을 이해하고 받는 이에게 내가 어떤 가치를 제공할 수 있는지를 고민하는 과정이지, 내 판매고를 올리기 위한 과정이 아니다.
내가 가진 제품의 확실한 경쟁력을 계속해서 제고하면서 진정성 있는 메일을 작성해야 할 것이다.
이더리움에서 eth 를 송금하거나 스마트 컨트랙을 부르는 등의 transaction 을 일으키기 위해서는 signing 이 필요하며, 블록체인 상에서 사용자를 인증하거나 메세지의 유효성을 검증하기 위해 signing 과 validation 이 사용된다.
가령, Alice 가 Bob 에게 어떤 메세지를 보내는 상황을 전제해 보자. 여기서 Bob 이 메세지를 받았다면, 인터넷 네트워크에서는 누구든지 메세지를 보낼 수 있고, 나쁜 의도를 가진 다수의 사용자들이 존재하기 때문에, 진짜 그 메세지가 Alice 로 부터 온 것인지 또, 메세지의 내용이 Alice 가 보낸 그 내용이 맞는지에 대해 의문을 품을 것이다. 이 때문에 블록체인 에서는 signing 의 개념이 존재하며 여기서 그 유효성을 검증하고자 하는 대상을 message 라고 한다.
그렇다면 이제 어떻게 signging 과 validation 이 진행되는 지를 알아보자.
먼저 위 사례에서 Alice 는 자신의 Private Key 로 해당 메세지에 서명을 한다. 이더리움에서 제공하는 web3 를 통해 다음과 같은 간략한 명령을 통해 이런 signing 이 진행되게 된다.
1
web3.eth.accounts.sign(message, privateKey);
위 에서 message 는 유효성을 검증하기 위한 메세지로 String 타입이다.
위 과정을 거치면 사용자가 서명을 한 메세지를 의미하는 Signature Object 가 나오게 되는데 그 형식은 다음과 같다.
위에서 알 수 있듯이 Signature 에는 사용자의 원래 메세지 뿐만 아니라 messageHash 라는 "\x19Ethereum Signed Message:\n" + message.length + message 의 양식으로 포맷팅된 후 keccak256(SHA3) 으로 해쉬된 해시값을 비롯하여 signature 를 구성하는 v, r, s 와 실제 서명인 Signature 가 포함된다. 여기서 r 과 s 는 각각 Signature의 첫 32byte 와 뒤 32byte 를 나타낸다.
* 위의 MessageHash 는 web3 가 제공하는 hashMessage(message) 함수 호출을 통해서도 얻을 수 있다.
위처럼 sign 을 완료했다면, 해당 Signature 를 받은 사용자는 다음과 같은 recover 함수 호출을 통해 올바른 사용자인지를 판별 할 수 있다.
EIP 712 란 Ethereum Improvement Proposals 712 의 약어로 이더리움에서 향후 지원하게 될 다양한 제안들중 하나이다. 과거 사용자가 어떤 거래를 함에 있어 sign 을 할때에는 sign 의 대상이 되는 message 가 hash 화 되어 존재하기 때문에, 서명을 하는 사용자가 자신이 서명하는 내용에 대해 잘 알기 힘든 문제가 있었으며, 가령 유사한 해시값을 가진 피싱 사이트로 유도하여 사이닝을 유도한다던가 하는 다양한 위험에 노출되어 있었다.
이를 해결하기 위해 안전하고 값이 변조되지 않는 해싱을 보장하면서도 readability 를 가질 수 있는 서명방법을 고안하게 되었으며 그 제안내용이 EIP712 에 제안되었다.
EIP712 를 통해 사용자는 자신이 서명하는 정보에 대해 명확하게 인지할 수 있게 된다고 볼 수 있다.
가령, 위에서처럼 일반적인 signing의 과정에서는 sign 을 요청하는 사람이 제공하는 message값(주로 hash값) 만을 보고 사용자가 sign 을 진행하지만 EIP712 에서는 json 데이터를 보고 자신이 서명하는 데이터를 명확히 알 수 있다.
다음은 EIP712 를 통해 수행되는 서명의 방법을 나타낸다.
먼저 서명을 하고자 하는 데이터의 형태를 정의하는데 이러한 typed structured data 를 먼저 정의하는 것으로 서명이 시작된다. 아래와 같이 서명을 하고자 하는 메세지를 json 형태로 정의를 한다. 서명자는 아래와 같은 데이터를 통해 어떤 내용에 자신이 서명을 하는지 알 수 있다.(아래의 메세지는 meta mask 등의 지갑 어플리케이션에서 파싱되어 보여지며, EIP712 는 이러한 지갑 어플리케이션에서 제공하는 기능을 말미암에 제공된다.)
web3.currentProvider.sendAsync( { method: "eth_signTypedData_v3", params: [signer, data], from: signer }, function(err, result) { if (err) { returnconsole.error(err); } const signature = result.result.substring(2); const r = "0x" + signature.substring(0, 64); const s = "0x" + signature.substring(64, 128); const v = parseInt(signature.substring(128, 130), 16); // The signature is now comprised of r, s, and v. } );
data 내의 types 필드는 스마트 컨트랙트 내에서의 데이터 구조를 나타내며 반드시 struct name 과 정확히 일치해야한다. 또한 PrimaryType 은 데이터 구조에서의 최상위 자료 구조형이 무엇인지 명시한다.
Validation in Smart contract
클라이언트에서 signing 을 위해 formatting 과 hashing 을 거친 것처럼 같은 내용의 코드가 Smart Contract 에도 포함되어야 한다. 이 과정을 통해 ecrecover 함수를 통해 해당 서명에 사인한 account 의 address 를 알 수 있다.
인증을 위한 Contract 를 위해 제일 먼저 EIP712 에서 앞서 정의한 data type 을 struct 로 정의해야 한다.
DEX 란 Decentralized Exchange 의 약어로 중간자 없이 가상화폐 지갑과 지갑 사이의 p2p 교환을 해주는 것이다.
DEX 에서는 custody 즉, 누가 거래에서의 키를 보관하는가에 대한 중대한 이슈가 있으며, DEX 는 모든 교환과정에 있어 개인이 키를 소유하고 완변하게 decentralized 되 토큰의 교환을 하는 것에 그 목적이 있다.
Basic Procedure
본 프로젝트에서 DEX 의 기본적인 프로세스는 다음과 같다.
먼저, 브라우저 상에서 토큰의 교환을 원하는 사용자가 매도 혹은 매수 주문을 걸면, 해당 주문정보를 EIP 712 로 서명한 서명정보와 함께 전달하면, Relayer API 를 통해 해당 기록이 off-chain 상의 orderbook 에 기록되게 된다. 그 이후, orderbook 을 조회하여 여러 주문들의 쌍을 받아와 교환이 가능한 거래쌍을 선정하여 스마트 컨트랙으로 전송한다.(즉, 하나의 taker order와 하나 이상의 maker order 들을 전달하며, 모든 주문은 서명이 완료된 주문이다. 여기서, 전송을 하는 과정에 있어 주문에 필요한 다양한 정보들을 전부 parameter 로 전달하면 gas 비 문제등 많은 문제들이 존재하기 때문에, 거래에 필요한 데이터를 data 라는 필드로 만들어 OrderParam 에 포함하여 전달한다.
또한, 위 과정을 진행함에 앞서 실제 거래의 주체가 아닌 relayer 를 통해 자금을 교환하기 때문에 플랫폼에서 만들어둔 relayer account 를 approve 하여 일정 자금에 대한 사용권한을 주어야 한다.
스마트 컨트랙에 전달된 인자들이 들어오면, 컨트랙은 주어진 주문 쌍을 분석하여 거래를 진행하기 위한 Result 들의 집합으로 나누고, 해당 주문을 filled 라는 orderHash:amount 쌍에 기록한다.
Smart Contract
주문의 전달
모든 주문은 Order Server 내의 orderbook 에 기록되고, 매칭이 가능한 조합들을 모아 스마트 컨트랙트의 matchOrders 를 호출하며, 그 인자는 다음과 같다.
위에서 base token 과 quote token 이 무엇인지 헷갈릴 수 있는데, taker가 매도 혹은 매수를 할때 기준이 되는 토큰을 base token이라고 한다. 즉, taker 가 특 정 토큰을 구매한다고 하면 그 구매의 대상이 되는 토큰을 base token 이라고 하며, 판매를 한다고 하면, 판매의 대상이 되는 그 토큰을 quote token 이라고 한다.
또한 여기서 OrderSignature 는 브라우저가 생성하고자 하는 주문정보를 EIP 712 로 서명한 서명정보로 추후 스마트 컨트랙트에서 해당 사용자의 주문정보를 해시하여 위 시그내처를 통해 유효성을 검증하는 과정을 거친다.
인증과정에서의 데이터의 흐름
사용자의 orderParam 쌍 => param 의 data property 에서 order rebuild => EIP712 hash 를 orderParam 의 signature 로 validate
* 여기서 signature 의 목적은 무엇일까?
여기서 signature 는 사용자가 본래 요청하고자 했던 주문정보가 스마트 컨트랙트에서 체결되는 시점에도 해당 사용자가 요청한 주문과 내용이 맞는지 검증하는 것이다.
가령, DEX 에서 두 사용자가 거래를 진행할때 다른 사용자가 특정 사용자의 계정을 스마트컨트랙에 거래를 요청하는 경우, 주문정보가 동일하더라도 주문을 작성한 유저가 요청한 주문이 아니므로 거래가 이루어 져서는 안되며, 특정 주문내용을 바꾸어서 컨트랙트에 체결을 요청하는 등 다양한 악용을 막는 역할을 수행한다.
* EIP 712 란 무엇인가?
EIP 712 란 Ethereum Improvement Proposals 712 의 약어로 이더리움에서 향후 지원하게 될 다양한 제안들중 하나이다. 과거 사용자가 어떤 거래를 함에 있어 sign 을 할때에는 sign 의 대상이 되는 message 가 hash 화 되어 존재하기 때문에, 서명을 하는 사용자가 자신이 서명하는 내용에 대해 잘 알기 힘든 문제가 있었으며, 가령 유사한 해시값을 가진 피싱 사이트로 유도하여 사이닝을 유도한다던가 하는 다양한 위험에 노출되어 있었다.
이를 해결하기 위해 안전하고 값이 변조되지 않는 해싱을 보장하면서도 readability 를 가질 수 있는 서명방법을 고안하게 되었으며 그 제안내용이 EIP712 에 제안되었다.
EIP712 를 통해 사용자는 자신이 서명하는 정보에 대해 명확하게 인지할 수 있게 된다고 볼 수 있다.
EIP 서명의 절차에 대해 간략하게 소개하면, 먼저 서명을 하고자 하는 데이터의 형태를 정의하는데 이러한 typed structured data 를 먼저 정의하는 것으로 서명이 시작된다. 데이터의 정의가 완료되면, 여러 DAPP 들 사이에 구별되기 위한 domain separator 를 정하여 서명을 검증할 스마트 컨트랙트의 주소부터 version, salt 등의 데이터로 구성된다.
EIP 서명이 완료되면, 사용자는 signature 를 얻게 되며 추후 스마트 컨트랙트는 주문정보를 직접 해싱하여 사용자가 제공한 signature 로 validation 을 진행하여 유효성을 검증한다.
거래의 체결
matchOrders 함수가 호출되는 시점에 매칭된 오더들의 정보가 filled 에 orderHash:filledAmount 의 mapping 형태로 기록되며, 만약 주문이 체결되었다면 기존에 있던 주문들에 새로운 주문이 발생하면서 체결이 될 것이다.
여기서 기존에 있던 주문을 한 사용자를 maker 라 칭하며, 주문을 체결하는 주문을 발생시킨 사용자를 taker 라 명명한다.
또한, 거래의 특성상 taker 가 넣은 주문을 만족시키는데 필요한 하나 이상의 주문이 필요하게 되므로 주문이 매칭 될 때에는 하나의 taker 주문에 한개 이상의 maker 주문이 매칭된다.
Taker 의 매도 주문
taker 의 매도주문으로 인해 주문이 체결되는 경우의 시나리오는 다음과 같으며, n개의 거래쌍이 이루어 졌다고 전제한다.
maker 가 relayer 에게 만족하는 quote token 과 maker fee, maker gas fee(maker rebate fee 는 차감한다.) 를 n번 지급한다.
taker 가 maker 에게 만족하는 base token 을 n번 지급한다.
relayer 가 taker 에게 만족하는 quote token 중 taker gas fee 를 제하고 1회 지급한다.
하나의 컨트랙트 내의 속성들은 다른 컨트랙트에서 접근 될 수 없는 것이 기본이며, public 키워드를 명식적으로 사용함에 따라 다른 컨트랙트에서도 접근이 가능하게 된다.
또다른 접근 제한자로는 modifier 가 있는데, 이는 특정 함수에 지정하여 해당 함수를 호출하기 전에 자격에 맞지 않으면 접근을 제한 할 수 있는 기능을 제공한다.
1 2 3 4 5 6 7 8 9 10 11 12 13
contract AccessRestriction { modifier onlyBy(address _account) { require( msg.sender == _account, "Sender not authorized." ); // Do not forget the "_;"! It will // be replaced by the actual function // body when the modifier is used. _; } }
Data types
memory and storage
solidity 에서 memory 와 storage 를 이루는 데이터 chunk 의 크기는 32bytes 이다.
memory 란 모든 함수 call 마다 새롭게 할당되는 임시 데이터 저장소로 이 메모리에 읽기, 쓰기 등의 작업을 수행할 때마다 가스비가 올라간다.
variables
솔리디티에서 변수는 memory address 를 가르치는 포인터이다.
array
solidity 에서 array 의 첫번째 인자는 그 array 의 길이값을 저장한다.
Bytes
solidity 에서 bytes 는 동적으로 할당되는 byte array 이며, 그 크기는 32byte 이다. 또한, solidity 에서 변수는 memory address 를 가르치는 포인터이므로 해당 포인터로 부터 32 바이트를 더해주면 실제로 해당 bytes 의 값이 저장된 주소값을 얻을 수 있다.
1 2 3 4
b = new bytes(32); add(b, 32); // b의 값이 저장된 주소값
mstore(add(b, 32), x); // b=x
Events
Event 는 솔리디티에서 일종의 로깅의 역할을 수행한다. 이더리움 상에서 일어나는 다양한 활동들에 대한 내역을 추적하고, 사용자 데이터를 자정하기 위해 이더리움은 로그 시스템을 적용하였으며, Event 의 발생은 일종의 로그 형태로 저장된다.
DAPP 개발자는 이더리움의 RPC 인터페이스를 통해 이러한 이벤트들을 구독할 수 있다. Topic 은 이러한 이벤트들을 찾을 수 있도록 해주며, 가령 특정 컨트랙트에서 발생한 로그들을 듣는 등의 일들을 할 수 있다.
여기서 각 이벤트를 정의할 때 다음과 같이 indexed 옵션을 주면, 이렇게 구독을 하는 중에 해당 속성을 읽을 수 있다. 만약 indexed 옵션을 주지 않으며, ABI 인코딩된 데이터가 내려오게 된다.
이더리움에는 두가지 종류의 account 가 있는데, External Account 와 Contract Account 가 그것이다.
먼저 External Account 는 공개키 방식의 계정이며, address 역할을 하는 public key 와 nonce 로 구성이 되며, Contract Account 는 일반적인 smart contract 이다.
여기서 account 와 account 사이에 자금이 이동하면 그것을 transaction 이라고 말한다.
Transaction
이더리움에서 transaction 이란 하나의 EOA 로부터 다른 account 로 전송되는 message 를 포함한 signed data packet 을 의미하며 다음과 같은 정보들을 담고 있으며, EOA 로 부터 EOA 로 가는 경우는 이더를 송금하는 것을 내포하고, 만약 수신자가 Contract Account 라면 해당 contract 가 어떤 코드를 실행하도록 trigger 하는 것이 된다.
receipient
signature
value
data: which can contain the message sent to a contract
여기서 data 는 contract 가 실행에 필요한 정보를 들고 있으며 Contract ABI(Application Binary Interface) 에 따라 컨트랙트와 소통한다. 이 ABI 는 외부 와컨트랙트 간의 상호작용에 필요할 뿐만 아니라 contract 간의 상호작용에도 적용된다.
따라서 data 는 다음의 규약에 따라 인코딩 되어 사용되며 ABI(Application Binary Interface 에 따라 정의된다.
함수 선택자
data 의 함수 시그니처의 Keccak-256 (SHA-3) hash 의 첫 4바이트는 어떤 함수를 호출할지를 나타내며 function 시그니처로 부터 도출된다.
1 2 3 4 5 6 7
pragma solidity >=0.4.16 <0.6.0;
contract Foo { function bar(bytes3[2] memory) public pure {} function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; } function sam(bytes memory, bool, uint[] memory) public pure {} }
위의 예제에서 baz 함수를 호출하고자 하는 transaction 의 data의 함수선택자는 0xcdcd77c0 인데 이는 method 의 id 로 ASCII format 으로 작성된 function signature 인 baz(uint32,bool 의 Keccak hash(sha3) 의 첫번째 4 바이트이다.
함수 Parameter
또한 해당 함수의 parameter 로 69와 true 를 제공한다면 69를 32바이트 로 패딩한 0x0000000000000000000000000000000000000000000000000000000000000045 가 값이 된다. 0x0000000000000000000000000000000000000000000000000000000000000001
ABI(Application Binary Interface)
컴퓨터 과학에서 흔히 말하는 ABI 는 두가지 종류의 binary 프로그램 모듈 사이의 인터페이스이다. 쉬운 예로는 library 와 operating 시스템의 경우가 있는데, 하나의 라이브러리는 여러 시스템 상에서 동작해야 하므로 해당 소스코드가 다양한 운영체제에서 동작하기 위해서는 약속된 인터페이스가 필요하다. 또 한 예로는 유저에 의해 실행되는 프로그램의 예가 있는데 가령 여러 운영체제에서 해당 프로그램을 실행하여도 동작하기 위해서는 이러한 ABI 가 정의되어 있어야 한다.
이와 같은 맥락에서 이더리움 에서의 ABI 란 특정 함수가 이더리움에서 실행되기 위한 포맷팅의 규약으로써 이해될 수 있다.
아래는 스마트 컨트랙트 내에서 특정 데이터를 ABI 인코딩 하는 것을 보여준다.
1
abi.encode("AAAA")
위 코드는 아래와 같이 3단어로 구성된 96(32byte *3) 사이즈의 바이트 문자열을 리턴한다.
한 줄씩 살펴보면 먼저 첫번째 라인은 해당 문자열의 starting offset(32 in decimal) 를 32byte 문자열에 padding 한 것이며, 두번째 문자열은 인코딩 하는 데이터의 길이인 4를 32바이트 문자열에 padding 하여 나타낸다. 마지막으로 세번째 문자열은 실제 우리의 데이터인 “AAAA” 를 UTF-8 인코딩하여 32 바이트 문자열에 padding 하여 나타내어 준다.
위의 encoding 방식보다 다소 간편하게 인코딩을 하고 싶다면 abi.encodePacked("AAAA") 라는 함수를 사용할 수 있다. 이 함수는 32바이트보다 작은 문자는 그냥 해당 문자열을 바이트로 출력하고 32바이트에 padding 하지도 않는다.
한가지 흥미로운 사실이 있는데, keccak256 을 사용하여 hashing 을 할때 복수개의 인자를 전달하면 이것을 내부적으로 abi.encodePacked 함수로 인코딩 하여 해싱을 한다는 것이다. 현재는 복수개의 인자를 전달하면 warning 을 출력하기는 하지만, 알아두도록 하자.
다음은 Keccak256 에서 abi.encodePacked 를 사용하는 예이다. 아래의 두 hashing 은 정확히 동일한 역할을 수행한다.
일반적으로 webserver라 하면 http 요청이 들어오면 필요한 작업을 수행하는 서버를 의미합니다.
즉, 브라우저에서 읽을 수 있는 html 파일 등을 전달해 주는 역할을 합니다.
하지만 단순한 정적파일의 제공으로는 동적 파일 렌더링등을 수행할 수 없기 때문에 webserver는 요청을 받으면 웹서버에서 요청을 받아 그 요청을 외부 프로그램에 넘겨주면, 외부 프로그램이 프로그램 파일을 읽어 html로 반환하는 단계를 거치게 되며, 이것을 CGI 라고 합니다.
대표적인 CGI 로는 php fpm 등이 있습니다.
* FAST CGI(Common Gateway Interface) 란 무엇인가?
FastCGI는 상호 작용 프로그램을 웹 서버와 통신하기 위한 바이너리 프로토콜이다.
FastCGI는 초기 공용 게이트웨이 인터페이스(CGI)의 변형이다.
FastCGI의 주 목적은 웹 서버와 CGI 프로그램 간 통신 시 발생되는 부하를 줄임으로써 서버가 한 번에 더 많은 웹 페이지 요청을 관리할 수 있게 하는 것이다.
* php-fpm
nginx가 요청을 받아 php인 경우 cgi 인 php fpm에게 요청을 전달하고 거기서 php 코드를 실행한 뒤에 요청을 다시 받아온다.
Forward Proxy & Reverse Proxy
NGINX 는 Web Server 로써 일반적으로 기업에서 서비스를 배포할 때 reverse proxy server의 역할을 수행한다.
Nginx 에 대해 설명하기 전에 먼저 forward proxy 와 reverse proxy의 개념부터 설명하고자 한다.
먼저, forward proxy server란 사용자가 원하는 정보를 좀 더 빨리 받게 하기 위해 중간에 요청을 가로채는 것이라 볼 수 있다. 가령 target.com 이라는 주소로 어떤 정보를 받아오고 싶은 유저가 있다고 생각해 보자.
이 유저는 주소창에 target.com 을 입력하고 데이터를 받아올 것이며, 이 유저는 자신이 보고 있는 정보가 target.com 에서 보낸 정보라고 생각할 것이다. 하지만 대부분의 경우 유저가 받아온 정보는 실제로 target.com에 있는 정보가 아닌 중간의 프록시 서버에서 이전에 받아놓은 target.com 의 정보를 전달해 주는 것이다.
이러한 proxy server 는 많은 이점을 제공해 주는데, 가령 우리가 목적으로 하는 target.com 의 경우 우리의 ip 주소에 대해서는 알수가 없고, 우리가 사용하는 proxy 서버의 url 에 대해서만 알 수 있다.(하지만 대부분의 경우 browser가 우리의 정보를 제공하기 때문에 사실상은 다 알수 있다..!)
또한, proxy server 는 이미 알고 있는 정보에 대해서 해당 데이터를 cache 하기 때문에 추가적인 요청 없이 바로 우리에게 원하는 정보를 전달해주기 때문제 네트워크 속도의 관점에서도 더욱 빠르게 동작한다.
NGINX 는 이러한 forward proxy 의 기능도 수행할 수 있지만, 대부분의 경우 reverse proxy 의 용도로 사용되는데, 이는 http server 의 보안 문제 해결과 로그 수집 등 여러 이점을 가지기 때문에 사용된다.
가령, 사용자는 proxy.com 이라는 url 로 요청을 주고 실제 서버의 위치인 target.com url 을 유저로 부터 접근을 불가능 하게 막고, 해당 서버를 VPN 에 구성함으로써 보안을 높일 수 있다.
How does it works?
NGINX 서버는 nginx 그룹의 nginx 라는 유저로 모든 프로세스를 실행한다.
요청이 들어오면 nginx 는 해당 요청의 hostname 을 보고 설정된 서버들 중 어떤 서버를 사용할지 선택한다.
NGINX 에는 다양한 서버 설정을 할 수 있으며 이는 /etc/nginx/conf.d/ 디렉토리에 수많은 conf 파일의 설정들 중에 어떤 서버 설정을 사용할지 결정하는데 있어 hostname 을 사용한다.
여기서 일치하는 hostname이 있다면 해당 설정대로 serving 하되, 만약 일치하는 hostname 이 없다면 default server 를 사용하며 이는 다음과 같이 server 에서 listen 하는 설정에 default_server 태그가 붙어있는 서버를 선택한다.
1 2 3 4
server { listen 80 default_server; server_name example.net www.example.net; }
어떤 서버로 부터 serving 할지 결정되었다면, nginx 는 url 패스를 읽어들이고 이를 location 과 비교하여 일치하는 리소스를 찾는 작업을 거치게 된다. 때문에, nginx 에는 여러개의 location 을 설정할 수 있으며, 이 중에서 가장 긴 location path를 우선적으로 선택하게 된다.
기본적으로 nginx 는 /etc/nginx/conf.d/default.conf 에서 최초의 웹 서버 설정을 해주는데, 별도의 설정파일을 추가한 이후에는 default.conf 파일을 제거하거나 중복된 설정을 없애 주어야 한다. 그렇지 않으면 해당 파일이 default configuration 으로 동작하여 사용자가 한 설정을 덮어써서 설정 내용이 반영이 되지 않게 된다.
개발자는 다음과 같이 example.com.conf 와 같은 설정파일을 작성할 수 있으며 여러 개의 서버와 location 을 지정하고, error page 및 proxy 등 다양한 설정을 할 수 있다.
Serving static file and access to local file system
다음과 같이 특정 url 에 파일 시스템 경로를 mapping 함으로써 특정 url 이 정적 파일들을 서빙하게 할 수 있다.
1 2 3 4 5
server { location /images { root /var/data/images } }
위 코드는 /image 로 오는 요청을 file system 에 매핑하여 요청자가 직접 file system 경로를 통해 리소스를 받아올 수 있게 해준다. 만약 특정 파일 형태만 정적 파일 시스템에 접근하도록 하고 싶은 경우 다음과 같이 regular expression 을 통해 해결 할 수 있다.
1 2 3 4 5
server { location ~\.(gif|jpg|png) { root /var/data/images } }
Reverse Proxy
NGINX 서버는 간편하게 Reverse Proxing 을 할 수 있게 해 주며, 다음과 같은 간단한 설정을 통해 reverse proxy 서버를 구축할 수 있다.
위 예제는 localhost/example1/proxy 로 들어온 요청을 http://naver.com/example1 로 proxing 해주는 설정파일이다.
Subdomain
서비스를 출시할때 흔히 다음과 같은 서브 도메인 양식을 가지고 리소스에 접근할 필요가 있게 된다.
가령 관리자 페이지를 위한 admin.example.com 이라던가 혹은 개발서버를 위한 dev.example.com 과 같이 여러 subdomain 이 필요하게 된다. nginx 는 host name 을 인식하여 간편하게 subdomain 을 reverse proxing 해주며 다음과 같은 세팅을 통해 간편하게 이를 적용할 수 있다.
다음 예제는 localhost 환경에서 앞에 example1 이라는 prefix 를 주고, 이를 reverse proxing 해주는 설정파일이다.
1 2 3 4 5 6 7 8
server { server_name example.localhost location / { root /var/www/example2.com; }
}
SSL
많은 서비스들은 http 프로토콜의 보안을 위해 https 프로토콜을 사용하며 이를 위해서는 443 port 를 통한 SSL 레벨에서의 통신이 필요하다.
이러한 요청을 처리하기 위해서는 다음과 같이 443 port 를 ssl 레벨에서 listen 해야 한다.
1 2 3 4
server{ listen [::]:80; listen [::]:443 ssl; }
GZIP
NGINX 는 통신시에 데이터를 줄이기 위해 gzip 압축을 통한 리소스 압축을 제공한다.
AWS 에는 수많은 기능이 있고, 이를 실제 웹사이트에서 console 을 통해 제어하는 것은 개발자에게 매우 비효율적인 일이 아닐 수 없다. AWS 는 이렇게 사용자의 컴퓨터에서 AWS 상의 많은 기능을 제어하기 위해 AWS CLI 프로그램을 제공하며, 본 포스트에서는 AWS CLI 의 기본적인 사용법을 알아보고자 한다.
Installation
Mac
Mac OS 에서는 다음과 같이 pip 명령어를 통해 aws cli 를 쉽게 설치할 수 있다.
1
pip install awscli
Ubuntu
Authentication
AWS 는 현재 cli 프로그램을 사용하는 내가 어떤 유저인지 모르기 때문에 이를 AWS 에게 알려주어야 하며, 그것이 authentication 자체이다.
사용자의 PC에는 여러 AWS 유저들에 대한 정보를 저장할 수 있는데, AWS 는 매우 복잡한 유저 관리 체계를 가지기 때문에 이는 매우 필요한 일이다.(가령 특정 AWS 서비스의 조작을 위한 별도 Program user 를 생성하는 경우 여러 개발자가 하나의 계정을 공유하여 사용하는 것은 매우 빈번한 일이다.)
Command 를 입력할 때 특정 사용자임을 밝히기
아래 명령어를 통해 aws access key 와 secret key 를 등록하고 사용할 수 있다.
1
aws configure
다음과 같은 command option 을 통해 내가 어떤 사용자인지 알리고, 미리 configure 되어있는 해당 사용자의 정보로 authentication 을 진행할 수 있다.
1
aws [command] --profile 사용자이름
MFA 를 사용하는 경우의 인증
콘솔 계정의 경우 cli 를 통한 작업을 진행할 경우 MFA 를 적용했다면 Access Deny 된다.
이 경우 MFA 유저를 위해 별도의 임시 인증을 거쳐야 하는데 이는 다음 명령어로 해결이 가능하다.
Route53 은 AWS 가 제공하는 DNS 서비스이며, 실제 도메인을 구입하고 도메인의 routing 을 설정하고 subdomain 지정 등 많은 일을 수행할 수 있다.
Route 53은 도메인과 동일한 이름의 퍼블릭 호스팅 영역을 자동으로 생성한다. 퍼블릭 호스팅 영역이란 특정 도메인(예: example.com)과 그 하위 도메인(apex.example.com, acme.example.com)의 트래픽을 인터넷에서 라우팅하는 방식에 대한 정보를 담고 있는 컨테이너로, 호스팅 영역에 레코드를 생성하여 도메인 및 하위 도메인에 대한 트래픽의 라우팅 방법을 지정한다.
트래픽을 리소스로 라우팅하려면 호스팅 영역에 리소스 레코드 세트 라고도 하는 레코드를 생성해야 합니다. 각각의 레코드에는 도메인의 트래픽을 라우팅할 방법에 관한 다음과 같은 정보가 포함되어 있습니다.
이름
레코드의 이름은 Route 53을 사용하여 트래픽을 라우팅하려는 도메인 이름(예: example.com) 또는 하위 도메인 이름(예: www.example.com)과 일치합니다.
호스팅 영역에 있는 모든 레코드의 이름은 반드시 호스팅 영역의 이름으로 끝나야 합니다. 예를 들어 호스팅 영역의 이름이 example.com이라면 모든 레코드 이름이 example.com으로 끝나야 합니다. Route 53 콘솔은 자동으로 이 작업을 수행합니다.
형식
레코드 유형은 일반적으로 트래픽을 라우팅할 리소스 유형을 결정합니다. 예를 들어 트래픽을 이메일 서버로 라우팅하려면 [Type]을 [MX]로 지정합니다. IPv4 IP 주소를 가진 웹 서버로 트래픽을 라우팅하려면 [Type]을 [A]로 지정합니다.
값
[Value]는 [Type]과 밀접한 관련이 있습니다. [Type]을 [MX]로 지정하는 경우, [Value]에 하나 이상의 이메일 서버의 이름을 지정해야 합니다. [Type]을 [A]로 지정하는 경우, 192.0.2.136과 같은 IPv4 형식의 IP 주소를 지정해야 합니다.
로드 밸런서는 특정 서브도메인을 특정 타겟 그룹으로 보내도록 설정할 수 있으며, 이는 로드밸런서가 특정 프로토콜 통신을 위해 듣고 있는 포트의 rule 을 바꾸어 줌으로써 해결할 수 있다. 가령 80 번 포트 리스너에서 특정 서브 도메인을 특정 타겟 그룹으로 전달 할 수 있으며, 이때 반드시 타겟 그룹을 미리 설정해 두어야 한다.
타겟 그룹은 여러 인스턴스들과 특정 포트를 등록해 두는 것으로 이를 통해 로드 밸런서가 특정 타겟 그룹의 여러 인스턴스들에게 부하를 분산해서 할당한다.
AWS CLI
AWS 에는 수많은 기능이 있고, 이를 실제 웹사이트에서 console 을 통해 제어하는 것은 개발자에게 매우 비효율적인 일이 아닐 수 없다. AWS 는 이렇게 사용자의 컴퓨터에서 AWS 상의 많은 기능을 제어하기 위해 AWS CLI 프로그램을 제공하며, 본 포스트에서는 AWS CLI 의 기본적인 사용법을 알아보고자 한다.
Installation
Mac OS 에서는 다음과 같이 pip 명령어를 통해 aws cli 를 쉽게 설치할 수 있다.
1
pip install awscli
Authentication
AWS 는 현재 cli 프로그램을 사용하는 내가 어떤 유저인지 모르기 때문에 이를 AWS 에게 알려주어야 하며, 그것이 authentication 자체이다.
사용자의 PC에는 여러 AWS 유저들에 대한 정보를 저장할 수 있는데, AWS 는 매우 복잡한 유저 관리 체계를 가지기 때문에 이는 매우 필요한 일이다.(가령 특정 AWS 서비스의 조작을 위한 별도 Program user 를 생성하는 경우 여러 개발자가 하나의 계정을 공유하여 사용하는 것은 매우 빈번한 일이다.)
Command 를 입력할 때 특정 사용자임을 밝히기
아래 명령어를 통해 aws access key 와 secret key 를 등록하고 사용할 수 있다.
1
aws configure
다음과 같은 command option 을 통해 내가 어떤 사용자인지 알리고, 미리 configure 되어있는 해당 사용자의 정보로 authentication 을 진행할 수 있다.
1
aws [command] --profile 사용자이름
MFA 를 사용하는 경우의 인증
콘솔 계정의 경우 cli 를 통한 작업을 진행할 경우 MFA 를 적용했다면 Access Deny 된다.
이 경우 MFA 유저를 위해 별도의 임시 인증을 거쳐야 하는데 이는 다음 명령어로 해결이 가능하다.