🪪 NAME

🙋 SYNOPSIS

개발팀이 조화롭게 일할 수 있도록 노력하는 백엔드 개발자 황세현입니다. 저는 대학원에서 웹 개발을 파고든 후로 현재 현업에서는 4년 차에 접어들었습니다. 제가 넥스트랩에서 일했던 지능형 자동화 팀은, 제가 입사하기 직전 신규로 꾸려져 현재는 차량, 보행자 영상 데이터를 다루는 서비스를 다양한 고객사에 납품할 수 있을 정도로 성장했습니다. 알고리즘 POC부터 서비스 운영까지 직접 발로 뛰었던 시기도 있었지만, 현재는 서비스 설계, 개발 방향 제안, 인프라 방면에서 역할을 다하고 있습니다.

  • Long-running task management를 효과적으로 수행하기 위한 MSA 설계, 배포에 대해서 풍부한 경험이 있습니다.
  • 이를 위해 클라우드 환경에서는 AWS의 Serverless service, 로컬 환경에서는 Redis와 Node.js를 이용합니다.
  • Linux 인프라부터 클라우드 기술 트렌드까지 폭넓은 이해하고 있으며, 배포와 서비스의 취지를 고려해 front-, back-end의 개발 방향을 설정하고 개발하고 서비스화할 수 있습니다.
  • 개발에서 지향하는 바가 뚜렷한 편이며, 이를 깊이 신뢰해주는 동료들과 함께 흉악하지만 빠르고 안정적인 소프트웨어를 만들어왔습니다.
  • 컴퓨터를 다루는 기술인으로서 내가 만든 소프트웨어가 미치는 영향에 대해서 책임감을 느끼고, 이를 위해 사용자/비개발자와의 의사소통에 관심이 많습니다.

🌮 Culture & Beliefs

⚖️ Use of Design Principles

개발자들은 기능적 요구와 기술적 제약이 충돌하는 상황을 항상 맞닥뜨립니다. 저는 이러한 상황에서 현재 서비스 구조에서 꼭 취해야 할 장점을 선별하고 이를 토대로 설계 원칙을 지정해 문제를 풀어왔습니다. 종합적으로 각 Microservice가 수행하는 역할을 구분, 정의하고 이를 침범하지 않도록 의사 결정을 내리는 업무를 맡아왔습니다. 설계 의도/원칙의 초안을 문서화해 팀원들과 논의하여 뼈대를 구성하고, 업무를 분담하고 살을 붙여나가 서비스를 완성합니다.

X-ray 식품 검사 시스템 을 전면 재개발하는 프로젝트의 초기 단계에서는 고유 규격을 가진 3종류의 하드웨어와 복잡한 비즈니스 로직을 적은 개발 인력으로 구현해야 하는 과제가 있었습니다. 저는 전체 시스템을 1) 각 장비에 명령을 전달하는 프로토콜을 구현한 Microservice와, 이들 Microservice를 동작 프로파일 단위로 추상화한 하드웨어 스택, 2) 검사 수행에 필요한 비즈니스 로직과 검사 이력을 관리하는 애플리케이션 스택으로 크게 나누고 기술 스택을 할당했습니다. 하드웨어 스택에서는 Stream interface와 이벤트 기반 처리에 강점이 있는 Node.js를 이용했으며, 객체 지향적인 TypeScript type을 Event Emitter에 적용해 하드웨어 프로토콜을 정교하고 편리하게 개발할 수 있도록 구성했으며, C++ SDK를 이용해 이미지 획득 모듈을 구현했습니다. 애플리케이션 스택에서는 제가 개발한 이미지 획득과 이물 검사 과정을 Postgre SQL에 적재할 수 있도록 하는 Schema 설계에 참여했으며, 이미지 데이터 S3 compatible storage를 채택했습니다. 명확한 시스템 구성 덕에 개발 양에 비해 인력과 시간이 매우 부족한 상황에서 실물 장비를 이용한 개발과 테스트를 최소화할 수 있었습니다.

넥스트랩에서 소속 팀의 가장 핵심 서비스라고 할 수 있는 번호판 인식 모듈은 1) Stateless하고 수평 확장이 용이해야 하며, 2) 이미지 처리 컨테이너는 Worker 역할을 수행하며, Worker 내에서는 검증되지 않은 비동기 코드를 사용하지 않는다는 원칙에 기반해 개발했습니다. 해당 모듈이 기업용 솔루션으로 납품될 때, 고객사마다 임베디드 보드, PC 환경, 고성능 서버 환경 등 다양한 인프라 위에서 구동될 것을 요구받은 상황에서 Horizontal scaling은 필수적인 고려 사항이었으며, 더 나아가 클라우드에서 구동되는 SaaS 서비스로 발전할 가능성이 논의된 사실도 반영되었습니다.

Worker 내부에서 비동기 처리를 지양했던 이유는 추론 가속을 위해 사용했던 Tensor RT framework의 비동기 처리와 충돌 가능성을 방지하고 안정성을 높이기 위함이었습니다. Main thread에서는 Redis stream 또는 mmap 에 적재된 메시지를 polling 하는 작업만 수행하는 단순한 구조를 선택했으며, 투입한 컴퓨팅 리소스에 비례해 Worker 개수만큼 선형적으로 성능이 향상함을 확인했습니다. Stateless를 지향했기 때문에 같은 코드베이스로부터 아래와 같은 다양한 형태의 서비스를 구현할 수 있었습니다:

  • 카메라와 직접 연결되는 실시간과 request/response 서비스 모드가 존재합니다.
  • 임베디드 환경에서는 산업용 카메라를 통한 이미지 획득 서비스는 60 FPS 이상, AI 모듈은 약 5 FPS으로 구동되는 상태에서, 고객사가 원할 때 추론 결과를 약 1 FPS로 가져갈 수 있었습니다.
  • 서버 환경에서는 40개의 GPU worker를 이용해 300 RPS를 달성했습니다.
  • 또한 정확도 확보를 최우선 과제로 하는 딥러닝 담당 팀원과의 협력에도 강점을 보였습니다.

🎨 Creativity

앞서 설정한 설계 원칙을 준수하기 위해서는 독창적인 구현이 필요할 때가 있었는데, 제가 일했던 팀은 실력 있는 개발자들로 이루어져 있으나 인원이 소수인 탓에, 코드 양이 많고 테스트가 복잡하다면 표준적인 개발 방식을 채택하기 힘든 경우가 많았습니다. 우선 전체 시스템이 예상 밖의 동작을 하지 않도록 인프라의 제약 사항, 사용하고자 하는 브라우저의 Web API와 Linux system call까지 고려해 각 모듈의 역할을 이상적으로 지정해 봅니다. (추후 타협될 가능성이 물론 있습니다.) 이때 다양한 이유에서 검토, 실험, 증명이 필요하다면, 풀어야 할 문제를 단순화한 개발 환경 또는 테스트 케이스를 별도로 구축해 커뮤니케이션했습니다. 이때 repl.it , CodePen , StackBlitz , TypeScript Playground , Google CoLab과 같은 Browser IDE 또는 컨테이너 환경을 이용해 실험에 대한 접근성을 높이고 실험 결과를 관리할 수 있도록 신경 썼습니다.

번호판을 인식하는 HTTP API를 개발하는 프로젝트 에서는 Web application server 없이 Nginx + Redis만으로 기능을 구현한 경험이 있습니다. 설계는 교과서적인 Fan-out 패턴이었지만, Python 번호판 인식 컨테이너에서 Fast API 등의 서버 프레임워크를 사용하지 않고 Nginx에서 담당하게 함으로써 속도와 안정성을 끌어올렸습니다. 표준적인 HTTP 상태 코드를 구현하기 위해서는 Nginx에서 HTTP 관련 로직이 작동해야 했는데, 이를 위해서 OpenResty 의 Lua scripting을 이용했습니다. 이때 코드의 독해가 어려운 문제를 해결하기 위해서 Node.js를 이용해 커버리지가 100%에 가까운 테스트 코드를 작성했습니다. 이와 같이 레퍼런스가 없는 설계를 구현할 때는 팀원들에 비해 상대적으로 과다하게 창의적이지 않도록 테스트 코드를 작성하고 배경을 문서로 작성해 팀원들과 합의를 이룰 수 있도록 노력했습니다.

🫂 Relational

Illustration by Scott Garrett

Illustration by Scott Garrett Source

저는 개성 있는 개발자들이 모인 개발 조직에서 일해왔고 앞으로도 그러한 조직에서 일하고 싶습니다. 모호한 문제를 맞닥뜨렸을 때, 다양한 희망과 우려가 자유롭게 논의되고, 이것이 설계와 개발에 반영되는 과정에서 즐거움을 느꼈습니다. 저는 세상 전반에 관심이 많고 다양한 분야의 경계를 넘나드는 직접을 가지고 싶다는 생각을 오래전부터 했었는데, 개발 업무도 이러한 이유로 제가 좋아하고 잘할 수 있는 일이라고 확신을 가지고 있습니다. 저는 잠재적으로 발생할 수 있는 문제들을 선제적으로 팀에 제기하고, 기술 스택과 AWS API들을 추천해 주면서 팀원들의 개발을 지원하는 업무를 해왔습니다. 또한 Linux OS와 C++에 대한 지식을 기반으로 컨테이너 내부, AWS Lambda, 임베디드 환경 등 특수한 환경에서 발생한 문제에 대해서 Troubleshooter 역할을 했으며, 이러한 역할을 할 수 있어 기쁘게 생각하고 있습니다.

🍱 Tech Lunchbox

🏗️ Architecture: Docker Compose, Nginx, Redis

  • OCI Specification, Docker API와 1:1 대응이 이루어진다는 면에서 Docker Compose를 개발, 배포에 모두 활용하고 있습니다.
    • MSA에서 각 서비스의 의존성을 통제하기 위해 서비스별 env/envfile과 volume, network 섹션을 주로 개발하고 유심히 코드 리뷰합니다.
  • Nginx는 프로젝트 외부와의 Gateway로서, Redis는 프로젝트 내부의 Message broker로서 활용해왔습니다.
    • Nginx reverse proxy rule을 작성하고 Nginx push stream module 과 같은 추가 Nginx module을 활용할 수 있습니다.
    • Redis의 거의 모든 내장 자료형을 적재적소에 활용할 수 있고, 컨테이너별 연동 방식을 제안할 수 있습니다.

🌩️ Serverless AWS

  • Python, Node.js 언어로 Lambda function을 Production에서 꾸준히 사용했습니다.
    • Container 형태의 Lambda 로 AI inference를 구동할 수 있습니다.
    • SNS, SQS, EventsBridge 등의 AWS 서비스들로 Lambda의 Trigger/destination을 구성할 수 있습니다.
  • Step Functions로 AWS 서비스들을 비즈니스 로직에 따라서 연동할 수 있습니다.
    • Step FunctionsAPI Gateway를 연동해 코딩 없이 Microservice를 구성할 수 있습니다.
  • TypeScript AWS CDK로 브랜치별로 Serverless CI를 구성할 수 있습니다.

🤟 Languages: JavaScript, Python, Shell, C++…

  • Node.js 를 이용해 복잡한 비즈니스 로직을 총괄하는 소프트웨어를 개발합니다.
    • JavaScript의 뛰어난 유연성과 비동기 처리 성능 때문에 해당 역할을 주로 할당했습니다.
    • 특정 프레임워크에 종속되기보다는 Frontend-, server-side에 관계 없이 JavaScript 기본기에 충실한 Full stack 개발을 지향합니다.
    • 애플리케이션 개발에 있어서 제가 가장 전문적인 언어입니다.
  • Python 은 주로 AI와 관련된 작업에 대해서 명확한 역할을 배정해왔습니다.
    • MSA 구조 속에서 주어진 역할을 정확히 할 수 있는, 너무 크지 않은 프레임워크를 선택해서 사용했으며, Python 개발자와 가장 많이 협업해봤습니다.
    • 필요한 기능 구현은 충실히 할 수 있는 수준이나, Python 고유의 특성을 이해하고 코드베이스를 리드할 수 있는 수준은 아닙니다.
  • Shell scriptMakefile 을 이용해 Linux OS와 직접 상호작용이 필요한 스크립트를 작성하고 DevOps 업무를 수행합니다.
    • 애플리케이션 개발에 쓰이는 다른 스크립트 언어 수준으로 Shell script에 능숙합니다.
      • 프로그래밍 언어와 무관하게 Linux system call, Child process와 직접 상호작용할 수 있어서 Shell script 를 중시하고 있습니다.
    • Dependency가 복잡할 경우나, 다양한 언어가 쓰이는 MSA 구조에서 특정 언어에 의존하지 않기 위해서 Makefile 을 유용하게 사용했습니다.
  • 하드웨어 연동 관련해서 C++ 를 이용해 재활용 가능한 코드를 작성할 수 있습니다.
    • 산업용 카메라, X-ray detector 등을 C++ SDK를 통해 제어하고 Linux IPC를 통해 다른 Microservice에 데이터를 제공하는 개발을 했습니다.
    • Modern C++ 문법과 객체를 이용해 안전하고 효율적인 개발을 지향했습니다.

🧪 Projects