// 에드센스

Ownable 컨트랙트

오픈제플린 라이브러리의 Ownable 컨트랙트

 

즉, Ownable 컨트랙트를 상속시키면

  1. 컨트랙트가 생성되면 컨트랙트의 생성자가 owner에 msg.sender(컨트랙트를 배포한 사람)를 대입한다.
  2. 특정한 함수들에 대해서 오직 소유자만 접근할 수 있도록 제한 가능한 onlyOwner 제어자를 추가한다.
  3. 새로운 소유자에게 해당 컨트랙트의 소유권을 옮길 수 있도록 한다.

의 역할을 수행할 수 있다. (일단 여기서 사용하는것들은 이렇다)

 

 

 


함수 제어자

함수 제어자는 함수처럼 보이지만, function 키워드 대신 modifier 키워드를 사용.

함수를 호출하듯이 직접 호출할 수는 없다.

함수 정의부 끝에 해당 함수의 작동 방식을 바꾸도록 제어자의 이름을 붙일 수 있다.

 

/**
 * @dev Throws if called by any account other than the owner.
 */
modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

이렇게 생김

 

 

contract MyContract is Ownable {
  event LaughManiacally(string laughter);

  // 아래 `onlyOwner`의 사용 방법을 잘 보게:
  function likeABoss() external onlyOwner {
    LaughManiacally("Muahahahaha");
  }
}

likeABoss 함수를 호출하면, onlyOwner의 코드가 먼저 실행된다. 그리고 onlyOwner의 _; 부분을 likeABoss 함수로 되돌아가 해당 코드를 실행하게 된다.

 

제어자를 사용할 수 있는 다양한 방법이 있지만, 가장 일반적으로 쓰는 예시 중 하나는 함수 실행 전에 require 체크를 넣는 것.

onlyOwner의 경우에는, 함수에 이 제어자를 추가하면 오직 컨트랙트의 소유자(배포한 사람)만이 해당 함수를 호출할 수 있다.

 

함수에 onlyOwner 키워드를 붙혀서 컨트랙트 소유자인지 확인하고 소유자인 경우에만 허용한다

--> 사용자들이 우리 컨트랙트를 마구 수정하지 못하게 하면서도 우리 디앱의 핵심적인 부분을 업데이트할 수 있도록 하는 방법이다

 

 

 

 

 


가스

솔리디티에서 사용자들은 함수를 호출할때마다 가스를 사용한다

 

솔리디티에서는 uint의 크기에 상관없이 256비트의 저장 공간을 미리 잡아놓기 때문에 하위 타입들을 쓰는 것은 아무런 이득이 없다.

예를들어, uint(uint256) 대신에 uint8을 쓰는 것은 가스 소모를 줄이는 데에 아무 영향이 없다.

 

하지만 struct 안에서는 다르다.

만약 구조체 안에 여러 개의 uint를 만든다면, 가능한 더 작은 크기의 uint를 쓰는게 좋다.

 

struct NormalStruct {
  uint a;
  uint b;
  uint c;
}

struct MiniMe {
  uint32 a;
  uint32 b;
  uint c;
}

// `mini`는 구조체 압축을 했기 때문에 `normal`보다 가스를 조금 사용할 것이네.
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30);

 

 

 

 


인수를 가지는 함수 제어자

함수 제어자는 사실 인수 또한 받을 수 있다.

// 사용자의 나이를 저장하기 위한 매핑
mapping (uint => uint) public age;

// 사용자가 특정 나이 이상인지 확인하는 제어자
modifier olderThan(uint _age, uint _userId) {
  require (age[_userId] >= _age);
  _;
}

// 차를 운전하기 위햐서는 16살 이상이어야 하네(적어도 미국에서는).
// `olderThan` 제어자를 인수와 함께 호출하려면 이렇게 하면 되네:
function driveCar(uint _userId) public olderThan(16, _userId) {
  // 필요한 함수 내용들
}

olderthan 제어자가 함수와 비슷하게 인수를 받는 것을 볼 수 있다. 그리고 driveCar 함수는 받은 인수를 제어자로 전달하고 있다.

 

 

 

 


View 함수는 가스를 소모하지 않는다

view 함수는 사용자에 의해 외부에서 호출되었을 때 가스를 전혀 소모하지 않는다.

이건 view 함수가 블록체인 상에서 실제로 어떤 것도 수정하지 않기 때문 - 데이터를 읽기만 하지.

 

함수에 view 표시를 하는 것은 

 

"이 함수는 실행할 때 자네 로컬 이더리움 노드에 질의만 날리면 되고, 블록체인에 어떤 트랜잭션도 만들지 않아"

 

라고 web3.js에 이렇게 말하는 것과 같다.

(트랜잭션은 모든 개별 노드에서 실행되어야 하고, 가스를 소모한다).

 

 

참고: 만약 view 함수가 동일 컨트랙트 내에 있는, view 함수가 아닌 다른 함수에서 내부적으로 호출될 경우, 여전히 가스를 소모할 것이다. 이것은 다른 함수가 이더리움에 트랜잭션을 생성하고, 이는 모든 개별 노드에서 검증되어야 하기 때문.

그러니 view 함수는 외부에서 호출됐을 때에만 무료다.

 

 

(pure는 블록체인으로부터 어떤 데이터도 읽거나 쓰지 않는다는 뜻)

 


storage

솔리디티에서 storage는 비싸다

storage 대신 함수가 종료될때 사라지는 memory를 사용하는것도 하나의 방법

function getArray() external pure returns(uint[]) {
  // 메모리에 길이 3의 새로운 배열을 생성한다.
  uint[] memory values = new uint[](3);
  // 여기에 특정한 값들을 넣는다.
  values.push(1);
  values.push(2);
  values.push(3);
  // 해당 배열을 반환한다.
  return values;
}

 

 

 

 

 

https://cryptozombies.io/ko/course

 

#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies

CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts

cryptozombies.io

 

'Block Chain' 카테고리의 다른 글

[BlockChain] 크립토좀비 레슨5  (0) 2023.02.28
[BlockChain] 크립토좀비 레슨4  (0) 2023.02.14
[BlockChain] 크립토좀비 레슨2  (0) 2023.02.11
[BlockChain] 크립토좀비 레슨1  (0) 2023.02.10
[BlockChain] Move 언어  (0) 2023.02.03

매핑과 주소 자료형

 

msg.sender: 전역변수, 현재 함수를 호출한 사람의 주소를 반환함

 

require: 특정 조건 검사(이프문은 따로있다, require는 if+revert 느낌)

 

솔리디티는 상속이 된다

https://bloccat.tistory.com/35

 

솔리디티에는 변수를 저장할 수 있는 공간으로 storage와 memory가 있다

 

storage는 블록체인상에 영구적으로 저장되는 변수

memory는 일시적으로 저장되는 변수

 

일반적으로 변수에 사용할때는 솔리디티가 기본적으로 

함수 외부에 선언되는 변수(상태변수)는 기본적으로 storage

함수 내부에 선언되는 변수는 기본적으로 memory -> 함수 호출 종료시 사라짐

으로 설정해준다

근데

구조체나 배열을 사용할때는 storage나 memory 키워드를 명시해야한다

 

 

다른 컨트랙트와 상호작용하려면 인터페이스를 정의해야한다

 

다음 코드는 크립토키티 컨트랙트의 getKitty 함수인데 얘랑 상호작용하려면 인터페이스를 정의해야한다

function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
) {
    Kitty storage kit = kitties[_id];

    // if this variable is 0 then it's not gestating
    isGestating = (kit.siringWithId != 0);
    isReady = (kit.cooldownEndBlock <= block.number);
    cooldownIndex = uint256(kit.cooldownIndex);
    nextActionAt = uint256(kit.cooldownEndBlock);
    siringWithId = uint256(kit.siringWithId);
    birthTime = uint256(kit.birthTime);
    matronId = uint256(kit.matronId);
    sireId = uint256(kit.sireId);
    generation = uint256(kit.generation);
    genes = kit.genes;
}

 

 

인터페이스는 이렇게 생김

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

중괄호부분은 날리고 리턴값까지만 정의하고 중괄호부분에 세미콜론으로 마무리한다

 

 

 

이 인터페이스를 활용하려면

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  // `ckAddress`를 이용하여 여기에 kittyContract를 초기화한다
  KittyInterface kittyContract = KittyInterface(ckAddress);
}
  function feedOnKitty(uint _zombieId, uint _kittyId) public {
      uint kittyDna 
      (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
  }

솔리디티는 리턴값이 여러개가 될 수 있기에 콤마를 저렇게 나열해서 getKitty 함수의 10번째 반환값인 genes만 뽑아왔다.

 

 

 

 

 

https://cryptozombies.io/ko/course

 

#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies

CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts

cryptozombies.io

 

다시 초심으로 돌아가 솔리디티부터 해볼려고한다.

걷지도 못하는데 어떻게 뛰겠는가!

그래서 크립토좀비를 한 챕터마다 정리하면서 진행해보려고했다만

첫 챕터는 그냥 완전 기본 문법(변수형 선언, 형변환, 구조체)이런것들이라 딱히 적을게 없긴하다.

 

 

 


 

 

 

그래도! 몇 가지 기존의 프로그래밍 언어와 조금 다른 부분을 정리해보자면

 

1. view

function sayHello() public view returns (string) {}

이런식으로 view라는 키워드를 쓴다.

얘는 솔리디티에서 상태를 변화시키지 않는다는 의미다.

즉, 어떤 값을 쓰거나 변경하지 않는다는 의미. only view만 하는 함수라는 의미.

 

-> storage를 읽을 수 있지만 변경은 안됨

 

 

2. pure

function _multiply(uint a, uint b) private pure returns (uint) {
  return a * b;
}

pure는 함수가 앱에서 어떤 데이터도 접근하지 않는것을 의미한다.

 

-> storage를 읽을수도 없고 변경도 안된다

 

 

3. 이벤트

// 이벤트를 선언한다
event IntegersAdded(uint x, uint y, uint result);

function add(uint _x, uint _y) public {
  uint result = _x + _y;
  // 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다:
  IntegersAdded(_x, _y, result);
  return result;
}
YourContract.IntegersAdded(function(error, result) {
  // 결과와 관련된 행동을 취한다
})

이벤트는 나중에 더 깊게 다뤄볼 것이고 일단 챕터1에서는 간단히 보고 넘어간다.

컨트랙트는 특정 이벤트가 일어나는지 "귀를 기울이고" 그 이벤트가 발생하면 행동을 취한다.

 

사용자단의 자바스크립트 코드가 IntegerAdded 이벤트를 듣고있다가 컨트랙트에서 실행되면 행동을 취한다.

 

 

 

 

 

 

https://cryptozombies.io/ko/course

 

#1 Solidity Tutorial & Ethereum Blockchain Programming Course | CryptoZombies

CryptoZombies is The Most Popular, Interactive Solidity Tutorial That Will Help You Learn Blockchain Programming on Ethereum by Building Your Own Fun Game with Zombies — Master Blockchain Development with Web3, Infura, Metamask & Ethereum Smart Contracts

cryptozombies.io

 

무브랭이 뭐고 솔리디티보다 뭐가 나은걸까?

 

 

 

무브언어는 리브라 스마트 컨트랙 프로그래밍을 위해 개발된 언어다

 

리브라는 페이스북이 발행한 결제용 암호화폐 -> 현재는 명칭이 바뀌어 디엠(Diem)이다

 

페이스북은 왜 결제용 암호화폐를 만든걸까? -> 국가가 발행하는 화폐를 대체하고 은행 계좌가 없는 수십억명에게 지불 네트워크를 제공하기 위해ㅇㅇ -> 지갑 이름은 "캘리브라"

 

리브라는 주요국의 법정통화와 채권을 담보로하는 안정된 스테이블코인이라는 강점을 내세웠었다.

 

하지만 각국 규제 당국들은 리브라를 전통 통화에 대한 도전이라고 여기어 강한 규제를 맥여버렸고 리브라는 디엠이 되었다

 

즉, 무브언어는 디엠코인 스마트 컨트랙 프로그래밍을 위해 개발된 언어이다

 

 


 

 

솔리디티랑 뭐가 달라?

 

솔리디티도 많이 안해보았기에.. 체감되는 부분은 없고 글로만 이해해보자면

 

  • 블록체인에서 사용되는 기존 스크립트 언어들의 한계 -> 코인을 매개하는 과정상 단순한 정수형을 사용한다는 점. 코인, 토큰을 규정하는 자료타입이 없다. 근데 무브는 있다(고한다 아직안써봄ㅎㅎ)
  • 무브는 지금까지 발생했던 스마트 컨트랙 관련 사고들을 참고하여 설계되었고 개발자의 의도를 쉽고 정확하게 표현할 수 있기에 예기치 못한 버그를 줄일 수 있다(고 한다)
  • 디지털 실물 자산에 성격을 부여하여 디지털 자산의 주인을 한명으로 규정하고 한번밖에 사용될 수 없도록하며 동일한 자산의 생성을 제한하는 기능도 있다(고한다)
  • 선언된 데이터는 해당 리소스를 선언한 모듈에서만 변경, 삭제할 수 있다(고한다)
  • 무브언어에서 리소스 객체는 소모될 뿐 복사는 되지 않는다(고한다)

 

eth면 eth지 weth는 모지?

 

랩드 토큰(Wrapped Token)이란 한 메인넷 기반 토큰을 다른 메인넷 위에서 사용할 수 있도록 래핑한 것을 의미

 

예를들어 비트코인은 이더리움 네트워크에서 사용할 수 없다.

하지만 비트코인을 래핑하여 WBTC로 만든다면 이더리움 메인넷 위에서 비트코인을 사용할 수 있다.

 

 

랩드 토큰은 중간관리자가 필요하다

 

  • 사용자는 중개인에게 1BTC를 전송한다(담보로 맡긴다)
  • 중개인은 1BTC 만큼의 가치를 가지는 1WBTC를 이더리움 네트워크상에서 생성하여 사용자에게 반환한다
  • 사용자가 중개인에게 보냈던(담보로 맡긴) 1BTC는 일종의 디지털 금고인 래퍼(Wrapper)에 보관
  • 사용자가 1WBTC를 가지고 볼 일을 마치고 다시 중개인에게 1WBTC를 보내면
  • 중개인은 1WBTC를 소각(burn)시키고 1BTC를 돌려준다

 

 


래핑의 장점

  • 트랜잭션 속도와 가스비 절약
    • 비트코인은 느리고 가스비 높지만 WBTC로 래핑하여 이더리움 메인넷에서 사용한다면 이더리움 메인넷의 트잭속도와 가스비를 적용받는다
  • 유동성 증대
    • 유동적으로 메인넷을 오가며 사용가능

 


래핑의 단점

  • 관리자에 대한 신뢰
    • 래핑과정이 완전히 탈중앙화가 이루어지지 않았기에 관리자(중개인)을 거쳐야한다는 리스크(결국 래퍼는 중앙화된 디비)
    • 담보를 보관하는 디지털자산 금고가 해킹당할수도있다
  • 가스비
    • 래핑 후에는 가스비가 적을 수 있지만 래핑과정에서 가스비가 소모됨

 

 

 

출처:

'어질어질 코인판' 카테고리의 다른 글

[Coin] 앱토스?  (0) 2023.02.01

앱토스란?

블록체인이 진정으로 대중화되기 위해서 L1의 보안과 확장성이 개선되어야한다는 문제의식에서 출발한 레이어1 블록체인 프로젝트다

 

가장 안전하고

확장성이 뛰어난 블록체인을 구축하는 것을 목표로 삼고있다

초당 1000건 이상의 빠른 트랜잭션 수를 자랑한다

 

앱토스는 페이스북(메타)의 블록체인 사업인 Diem의 핵심 인력들에 의해 시작되었다

 

앱토스 블록체인의 코드는 러스트기반의 언어인 무브로 작성되었다. 이더리움보다 빠르고 저렴한 레이어1 프로토콜을 지향한다

 

앱토스의 특징

1. 무브언어

토큰 정보와 스마트 계약을 기록할 때 데이터 복사 및 분실이 발생하기 어려운 방식으로 설정돼있다. -> 따라서 이중 지불, 소유권의 중복등 부정이 일어나기 힘들다는 이점이 있다. -> 솔리디티와 비교해도 안전성이 우수하다고 할 수 있다.

 

2. Diem BFT라는 컨센서스 알고리즘 사용

Diem BFT는 HotStuff라는 비잔틴 장애에 내성을 가진 프로토콜을 가지고있다.

 

앱토스의 생태계

 

 

 


 

왜 앱토스?

1. 페이스북(메타)에서 추진하던 블록체인 기반 결제용 자산 Diem을 기반 기술로 하고있다

2.  Diem이 페이스북 유저 30억명을 온보딩 할 수 있는 블록체인이 목표였던 만큼 큰 기대

 

 


블록체인 레이어란?

 

Layer0

데이터 전송과 채굴(표준 프로토콜, 생태계 환경)

블록체인이 서로 상호작용할 수 있도록 네트워크를 형성

 

Layer1

블록체인 플랫폼

비트코인과 이더리움 등

여기서 합의(PoW, PoS등)와 같은 작업과 블록 시간 및 분쟁 해결같은 과정이 발생

 

Layer2

속도와 확장(분산 아키텍처)

확장성과 초당 트랜잭션을 높이기 위해 사용되는 기술

폴리곤, 이더리움 라이트닝등

레이어1에서 처리할 양을 레이어2에서 일부 부담하여 레이어1의 처리부담을 줄여준다

 

Layer3

애플리케이션

소비자로서 우리가 상호작용하는 UI를 가지는 비즈니스 영역

디센트럴랜드, 유니스왑, 크립토키티 등

 

 


비잔틴 장애 허용(BFT)이란?

시스템 내부에 장애가 있더라도 문제가 되는 부분의 1/3을 넘지 않는다면 정상 작동하도록 허용하는 합의 구조

 

비잔틴 장군 문제

어떻게 하면 멀리 떨어져 있는 독립된 사람들이 특정 시점에 특정 행동을 하기전에 서로 의견을 일치할 수 있느냐 라는 문제

 

비잔틴 장애 허용 -> 장부를 유지하는 참여자 중 2/3 이상의 장부가 동일하다면 1/3의 장부가 일치하지 않더라도 2/3의 장부를 공식적으로 인정하는 개념

 

 

 

https://kr.beincrypto.com/learn-kr/%EC%95%B1%ED%86%A0%EC%8A%A4-%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90apt%EC%99%80-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A7%91%EC%A4%91%ED%83%90%EA%B5%AC/

 

앱토스 암호화폐(APT)와 네트워크 집중탐구 

앱토스는 무브 프로그래밍 언어를 사용하여 구축된 레이어1 블록체인이다. 또는 지금은 포기한 메타(Meta)의 디엠 블록체인 프로젝트의 개념적인 후계자라고 부를 수도 있다. 

kr.beincrypto.com

 

'어질어질 코인판' 카테고리의 다른 글

[Coin] 래핑된(Wrapped) 토큰?  (1) 2023.02.02


1. 느슨한 결합을 위한 노력

 

예시 정책.

1. 회원 도메인과 가족계정 도메인이 있다.

2. 회원의 인증정보가 초기화되면 가족계정에서 탈퇴된다.

 

 

 

1단계. 모놀리틱한 구조(그저 동기적인 코드로 구현)

회원의 인증이 초기화되면 -> 가족계정 서비스에서 탈퇴된다.

 

행위의 주체는 회원이다.

즉, 회원 본인인증 해제 로직과 가족계정 탈퇴 로직이 강한 결합을 이루고 있다.

 

 

 

 

 

2단계. 시스템의 물리적인 분리

유튜브 영상을 캡쳐해 온 것이라 발표자님 얼굴이 나온다ㅠ 출처는 밑에 정리해 두었다.

물리적으로는 분리되었지만 코드레벨의 호출이 HTTP 요청에 의한 호출로 변경되었을 뿐 여전히 강한 결합이다.

 

 

 

 

 

3. 비동기 HTTP 요청

비동기로 구현된 HTTP 요청은 쓰레드레벨의 의존을 제거할 뿐 여전히 강한 결합이다.

 

 

 

 

 

4. 메시지 큐를 사용한 요청

 

 

회원 인증 초기화시 가족계정 탈퇴를 요구하는 메시지를 발행함으로 물리적인 결합은 제거되었지만 논리적으로는 강하게 결합된 상태다.

회원인증을 담당하는 도메인에서 가족계정을 담당하는 도메인에서 해야할 작업을 명시하고 있으니까 말이다.

 

주의해야할 것.

요청을 위해 발행한 메시지가 대상 도메인에게 바라는 행위를 담은 메시지라면 그저 비동기 구조일 뿐,

우리가 다루고자 하는 이벤트가 아니다.

 

.

.

.

.

.

.

.

 

5. 드디어 느슨한 결합

메시지큐에 똑같이 메시지를 발행하였지만 4단계와 다른점이 있다면

회원 도메인에서 발행한 메시지는 가족계정의 탈퇴를 지시하는 메시지가 아니라 그저 "회원인증 초기화"를 알리는 메시지라는 점이다.

 

 

 

출처:

https://youtu.be/b65zIH7sDug

캡쳐본에서는 얼굴 가렸는데 썸네일에 대문짝만하게 나오네ㅋㅋ

 

 

 

 

 


2. 이벤트란?

여기서 말하는 이벤트는 다음과 같다.

밥을 먹고 설거지를 하는 서비스라면

밥을 먹고나서 "설거지를 하라"는 이벤트를 발행하는 것이 아닌

"밥을 먹었다"라는 이벤트를 발행하는 것이다.

 

 

다른 예시로,

 

 

경찰관이 자동차에게 "직진하라"는 이벤트를 발행한다면,

 

 

 

 

 

이렇게 차들이 많아지면 이벤트를 발행하는 경찰관에게는 부하가 걸리게 된다. 차 하나하나마다 발행을 해줘야하기 때문

 

 

 

 

하지만, 신호등은 우리에게 이벤트를 발행하지 않는다. 정확히 말하면 경찰관처럼 우리에게 직진하라고 지시하지 않는다.

 

그저 지금이 빨간불인지 파란불인지만 알려줄 뿐 자동차가 신호등을 관찰하다가 판단한다.

 

 

 

 

따라서 신호등은 자신의 상태만 메시지로 발행하고 후속 행동은 이를 관찰하는 자동차들이 판단하여 처리한다.

한마디로 줄여서

 

"주목할 만한 상태를 이벤트로 발행한다"

 

고 보면 될 것같다.

 

 

 

AWS에서 발생하는 거의 모든 동작은 이벤트를 발생시킨다. 다음은 EC2의 생명주기 변경 이벤트이다.

 

 

 

출처:

https://youtu.be/4aoKv545ViA

 

 

 

 

 


3. 이벤트 드리븐 아키텍쳐의 이모저모

 

이벤트 드리븐 아키텍쳐의 제약사항

  • 이벤트는 발생한 사건에 대해 약속된 형식 사용
  • 불변 : 과거의 메시지를 변경할 수 없음
  • 발생한 사건에 대한 결과 상태 전달
  • 생성자는 이벤트를 누가 처리했는지 이벤트 처리 상태에 관여하지 않음
  • 소비자는 이벤트를 누가 생성했는지, 생성자에 관심 갖지 않음

 

 

이벤트 드리븐 아키텍쳐 설계시 고려사항

Loosely coupling
  • 한 모듈이 따른 모듈에 너무 종속적이면 안된다.
  • 고려사항이라고는 하지만 비동기적인 프로그램을 하기 위해선 사실상 필수 사항

 

Removing dependencies 
  • 의존성을 제거
  • 의존성을 제거 해서 Loosely한 coupling 상태로 만들어야 함

 

Nonblocking transaction
  • 트랜잭션 단계에서 서로 영향을 주지 않도록 해야함.
  • 회원가입을 할때, 핸드폰 인증을 하고 이메일 인증을 순차적으로 진행을 하는데 이메일 인증에서 오류가 났다고 해서 핸드폰 인증까지 같이 취소가 되면 안됨

 

Asynchronized callback
  • 비동기 콜백은 필수는 아님
  • 그러나 사용의 편리성으로 가장 많이 사용

 

Fallback /Retry
  • 실패 했을 경우 어떻게 하고 어디까지 돌아가서 재시도 할것인가를 구분 지을 필요 있음
  • 딱히 정해진 방식이 없어서 설계자의 능력이 중요

 

Event logging
  • 비동기는 로그가 중구난방으로 찍히기 때문에, 이벤트 로그가 잘 찍히게 설계해야함.

 

 

 

 

이벤트 드리븐 아키텍쳐의 장단점

특징과 장점
  • Sevice간 호출이 많은 Microservice Architecture에 적합.
  • Infra Structure의 유연한 사용
  • Polyglot 구성에 용이 -> 모듈별로 서로 다른 언어와 DB 사용 가능
  • 가용성, 응답성 ↑ -> 효율성, 성능 ↑ -> 요청이 많아질 수록 성능 향상 기대
  • Fault Isolation : 에러가 생겨도 각각의 모듈에 독립적으로 생기기 때문에 다른 모듈은 정상 가동이 가능

 

 

단점
  • 설계 복잡도, 운영 복잡도 증가
  • Message Broker 의존성 증가 ->Fault Isolation의 장점이 있어도 모든 Message가 모이는 Message Broker 자체가 고장나면 시스템이 멈춰버림
  • 코드 가독성 하락 -> 눈에 익은 코드가 아니므로 코드 읽기가 어려움
  • 디버깅 난이도 상승 -> 스텝투스텝으로 동기적으로 따라갈 수가 없고, 중구난방으로 로그가 찍히기 때문에 어디서 고장이 났는지 파악하기 힘듬
  • log를 통한 system flow 가독성 하락

 

 

출처: https://be-developer.tistory.com/37

 

[빗썸테크아카데미] 첫번째 - OT, 동기/비동기와 Event-driven Architecture

[빗썸테크아카데미] 첫번째 - OT, 동기/비동기와 Event-driven Architecture https://be-developer.tistory.com/34 [빗썸 테크 아카데미] BE 심화 과정 최종 합격 후기 >< (+기술과제) [빗썸 테크 아카데미] BE 심화 과정

be-developer.tistory.com

 


1. 객체 정렬?

class Person {
    String name;
    int weight;
    int height;

    public Person(String name, int height, int weight) {
        this.name = name;
        this.weight = weight;
        this.height = height;
    }

    public void print() {
        System.out.println(name + " " + height + " " + weight);
    }

    public int getWeight() {
        return weight;
    }

    public int getHeight() {
        return height;
    }
}

여기 Person 클래스가 있다.

 

 

List<Person> list = new ArrayList<>();

Person 클래스로 이루어진 어레이리스트도 있다.

 

이 리스트를 정렬하고하자한다.

name을 기준으로 정렬을 해야할까 weight를 기준으로 정렬을 해야할까?

우리는 이 문제를 Comparable이나 Comparator를 통해서 해결할 수 있다.

 

 

 

 


1. Comparable

정렬하고자 하는 객체의 클래스에 Comparable 인터페이스를 구현하는 방법이다.

물론 그 클래스에 Comparable 인터페이스를 구현할 수 있다면 말이다.

// 정렬 대상 클레스에 인터페이스를 구현할 수 있다면 Comparable 사용 가능
class Person implements Comparable<Person> {
    String name;
    int weight;
    int height;

    public Person(String name, int height, int weight) {
        this.name = name;
        this.weight = weight;
        this.height = height;
    }

    public void print() {
        System.out.println(name + " " + height + " " + weight);
    }

    public int getWeight() {
        return weight;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public int compareTo(Person p) {
        // 오름차순: this 객체 - 인자로 넘어온 객체
        // 내림차순: 인자로 넘어온 객체 - this 객체
        return this.height - p.height; // 오름차순 정렬
    }
}
Collections.sort(list);

Comparable 인터페이스의 compareTo 메소드를 구현하면 된다.

위 코드는 height를 기준으로 오름차순 정렬의 예시다.

 

 

 

만약 1순위로 height를 기준으로 오름차순 정렬하고

height가 같은 객체가 있다면

2순위로 weight를 기준으로 내림차순 정렬하고 싶다면?

 

class Person implements Comparable<Person> {
    String name;
    int weight;
    int height;

    public Person(String name, int height, int weight) {
        this.name = name;
        this.weight = weight;
        this.height = height;
    }

    public void print() {
        System.out.println(name + " " + height + " " + weight);
    }

    public int getWeight() {
        return weight;
    }

    public int getHeight() {
        return height;
    }

    @Override
    public int compareTo(Person p) {
        // this 객체 > 인자로 넘어온 객체 => return 1이라는것은
        // this 객체 - 인자로 넘어온 객체 > 0 => 오름차순
        if (this.height > p.height) return 1;
        else if (this.height == p.height) { // height가 같다면
            // this 객체 < 인자로 넘어온 객체 => return 1이라면
            // this 객체 - 인자로 넘어온 객체 < 0 => 내림차순
            if (this.weight < p.weight) return 1; // weight를 내림차순으로
        }
        return -1;
    }
}

이렇게 하면 된다.

 

 

 

 


2. Comparator

정렬하고자 하는 객체의 클래스에 Comparable 인터페이스를 구현할 수 없을때 사용하는 방법이다.

혹은 Comprable 인터페이스를 통해 이미 정렬기준이 정해져있지만 다른 기준으로 정렬하고 싶을때 사용하는 방법이다.

 

// 정렬 대상 클레스에 인터페이스를 구현할 수 없다면
// 혹은 Comparable을 통해 이미 정해져있는 정렬 기준과 다르게 정렬하고 싶다면
class Person {
    String name;
    int weight;
    int height;

    public Person(String name, int height, int weight) {
        this.name = name;
        this.weight = weight;
        this.height = height;
    }

    public void print() {
        System.out.println(name + " " + height + " " + weight);
    }

    public int getWeight() {
        return weight;
    }

    public int getHeight() {
        return height;
    }
}

Person 클래스는 동일하다.

 

 

 

우리는 Comparator라는 일종의 정렬기준을 정의한 객체를 만들어서 Collections.sort()의 인자로 넣어주어야 한다.

 

    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }

 

Collections.sort 함수를 까보면 첫번째로 정렬 할 리스트와 두번째로 정렬 기준인 Comparator를 받는 것을 볼 수 있다.

 

 

 

Collections.sort(list, new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
    	// 오름차순
        return o1.height - o2.height;
    }
});

 

Comparator객체를 한번쓰고 버릴것 이니 익명클래스로 넣어주었다.

저 익명클래스의 객체는 Comparator 인터페이스를 구현받고있기에 compare 함수를 오버라이드하고있다.

 

 

 

정렬 기준이 2개 이상일 때도 Comparable과 동일하게 해주면 된다.

Collections.sort(list, new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
    	// height 기준 오름차순
        if (o1.height > o2.height) return 1;
        else if (o1.height == o2.height) {
        	// height가 같다면 weight 기준으로 내림차순
            if (o1.weight < o2.weight) return 1;
        }
        return -1;
    }
});

 

 

 

Comparator를 람다함수로 조금더 간단하게 표현할 수 있다.

Collections.sort(list, (a, b) -> a.getHeight() - b.getHeight());

 

 

 

stream을 쓰면 더더더 간단하게 표현할 수 있다.

list.sort(Comparator.comparing(Person::getHeight));

 

 

 

stream을 사용했을때 2개 이상의 기준을 적용하고 싶다면

list.sort(Comparator.comparing(Person::getHeight).thenComparing(Person::getWeight));

 

이렇게 thenComparing을 통해 이어주면 된다.

하나의 조건은 역순으로 하고싶다면 .reversed()를 붙혀주면 되는데 주의 할 것이

 

 

 

list.sort(Comparator.comparing(Person::getHeight).thenComparing(Person::getWeight).reversed());

 

이렇게 뒤에 reversed()를 바로 붙이면 height에 대한 정렬도 reversed()에 영향을 받아서 두가지 기준 다 내림차순 정렬이 돼 버린다.

 

 

 

두번째 기준만 내림차순으로 하고 싶다면

Comparator<Person> reverseCompare = Comparator.comparing(Person::getWeight).reversed();
list.sort(Comparator.comparing(Person::getHeight).thenComparing(reverseCompare));

 

이렇게 reverse용 Comparator를 따로 만들어서 넣어주어야 한다.

 

 

 

 

참고:

 

+ Recent posts