본문 바로가기

Project/기능 개발

Web3j 라이브러리를 활용한 이더리움 통신 Spring boot 프로젝트

개요

 최근 블록체인 기술에 흥미가 생겼다. 빠르게 블록체인 생태계를 경험해보고 싶어 DApp을 개발해보려 한다. 흔히 DApp은 Truffle, Hardhat 프레임워크 상에서 Solidity 언어로 Smart Contract를 작성하고 배포한 뒤, 자바스크립트 라이브러리 web3.js를 통해 스마트 컨트랙트를 호출하여 동작한다.

 

 하지만 이번 프로젝트는 블록체인 생태계에 대한 빠른 이해를 위해 진행하는 것이다. 따라서 Java 백엔드 개발을 해왔던 나에게 있어 개발 편의성 및 효율성을 따져, Web3j 라는 자바 진영 이더리움 라이브러리를 사용해보았다. 다음 프로젝트에서 Smart Contract를 작성해 Klaytn 블록체인의 Baobab 테스트넷에 배포하고, web3.js 자바스크립트 라이브러리로 호출해보려 한다.

 

요구사항 정의 및 API 설계

요구사항

 이더리움 블록체인에서 블록, 트랜잭션, 월렛 데이터를 조회할 수 있는 간단한 어플리케이션을 개발해보자.

 

API 설계

설명 URI METHOD
메인 화면 / GET
블록 데이터 조회 /search/block POST
트랜잭션 데이터 조회 /search/transaction POST
월렛 데이터 조회 /search/address POST

 

화면 설계

  1. index 화면
  2. 세가지 타입의 search 결과를 보여줄 result 화면
  3. error 화면

 

구현

SearchController

 API 설계 대로 SearchController 핸들러 메소드를 4개 작성했다. SearchService를 통해 조회한 이더리움 데이터를 View에 전달한다.

@Controller
@RequiredArgsConstructor
@RequestMapping
public class SearchController {
    private final SearchService searchService;

    @GetMapping
    public String index() {
        return "index";
    }

    @PostMapping("/search/block")
    public String searchBlock(@RequestParam(name="blhash")String blockHash, Model model) {
        model.addAttribute("map", searchService.getBlockByHash(blockHash));
        return "block";
    }
    
    // 생략
}

 

SearchService

 SearchService는 인터페이스로 두었는데, 추후 이더리움이 아닌 다른 블록체인에 대해 조회할 경우를 고려했다. 클레이튼 데이터를 조회하고 싶다면 이 인터페이스를 구현해 기능을 확장할 수 있다. 또한 메서드 반환 타입을 Map<String, String>을 두었는데, 이는 Controller에서 View로 전달할 attribute를 바로 반환하기 위함이다.

public interface SearchService {
    Map<String, String> getBlockByHash(String blockHash);
    Map<String, String> getTransactionByHash(String txHash);
    Map<String, String> getAddressByHash(String addrHash);
}

 

EtherService

 SearchService를 구현한 EtherService는 Alchemy node 서비스를 통해 이더리움 데이터를 조회한다. 주석으로 번호를 매긴 부분을 살펴보자.

@Service
public class EtherService implements SearchService {

    @Value("${alchemy.api}") // (1)
    String api;
    Web3j web3;

    @PostConstruct
    private void initialize() {
        web3 = Web3j.build(new HttpService(api.toString()));
    } // (2)

    @Override
    public Map<String, String> getBlockByHash(String blockHash) {
        Map<String, String> attributes = new HashMap<>();
        EthBlock.Block block = null;

        try {
            block = web3.ethGetBlockByHash(blockHash, true).send().getBlock();
        } catch (IOException e) {

        } // 예외처리는 프로젝트 목적에서 벗어나기 때문에 스프링 MVC 제공 RestControllerAdvice를 따로 적용하지 않았다.

        if (block != null) {
            attributes.put("blockHash", block.getHash());
            attributes.put("parentHash", block.getParentHash());
            attributes.put("miner", block.getMiner());
            attributes.put("stateRoot", block.getStateRoot());
            attributes.put("size", block.getSize().toString());
        }

        return attributes;
    }
    
    // 생략
}

 

  1. @Value
    • @Value를 사용하면 application.properties 또는 yml에서 값을 가져올 수 있다.
    • @Value를 통해 프로퍼티 값을 가져오는 시점은 Bean 객체 내 다른 필드들이 초기화 된 이후이다.
  2. @PostConstruct 적용 이유
    • 위 사진처럼 @PostConstruct를 적용하지 않으면 Web3j를 초기화 할 때 api 프로퍼티 값이 비게 된다. 결국 HttpService의 생성자에 String의 기본 초기 값 null이 전달된다.
    • 때문에 @PostConstruct를 적용해 api 프로퍼티 값을 가져온 이후에 Web3j 필드를 초기화해 HttpService 생성자에 null이 대입되는 것을 방지할 수 있다.

 

결과 및 느낀 점

결과 화면

index 화면

 Let's scan Ether 토이 프로젝트를 도커 이미지로 build 하고 EC2 에서 run 시켜 배포했다. 현재는 EC2의 퍼블릭 ip에 예전에 발급해뒀던 도메인을 적용했다. 아래는 트랜잭션 해시를 통한 조회 성공 화면과 조회 실패 시의 결과 화면이다.

 

transaction 조회 결과
조회 실패 결과

 

느낀 점

 이 프로젝트는 사실 Alchemy를 통해 이더리움 네트워크 상의 데이터를 조회한 것에 불과하다. 하지만 Web3j 라이브러리를 이해하는 과정에서 블록체인 자체에 대한 개념 학습과 블록체인 기술 생태계를 간접적으로 체험할 수 있었다.

 

 다음 목표는 Truffle, Hardhat 과 같은 프레임워크 상에서 Solidity로 스마트 계약을 작성 및 배포하는 것이다. web3.js 자바스크립트 라이브러리로 계약을 호출해 DApp 개발을 해보려 한다. 클라우드의 경우 ALB를 통해 EC2와 Internet Gateway 간 직접적인 연결을 끊어내고 HTTPS를 적용하는 것이 다음 스텝이다.

 

Reference