API 서버 개발 1년 회고: 안정성과 확장성을 향한 여정

지난 1년간 A-api(가칭) 프로젝트를 개발하고 개선하며 경험했던 여정을 회고하는 글을 드디어 작성한다. 더 빨리 적었어야 했겠지만… 아무래도 네이버 부스트 캠프라던가, 그 외에 정리 라던가, 사실 8월 쉬지도 못하고 작업 하고 있는 상황이라 ㅋㅋㅋ.. 약간의 변명 섞어 가지만, 이제는 좀 정리 해봐야 겠다 싶어 이렇게 남겨 본다.

이 글을 통해 1년 간의 메인 서버로 API 서버가 어떻게 구축되었고, 어떤 기능들이 추가되었으며, 안정성과 효율성을 위해 어떤 노력들이 있었는지 스스로 정리를 위한 글을 남겨보려고 한다. (기업 사용 명칭들은 모두 가칭으로 대체 된다.)

1. 프로젝트 개요 및 핵심 기술 스택

A-api(가칭) 프로젝트는 아동의 재활 및 치료 과정을 돕는 게임형 콘텐츠의 핵심 비즈니스 로직과 이에 관련된 데이터를 관리하고, 사용자 및 아동 정보를 처리하며, 결제(구독), 알림, 스케줄링 등 다양한 부가 기능을 제공하는 백엔드 애플리케이션이다.

주요 기술 스택은 다음과 같다:

  • 프레임워크: NestJS (TypeScript 기반)
  • 데이터베이스: MongoDB (Prisma ORM 활용)
  • 캐시/메시지 큐: Redis (캐싱 및 세션 관리), BullMQ (비동기 작업 큐)
  • 인증: JWT 기반 인증
  • 로깅: Winston (파일 및 DB 로깅), 자체 구현 로거
  • API 문서화: Swagger
  • 배포/운영: Docker, Jenkins, Nginx, (GitHub Actions - 현재 버전에선 삭제)
  • 기타: axios, agenda, mixpanel, notionhq/client, 인앱 결제 라이브러리 등

최초 설계 시에는 MVC 구조를 기반으로 고려한 전형적인 모놀리스 식 서버 애플리케이션이었으며, 이후 3.0.0 이후 NestJS 공식 권장하는 모듈 기반 아키텍처를 적극적으로 활용하여 기능별로 책임과 관심사를 명확하게 분리한 견고한 구조로 최종 구현되었다.

2. 주요 기능 개발 및 개선 내역

지난 1년간 A-api 서버의 핵심 기능들을 개발하고 개선하는 데 집중하였다. 게임의 기본 구현이 클라이언트에서 진행되다보니, 최대한 유저의 행적, 운동, 운동 스케쥴링 등에서 로그를 남기는 쪽이 핵심이 되었다.

  • 사용자 및 인증 시스템:
    • 로컬(이메일/비밀번호) 및 소셜(Google, Apple) 로그인/회원가입 기본 골자를 기반으로 개선 작업을 수행했다.
    • JWT 기반의 인증 시스템을 구축하여 API 접근을 안전하게 제어하고, 사용자 정보 및 탈퇴 처리 로직을 관리하였다.
    • 임상 시험이 이루어져(2024년 ~ 2025년), 이에 대응하고자 UserAttribute 모델을 통해 임상시험 참여자와 같은 특정 사용자에게 특별 권한이나 속성을 부여하는 기능을 추가하였다.
    • 이후 UserAttribute를 기반으로 사용자의 클라이언트 디바이스에 맞춰 최적 설정으로 게임 플레이 가능하도록 대응하였다.
  • 아동 및 치료 관리:
    • 보호자 계정에 여러 자녀를 등록하고 관리할 수 있는 기능을 개발하였다.
    • 자녀의 재활 치료 게임 플레이 기록(PlayRecord), 치료 결과(TherapyResult), 일일 활동 로그(DailyActivityLog)를 상세히 저장하는 시스템을 구축하였다.
    • TherapySchedule을 통해 개인별 맞춤 치료 스케줄을 관리하고, 특히나 Agenda를 활용하여 스케줄링된 작업을 처리하도록 하여 최대한 분산 처리 가능하도록 구축하였다.
    • TherapyHand, HandClassLabel 등 재활 치료에 특화된 상세한 데이터 타입을 Prisma 스키마에 정의하여 전문적인 데이터 관리가 가능하도록 하였다.
  • 게임 콘텐츠 및 설정 관리:
    • 치료에 사용되는 손동작(Motion) 데이터(가이드 영상, 설명 등)를 관리하는 기능을 구현하였다.
    • 메인 치료 게임 외에 미니게임(MiniGameResult)의 결과도 기록하고, GameSettings 모델을 통해 게임의 난이도, 패치노트 등 다양한 설정을 동적으로 관리할 수 있도록 하였다.
    • 미니 게임의 경우, 모듈 구조를 적극 활용하여 클라이언트가 미니 게임에 저장할 데이터를 직접 설정도 가능하고 최대한 백엔드 어플리케이션와 클라이언트 사이의 의존성이 필요시를 제외하면 최대한 의존성을 분리하도록 구조화하였다.
  • 결제 시스템 및 비즈니스 로직:
    • Apple App Store 및 Google Play Store 인앱 결제(구독)를 처리하고, 구독 상태를 관리하는 시스템을 구축하였다.
    • PaymentResult, PaymentHistory, Subscription 모델을 통해 결제 및 구독 이력을 추적하고, 프로모션 코드(Promotion) 발급 및 관리 기능을 추가하였다.
    • TherapyTicket을 통해 게임의 보상이 적절하게 제공될 수 있는 내부 시스템을 구현하였다.
    • Google 의 Pub/Sub 과 Apple 의 ServerNotification V2 를 분석하여 이벤트들에 대응, 결제 사이클에 따라 서버에서 구독 상태의 완전한 관리가 가능하도록 구축하였다.
  • 알림 및 외부 연동:
    • NCP SENS를 통해 알림톡(회원가입, 치료 목표 달성 등)을 발송하는 기능을 구현하였다.
    • Discord 웹훅을 이용하여 서버의 주요 이벤트(서버 시작, 에러 등)를 개발팀에 실시간으로 알리도록 하였다.
    • Mixpanel을 사용하여 사용자 행동 데이터를 수집 및 분석하고, Notion API를 연동하여 내부 데이터를 동기화하거나 리포트를 생성하는 기능을 개발하였다.
    • 외부 연동 기능에서 외부 서비스의 상황과 조건에 따라, AgendaBullMq를 활용하여 로깅에 실패하지 않는 구조룰 구축해 냄.
  • 서버 안정성 및 성능 테스트:
    • 파일 로깅, 표준 출력 로깅을 포함하여 Winston 을 라이브러리를 최적화하여, 월별 로깅 저장, 자동 로깅 압축 등 실 서비스에 최적화된 로깅을 구현하였다.
    • 더불어 비즈니스 로직에서 발생한 로깅 사항이나 외부 접속 시의 Req/Res 의 로깅 저장의 필요성 대두에 Interceptor 개념을 활용한 미들웨어를 구축하였으며, 인메모리 큐 기반으로 하여 로그 데이터 쌓기로 인해 발생 가능한 서버 부담을 최소화 하였다.
    • 그레이스풀 셧다운 을 도입, 리눅스 시스템의 긴급한 정지나 강제 종료 등 상황에서 시스템 콜에 의한 강제 서버 종료를 막을 수 있게 했다. 이를 통해 로그의 소실 없이 해야할 작업들을 온전히 마무리 되었을 때 종료 될 수 있는 형태로 구축하여 로그 소실을 최소화 시켰다.
    • k6 를 도입 AWS 의 현재 서버 자원 상태에서 최대 얼마나 처리 가능한지, 필요한 응답을 얼마 안에 받을 수 있는지 등을 테스트하여 체계화 하였다.

3. DevOps 및 인프라 개선

안정적이고 효율적인 개발 및 배포 환경을 구축하기 위해 DevOps 프로세스 개선에 많은 노력을 기울였다.

  • Jenkins CI/CD 파이프라인용 배포 스크립트 통합 및 개선:
    • 최초에는 AWS App Runner 서비스를 활용한 완전 관리형 서비스를 통해 배포 및 실행됨.
    • 기존 방식의 문제점들로 인해 일반적인 EC2 인스턴스로 서버를 이전 ELB 를 활용한 간단한 로드벨런싱 서버 형태에서 점진적으로 개선하여 ASG(Auth Scaling Group) 기반의 이미지 푸시 및 배포 기능으로 구현 하였다.
    • 이후 무중단 배포 및 개발환경 개선을 위해 전 서버 구성목록을 컨테이너화하였다.
    • 무중단 배포를 위하여 개별 서비스 배포 스크립트(tag_and_push_nestjs.sh, tag_and_push_nginx.sh)를 구축, ECR 서비스를 활용하여 target 태깅을 통해 이미지를 자동 추적 및 배포 가능한 서버 형태로 구현하여 기존 형태에서 비용, 자동 배포, 무중단성 등 이점들을 극대화한 운영하도록 개선됨.
    • 모든 사용자 입력을 제거하고, 환경, 서비스별 버전, Target 태그 여부를 명령행 인자로 받아 처리하도록 설계하여 자동화에 적합하도록 변경하였다.
    • 스크립트들은 반복되는 이미지 푸시 로직을 push_image 함수로 모듈화하여 코드 재사용성과 가독성을 높였다.
    • NestJS 버전은 설정 파일에서 동적으로 읽어오거나 직접 명시할 수 있도록 유연성을 추가하였고, 특정 서비스의 배포를 건너뛸 수 있는 skip 옵션을 추가하여 파이프라인의 선택적 실행을 가능하게 하였다.
    • aws ecr put-image를 사용하여 target 태그를 갱신하는 방식으로 기존 로직을 개선하여 효율적이고 원자적인 태그 관리가 가능하도록 하였다.
    • package.json 을 적극 활용하여 패키지 매니징을 통한 빌드, 이미지 관리, 배포 등을 명령어로 가능케 만들었고, 이를 통해 서버 개발 및 배포 시간의 효율을 극대화 하였다.
    • Husky 와 Lint 의 추가적인 도입 및 CodeRabbit AI 와 같은 서비스 도입을 통해 코드 품질을 끌어올려, 에러가 실 서비스로 최대한 넘어가지 안 도록 관리하였다.

이 외에도 Docker를 활용한 컨테이너화, Jenkins를 통한 CI/CD 파이프라인 구축, Nginx를 통한 정적 파일 서빙 및 리버스 프록시 역할 수행 등 전반적인 DevOps 환경을 개선하였다.

5. 버전별 주요 업데이트 내역

지난 1년간 진행된 주요 기능 추가 및 개선 내역은 다음과 같다.

  • API 2.2.1:
    • 성능 개선 (Throttler): 인증 컨트롤러에 Rate Limiting을 적용하여 성능, 부정 요청을 제한하는 것으로 개선하였다.
    • 테스트 계정 관리 API: 유저 계정에 다중 테스트 계정 삽입 및 삭제 API를 개발하였다.
    • 사용자 및 자녀 컨트롤러/서비스 리팩토링: 자녀 삭제 로직을 개선하고, 유저 생성 과정 로직을 리팩토링하였다.
  • API 2.2.2:
    • 데이터 처리 개선: 데이터가 없는 경우 API에서 빈 배열을 전달하고, DTO를 Partial로 래핑하여 유연성을 높였다.
    • 일일 로그 Public API 핫픽스 및 개선: 일일 로그 치료 결과 Public API 버그를 수정하고, Prisma find 메서드 및 MongoDB relation 활용을 개선하였다.
  • API 2.2.3:
    • 로깅 점검 및 개선: 스케줄링 모듈의 로거를 점검하고, 로깅 누락 문제에 대응하기 위해 로깅을 추가하고 에러 핸들링을 강화하였다.
    • 자녀 등급 추가: Child 모델에 grade 필드를 추가하였다.
  • API 2.3.0:
    • 로깅 통합 및 리팩토링: 로깅 통합 작업을 완료하고, 파일 로거 및 Winston 설정을 개선하였다.
    • 오늘 치료 결과 전송 기능 수정: 오늘 치료 결과를 확인할 수 있도록 기능을 수정하였다. Agenda 라이브러리를 도입하여 치료 기록의 수집과 서버 성능 최적화를 감안하여 개발함.
    • 사용자 속성 및 임상 시험 기능 구현: 사용자 속성 및 임상 시험 관련 기능을 구현하였다. 임상 대상자에 맞춰 손 동작 등을 변경될 수 있는 기능을 Decorator 와 Interceptor 를 활용하여 개발하였다. 이를 통해 기존 비즈니스 로직을 그대로 사용 가능하도록 개발함.
  • API 2.4.0:
    • 미니게임 관리 모듈: 미니게임 관리 시스템, 모듈형으로 구현하여 다양한 미니게임 추가를 코드 개발 없이 가능하도록 구현함.
    • 게임 설정 분리: 게임 설정을 독립적인 모듈로 분리하였다. 미니게임과 연동되는 게임 설정 기능.
  • API 2.5.0:
    • 아산 임상 시험 개정: 아산 임상 시험 관련 기능들의 개정 요청에 따라 작업을 진행하고, Prisma 모델링 및 치료 캘린더 컨트롤러 API를 업데이트하였다. 요청 사항에 따라 전체 기능 리 팩토링 완료 하였다.
  • API 2.6.0:
    • 자녀 진단 기록 추가: 새로운 진단 기록 기능 및 기본 관리 API 명세 작성을 완료하였다.
    • 관리자 추가 보안 기능 구현: 관리자의 유저 비밀번호 초기화 등의 어드민 기능 구현을 완료 하였다.
  • API 2.7.0:
    • 일일 로깅 및 로그인 프로세스 리팩토링: 일일 로깅 및 로그인 프로세스 리팩토링을 진행하였다.
    • 코드 컨벤션 통합: 코드 컨벤션 통합 작업을 진행하였다. 기존 레거시 코드들에 대한 컨벤션과 맞지 않은 개발 사항들 전체를 분석 및 정리하였다.
    • CI/CD 개선: deploy-to-dev.yamlgemgem-hosted-test.yaml 파일을 만들어서 AWS App Runner 에서 GitHub Action의 방식으로 CI/CD를 개선하였다.
  • API 2.8.0:
    • 미국 SMS 인증: 외부 서비스를 활용하여 미국 SMS 인증 기능 개발을 진행하였다.
  • API 2.9.0:
    • 구독 시스템 구현: 구독 구글 및 애플의 스토어 및 결제 시스템 구현을 진행하였다. 전체 라이프 사이클 중 4개
    • 로컬 회원가입 개선: 로컬 회원가입 시 이미 가입된 메일이면 가입 수단을 리턴하도록 변경하였다.
    • 에러 핸들링 개선: 에러 핸들링 응답 바디 복구 및 iOS 취소 조건 추가를 진행하였다.
  • API 2.10.0:
    • 미니게임 결과 제한/잠금 리팩토링: 미니게임 결과 제한 및 잠금 관련 리팩토링을 진행하였다.
  • API 2.11.0:
    • Notion CRM 기능 구현: Notion API를 활용하여 고객들의 정보를 비개발 구성원이 보기 좋게 동기화 기능 구현하였다.
    • 새로운 미니게임 설정: 새로운 미니게임 설정 관련 작업을 진행하였다.
    • 일반 사용자 손 스케줄링: 일반 사용자 손 스케줄링 기능을 추가하였다.
    • 새로운 회원가입 로직: 새로운 회원가입 로직을 추가하였다.
    • 외부 API 모듈화: 모든 외부 API 부분을 수정하고 모듈화하였다.
  • API 2.12.0:
    • 치료 캘린더 수정: 치료 캘린더 관련 버그를 수정하였다.
    • 출석 기능 업데이트: 출석 기능 관련 새로운 업데이트를 진행하였다.
    • 회원 탈퇴 로직 리팩토링: 회원 탈퇴 로직 리팩토링을 진행하였다.
    • DI 구조 개선: DI(Dependency Injection) 구조 개선을 진행하였다.
    • Discord 개선: Discord 관련 개선을 진행하였다. 기존 디스코드 로깅의 간단한 시스템으로 인해 Rate Limitting 문제를 발견. 이에 따라 테스트 후 얼마나 로깅 소실 되는지 판단하고, 개선을 위한 구조 리펙토링 수행. 워커 스레드와 큐 기능을 활용, 지수 백오프 로직 등을 통해 99% 유지 상태로 구현 완료.
    • 컴파일러 빌드 구조 수정: 컴파일러 빌드 구조를 SWC와 babel 컴파일러 사이에서 필요 상황에 따라 컴파일 방식을 선택하고, 빌드 속도 등을 개선함.
  • API 3.0.0:
    • 주요 변경 및 버전 업그레이드 이유: API 3.0.0은 단순히 기존 기능의 개선을 넘어, 레거시 코드에 대한 대대적 리펙토링 및 모듈 구성 변경, 새로운 결제 시스템(구독 인상 대응, 스토어 이벤트 핸들링)의 대대적인 도입, 새로운 게임 콘텐츠(미니게임, VideoPinkfong)의 추가, 그리고 핵심 사용자 기능(운동 리포트)의 구현 등 서비스의 핵심 비즈니스 로직과 사용자 경험에 큰 영향을 미치는 대규모 기능들이 통합된 버전이다. 또한, 이러한 대규모 변경 사항들을 안정적으로 서비스하기 위한 CI/CD 개선 및 여러 중요한 버그 수정이 동반되었다. 이러한 변화의 폭이 컸기 때문에 메이저 버전 업그레이드(2.x.x -> 3.0.0)가 이루어졌다.
    • 서버 구조 개선: 공통 모듈의 라이브러리화 이전에, common 모듈로 분리하여 DI 적절 구조로 리펙토링, feature 에 해당하는 기능들만 분리하고 순환의존성 문제 대거 개선.
    • 새로운 스토어 이벤트 핸들링 및 구독 인상 대응: 구독 인상에 대한 핸들링 구현 및 새로운 스토어 이벤트 핸들링 기능이 완료되었다.
    • 미니게임 추가 및 기존 게임 구조 변경: 미니게임(brushing, cooking), 영상 재생 게임이 추가되었고, UpStairs 복잡한 구조를 리펙토링하였다.
    • 운동 리포트 기능 추가: 메인 화면 운동 리포트를 위한 기능이 추가되었다.
    • CI/CD 및 개발 환경 개편: Husky 와 같은 린팅 후킹을 통해 개발 시 놓치기 쉬운 영역, 코드 포멧팅을 휴먼 에러 케이스를 없도록 개선, 개발 환경의 Container 화 스크립트를 전체 구축하여 local, Dev, Staging, Prod 환경을 로컬에서 테스트 및 빌드 가능하도록 구축함. AWS 설정을 개선하여 무중단 배포가 가능하도록 구축 이후 Jenkins 를 기반으로한 CICD 파이프라이닝 구축.
    • 린트 설정 동기화: 린트 설정 동기화 및 포맷팅 작업을 진행하였다.
    • Google 구독 취소 API: Google 구독을 업체에서 갱신 취소를 신청하는 API 기능을 탑재하였다.

6. 1년의 여정, 그리고 아쉬운 점과 향후 학습/개선 사항

나의 1년간의 작업은 NestJS 기반의 백엔드 시스템을 안정적이고 확장 가능하게 구축하는 데 매우 큰 기여를 하였다. 특히, 체계적인 모듈화, 상세한 데이터 모델링, 그리고 DevOps 자동화에 대한 노력은 내가 무얼 해야 하고 Backend 개발이란게 무엇인지를 이해하는, 치열한 시간이었다고 평가한다.

하지만, AI의 비약적 발전 덕에, 그리고 그렇게 점점 더 업무 효율이 올라가면서, AI가 이 서버를 분석하면 어떤 부분에서 아쉬운 점과 향후 학습 및 개선을 통해 더욱 발전할 수 있는 영역이 있는지를 분석하였고, 이를 기반으로 다음 개발의 역량의 지표로 삼으려고 한다.

  • 문서 관리 프로세스 개선: 문서의 최신성을 유지하고 협업 과정에서 발생할 수 있는 문제를 방지하기 위한 명확한 문서 관리 프로세스(예: 문서 변경에 대한 코드 리뷰 강화, 자동화된 병합 도구 활용, 문서 버전 관리 전략 수립)의 필요성을 느꼈었다. 물론 테크 리더께서 온전히 나를 신뢰해주시고 덕분에 개발 요청 상황과 현 서버들의 상태를 기반으로 개선작업들을 할 수 있었지만, 결과적으로 다른 사람들과 함께 개발한 것이 아니라는 점, 이런 프로세스가 미리미리 준비되지 않았기 때문에 아쉽다고 느꼈다.
  • DB 로깅의 안정성 강화 및 로깅 시스템 강화: LoggerMiddleware가 기존의 로깅 방식이고, 새롭게 만든 HttpLoggingInterceptor 간의 역할 중복이 있었다. 특히나 프로덕션 수준에서 볼 때 로깅으로 서버에 시스템 부하를 최소화 시켜야 할 것이며, 에러 상황을 판단하기 위해서 파일 로그가 반드시 어딘가에 저장되도록 만들어야 할 텐데, ASG 로 인스턴스가 최신화가 되는 구조는 파일을 날려버리는 형태가 되다보니, 이를 고려하지 않은 설계는 대단히 초보적인 실수라고 보였다.(하다 못해 EC2 볼륨 설정을 바꿔도 충분하리라…)
  • 민감 정보 마스킹 활성화: 보안 강화를 위해 로그에 민감한 정보가 기록되지 않도록 maskSensitiveInfo() 함수를 구현했었다. 하지만 로그들 전체를 분석하거나, 체계화 하는 과정을 거치지 못해, 활성화 시키지 못했다. 민감 정보를 잘 처리해내고, 로깅은 확실히 남기는 두 가지를 잘 다 이루었다면 좋았으리라 생각이 된다.
  • Nginx 역할 확장 고려: Nginx가 현재 정적 파일 서빙 및 로깅 역할을 한다고 명시되어 있는데, 향후 API Gateway 역할(인증, 라우팅, 속도 제한 등)을 확장하여 백엔드 서비스의 부하를 줄이고 보안을 강화하는 방안을 학습하고 적용하는 것을 고려할 수 있다.
  • 보다 진보된 파이프라이닝: CICD 의 구현은 가능했으나, 다양한 기능으로 적절히 서버로 서빙되지 못한다는 점, 유저 활성화가 아쉬워 보다 넓은 스케일링에 도전해보지 못했다는 점은 다소 아쉬운 부분이다.
  • 어드민 기능 구현의 부재: 백엔드 개발만이 전부가 아니라, 데이터의 취득, 데이터 가공, 데이터 사용 대상이 보다 쉽게 접근 가능한 구조화가 필요했으나, 그러한 개발은 시간상 하지 못했었다. 이러한 점은 이후 운영이란 차원에서 매우 아쉬움으로 남았다.

7. 결론

지난 1년간 A-api 서버 프로젝트를 개발하며 NestJS 기반의 견고한 백엔드 시스템을 구축하고, 다양한 핵심 기능들을 구현하였다. 특히 DevOps 프로세스 개선을 통해 개발 생산성과 배포 안정성을 높이는 데 기여하였으며, 로깅 시스템 분석 및 잠재적 오류 분석을 통해 시스템의 안정성과 품질을 향상시키기 위한 노력을 지속하였다. 그 결과들은 3.0 버전을 구현할 수 있었고, 테크 리더님에게도 상당한 호평을 받은 대단히 체계화된 구조라는 칭찬을 받았고, 이는 다음 단계로 가야하는 구나, 갈 수 있겠구나 하는 자긍심, 자부심도 될 수 잇었다고 생각한다.

여러 상황이 겹쳐 이제는 해당 업체를 나와 AI 라는 키워드 DevOps 라는 키워드를 더 집중하기 위해 노력하려고 하는데, 이러한 나의 노력이 다음 곳에서 다시 또 무언가로 나타내질 수 있기를 기원한다.

나란 존재 빠이팅…