2023년을 떠나며
영화 “이터널 선샤인”에서 주인공 조엘은 옛 연인 클레멘타인이 자신에 관한 기억을 지워버렸다는 것을 알고 자신도 클레멘타인에 관한 기억을 제거하려 한다.
기억 제거 시술이 진행되는 의식 세계 속에서 조엘은 클레멘타인과의 기억이 소중했음을 깨닫고, 사라져가는 기억을 붙잡기 위해 클레멘타인을 데리고 도망치기 시작한다.
비록 힘들고 고통스럽더라도 누군가를 기억하고, 누군가에게 기억된다는 사실은 굉장히 소중한 것임을 영화는 말하고 있다. 조엘과 클레멘타인의 행복하고 행복했던 장면들이 오버랩되며 넷플릭스에게 뺐겼던 내 정신은 이윽고 현실로 돌아온다.
이렇게 소중한 기억을 나는 소중하게 여기고 있었는가? 스스로에게 되물으며 생각에 잠긴다. 평소 사라지면 좋겠다고 생각했던 기억이 있었기에 이 영화의 감상은 다른 영화에 비해 꽤 오랜 여운이 남았다.
거창하게 어그로를 끌었는데, 아무튼 이런저런 이유로 이번 연말에는 2023년 소중한 기억들을 글로 남겨보고자 한다.
11~12월
이직한지 두 달이 다되어 간다. 베일에 싸인듯한 회사의 바깥 이미지와 달리 여태 다녔던 회사들 중에서 가장 훌륭하고 따뜻한 온보딩을 경험하고 있는 중이다.
새로 합류하게된 팀에서는 지금까지 데이터 엔지니어가 없었기에 데이터 분석가와 사이언티스트 분들이 직접 데이터 파이프라인을 개발하고 있었다. 그렇다 보니 구현된 배치 로직 전반에서 성능이나 비용보다는 데이터 품질에 훨씬 더 많은 신경을 쓰고 있다는게 느껴졌다. 데이터를 사용하는 소비자 관점에선 어떤 기술을 썼느냐 보다는 데이터를 믿을 수 있느냐가 중요하기에 결국 데이터 엔지니어로서의 가장 큰 책임은 신뢰 가능한 데이터 사용 환경 구축임을 다시 한번 느낄 수 있었다.
따라서 이곳에서의 내 첫 임무는 데이터 파이프라인을 신뢰성 있게 개선하고 데이터 마트를 개편하는 작업이 되었다. 지금까지 데이터팀에서 쌓아두었던 엔지니어링 과제를 하나씩 해결하고 있으며, 그 과정 속에서 회사의 도메인과 데이터에 익숙해지고 있다.
한가지 문제가 있다면 바로 언어(자연어)인데, 현재 속한 팀은 다국적으로 구성되어 있어 영어로 의사소통하고 있다. 덕분에 학창시절에 열심히 하지 않았던 나를 열심히 원망하며 뒤늦게 영어 공부를 열심히 하는 중이다. 가끔 들리지 않는 악센트로 인해 벽을 느낄 때도 있지만.
8~10월
마침내 다니고 있던 회사를 퇴사하기로 마음 먹었다. 충동적으로 결정한 것은 아니고, 퇴사해야만 하는 이유를 밤새워가며 정리해 내린 결정이었다. 사실 한켠에는 계속 남아있고 싶은 마음도 없지 않아 있었지만 회사도 딱히 붙잡고 싶어하는 것 같지 않아 떠나기로 결심하였다.
퇴사 후에는 시간 맞는 친구들과 베트남 다낭과 일본 삿포로로 여행을 갈 예정이다. 그전까지는 회사를 다니면서 배우고 느꼈던 점, 스스로 개선해야할 점들을 정리하는 시간을 가지려고 한다.
3~7월
일하기에 너무 좋은 날씨이다. 회사에서 제법 다양한 업무들을 소화하였다. 개중에는 평소 관심있던 기술 스택을 사용해볼 기회도 많았는데 이번 기회에 쭈욱 정리해보려고 한다.
1. Recommender System
주로 추천 서버를 개선하는 작업을 진행하였다. 서버 개발에 사용하는 언어는 Kotlin으로 통일되어 있었고 IO를 효과적으로 non-blocking 처리하기 위해 Webflux를 사용하고 있었다. 그런데 추천 서버의 복잡성이 커지다보니 서비스 계층의 깊이가 깊어지면서 코드의 가독성이 떨어지고 디버깅이 힘들어지는 문제점이 발생하였다.
따라서 우리는 Reactive 방식을 유지했을 때 이점이 되는 부분을 제외하곤 나머지를 Kotline Cororutine으로 전환하기로 결정하였다. 아예 새로운 서버를 띄워 Coroutine으로 작성된 코드를 배포한 후 API Gateway를 통해 엔드포인트, 즉 트래픽을 차례대로 이관하는 작업을 진행하였다.
프로젝트를 진행하는 동안 Redis 캐싱이나 필터를 개선하거나 nGrinder를 통해 성능을 테스트 하는 등 벡엔드의 많은 부분을 공부하고 직접 개발해 볼 수 있어서 만족스러웠다.
2. Data Engineering
평소 관심 있던 Snowflake, Airbyte, OpenMetadata 등 데이터와 관련된 여러 기술 스택을 PoC하는 기회를 얻어 진행하였다. 우리가 이미 사용하고 있던 추천 모델 학습-추론 파이프라인이 있었는데 그 일부를 Snowflake로 대체하는 작업이었다.
Data Ingestion 파이프라인은 Spark를 이용해 원천 DB로부터 S3에 적재한 후, COPY를 통해 Snowflake의 stage로 떠오는 방식으로 개발하였다. 이후 Snowflake에서 간단한 쿼리와 UDF의 조합으로 추천 모델을 개발하여 추천 서버에서 사용하는 MySQL로 reversed ETL하는 파이프라인과 연결하였다.
결과적으로는 AWS Redshift를 온디멘드로 사용하는 것보다 더 많은 비용이 예상되어 도입하지 않기로 결정하였다. 성능이나 권한 관리 측면에서 Snowflake가 만족스럽긴 했지만 사용한 만큼 요금을 내야 했기에, 비용을 아끼기 위해서는 데이터 수집과 배치 스케쥴링 전략에 상당한 생태계 지식과 숙련도가 요구되었다. 스키마 추론이나 에러 헨들링이 간편하다는 점은 매우 만족스러웠다.
개인적으로 Databricks나 Snowflake와 같은 데이터 클라우드가 발전할수록 데이터 엔지니어의 역할이 줄어들거라 생각해 헷지 목적으로 Snowflake 주식을 매입하고 있다. 그리고 물타기 중이다
3. A/B Test Framework
회사 임직원들 누구나 A/B 테스트를 진행해볼 수 있도록 실험 플랫폼 및 프레임워크를 자체 개발하였다. 처음으로 프론트엔드 개발자와 협업한거라 재미도 있었고 성장한다는 느낌이 들어 좋았다.
Kotlin Coroutine과 JPA, Spring Boot, Redis에 딥다이브할 수 있었는데 이에 대한 내용은 다른 글을 통해 다루어보려 한다.
4. Airflow Dependency
현재 회사에서는 다양한 팀이 Airflow를 통해 배치 잡을 스케쥴링 및 트리거하고 있다. 만약 PythonOperator를 사용하고 있다면 Executor가 Job을 수행하기 위해 필요한 파이썬 디펜던시를 Airflow 환경에 설치하고 관리해야 했다.
그런데 Airflow에서 파이썬 디펜던시를 추가하고 수정하는 것은 절대 쉬운일이 아니다. Job이 실행되고 있는 와중에 stop-the-world가 발생할 수 있고, 다른 팀이 사용하고 있거나 심지어 Airflow에 내장된 파이썬 디펜던시와 충돌이 일어날 수도 있다.
따라서 이 문제를 해결하기 위해서는 실행시키고자 하는 로직을 도커 이미지 등의 형태로 배포하고, Airflow Executor가 이 이미지를 외부에서 실행하도록 변경해야 했다. 회사에서는 이미 Kubernetes를 잘 활용하고 있었기 때문에 KubernetesPodOperator를 이용해 손쉽게 문제를 해결할 수 있었다.
뿐만아니라 파이썬 디펜던시 뿐만 아니라 모든 팀이 공유할 수 밖에 없었던 리소스 권한들도 namespace 별로 격리 및 관리할 수 있다는 이점이 있었다.
프로젝트는 다음과 같은 순서로 진행되었다.
- Airflow DAG에서 사용하고 있던 서비스 로직을 별도의 레포지토리로 이관
- 도커 컨테이너에서 서비스 로직을 실행할 수 있도록 entry point 작성 및 이미지 생성
- 서비스 로직이 담긴 이미지를 저장할 ECR Repository 생성 및 CI/CD 구축
- 미리 taint를 묻혀둔 노드에서 도커 이미지를 실행할 수 있도록 Airflow의 KubernetesPodOperator에서 toleration 구성
5. Vector Database
이전에 검색어 추천 시스템을 개발한 적이 있었는데, 이때 RoBERTa 모델로부터 추론한 후보 단어의 벡터를 서빙하기 위해 faiss라는 라이브러리를 이용했었다.
살펴보니 faiss 이외에도 벡터 유사도 계산을 지원하는 다양한 프레임워크들이 있어 이를 테스트하고 벤치마킹하였다.
- 알고리즘: HNSW, LSH, IVF-PQ
- 프레임워크: faiss, pgvector, milvus, redisearch, lance, ScaNN, hnswlib
어떤 프레임워크는 디스크에서 매번 데이터를 읽는 반면, 어떤 프레임워크는 메모리에 올려두고 계산을 하기 때문에 단순히 처리 속도만 비교하는 것은 적절치 않았다. 게다가 precision과도 trade-off가 있기 때문에 같은 수준의 정확도에 도달하기까지 희생시켜야 하는 정도도 천차만별이었다. 따라서 측정한 지표들은 참고만 하고 확장성이나 편리성에 중점을 두어 벤치마킹을 진행하였다.
1~2월
검색어 추천 시스템을 개발하는 업무를 주로 진행했다. 데이터 사이언티스트가 설계한 모델을 주기적으로 학습시키는 배치 파이프라인과 학습이 완료된 모델을 서빙하는 API 서버를 개발하였다.
FastAPI와 HuggingFace 등 이미 잘 알려져있는 프레임워크들을 사용하였기 때문에 개발하는데는 크게 어렵지 않았으나 이해관계자들에 의해 일정이나 아키택처가 여러번 바뀌는 일이 있었다. 그리고 아무래도 자연어 처리의 영역이다보니, GPU core 하나로는 어려움이 있어 분산 학습을 도입했었는데 이 과정도 순탄치는 않았다.
결과적으로 프로젝트의 좋지 못한 성과에 대한 책임은 1차적으로 데이터 엔지니어에게 돌아왔다. 모델 설계에 문제가 없는지를 그 외 나머지 영역, 그러니까 학습 배치와 추론 서버가 의도한 대로 동작하고 있는지를 점검함으로써 검증해야 했고, 만약 모델이 수정되기라도 하면 데이터 엔지니어가 나머지 영역을 매번 다시 개발하고 점검하는식으로 모델을 검증해야 했다.
이 문제를 해결하는 한가지 방법은 데이터 사이언티스트가 본인이 개발한 모델의 생애 주기를 끝까지 관리하는 것이다. 하지만 이는 곧 모든 데이터 사이언티스트가 머신러닝 엔지니어가 되어야 한다는 가혹한 의미이기도 하다. 실제로 주변에 물어보니 데이터 사이언티스트가 Airflow나 BentoML과 같은 도구를 직접 사용하는데 생각보다 어려움을 겪는다는 의견도 많았다.
이러한 고민에 대해서는 연차가 쌓이면서 언젠가 나 스스로 실마리를 풀 수 있길 바라본다.