미리보기
기본 정보
대용량 시스템 디자인/개발 경험이 있습니다. 현재에 안주하지 않고 도서, 사내 기술문서/발표자료, 깃헙트랜딩, 레딧, 해커뉴스를 참고하며 자기계발 하고 있습니다.
기술 스택
Java, Go, Python, MySQL, Redis, Kubernetes, Kafka
경력
네이버
백엔드 개발 | Cue System | 재직 중
2023.09. ~ 재직 중 (1년 5개월)
생성 AI 검색 Cue: 백엔드 개발
다양한 LLM 및 사내 API를 연동해서 생성 AI 검색을 제공하는 백엔드를 Python으로 개발했습니다.
Cue: latency 개선
일련의 LLM 호출에서 불필요한 순차 호출이 있었고, 이를 멀티 스레드 병행 호출로 변경해서 latency를 개선했습니다. 문제 해결의 난이도는 쉬웠으나, 가장 먼저 문제를 인식하고 빠르게 해결 했으며 이를 통해 LLM의 느린 응답속도를 1~2초(쿼리 생성 갯수에 따라 변동) 가량 개선시킨 것이 주요했습니다.
더불어 LLM 별 서버와 http connection pool로 통신하는 과정에서 connection reuse가 안되는 문제를 발견했습니다. 그러나 최소 재현가능한 코드로 테스트 시 keep-alive 및 connection reuse가 동작하는 상반된 결과를 보였습니다. 추가 확인한 결과 이는 사내 웹서버 솔루션을 쓰는 과정에서의 버그였고 connection pool 제거로 해결 후 응답속도를 0.3초 개선시켰습니다.
Cue: 부하테스트 및 rate limit 적용
서비스 오픈 전/후로 모델러와 함께 E2E 부하테스트를 수행해서 모델 별 적정 GPU를 산정했습니다. 이때 확인한 지표를 기반으로 rate limit을 적용했습니다.
또한 기존 rate limit은 redis의 atomic 연산을 활용하지 않고 read/write 2개의 연산으로 구현되어 있었는데, 이 경우 redis가 단일 스레드로 동작하더라도 다수의 인스턴스가 동시에 rate limit이 풀리고 그 순간 rate limit을 초과하는 트래픽이 허용될 수 있으므로, INCR를 통해 이것을 막도록 수정했습니다. 또한 rate limit 용 key에 초단위 timestamp를 붙여서 hot key가 되는 것을 방지했습니다.
널리 쓰이는 envoy에서 동일한 방식으로 rate limit을 구현하고 있기 때문에 높은 트래픽에서도 유효한 방식이라 판단했습니다.
Cue: 대화(검색) 이력 저장소 이관
사내 개인정보 보호 규정을 준수하기 위해 저장소를 ElasticSearch에서 MongoDB로 이관했습니다.
기존 ElasticSearch 쿼리를 MongoDB에 맞게 수정했습니다. 이때 구현의 난이도 보다는 ES 쿼리의 용도와 구현이 잘 매치가 안된 것들이 존재해 크로스체크 하는 과정이 필요했습니다. 예를들어 exact-match search가 적절해보이는 스펙에서 full-text search를 쓰는 경우가 있었는데, MongoDB에서 full-text search에 한계가 있으므로 이점에 유의해 이관했습니다.
또한 기존 ES 쿼리는 클라이언트가 직접 구현하고 서버는 그대로 받아서 ES로 전달하는 SQL Injection 패턴이 존재했고, MongoDB에서는 서버가 쿼리를 관리하도록 일괄 리팩토링/배포 했습니다.
MongoDB에 기존 데이터 조회 패턴을 지원하기 위한 인덱스를 생성/관리 했습니다. 복합인덱스 생성 과정에서 ESR rule을 비롯한 최적화를 신경썼습니다.
네이버 파이낸셜
백엔드 개발 | 내자산&증권
2022.04. ~ 2023.08. (1년 5개월)
네이버페이 증권 백엔드 개발
14M MAU의 네이버페이 증권 백엔드를 Java, Spring Boot, MySQL, Redis로 개발 및 운영 했습니다.
네이버페이 증권 알림 개선
분 단위 배치 기반 주식 알림을 CDC, Kafka Connect, Kafka Streams를 통해 실시간 알림으로 개선 했습니다. 운영 당시 일 1500만건의 알림을 발송 했고 타사 대비 빠른 알림 속도를 보였습니다. 이 프로젝트는 문제 정의, 시스템 디자인, 개발 및 운영을 모두 리드했다는 점도 주요했습니다.
별도 기능이였던 관심종목과 알림을 합치게 되면서 알림 트래픽이 20배 이상 증가하는 상황이었고, 기존 분 단위 배치 기반 알림은 부하가 큰 쿼리를 불필요하게 반복 수행해야 하고 초단위 알림이 어려웠기 때문에, 실시간 변경분 분산 알림처리 방식으로 개선하게 됐습니다.
이 시스템을 설계할 때 가장 먼저 들여다본 것은 이벤트 순서/전달 보장이었습니다. CDC, Kafka Connect, Kafka Streams 각 컴포넌트 마다 체크가 필요했고 Apache JIRA와 Debezium 문서, debezium-connector-mysql 소스코드를 참조해 유저 A의 삼성전자 알림이 순서/전달 보장 가능하다는 것을 확인했습니다. 예를들어 debezium-connector-mysql로 MySQL binlog를 CDC를 할 때 binlog rotate 발생 시에도 exactly/at-least once가 보장되는데, 이는 MySqlStreamingChangeEventSource 클래스에 mysql::binlog::event::Rotate_event 관련 대응이 되어 있기 때문입니다.
다음으로 processor/generator/sender 3가지 역할별 모듈을 나누어 ETL 파이프라인의 개발/배포 용이성을 높였습니다. processor는 서로 다른 MySQL 테이블 스키마를 도메인 별로 통합된 스키마로 가공하는 역할입니다. 예를들어 국내/해외 주식을 하나로, 원자재/환율/지수를 경제지표로 통합합니다. generator는 도메인 별 로직 적용 및 알림 생성을 담당합니다. 예를들어 5% 상승 시 알림을 생성하며, 다른 규칙이 추가되거나 수정될 수 있습니다. sender는 생성된 알림을 네이버앱 통합 알림 시스템에 발송 처리합니다. 외부 API와의 통합과 client rate limit 같은 로직을 관리합니다.
Kafka Streams에서 삼성전자 등 인기 종목 알림의 skew를 해결하는 것도 주요했습니다. 아이디어는 삼성전자 가격을 단일 key로 관리하지 않고 삼성전자-0, 삼성전자-1 의 형태로 분할 관리하는 것입니다. 이때 유저 A의 삼성전자 알림설정은 항상 삼성전자-{num}으로 관리되어야 하므로, (userId, reuterCode)를 hash한 값 기준으로 할당했습니다.
마지막으로 Kafka Streams에서 유저알림설정(userId)과 종목가격(reuterCode)이 서로 다른 key를 가지지만 join이 필요했고, 이를 위해 custom partitioner를 사용했습니다. 이때 알림설정 key를 reuterCode:userId로 변환시키고 state store의 prefixScan()을 사용한 최적화된 join 적용했습니다. KTable-KTable non-key join 내부 구현이 이와 같은 구성으로 되어 있습니다.
증권사 수신 데이터를 네이버페이 증권 서비스 용으로 가공
국내/해외 증권사로 부터 수신한 데이터를 Spring Batch로 가공해서 서비스 테이블에 반영하고, API로 노출했습니다.
유지보수가 쉽지 않던 Spring Batch 2 기반 배치를 Spring Batch 5로 전환했고, 그외 사용중인 라이브러리 및 코드도 모두 갱신 했습니다. 기존에 bean 등록이 XML 기반으로 되어 있는 것과, Spring Batch 5 및 기타 라이브러리에서 더이상 유효하지 않은 클래스를 참조하는 것, 그리고 FTP를 통해 받은 전문 데이터가 json 같은 일반적인 형식이 아닌 점이 가장 까다로웠습니다. 라이브러리 내부 코드 및 공식 문서를 참조해서 수정하고, 같은 전문 데이터를 반복적으로 테스트하면서 동일한 동작을 보장하는데 집중했습니다.
크래프톤
백엔드 개발 | XTRM
2020.02. ~ 2022.03. (2년 2개월)
글로벌 모바일 게임 PUBG : NEW STATE 개발
PUBG : NEW STATE의 모든 마이크로서비스는 단일 인하우스 Golang 프레임워크를 통해 개발됩니다. 이 프레임워크는 codegen을 통해 gRPC 기반 마이크로서비스 boilerplate를 빠르게 생성할 수 있습니다. 서비스 init/start/terminate 등의 lifecycle을 지원하며 callback 등록이 가능합니다. Cache, database, pub/sub, message queue, filesystem 같은 기능을 AWS 인프라를 wrapping 해서 제공합니다. Goroutine을 적극적으로 활용해서 불필요한 순차처리를 최대한 제거합니다.
프레임워크 개발에 처음부터 참여했으며, 특정 기능을 도맡기 보다는 작은 작업 단위로 다양한 기능에 걸쳐 개발했습니다. 그 중에서는 filesystem 개발에 가장 많이 관여했습니다. 이것은 S3, local filesystem, in-memory 등 다양한 filesystem 구현체를 하나의 interface로 제공합니다. 그외에도 goroutine/channel 관련 버그들을 수정했고, 가독성이 떨어지는 goroutine 코드를 fan-in/fan-out 등의 관습적인 코드 기반으로 리팩토링해서 가독성을 높였습니다.
PUBG : NEW STATE 게임 기능 개발
파티/매칭/랭킹/친구/인앱알림 등 다양한 게임 기능 개발에 기여했습니다.
주요 문제 해결 중 하나로는 친구 서비스에서 follower/following 관리에 2-phase commit을 적용한 것입니다. A가 B를 follow 하는 순간, A의 following에 B가 추가되고 B의 follower에 A가 추가되어야 합니다. 즉 서로 다른 record 간의 트랜잭션이 필요한데 DynamoDB 분산 트랜잭션은 latency 이슈로 지양했습니다. 대신 pending/commited 2가지 상태로 follower/following을 관리해서 대응했습니다. 어떤 유저에 pending 중인 follower/following가 발생하면 보상 트랜잭션을 적용하는 방식입니다.
DynamoDB 분산 트랜잭션과 달리 Transaction Coordinator 등을 생략하고 간단하게 구현하여 latency가 낮습니다. 또한 특정 partition에 장애 발생 시, 2PC를 적용하지 않았을 때보다 데이터 일관성을 좀 더 보장해줍니다(heuristic). 대신 빌트인 분산 트랜잭션이 아니므로 최악의 경우 일시적으로 한쪽은 commited이고 한쪽은 pending 인 경우가 발생할 수 있는데, 이러한 일은 자주 발생하지 않으며 주기적인 배치 또는 A/B 간의 인터렉션이 발생할 때 일관성을 맞춥니다.
당시에 DynamoDB에서 비정규화된 단일 유저 document만 쓰기 위해 위와 같이 문제를 해결했으나, 정규화된 followers 또는 friendship 테이블에 단일 row로 follower/following을 atomic 처리하는 것도 좋아보입니다.
PUBG : NEW STATE kubernetes-native 세션 관리/배포 리팩토링
PUBG : NEW STATE의 게임 세션 관리 방식을 기존의 process 기반 관리에서 k8s CR과 Operator를 사용한 k8s-native 방식으로 개선했습니다. 수 십만 명의 동시접속자가 게임을 즐길 수 있도록 dedicated server를 scale out 할 수 있고, 유저가 적을 때는 비활성화 dedicated server만 scale in 해서 비용을 절감할 수 있습니다.
별도의 Golang 모듈로 동작하며 초기 버전 개발에 기여했습니다. 기능 개발 보다는 다양한 테스트 케이스로 dedicated server 생성/관리 할 때 발생하는 문제를 제보하는 형태로 기여했습니다. 예를들어 CR을 delete할 때 특정 상태 관리가 누락되어 delete 되지 않는 것, kubectl로 직접 pod를 삭제하면 CR이 삭제되지 않는 것 등이 있습니다. 또한 당시 k8s operator가 국내에서는 잘 활용되지 않아 레퍼런스가 부족했는데, 차세대 버전을 개발할 때 공식 문서와 해외 사례를 조사해 reconcile, custom deployment 같은 구현 방안을 제시해서 개발 방향을 잡는 걸 지원했습니다.
디플리
백엔드 개발 | 개발팀
2019.06. ~ 2020.01. (8개월)
음성 인식 API 서빙
아기 울음 소리를 분류 하는 모델 서빙 백엔드를 gRPC, Python, TensorFlow Serving으로 구현했습니다.
gRPC streaming 부하 분산 문제 해결
부하 테스트를 통해 k8s loadbalancer type service 및 tcp sticky session으로 인한 gRPC streaming 부하 분산 문제를 발견했고, 이를 해결하여 전체 인스턴스 평균 CPU 사용량이 30%에 머물렀던 것을 70%까지 끌어올렸습니다.
이 문제를 해결할 때 L7 load balancer를 사용하는 방법이 있으나 당시 GCP 에서 service mesh를 적용하지 않고는 이를 사용하기가 어려웠고, service mesh를 적용하는 건 오버 엔지니어링 이었습니다. 따라서 k8s headless service를 활용한 client side load balancing을 적용해서 간단하고 저비용으로 해결했습니다.
Kubernetes CI/CD 구현
Spinnaker로 Kubernetes CI/CD를 구현해서 매일 직접 수동 배포 하는데 낭비되는 30분 가량을 절약했습니다.
교육
광운대학교
대학교(학사) | 전자융합공학
2012.03. ~ 2019.02. | 졸업
대외활동
K Startup & OpenAI Matching Day
개인
OpenAI API 활용 분야에 '시각장애인을 위한 네비게이션 앱'으로 지원했습니다. 본선 진출하여 OpenAI 기술 스태프에게 현장 심사를 받았고, 최종 수상은 하지 못했지만 시상식에 참여했습니다.
외국어
영어
비즈니스 회화 가능
자기소개
자기주도적으로 일하며 꼼꼼하고 깔끔하게 업무 한다고 동료 평가를 받아왔습니다.
규모 있는 프로젝트를 리드하고 성공시킨 경험이 있습니다.
새로운 도전에 열려 있고 실제로 K Startup & OpenAI Matching Day 로 창업에 도전한 바 있습니다.