반응형
안녕하세요, 오늘은 제가 2년 전에 했던 사이드 프로젝트 'nomadcoin-rs' 경험을 소개해 볼까 합니다. 작업한 지 2년도 더 된 프로젝트이지만 블로그에서 한 번도 다뤄본 적이 없어 다루어 보려고 합니다. 해당 프로젝트는 '노마드 코인: Go로 암호화폐 만들기'라는 노마드 코더의 온라인 강의를 Rust로 클론 코딩하는 프로젝트였습니다. 해당 프로젝트의 메인 언어는 golang이었지만 Rust에 대한 관심이 높았고, 막 공식 문서 공부를 끝낸 참이어서 언어에 좀 더 익숙해지기 위해 Rust로 프로젝트를 진행하기로 결심했습니다.
프로젝트 목표
해당 프로젝트를 진행함에 있어서 저는 크게 두 가지 목표가 있었습니다.
- 블록체인과 그것을 어떻게 만드는지 기술적으로 이해하기
- Rust라는 언어에 익숙해지기 (Be rustcean), Rust로 TDD 진행해 보기
프로젝트 히스토리
- 간단한 Blockchain 생성하기 (4강)
- 위의 작업에서 간단한 블록체인
struct
와functions
을 만들었습니다. - 데이터를 해싱하는 기능을 간단하게 구현하였습니다.
- 위의 작업에서 간단한 블록체인
- Rocket 라이브러리로 Explorer HTML 웹페이지 만들기 (5강)
- 어떤 라이브러리를 사용할지 고민하던 중 가장 쉬워 보였던 Rocket을 사용하였습니다.
- Rocket으로 Rest API 구현하기 (6강)
- 강의에서는 explorer와 RestAPI를 모두 지원하도록 프로젝트를 구성했지만 프로젝트 구조에 어색함을 느껴 RestAPI만 메인 포인트로 지원하도록 하였습니다.
- 강의에서 니코쌤은 Block에 대해서 포인터(Reference)를 사용했지만 Rust에서는 Lifetime 이슈가 있어 이를 구현하기 어려웠습니다. 많은 시행착오를 거치다가 Korean Rust Discord에 질문을 했고, 대부분의 경우
clone()
을 사용하는 것이 그렇게 비싸지 않고 최적화는 나중에 하는 것이 좋다는 답변을 얻어clone()
으로 이를 구현했습니다.
- Blockchain 스냅숏과 Blocks를 저장하기 위해 데이터베이스를 사용 (8강)
- Blockchain의 자격 증명(Proof of work) 개념을 적용해서 코인 채굴 (9강)
- testutils을 통해 각 테스트시에 데이터베이스 드롭
- 각 테스트 이후 데이터베이스 리소스를 드롭하고 싶었습니다. 그래서
DBResoource
라는 struct를 만들었고Drop
trait을 사용하여 이를 가능하게 하였습니다. 이런 방식으로 데이터베이스를 초기화하는 것이 좋은 인터페이스라고 생각되지 않았습니다. 그래서 나중에는/tmp
에 테스트마다 다른 DB를 생성하도록 처리하였습니다. (이때 당시에 베스트 프랙티스를 찾고 싶었는데 아직까지 찾아보지 않았네요.)
- 각 테스트 이후 데이터베이스 리소스를 드롭하고 싶었습니다. 그래서
- Blockchain 네트워크에서 transaction 구현 (10강)
- 작동하게 만들기 굉장히 어려웠지만 어떻게든 해냈습니다.
- 강의에서는 golang이 레이스 컨디션 이슈가 있었는데, Rust는 동시성 이슈가 있는 상황에서 Rust는 컴파일 타임에
mutex
잠금을 강제했기 때문에 상당히 안전한 언어라고 느껴졌습니다.
- transaction 검증을 위해 지갑 구현 (11강)
- P2P 기능 구현 (12강)
- 강의에서는 websocket을 사용하여 이를 구현했습니다. 하지만 당시에 Rocket 라이브러리에서 websocket을 지원하지 않았습니다. 이때 이를 알고 멘붕이 왔는데 기지를 발동하여 SSE(Server Sent Event)를 통해 이를 구현하였습니다. 좀 더럽게 구현하게 된 측면이 있었습니다.
- 비동기를 지원하기 위해 tokio 라이브러리를 사용하였습니다.
- 비동기 상황에서 lifetime을 컨트롤하기 위한
Arc
에 대해서 좀 더 자세히 알게 되었습니다.
- p2p 기능 리팩터링
- 코드 적응력을 높이기 위해 이벤트 핸들러들이 같은 인자들을 받도록 하였습니다.
- 전체 코드와 테스트 코드 리팩터링
- 저장소 계층을 만들고, 테스트를 위해 테스트 더블을 사용하였습니다.
- immutables를 mutables로 빌리기 위해서
Mutex
나RefCell
을 사용해야 함을 알게 되었습니다.
- [Dockerfile, docker-compose 생성 및 http 파일 리팩터링]
- 강의에서는 다루지 않았지만, 도커 환경에서 해당 프로젝트를 띄우고 싶어 이를 만들었습니다.
- First steps with Docker + Rust블로그를 참고하였습니다.
결과 및 교훈
- 무사히 해당 프로젝트를 완수했습니다. 꽤 오랜 시간이 지났지만 마지막 커밋을 넣고 문서를 작성하면서 기뻤던 기억이 생생하게 납니다. 처음 목표했던 2가지 내용도 달성했습니다.
- 해당 프로젝트를 마무리하고 '노마드코더'에 해당 프로젝트를 제보하였고 뉴스레터에서 소개되기도 하였습니다.
- 이후에 flopha라는 git versioning cli를 Rust로 만들기도 하였습니다. (아직도 잘 쓰고 있어요.)
- 다른 언어/프레임워크로 강의내용을 구현한 경험이 매우 좋았습니다. 보통 강의를 들을 때 아무 생각 없이 코드를 따라치게 되는데 다른 언어나 프레임워크를 쓰게 되면 작동원리를 이해하고, 고민할 수밖에 없기 때문에 흡수가 더 잘되었습니다.
2년간 잊고 있던 프로젝트인데 복기하면서 그 때의 감정도 느껴보게 되고 최근 하고있지 않던 Rust에 대한 열의도 불타오르네요. 조만간 Rust로 사이드 프로젝트를 하나 더 해볼까 합니다 😎
반응형
'개발 > Rust' 카테고리의 다른 글
[TIL] Rust 공부 : Attribute, Modules, Null in Rust 등 (0) | 2020.10.18 |
---|---|
[TIL] Rust에서 자동화된 테스트 작성하기 (1) | 2020.06.20 |