Lacti's Archive

About me

서버리스를 지향하는 서버 프로그래머입니다. 시스템 아키텍트를 지향하기 때문에 시스템의 구조와 설계를 많이 공부하고 있습니다.

  • Server programming 쪽에서는 Distributed actor system과 concurreny control을 통한 성능 최적화를 좋아합니다.
  • 시스템 디자인을 공부하기 위해 다양한 시스템에 대해 많이 읽어보고 토론하기 위해 노력하고 있습니다. 정리도 하려고 마음만 먹고 있습니다.
  • 취미 프로그래밍을 좋아하기 때문에 지인들과 잉여톤을 주기적으로 하고 있고, 이 때의 인프라를 지원하기 위해 어느 정도의 AWS engineering을 같이 하고 있습니다.
  • 무의미한 패턴 반복이나 중복이 없고 간결하면서도 의미를 명확하게 전달할 수 있는 프로그래밍을 지향하고, 연습하고 있습니다.

GitHub | Twitter | LinkedIn

경력

VoyagerX #

스타트업의 특성 상 하나의 프로젝트를 계속 진행하지는 않았고,

  • 작은 단위의 프로젝트를 짧은 기간 내에 완료하거나
  • 특정 프로젝트의 구축을 돕거나
  • Backend나 모니터링 시스템을 구축하거나
  • 자동화된 배포를 구축하거나
  • 사내 서버나 클라우드 인프라를 관리하는 일을 진행하였습니다.

웹 데이터 크롤러 개발

NLP 학습 등을 위해 대량의 한국어, 일본어, 영어의 텍스트 데이터를 수집할 일이 있었습니다. 사내에서는 주로 Python을 사용했으므로 처음에는

  • Python3으로 수집기를 만들고
  • robots rule을 지키고URL scheduling을 위해 RabbitMQ를 사용하고
  • MySQL에 저장하는 구조로 시스템을 작성하였으나,

Synchronous IO를 사용하는 시스템의 효율은 수집 대상 페이지의 응답 속도에 따라 비효율적으로 대기하는 경우가 많아 성능이 좋지 않았고 Asynchronous IO를 위해 asyncio나 uvcore를 써도 event loop를 처리하는 Python 내부의 알 수 없는 CPU 소모가 제어되지 않아 수집 효율이 별로 좋지 않았습니다.

또한 Docker로 운용하는 MySQL 시스템이 높은 수의 INSERT 요청이 몰릴 때 자주 크래시가 발생하는 문제가 있어 이를 Host에서 운용하기도 하고 SSD 위에서 사용하기도 해봤지만 효율이 별로 좋지 않았습니다.

단순 수집을 위한 시스템이므로 중간에 수정할 일도 없으니 이 모든 요소가 불필요하다고 판단, 두 번째 시스템에서는

  • TypeScript로 수집기를 작성하고
  • URL은 Redis의 queue를 사용하고
  • 수집된 데이터는 파일 시스템에 쓰다가 용량이 어느 정도 이상으로 커지면 적당히 압축해서 수집 서버에 업로드하는 방식으로 변경하였습니다.

예전보다 더 적은 agent로도 10배 이상 높은 효율을 보여 목표치인 1TB/week를 달성할 수 있었습니다.

Docker로 관리되는 각 agent의 지표를 빠르게 수집하기 위해 간단한 metric mediator를 개발해 agent로부터 지표를 전달받아 합산했고, 그 지표를 Prometheus로 보내 Grafana로 시각화하여 모니터링을 수행했습니다.

Transcript 기반의 영상 편집기 개발

영상의 음성에서 Google Cloud Platform의 Speech-to-text API를 사용하여 Transcript를 추출해 timeline을 기반으로 하는 기존 영상 편집기와는 다르게 transcript 기반으로 영상을 편집할 수 있는 시스템의 초기 구조 설계와 일부 개발에 참여하였습니다.

  • Client 개발 환경을 React, Redux, TypeScript, Electron으로 구축하고 기반 설계를 하고,
  • Client로부터 전달되는 음성을 AWS S3에 업로드하여 AWS Lambda에서 transcript를 추출할 수 있는 파이프라인을 TypeScript, Python3, Serverless Framework을 사용해 구축하고,
  • 각 시스템에서 발생하는 로그를 수집하여 In-house에 운영 중인 Elasticsearch로 보내어 Kibana로 시각화해 관리 및 모니터링을 수행했습니다.

다른 Backend engineer가 없었기 때문에 최대한 Managed로 서비스를 구축하였고, 관련한 모든 배포들도 가급적이면 CloudFormation에 비해 설정이 적은 Serverless framework을 사용하였고 In-house Jenkins로 배포가 관리될 수 있도록 구축하였습니다.

Trial을 위한 Web 뿐만 아니라 Windows, MacOS, Linux Desktop 환경을 위해 electron-builder를 사용하여 각각 AppX, nsis, dmg, snap으로 배포했고 이들의 signing을 위해 DigiCert에서 발급받은 CodeSign Cert와 Apple Developer에서 발급받은 Certificate를 사용했고 이들을 적절한 Jenkins slave에 배치하여 배포를 최대한 자동화하였습니다.

TensorFlow Backend 연동 서비스 개발

TensorFlow로 이미지를 처리하는 시스템을 서비스로 만들기 위해,

  • 유저의 요청이 AWS API Gateway를 통해 들어오면 S3 SignedURL을 통해 업로드하고 그 Key를 SQS에 넣고
  • In-house worker는 SQS를 보고 있다가 그 이미지를 처리해서 다른 S3에 올리고,
  • 유저는 주기적으로 완료되었는지를 검사하여 처리된 이미지를 가져가는

기본적인 Backend 시스템을 만들어서 배포 및 모니터링 요소를 추가하여 관리를 진행하고, 그에 대한 간단한 Web FrontEnd를 React, TypeScript로 작성하여 AWS S3 static web site와 CloudFront로 배포하여 관리했습니다.

또한 React, Parcel, Adobe CEP SDK를 사용하여 Adobe CEP을 작성하여 Adobe Store에 배포했습니다. Signing을 위한 Certificate는 DigiCert에서 Adobe AIR의 full trust chain을 발급받아 진행했습니다.

Speech-to-text 기반의 회의 기록 서비스 개발

Google의 Speech-to-text API를 사용하여 Audio stream의 transcript 생성과 다른 팀에서 개발한 화자 구분을 통합하여 회의 기록 서비스를 개발하기 위해,

  • Flutter로 app을 개발해 Audio stream을 WebSocket을 통해 AWS Backend로 보내고,
  • AWS에서는 WebSocket으로 audio payload를 받아서 streaming과 chunk의 2-phases 처리를 통해 실시간 transcript와 화자 구분까지 처리된 transcript를 app으로 전달하고,
  • app은 실시간 STT를 먼저 보여주었다가 추후 화자 구분까지 처리된 transcript로 덮어쓰는 구조입니다.

개발 인력 부족으로 native app을 개발하는 것은 무리가 있을 것이라 판단해 Flutter로 UI와 대부분의 기능을 구성하고 필요한 부분만 Java로 작성하여 Android을 먼저 개발했습니다. Web으로도 개발해봤으나 Audio stream을 충분히 빠르게 처리하지 못하면 WebWorker를 사용해서 audio frame이 누락되는 현상이 있어서 포기했습니다.

Backend에서는 WebSocket으로 전달받은 payload를

  • stateful한 ECS로 bypass하여 STT streaming을 진행하고
  • Redis로 모아서 S3 event를 통한 Lambda chain으로 chunk 분석을 진행했습니다.

역시 인력의 부족으로 최대한 Managed resource를 사용하였고 이 때문에 가급적이면 Lambda를 사용하도록 시스템을 구축하였습니다. 물론 다른 시스템과 동일하게 실행 로그와 지표를 Elasticsearch와 Kibana를 통해 시각화하고 Jenkins로 자동화된 배포 체계를 구축하였습니다.

LINE+ #

입사부터 퇴사까지 라인 뮤직 개발 서버 파트에서 업무를 진행하였으며 크게 API 서버 개발 및 장애 대응, 운영툴 개발, 음원 입수 배치 개발, 검색 시스템 개선 업무를 진행하였습니다.

Music API와 운영툴 개발

Java 7, Spring framwork 3 기반의 라인 뮤직의 RESTful API를 개발하였습니다.

  • 신규 사양에 대한 기획의 개발 요청이 들어오면,
  • 그에 대해 API 성능과 데이터베이스 효율을 고려하여 스키마와 쿼리를 작성하여 DBA의 검토를 받고
  • 기능을 개발하여 App 파트와 테스트를 하고
  • 관련 기능을 제어하기 위한 운영툴 기획서를 바탕으로 운영툴의 API와 jQuery, thymeleaf 기반의 FrontEnd 페이지를 추가하고
  • 필요하다면 그 기능을 제공하기 위한 데이터를 준비하기 위한 batch job을 Spring batch를 사용해 개발하고
  • 혹은 필요하다면 비동기 처리 작업이 필요하다면 RabbitMQ로 Job이 전달되는 worker service에 기능을 추가하고
  • 필요하다면 사전 쿼리 성능 테스트나 API 성능 테스트를 통해 병목 지점을 찾고 튜닝하고
  • 배포 이후 해당 기능이 정상 동작하는지 모니터링하고 혹시 장애가 발생한다면 대응합니다.

공통된 코드가 반복되는 것을 좋아하지 않기 때문에 무리한 Reflection을 사용해 코드를 묶어내다가 제어가 안 되어 다시 풀어낸 적이 있고, Spring 특유의 코드 반복을 제거하기 위해 lombok이라도 도입하려 하였으나 여러 다른 일정 때문에 실패하고, 비슷한 이유로 Java 8을 사용한다거나 Spring 4로 올리는 작업 역시 제대로 시도하지 못했습니다.

추후 대부분의 API 코드가 정의된 Domain 객체를 운영툴에서 정의한 구조 기반으로 DB와 cache layer를 거쳐 fetch해 채운 fill 후 조립하여 응답하는 구조를 정리하기 위해 전반적인 코드 정리와 의존성 정리를 하였고 이를 위한 공용 라이브러리를 Generics를 최대한 사용하여 일반적으로 작성하였습니다.

서비스를 위해 여러 중요한 요소가 많이 있지만 그 중에 운영적 요소가 많이 간과되고 있다는 점을 배웠습니다. 때문에 어떤 기능을 추가할 때 성능과 안전성 뿐만 아니라 운영적 요소를 함께 고민할 수 있는 좋은 경험이 되었습니다.

Music API 서버의 성능 튜닝과 최적화

서비스가 지속되면서 복잡한 컨텐츠 구성과 개인화된 추천 요소가 첫 화면에 들어가기 시작했고 이 때문에 성능 저하가 발생하게 되었습니다. API 응답 시간이 지속적으로 증가했기 때문에 이를 개선하기 위해서 따로 테스트 환경을 구축해 지속적인 성능 테스트를 진행하였고, 이 때의 프로파일링 정보로 성능 저하 지점들의 코드를 개선하였습니다.

대부분은 오래된 서비스 로직들의 중첩에 의해 데이터베이스나 Cache의 낭비가 발생했기 때문이므로

  • 최대한 코드를 정리해 이러한 낭비가 발생하지 않도록 전반적인 시스템을 정리하였고,
  • 유저와 상관있는 부분과 없는 부분을 최대한 나누어 다른 Cache를 사용하게 만들어 효율을 높였습니다.
  • 그리고 실시간성이 조금 떨어져도 괜찮은 데이터는 Batch를 통해 주기적으로 갱신에 데이터베이스에 넣어두고 API에서는 그것을 사용할 수 있도록 수정했고,
  • 그러한 처리조차 까다로운 데이터들은 유저가 처음 접근했을 때 Job을 발행해서 Worker에서 비동기로 처리될 수 있도록 수정했고,
  • 데이터베이스 기반의 집계를 통해 서비스하던 요소를 Hadoop cluster를 통해 집계할 수 있도록 시스템을 전환하여, 부족한 부분은 Impala UDF를 추가하여 Impala 쿼리로 집계할 수 있도록 시스템을 개선하였습니다.

또한 음원 수가 계속 증가하면서 음원의 메타데이터가 지속적으로 증가하게 되었고 이는 음원 검색 시스템에 많은 부담을 주게 되었습니다. 기존의 검색 시스템은 Elasticsearch에서 nGram 기반으로 구축되어 있었는데 검색 요소의 추가와 음원 자체의 증가로 이 용량이 상당히 비대해졌고 이는 짧은 키워드에 대해서는 수 초 내에도 응답할 수 없는 문제가 발생했습니다. . 이를 개선하기 위해

  • 더 이상 nGram을 사용하지 않고 kuromoji를 통한 tokenizing한 글자를 색인하도록 수정하고,
  • Impala 쿼리를 통해 주기적으로 집계된 음원 접근 점수를 검색 시스템에 반영할 수 있도록 통합하고,
  • Impala 쿼리를 통해 집계된 유효한 아티스트 목록을 사용자 사전에 추가하여 무의미한 색인이 만들어지는 일을 막았습니다.

덕분에 보다 적은 색인으로도 더욱 다양한 메타를 검색할 수 있게 되었고 좀 더 유기적인 점수를 활용하는 시스템을 구축할 수 있었습니다. 특히 Hadoop cluster 도입 이후 Impala를 통해 서비스에 영향을 주지 않으면서도 유의미한 집계를 빠르게 수행할 수 있어서 많은 부분의 개선에 도움이 되었습니다.

음원 입수 체계 개발과 스토리지 관리

서비스 전후로 새로운 음원의 입수나 기존 음원의 갱신은 지속적으로 이루어져야 하기 때문에 음반사나 음원 Aggregator로부터의 음원 입수는 계속 진행됩니다. 하지만 이렇게 많은 음원이 입수되어 서비스에 맞게 전처리되고 service ready 상태가 될 때까지 서비스에 영향을 주면 안 되기 때문에 이에 대한 관리가 필요합니다.

이를 위해 다른 분께서 설계한 입수 시스템 내에서

  • 성능을 조절하며 입수할 수 있는 시스템이나,
  • 입수된 음원들의 메타데이터 혹은 음원 파일들의 배치 처리와
  • 그것들을 조회하고 추가적인 작업을 처리할 수 있는 도구의 개발을 진행했씁니다.

음원 파일들은 크기가 작고 굉장히 많기 때문에 보통의 파일 시스템에서는 대응할 수 없고 때문에 GlusterFS를 사용했습니다. Read가 과도할 때 Write를 많이 하게 되면 Client에서 replication을 수행하는 GlusterFS의 특성 상 split-brain이 발생해 서비스의 큰 장애가 발생하게 됩니다. 때문에 이 시스템에 대한 튜닝을 인프라팀과 함께 진행하였고 지속적으로 모니터링하면서 장애가 발생하면 입수를 제한하여 서비스에 문제가 없도록 제어하거나 그것을 적당히 제어할 수 있도록 입수 시스템을 변경했습니다.

추후 대량의 음원 입수가 필요할 때에는 새 storage-set을 받아서 서비스에 연결하지 않고 일단 대량 입수를 진행한 후에 추후 read-only로 설정하여 서비스에 노출하기도 하였는데 이에 대한 제어를 위한 기능을 운영툴에 추가하거나 기존 입수 시스템을 수정하는 작업을 하였습니다.

service ready 에 대한 개념에 대해 많이 고민해볼 수 있었고, 대량 입수를 위한 bulk pipeline과 급한 수정을 위한 hot-line을 동시에 유지하는 것이 시스템의 중복이 아님을 배울 수 있었습니다.

음악 메타데이터 검색 시스템 개선 실험

초기 검색 시스템은 Elasticsearch 1.x 버전의 nGram을 사용했기 때문에 음원 수 증가에 따라 검색 성능이 현저히 저하되었습니다. 이를 개선하기 위해

  • Elasticsearch 5.x cluster를 준비하고
  • kuromoji를 사용해 tokenizing한 데이터를 색인하고
  • 적절한 검색 점수와 사용자 사전이 구축될 수 있도록

데이터를 색인하고, 사용자 접근 데이터를 집계하여 점수 계산 모델을 만들어 통합하고, 기존 검색과의 성능 비교를 지속적으로 진행하였습니다. 검색에 사용되는 메타의 종류도 계속 늘어났기 때문에 추후에는 운영팀의 도움을 받아 키워드 별 바람직한 검색 결과를 운영툴을 통해 수집받고 그 결과와 유사하게 검색이 될 수 있도록 모든 가중치를 변수화하여 적절한 값을 찾는 실험을 진행했습니다.

추후 적절한 값을 찾아 실제 시스템에 반영하였고, 훨씬 적은 Elasticsearch node 수로도 더 빠른 시간에 응답하면서 메타의 적절성과 사용자의 접근 점수, 운영자의 추가 점수 등이 고려되는 검색 시스템으로 개선할 수 있었습니다.

점수에 대한 통계적 모델을 만들 때 통계적 지식이 없어서 꽤 고생을 했습니다. 또한 수많은 가중치 변수들을 제어할 때에도 처음에는 Brute-force로 풀다가 도저히 기대 시간 내에 응답을 받을 수 없어 적당한 Simulated annealing 방법으로 선회하였으나 역시 적절한 변수를 찾을 수는 없어서 아쉬웠습니다. 해당 부분에 대한 많은 공부가 필요함을 느꼈습니다.

재생 기록 기반의 추천 연구

Spotify에서 초기 추천 시스템으로 Word2Vec을 사용했다는 기록을 보고 공부하여,

  • Hadoop cluster로 수집되는 재생 이력을 유저의 연속 재생 시간 범위 내에서 TrackId를 word로 갖는 document로 간주하고
  • 추출한 track 문서를 gensim의 word2vec에 넣어 학습해 embedding을 확인하고,
  • cosine similarity를 통해 특정 track에 대해 근처에 있는 다른 track을 찾아보고 결과를 정성적으로 확인했습니다.
  • 추후 아티스트나 앨범 단위에서의 embedding으로 확장해 추천 연관성을 계산하기 위한 다양한 방법을 고민해봤습니다.

이미 외부에서 추천 데이터를 공급받고 있었기 때문에 이 실험은 개인적으로 진행하게 되었고, 때문에 추천에 대한 기획이나 학습에 대한 고민보다는 데이터 파이프라인을 구축하고 실험할 수 있는 환경을 만드는데 더 집중하게 되었습니다.

  • 학습을 위한 데이터를 빠르게 가공할 수 있는 Impala 쿼리와 cppscript를 작업하고 주기적으로 embedding을 만들 수 있도록 batch 작업을 만들고,
  • 혹시나 서비스에 투입하여 A/B 테스트를 할 수 있지 않을까 하는 기대에 A/B 테스트를 위한 코드 추상화 작업을 진행했으나,
  • 서비스에 반영하는 것은 도저히 무리라는 판단 하에 따로 테스트 서버와 웹 페이지를 구축하여 추천 시스템에 대해 간단히 테스트해볼 수 있도록 시스템을 구축하고,
  • 이 시스템에서는 서비스에서 사용하는 재생 모듈을 사용할 수가 없어 순수 JavaScript 기반의 재생 모듈을 새로 만들게 되었습니다.

몇 차례 실험 결과, 재생 이력을 기반 데이터로 사용하는 것보다는 차라리 사용자가 작성한 플레이리스트 기반으로 작성할 때 좀 더 연관성 있는 추천이 일어나는 것 같은 느낌을 받았습니다. 그리고 별다른 규칙 없이 단순히 embedding 내의 유사도로만 보면 특정 앨범이나 아티스트로 묶이는 경우가 많아 추천이 아닌 것처럼 보였습니다. 하지만 이것은 오로지 혼자의 평가였기 때문에 굉장히 신뢰할 수 없습니다.

추천 시스템을 작업하기 위해서는 엔지니어링 측면에서 데이터 파이프라인의 구축과 A/B 테스트를 위한 환경, 그리고 빠른 테스트가 가능한 테스트 환경 구축과 피드백 시스템을 만드는 기계적인 작업도 중요하지만 그에 앞서 데이터 정리와 어떤 추천을 할 것인지에 대한 기획 정리, 그리고 그에 적합한 학습 시스템 설계도 중요하다는 것을 배웠고, 이 모든 작업을 혼자서 진행하는 것은 무리라는 것을 배웠습니다.

Bluehole studio #

입사부터 퇴사까지 TERA 프로젝트에 배속되어 업무를 진행하였습니다. 필요에 따라 서버나 툴팀에 배치되거나 북미유럽팀, 중국대만팀 등의 해외 대응 개발 부서에 배치되기도 했습니다. 주로 게임 기능을 추가하거나 그에 대한 운영툴을 개발했고, 혹은 생산성 향상을 위해 게임 개발 도구를 개발했습니다.

C++로 작성된 비동기 게임 서버 개발과 게임 컨텐츠 운영툴 개발

WIP

보관 관련 시스템 혹은 외부 시스템 모듈과의 연동

WIP

게임 개발 보조 도구 개발

WIP

게임 라이브 서비스 장애 대응과 크래시 덤프 분석

WIP

데이터베이스 연구실 #

WIP

C언어 기반의 연속 질의 처리를 위한 네트워크 기반 프로그램 개발

WIP

Java 기반의 외부 라이브러리 연동이 가능한 모듈 관리 시스템 개발

WIP

활동

WIP

잉여톤

WIP

대학교 동아리: PoolC

  • C++, Java, C#, Android, 보안, 운영체제, 네트워크, 임베디드 프로그래밍에 대한 세미나 진행.
  • 기본 프로그래밍 능력, 알고리즘, 설계에 대한 프로그래밍 대회를 2007 년, 2008 년, 2011 년 개최.
Loading script...