웹 서비스 배포 플랫폼 Luidium 개발기 - 1

Luidium 이란?

Luidium의 초기 구상은 내가 초기 스타트업에서 개발을 하며 느낀 불편함에 의거한 것이다. 팀에 개발자가 2명이 있어 한 명이 백엔드를, 내가 프론트를 맡았을 때에는 서비스 개발에 있어 큰 어려움을 느끼지 못했다. 대규모 트래픽 처리, 엄청난 보안 대책을 가지고 서비스를 구축하는 것이 아닌 기능 개발과 빠른 유저 테스트 및 검증에 초점을 맞춘 개발이 요구되었기 때문에, 기획 회의 후 디자인이 완성되면 이를 퍼블리싱하고, 서버 API를 연동하고 간단한 QA를 진행하는 과정이 빠르고 순조로웠다.

문제는 백엔드 개발자가 병역 문제로 잠시 팀에서 이탈하게 되어 발생하였는데, 내가 서버와 클라이언트 개발을 모두 맡아 개발하게 되며 기획 회의 후 디자인이 완성되면 바로 퍼블리싱에 착수하는 기존의 타임라인이 사실상 어려워졌기 때문이다. 이 타임라인이 성립하려면 디자인이 나올 동안 서비스 아키텍쳐 구상 및 서버, 데이터베이스 구축 등 퍼블리싱 이외의 절차가 프론트 디자인이 완성되기 전까지 완료되어야 하는데, 현재 나의 개발 역량으로는 어려웠기 때문이다.

하지만 다양한 웹 서비스를 개발하고 배포하면서, 반복적이고 단순한 코드 찍어내기 과정이 수반되는 것을 알고 있었기에, 이를 자동화하여 충분히 빠른 시간 내에 웹 서버의 초기 토대를 구축해내고, 핵심적인 로직과 디자인 퍼블리싱만 해도 자동으로 배포 및 테스트가 가능하도록 하는 서비스를 구상하였다. 그러한 과정에서 1차적으로 AWS, Google Cloud에 대한 의존성 없이 다양한 서버, 클라이언트의 인스턴스를 containerization하여 배포할 수 있는 솔루션이 필요하다는 구상을 하였고, 개발에 착수하였다.

현재 우리 팀은 개인 Linux 서버에서 Nginx를 이용하여 웹서버를 호스팅 하고 있었기에 쉽게 가능할 것이라 생각했고, 도메인을 구매한 Cloudflare의 API를 이용하면 DNS Record 추가도 가능해 원하는 주소로 배포하는 과정까지 자동화 할 수 있을 것으로 판단했다.

여담으로 원소 이름 느낌이 나는 Luidium 이라는 이름은 영어 원소 이름의 어감이 마음에 들었었는데 대부분의 이름마다 하나씩은 모종의 서비스 이름으로 쓰이고 있었기에, 쓰이지 않는 원래는 없는 이름을 만들었다. 로고도 GPT-4.0을 이용해 초안을 얻어내고 약간의 수정을 가했는데, 개인적으로는 색감이 마음에 든다.

아키텍쳐

초기 Luidium 서비스의 아키텍쳐이다. 클라이언트에서는 어떤 유형의 어떤 블럭을 조합하여 서비스를 만들 것이지 규정하고, 클라이언트에서 배포, 혹은 업로드, 다운로드 요청을 보내면 이를 메인 서버에서 처리한다. 개략적인 과정은 다음과 같다.

  1. 회원가입 및 유저 생성
  2. Application 생성
  3. Application 내에서 Block 생성 및 연결
  4. Block 별로 Configuration 설정
  5. 초기 Deploy
  6. 소스코드를 내려받아 기능 구현
  7. 새로운 소스코드를 업로드
  8. 새로운 Docker Image를 빌드 후 재배포
  9. 6-8번을 반복

문제점

하지만 초기 구축한 아키텍쳐에는 몇 가지 문제가 발생할 것으로 예상하였다.

  1. 서버 파일 시스템에 직접적으로 엑세스:

    이러한 아키텍쳐에서는, 유저가 업로드한 소스코드를 직접 서버에 업로드하고, 이를 빌드하는 과정이 수반되므로, 악성 코드를 감지하지 못할 경우 서버 내부의 문제를 초래할 수도 있고, 서버의 저장 공간에 의존하므로 확장성이 높지 않다.

  2. 파일 입출력을 이용한 코드 생성 문제

    만약 동시에 많은 코드 생성 요청을 메인 서버가 받게 되면 서버의 요청 처리 속도 저하가 우려되었고, 파일 시스템에 직접 템플릿 코드를 복제하여 사용하는 방식에서는 파일의 무결성을 장담하지 못할 것 같다는 생각이 들었다.

  3. GraphDB 사용

    기존의 소규모 기능 개발 위주의 개발 환경에서 데이터베이스의 중요성을 크게 체감하지 못하였기 때문에, 단지 익숙하다는 이유로 GraphDB(NoSQL) 중 하나인 Neo4j를 이용하였다. GraphDB는 대표적인 NoSQL 데이터베이스의 유형 중 하나로, 데이터 간의 연결 관계를 표현하고 빠르게 쿼리하는데 강점이 있지만, 이번 프로젝트인 Luidium의 경우 데이터의 연결성보다는 안정성이 중요하다고 판단하였기 때문에 굳이 Neo4j를 사용할 필요도 없었을 뿐더러, 스키마를 아예 규정하지 않으며, 비교적 확장성이 떨어지는 Neo4j를 사용할 이유가 없다고 판단하였다.

차기 버전 준비

위와 같은 문제를 해결하기 위해, 새로운 아키텍쳐를 구상하였고, 이를 다음 버전 개발에 반영하기로 하였다.

  1. 소스코드 등 파일 관리에 Object Storage를 이용한다.

    기존에도 이미지 업로드 처리를 위해 Amazon S3 Bucket Storage와 호환되는 오픈소스 object storage인 Minio를 이용하였다. presigned Url을 이용한 이미지 업로드 처리를 통해 서버의 부하를 줄일 수 있었고, 파일 엑세스 권한 관리 등이 편리해지며 보안성도 증대할 수 있었다.

    이에 소스코드와 빌드된 도커 이미지 또한 Minio Bucket에 저장하는 것이 옳다고 판단하였다.

  2. 목적에 맞는 마이크로서비스 구축

    기존 아키텍쳐는 CI/CD 를 염두에 두고 구상한 것이 아니라, 초기 소스코드 생성 및 배포의 일방향적 과정에는 문제가 적을지 몰라도, 코드를 업로드하고, 배포하는 등 다양한 액션이 취해질 경우 이를 메인서버 하나로 적절하게 컨트롤하기 어려울 것이라고 판단하였다.

    이에 소스코드 업로드, 다운로드, 빌드를 위한 세 개의 마이크로서비스를 따로 만들고, 이를 Kafka를 이용하여 바인딩하여 느슨한 연결을 유지하고, 클라이언트에서 느껴지는 지연을 최소화하는 방안을 고안하였다. 기존의 방식으로는 메인서버에서 코드 템플릿을 바탕으로 코드를 생성하고, 이를 도커 이미지로 빌드, 그리고 컨테이너로 실행하고 이를 서빙하는 과정까지 직렬적으로 진행되었으며, 이 통합된 과정을 관리하는 것도 어려웠다. 따라서 다수의 파일 업로드 및 다운로드, 빌드, 배포 요청을 관리하는 서비스를 분리하는 것이 적합하다는 판단하에 Kafka를 도입하기로 결정하였다.

이 외의 부수적인 개선사항은 다음과 같다.

  • Neo4j에서 ScyllaDB로 변경

    ScyllaDB는 뛰어난 확장성과 최고 수준의 읽기 속도를 가진 NoSQL 데이터베이스로, Cassandra의 CQL을 이용한다. 개인적으로 한번쯤 사용해보고 싶었던 데이터베이스이기도 하면서, 확장성이 뛰어나고 schema-less인 점이 마음에 들어 유저 정보를 저장하거나, Application의 메타데이터, 로그 데이터 등을 저장할 데이터베이스로 채택하였다.

  • 서버 프레임워크를 Node.js 기반 Nest에서 Rust 기반의 Axum으로 변경

    Nest.js의 modular 구조와 큰 생태계, 그리고 Typescript지원으로 하나의 언어로 웹개발과 서버까지 개발한다는 점에서 이점이 있었고, 빠른 개발을 위해 줄곧 Nest.js를 이용해왔으나, 더 나은 파일 입출력 속도와 대규모 트래픽 처리, 그리고 더 빠른 REST API 구현을 위해 Rust를 이용하기로 결정하였다. Rust의 Axum은 rust의 강력한 asynchronous runtime인 tokio를 기반으로 하는 뛰어난 속도의 웹 서버 프레임워크로, 효율적으로 Rust 웹 서버를 구현해내기 위해 이용하기로 결정하였다. Node.js에 비해 너무나도 빈약한 개발 생태계가 가장 우려되었으나, 빠르게 성장 중인점, 그리고 scyllaDB client와 Docker daemon driver 등 일단 필요한 라이브러리는 존재하였기에 이용을 결정하였다.

  • 더 나은 사용자 경험을 위해 데스크탑 애플리케이션 추가 구현

    물론 웹 애플리케이션이 접근성이 좋고, 실시간에 가까운 업데이트가 가능하다는 점이 매력적이지만, 개인적으로 웹 브라우저에서 작동하는 웹 애플리케이션은 reliability 측면에서 데스크톱 애플리케이션을 따라올 수 없다고 생각한다. 물론 초기에는 native 기능을 최소로 웹과 동일한 UI를 렌더링 하겠지만, 장기적인 관점에서 데스크톱 애플리케이션을 개발해두는 것은 나쁠 것이 없기에, 시간적 여유가 있을 때 개발하기로 결정하였다. 마침 tauri라는 Rust 기반의 데스크톱 애플리케이션 프레임워크를 알게 되었고, react, next.js 등 내가 친숙한 javascript 기반의 프론트엔드 프레임워크와 호환이 되었기에 비교적 쉽게 개발도 가능할 것이라는 생각이 들었다.

위와 같은 개선 사항을 반영한 차기 버전의 아키텍쳐는 다음과 같다

현재 상황 및 계획

현재 초기 버전의 서버 개발이 완료 된 후 웹 클라이언트 부분을 Next.js로 개발 중인데, 지속적으로 Block Configuration 부분의 변경점이 발생하여 개발이 지체되었다. 또한 현재는 매우 제한적인, Database-Neo4j, Server-Nest.js, Web-React, Next.js 라는 4가지의 스택만을 제공하는 상태이고, 차기 버전을 고려하였을 때 차라리 Version 1.0.0 개발을 중단후 Version 2.0.0 개발로 넘어가는 것이 효율적일 것 같아 프론트엔트 개발을 완료 후 기존 서버를 배포하지 않고, 다음 아키텍쳐를 구체화할 예정이다. 프론트엔드 개발은 4월 이내로 마무리하고, 6월 이내로 새로운 Rust 마이크로서비스 기반의 아키텍쳐를 구현하는 것이 목적이다.

또한 여러 서비스를 개발하면서 기록의 중요성을 깨닫게 되었는데, 혼자서 백엔드와 프론트엔드를 개발하다보니 변변한 API Documentation 정도도 만들지 않다보니, 현재 개발 진척도와 발견한 문제점, 바로 다음에 개발할 것 등을 전혀 파악하지 못하는 문제를 느꼈다. 이에 시간을 조금 더 들이더라도 적어도 일주일에 1회 이상은 이와 같이 개발 일지를 작성하고, 진척도를 기록할 예정이다.




    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • 웹 서비스 배포 플랫폼 Luidium 개발기 - 2
  • 웹 서비스 배포 플랫폼 Luidium 개발기 - 4
  • C++로 Headless CMS 개발하기
  • 나 자신을 인터뷰할 수 있다면 어떨까
  • 동아리/학회 출석 확인 웹 서비스 만들기