Bullshitting Blog

개소리하는 블로그

존버록

난 현 회사의 제품 1.0 버전이 성공하여 2.0 버전 개발을 완료한 후 설치를 앞두고 있던 시기에 입사했다. 2.0 버전은 B2B2C까지도 가능한 시스템을 목표로 하여, 고객사의 클라우드에 설치를 진행하는 IaaS 형태로 설계, 개발되었다.

그래서인지, 구조가 매우 복잡하였다.

먼저, 6개의 마이크로서비스로 서비스가 분리되었다. 그리고 각 서비스는 stateless한 구조로 개발되어, 쿠버네티스 클러스터에 디플로이되었다. 그리고, stateless한 서비스의 상태 복구를 위해 DB를 이용했는데, 복구만을 위해 이용하니, 그냥 팟으로 같이 올렸다. 마이크로서비스로서 동작하기 위해, 또한 작업 등의 상태 복구를 위해 persistence한 메시지큐도 필요했다. 그리고 이 메시지큐로 채택된 것은 Apache Pulsar. IaC도 이용했다. helm chart와 terraform을 구성한다. 로깅도 빠질 수 없다. Elasticsearch + Fluentbit + Kibana 스택을 이용한다. 장애대응도 해야 한다. Kibana에서 Cloudwatch 메트릭을 쏴서 고객사에 문제 알림이 가능하도록 세팅해 줬다.

아닛? 보안 컴플라이언스? 코드를 고객사 서버에서 빌드하는데 CI/CD로 해달라고? 고객사 jenkins에 세팅해 줬다. 파일 반출이 간단하지 않다. 그냥 남겨둠. 보안? 그러고보니...? 네, 방화벽 타이트하게 쪼아주세요. 정신없이 VPC와 subnet, 그리고 EKS, ES 등의 매니지드서비스의 엔드포인트들을 이어가면서 방화벽 작업도 진행했다. 보안은 끝이 없다. 서비스의 IP를 고정해주세요. 네? 쿠버네티스 클러스터의 IP를 고정하라고? nginx로 로드밸런서 켜서 어떻게든 고정해줌. 아니 또 이 로드밸런서 키니까 any allowed 방화벽 룰이 추가되네? 다시 쪼아줌.

...(후략)

눈치챘겠지만, 드러나는 한계 feat. 소규모 팀

하지만 갈수록 한계가 드러났다. 6개의 마이크로서비스, 메시지큐, 디플로이를 위한 IaC들, 고객사의 요청에 따른 자잘한 커스터마이징들... 그와중에 terraform은 신나게 만들어 놓고, 고객사에서 사용을 거부하여 결국 손으로 세팅해야 했다.

가장 문제는 인원이었다. 상기한 것들을 감당하는 사람의 수는 초기엔 6명, 퇴사와 충원을 거처 4명이 되었다. 보통 한 팀이 하나의 마이크로서비스를 맡는다고 알고 있는데, 이 팀은 한 명이 여러 개의 마이크로서비스를 관리해야 했다.

존버는 승리한다 feat. 3.0

관리포인트 줄이기

분산된 마이크로서비스 중 기능이 겹치는 서비스들을 합쳤다. 그 결과, 6개의 마이크로서비스는 3개로 줄었다. 또한, 필요없는 레이어를 날려버렸다. SI처럼 타사에 설치를 해주는 상태에서 IaC는 사치였다. 일단 나 말고 아무도 못알아봄. helm chart를 날리고 모두 kubectl로 바로 deploy 가능하게 yaml파일을 구성했다. terraform은 애초에 2.0 설치하면서 고객사가 거부하여 못쓰게 되어 구경도 안해봤으므로 패스. 그냥 갖다버렸다. 추가로, 모 증권사만 이용하는 클라우드는 해당 증권사 전용이므로, 온프레미스 환경을 표준으로 삼고 bare-metal 설치를 중심으로 관리하면서, 혹시 클라우드를 이용하는 경우 온프레미스 환경에서 필요한 부분만 바꿔서 이용하는 방향으로 변경하였다. 아 그리고, 클라우드를 이용하던 그 증권사도 결국 엄청난 비용에 경악하며 온프레미스로 전환하였다.

코드베이스 리뉴얼

마이크로서비스를 통합하면서 구조를 변경하는 김에, 담당하던 마이크로서비스를 완전 리뉴얼하였다. 당시 난 레이어드패턴에 대해 막 배워가던 시절이었다. 근데, 난 개념을 배웠을 때 아무렇게나 응용하는게 특기라서, 액터 구조랑 혼합했다. type, interface, actor, loop, util 이렇게 5가지 요소로 나누었고, actor는 데이터 계층과 연결되는 interface를 이용하는 역할과, 동기화 이슈가 있을 수 있는 리소스를 관리하는 역할을, loop에는 비즈니스 로직을 몰아넣고, util엔 길고 가독성 떨어지거나, 반복적인 로직을 함수로 빼다가 넣었다. type은 말그대로 type.

그 결과, 내가 짠 코드라서 그런건지, 아니면 비즈니스로직이 한 군데에 몰려있어서 그런지는 모르겠지만, 이전엔 1~2주 걸리던 피쳐들을 하루이틀에 쳐낼 수 있게 되었다.

설치 난이도 줄이기

고객사에 설치 작업을 할 때 진행되는 과정 중 가장 어려운 것은 쿠버네티스 설치이다. 인터넷이 안되는 환경으로 필요한 바이너리와 이미지를 한 번에 들고가서 진행해야 하기 때문이다. 그런데, 그동안은 이 준비 과정을 매뉴얼하게 진행하고 있었고, 무슨 비급같이 생긴 문서를 참고하며 진행하였다.

네 잘하고 있어요. 하지만 쿠버네티스는 버전업 주기가 빠르답니다? 하하하하ㅏ핳....

설치할 때마다 대작업이었다. 그래서 설치 준비과정을 자동화했다. 그 김에 그냥 인터넷 될 때 이용하는 설치 스크립트도 같이 만들어서, 사내에서 이용하는 테스트용 서버 구축도 쉽게 되도록 했다.

문제는 1.26버전부터 dockershim 지원 중단... 그래서 싹 다시 짰는데 내 개발PC와 CI 서버에만 세팅 성공하고 고객사 설치는 아직 실패해서 1.22버전에 머물러 있다... 하지만 이 버전은 공식지원 종료라고 알고 있어서, 조만간 업그레이드해야함.

존버 승리?

승리한 듯 하다. 난 한동안 편해졌다. 그리고 새 역할이 추가되었다. 어...?

아 싫진 않다. 원래 인프라쪽 담당이라 리서치와는 좀 멀었는데, 추가된 새 역할은 리서치의 학습과 연관된 일이라서, 뇌가 덜심심하다. 인프라는 이건 이런데 저건 저렇고 그래서 이걸 바꾸면 또 저게 어쩌고 하는 정신없는 와중에 정신줄 붙잡는 느낌인데, 이쪽은 문제푸는 느낌이다. 인프라 하다가 현타오면 리서치쪽 업무 건들다가 진빠지면 인프라 건드리고 하면 됨.

또한 서비스 안정성이 미친듯이 높아졌다. 원래 우리 서비스는 성능은 좋지만 그와 동시에 장애의 아이콘이었는데, 내심 앞으로가 좀 기대된다.

얻은 것

  • 서비스 안정성
  • 직무능력
  • 처우개선

잃은 것

  • 2.0을 유지하다가 번아웃되어 사라진 팀원들
  • 눈알의 수분
  • 건강

배운 점

  • Simple is the best
  • 유지보수를 신경써서 설계하자
  • 힙해보이면 일단 다시 한 번 생각해보자
  • 이력서 쓰기 좋은 테크스택은 이런거구나

시스템 트레이딩 도전

핀테크 분야에 있다보니, 개발을 진행할 수록 금융에 대해 알아야 할 것들이 많아져서 내친김에 기본 지식도 갖출 겸 트레이딩 서적을 구매했다. 하지만 책만 읽으면 재미없을까봐 내친 김에 시스템 트레이딩에 도전하기로 했다. 그리고, Python 쓰면 재미없을까봐 내친 김에 Rust로 개발하기로 했다. 그리고, 트레이딩 서적을 그냥 읽기만 하면 기억이 안날 수 있으니, 계산식들을 직접 구현하고 있다. 그리고, Rust 개발자로서... 왠만하면 라이브러리가 없다는 고충을 잘 알기에, 계산식들은 물론 사용할 증권사의 OpenAPI 호출 로직까지 따로 빼내어 클라이언트 라이브러리로 만드는 중이다.

겸사겸사 끝판왕인 듯 하다.

아 물론, 메인 전략은 private이다. 안알랴줌. 물론 아직 존재하지도 않는다.

나름의 계획

  1. 일단 주식 현금주문, 정정/취소주문, 시세 OpenAPI 정상 작동하도록 완성(korea-investment-api)
  2. OpenAPI의 시세 스트림으로부터 trading-toolkit의 각종 수치들을 계산해서 HTS의 수치랑 비교해서 맞는지 확인, 틀리면 버그픽스
  3. 라이브러리 문서화
  4. 트레이딩 책에 나온 대로 아주 기본적인 전략 하나 구현 -> 평가 시작
  5. 고유한 전략 제작 -> 평가 반복

알리지 않고서는 못베기는 삽질

주식 현금주문 response의 status가 500으로 뜨는데 body조차 출력되지 않는 초유의 사태가 발생했다. 설상가상으로, 시니어인 팀장님이, OpenAPI 관리가 생각보다 개떡이라, 문서와 다른 경우가 있어 하나하나 찾아야 한다는 정보를 주셨다. 그래서 각종 auth 관련 리퀘스트도 싹 다 확인하고, 파라미터 대소문자, 언더바 바꿔보기 등 별의 별 짓을 다해보고 있었다.

윈도우 가상머신을 띄워서 과거 API 신청한 내역까지 다시 살펴봐도 별다른 문제는 없어 보였다.(그나저나 고작 API 신청한 계좌가 뭔지 보는데 윈도우 켜게 만드네. 화가 난다. 개발자센터는 왜 있는거임? 쓸 기능도 없는데 로그인은 왜 함?) 그러던 중, 그냥 curl로 호출해 보기로 했다. 왜 진작 안해봤을까. curl로 리퀘스트를 날리니, 사실 body가 있었다. 그리고 계좌번호가 틀렸다는 꿀정보를 담고 있었다.

띠용? 하고 다시 고생스럽게 켠 윈도우에서 계좌번호를 확인해보니, 뒷자리 3개가 달랐다. 그리고 그제서야 전에 모의투자 계좌를 재발급받았던 것이 떠올랐다............

그럼 이 문제의 원흉을 다시 색출하면 어느 놈일까.

답은 내가 짠 소스에 있다.

println!("{:?}", response);

잡았다 요놈!

response의 자료형에서 derive했을 Debug trait 탓이다. Debug trait을 impl하면서 fmt를 구현할텐데, 여기다 body를 안넣어놓은 것으로 보임. 그래서 header 까지만 딱 출력되고 body는 저기 어디 숨어서 코빼기도 보이지 않았던 것이었다.

결론: 사실 이 문제의 원흉은 진작에 curl 안 때려본 본인이다.

Rust를 업무에 사용하면서 느끼는 점들

현재 다니고 있는 회사의 소속팀에서는 Rust를 주 언어로 채택했다.
도입하신 분이 말씀하시길, 속도를 포기하지 않을 때 쓸 수 있는 언어는 C, C++, Rust 정도인데, C, C++은 개발자 마다 스타일이 천차만별이라서 Rust를 채택했다고 한다. 물론, 그 이외에 보안성 등은 말할 것도 없을 것이다.

여튼, 이 언어를 근 2년 가까이 사용하다 보니, 거의 적응이 된 듯 한데, 뭔가 불편하면서 편하다. 역설적인데, 딱 이렇게 느끼는 중이다. 애증의 관계이기도 하고. 근데 내 성격엔 이게 맞는 듯 하다.

1. 컴파일러가 일거수일투족 감시하면서 (딴지를 건다 / 가르쳐 준다[v])

이 컴파일러는 단순히 이걸 바이너리로 빌드했을 때 프로그램으로서 잘 돌 것인가만 중요한게 아니다.
다른 언어였으면 런타임에서 오류가 발생할 것들도, rust는 일부 잡아줄 수 있을 정도로 빡세다. 그리고 이건 rust의 소유권과 라이프타임 개념에서 오는데, 쭉 써오면서 느낀 점을 단순히 표현해 보자면, 아나바다 운동을 철폐하라는것이다. 아껴쓰고 나눠쓰고 바꿔쓰고 다시쓰다가 취약점 발생해서 쉘 뜯기지 말라는거.

그래서 가끔 정말 귀찮으면 클론 범벅의 코드가 된다. 이것도 신경 좀 쓰면 최소화 할 수 있는데, 당장 급해서 막 짜다 보면 뭔 문장 끝마다 clone을 남발하고 있다. 만약 C/C++이랑 퍼포먼스 비교했을 때 Rust가 더 떨어진다면, 아마 이 클론들 때문일 듯 하다. clone을 하지 않는 바이너리는 서로 비교했을 때 거의 똑같지 않을까 싶다.

그래서, copy를 잘못하거나, 포인터를 잘못 이용해서 뜻밖의 곳에서 수정이 일어나는 등의 사소하고 찾기 힘든 것들은 왠만하면 거의 일어나지 않는다. 덕분에 지금까지 발생한 버그들은 대부분 비즈니스 로직이나, Network 문제, 비동기 이슈 이 세 가지 안에서만 일어났다.

반대로 불편한 점도 있는데, 라이브러리를 만들 때이다. 최근 회사에서 actor 구조를 라이브러리로 만들었다가 재밌어서 왠만한 돌려쓸 수 있을 것 같은 것들은 다 라이브러리로 빼서 만들고 있는데, 제네릭, 트레잇, 소유권, 라이프타임 이 네 가지가 콜라보되니까 난이도가 급상승한다. 심지어 컴파일러도 여기까지 오면 자기도 헷깔려서 핵심 원인을 안짚어주는 경우가 많다. 그래서 컴파일러에서 뱉는 오류 메시지들의 패턴을 익히는 중이다.

개인적으로는 '가르쳐 준다'에 한 표 던진다. 경력이 2년밖에 안되는 쩌리 개발자가 여따 대고 딴지 건다고 하면 이건 오만이다. 지금 회사가 첫 개발 커리어를 시작한 곳인데, 사수 시스템이 따로 없다. 그런데 업무 지시/지도를 해주시는 팀장님과 함께 사수 역할을 도와준 것이 바로 rust 컴파일러다.

2. 라이브러리가 많지 않다

이제는 개발을 시작할 때, 라이브러리 찾는데 시간을 별로 할애하지 않는다. 실무에서 rust를 사용하는 케이스가 아직 많지 않다 보니, 알아서 구현해야 하는 경우가 꽤 있다. 한국투자증권 API도 마찬가지 이유로 시작된 프로젝트다. "한투 분들이 python으로 예제코드까지 방대하게 잘 구현해 놓으셨지만, rust 사용자는 python은 죽어도 쓸 수 없다며 알 수 없는 오기에 사로잡혀 스스로 API 클라이언트를 개발하게 되었다." 식의 스토리가 꽤 자주 있다. 그래서 시스템 트레이딩 전략을 만들기는 커녕 아직도 라이브러리단 개발만 이어지고 있다. 근데 뭐 이거 당장 안끝낸다고 밥 못벌어먹는 것도 아니고, 일단 재밌으니까 쭉 이어서 하고 있긴 하다.

문제는 이렇게 개발이 늘어지는걸 실무에서는 용납하지 않는다. 그래서 그냥 본인이 미친듯이 빨라져야 함. 근데 개인적으로, 갈수록 빨라져서 지금은 딱히 불편하지 않다. 다만 내가 짠 라이브러리를 돌려쓰는데 문제가 없을지 걱정되긴 한다. 아무리 라이브러리로써 개발한다고 해도 내가 담당하는 마이크로서비스에 알게모르게 맞춰지기 마련이기 때문이다. 게다가, 나중에 만약 이직하거나 퇴사하게 된다면, 인수인계를 했을 때 계속 쓸 지도 의문이다. 만약 퇴사 타이밍에 남아있을 rust 개발자들의 실력이 올라오지 않으면 제네릭, 트레잇, 매크로를 이해해서 라이브러리를 유지보수하는 것보다 각 마이크로서비스에서 따로 개발하는게 빠를 수도 있다. 물론 지금 팀원들이 그때도 계속 남으면 인수인계 쌉가능일 듯 하다.
근데, 라이브러리를 직접 만드는게 재밌긴 하다. 뭔가를 만들려고 할 때 제약이 사라진다. 일단 라이브러리 없다고 포기하는 개발자는 아니게 되었다. 컴퓨터 뺨을 쎄리며 개발하는 느낌이다. 안되면 되게 하라.

3. C/C++과는 다른 개념으로 높은 적응 난이도

적응하기가 쉽지 않다. C/C++은 스타일이 너무 다양하다는 것, 그리고 메모리 누수와 취약점이 쉽게 발생하는게 어려운 요소라면, rust는 컴파일 조건이 너무 까다로워서 실행조차 해볼 수 없다는 것이 어려운 요소이다. 소유권, 라이프사이클은 그 다음의 문제이다. 비컴파일 언어를 좋아하는 개발자는 가장 극혐하는 언어가 될 가능성이 높아 보인다. 로그 찍어서 보는 것조차 허용되지 않으니, 구조를 잘 짜고, 뇌내에서 잘 기억하면서 짜야 한다(잘 메모해 두던가). 물론 그걸 해결해주는 것이 테스트코드이기 때문에, 어떤 경우엔 TDD에 아주 적합한 언어가 될 지도 모르겠다. 하지만 컴파일이 되지 않는 상태에서는 테스트코드도 실행이 안되므로, 작게 작게 완결된 코드를 짜야 한다. 안그럼 지옥을 맛볼 것이다. 덕분에 개발 습관은 잘 잡히는 것 같다고 생각한다. 아님 말고.

대충 요---런 특징들이 있는데, 이 세 가지 모두 나는 좋아한다. 일단 1번은 이미 이유를 설명했고, 2번의 경우, 원래 내가 라이브러리 대충 익혀서 가져다 쓰는걸 좋아하지 않는다. 라이브러리를 써서 빠르게 결과물을 내는 것은 회사 입장에서 아주 중요하겠지만, 개발자로서는 '라이브러리 사용법 빨리 익히기', '라이브러리 빨리 찾기'와 같이 나이가 먹으면서 밀려날 수밖에 없는 능력만 기른다는 생각을 떨칠 수가 없다. 게다가, 이런 능력을 자신의 무기라 할 수 있을까? 토익과 같을 것이라 생각한다. 이건 당연히 어느정도 잘 해야 하는 것이고, 그 다음 무언가가 있어야 그 이상으로 올라갈 수 있을 것이라 생각한다. 아 그리고, 라이브러리 써서 빠르게 결과물을 내는게 중요한 단계의 회사라면, 십중팔구 초기 프로덕트를 만들거나 이것저것 MVP 양산하면서 되는거 하나를 찾기 위한 회사일 가능성이 높은데, 확률상 제대로된 회사는 많지 않을 것이다. 그 회사에 인생을 걸어보겠다는 각오가 없다면, 이런 능력을 요구하는 회사는 더 철저한 검증을 해봐야 할 것이다. 토익점수만 요구하는 회사는 수상할 수밖에 없다.
3번의 경우, 마찬가지로 내 무기가 될 수단으로서 손색이 없기 때문에 선호한다. 어려운 만큼 따라오기도 힘들다. 물론 너무 어려우면 시장에서 사장될 수 있겠지만, 그럴 수준은 아니다. 메모리 취약점 없는 성능좋은 프로그램을 개발하는 수준에 도달하는데 1-2년 걸린다고 생각해보면, 오히려 rust는 쉬운 언어라고 해야 한다고 생각한다. 단지 러닝커브가 지수함수라서 처음에 느릴 뿐이다.

결론: X같은 rust 만만세