0. Introduciton

신규 프로젝트를 진행하는 과정에서 backend 개발자로서 Legacy 코드들의 대대적인 개선, 코드 품질 향상, 그러면서도 향후를 고려한 다양한 DevOps 적인 요청이 들어왔다.

아무래도 클라이언트 처럼 직접적인 개발이 필요한 입장에선 타이트한 일정 상황에서 구현이나, 현재의 목표에 중시해야 했지만, 백엔드라는 특성 상 테크리더의 요청으로 백엔드는 보다 안전하고, 보다 관리 용이성을 높이는 것 역시 추가로 필요하다는 것이 리더의 설명이자, 계획이었다.

그렇기에 나 역시 개발의 핵심 방향성을 잡고 한달의 달리기를 위한 목표 설정을 했었다. 내용은 다음과 같다.

  1. 배포와 개발 환경을 최대한 독립화하자.
    • Docker 이미지화 + ECR 을 활용하기 + 시작 템플릿을 활용하여 1 딸깍이면 신규 이미지를 불러오돌 구현하자
    • Jenkins 를 활용한 무중단 배포를 최대 목표로 잡자.
  2. 배포, 개발의 과정에서, 기계적으로 코드 품질을 유지할 장치를 마련하자.
    • Husky 의 도입을 통해 git 이벤트를 추적해 특정 품질 최적화 기능을 구동시킨다.
    • lint, lint-staged, tsc 컴파일러의 typechecking 등을 내부에 추가하기
  3. 코드 외적으로 Ops 관점에서 최대한 안정성을 확보하자.
    • Cloud Watch 서비스를 보다 고도로 파악하고 적용하자, 특히 각 서버의 특성(AI, API) 이에 최적화된 헬스 체킹의 기반을 준비하자.
    • 국내에선 당장 문자로 인스턴스 상태 알림을 수신 가능하게 만들자

말이 길어졌다. 사실 해당 프로젝트에 대해서도 정리가 필요하긴 할 것으로 보이지만, 그것은 또 다음 시간에… 사실 너무 바빠서 그 중간의 내용에 진행했던 내용을 다 적었다간, 논문이 되어버릴지도 모른다. 😂

1. SMS 어떻게 보내볼까

사실 요즘은 딸깍- 이라는 키워드를 활용하듯 B2B 서비스나, AWS 에서도 돈을 더 내고 고급 서비스를 써도 된다. 심지어 글로벌이라면 SNS 서비스를 활용하면 그냥 그걸로 끝날지도 모른다. 하물며 lamda 와 SNS API 를 활용하면 아마 그걸로 충분히 구현 될 수도 있다.(그러니 사실 따라하라고 추천하고 싶진 않다… 이 글의 목적도 엄밀히 말하면 서버리스 서비스와 AWS 의 서비스를 보다 내 스타일로 써보면 어디까지 되나? 가 궁금해서 일을 크게 키운거기도 하다.) 어쨌든, 그리하여 여러 고민을 해보았다.

  1. 기존 쓸 수 있는 서비스를 최대한 활용하고 싶다.
  2. 비용이나, 헬스 체킹에 너무 시간을 쏟기는 싫다.
  3. 배포 관리하기 귀찮다!
  4. 서버리스 서비스를 한번 써보고 싶다..!
  5. 대신 종속성은 최소화 하자

요정도를 고려해보았다.그리하여 나온 구조는 다음과 같은 구조이다.

1-1. Event 추적 ~ Proccessor 로 송신

CloudWatch 는 기본적으로 모니터링할 대상을 바라본다. 특히 여기서 핵심은 각 서버마다 이벤트를 지정 시 Health 의 지표가 될 만한 핵심을 파악해야 한다는 점이다. 연산장치인 CPUUtilization 을 보는 지표도 있겠지만, 자사의 경우 유저의 일정한 수준의 처리 성능을 중요시 했고, 따라서 네트워크 사용량에 대한 구체적인 정보 쪽이 훨씬 서버 관리에 중요하였다.

여기서 중요한 포인트라고 생각되는 것은, 사실 SNS 를 쓰지 않아도 된다는 사실이다. SimpleNotificationService 를 통해 주제를 설정하지 않더라도, CloudWatch 는 직접 Lambda 를 호출하는 게 가능하다. 하지만 그렇게 하게 되면 CloudWatch 의 알람마다 Lambda 설정을 건드려야 할 필요가 발생하고 만다. 따라서 1:N 의 관계가 필요하다면, 반드시 중간 매개체로 N이 1과 바로 연결되지 않는 구조가 필요하기 때문에 SNS 의 존재가 꼭 중요하다 볼 수 있다.

그 뒤 Lambda 는 SNS 를 통해 일원화 되어 들어오는 이벤트들에 대해 내가 지정한 위치의 URL 로 보내면 되는데, 이때 주요한 내용으로 알아두면 하는 것은, ‘파이썬’ 기반으로 사용한다고 하더라도, 순정 상태에선 외부 라이브러리를 쓰지 못한다는 점이다(괜히 이점 때문에 AI 바이브 코딩 했다 왜 안되나 한참 찾았다.) 다행히, 수동으로 넣어줘도 되고, 간단한 통신 정도만 수행한다면 제일 좋은 건 내장 통신 라이브러리로 전달만 하는 것이다.

1-2. 수신 및 프로세싱 ~ SMS

Processor 라고 이름을 지은 이 서버의 목적은 Naver의 SENS API 에 맞게 전달해주는 역할이다. NaverCloud 라니! 라고 생각할 수도 있다. 하지만 어떻게 하겠는가, 회사에선 이걸 쓰고 있는데(…) 이미 실서비스에 사용 중이기도 했고, API 가 익숙하다는 점을 생각하니, 이부분 만큼은 빠르게 가기로 했다.

그리하여 API ~ SMS 는 처리 했고, 그 다음의 포인트는 역시 수신 받는 곳의 구현이었다.

NestJS 를 사용해도 되긴 했다. 왜냐면 이미 NestJS 서버는, 우리 회사 상용 서버 역시 3.0.0에 해당하는 수준으로 판을 올려놓은 상태였고, 해당 템플릿을 사용한다면 충분히 할만하지 않을까? 싶었다.

그러나 이미 써본 기술, 동시에 장황하게 설정된 기능들이나 관리 코드들을 그대로 가져오기엔 용도에 맞지 않는 다는 점은 분명해 보였다. 이에 이왕 현재 자사 서비스를 위해 사용 중인 FastAPI 를 써보면 좋겠다는 생각을 했고, 이왕 하는 김에 의존성의 관리는 Poetry 를 사용해보기로 했다.

써본 후기로는… Poetry 는 가상환경 커맨드 안쳐도 된다! 편하다! 와, FastAPI 는 우와 자동 Swagger 쩐다…! 😯 라는 느낌을 받았다. Pydantic을 통한 자동 데이터 유효성 검사, 그리고 /docs 경로에 자동으로 생성되는 API 문서는 Postman 없이도 즉시 테스트가 가능한 환상적인 개발 경험을 제공했다.

여기서 핵심은 API 를 수신하고, 각 이벤트에 맞춰 지정된 대상에게 전달하는 구조를 작성하는 것이었고, 그 뒤엔 이를 배포하는 걸 조금이라도 ‘편하게’ 하는 방법을 구현하는 것이었다.

이에 ECR(Elastic Container Registry)에 tag 를 활용하여 가장 최신으로 관리되는 것, 그리고 EC2를 활용하여 인스턴스로 관리하는 것이 아니라, 알아서 필요시 사용되기 + 스케일러블 하게 대응이 가능하게 만드는 것으로 하였다.

특히나 이렇게 구현 하면서 ECS(Elastic Container Service) 를 처음 써보게 되었는데, Task 라는 개념으로 Fargate 방식을 제공하는 ECS 를 사용했고, 초기 사용은 복잡했지만, ELB 와 기타 조합을 통해 알아서 이미지로 갱신되도록 만들었다. Task Definition, Service, 그리고 ALB Target Group과 Security Group 간의 네트워킹 설정은 처음 접하는 입장에서 많은 시행착오를 요구했다. 코어 0.25개에 0.5GB 의 메모리 사용은, 인스턴스처럼 항상 준비된 하드웨어요금이 나가는 것보다 계산 시 훨씬 경제적이었다. (물론 함정은 다른데 있다)

2. 그래서 이 구조의 장점과 단점은 뭘까

전체적으로 작업이 끝나고 SMS 를 받아보니, 확실히 이메일 받을 때 보단 상태를 확인도 가능하고, 설정에 따라 주기적으로 헬스 체크도 가능해진다는 점은 매우 편리하였다. 바이브 코딩 + 각종 기술 맛보기 + AWS 서비스 구성을 배워 본다는 점에선 매우 영양가 있던 시간(?) 이라고 밖에 할 말이 없을 것 같은데, 그럼에도 구조를 짜고 내용을 바라보면 다소 아쉬움도 남았다. 😎

2-1. 장점 (Pros)

우선, 완벽하게 모듈화된 설계 구성은 예를 들어 lambda 하나로만 한다거나 하는 것 보다 훨씬 직관적이었다. 각 역할(이벤트 수신, 처리, 발송)이 명확했고, 문제가 발생했을 때 어느 지점의 문제인지 파악하기 용이했으며, 특히 각 영역의 수정이나 배포 등에서 매끄럽다고 생각한다.

데이터 편집은 FastAPI 서버를 건드리면 되고, 알람은 CloudWatch 경보를 추가하거나 세밀하게 만들어 주면 될 문제였다. lambda는 필요할 때만 활성화 되니 필요하지 않을 땐 사실상 없는 것이나 마찬가지. Fargate의 경우에도 그냥 켜둔 다면 0원에 수렴하기 때문에 가장 작은 EC2 인스턴스를 쓴다고 해도, 이미 프리티어는 넘어버린 자사 입장에선 훨씬 양호했다.

개인적인 면에서도 서버리스 + ECR + 도커 이미지로의 빌드 + 태깅을 통한 최신 이미지 추적의 형태는 개발 이후 복잡할 수 있는 배포 과정을 deploy.sh 스크립트 하나로 매우 깔끔하게 만들어주었다. Fargate 라는 서비스는 생각 이상으로 편리하다는 점, 직접 프로비저닝이나 관리 안해도 되는 점은 확실히 좋았다. 응용할 수 있는 방법은 특히나 많았고, 서비스의 특성에 따라 경제적인 서버 운용에 키 역할을 해줄 수 있겠구나 하는 생각을 할 수 있었다.

람다의 경우, 생각 이상으로 너무 편하다는 생각은 확실히 들었다. 데이터의 중간 인터셉터 역할을 하기엔 확실히 편리했고, 그냥 코드를 짜고 deploy 만 하는 순간 끝난다는 점은 ‘서버로 만들긴 애매한’ 무언가를 위한 상당히 좋은 툴이었다는 경험이(?) 추가 되었다고 볼 수 있을 것 같다 😁

또한 먼 이야기겠지만, 이벤트가 늘어나면 조건만 추가하면 자연스럽게 스케일러블 한 설계가 가능하고, 민감한 정보는 모두 컨테이너화 되어 있으니(특히 전달 받는 분들의 연락처 등), 이러한 점에서 알림 서비스로서 향후를 고려한 꽤 괜찮은 구성이 아닐까 싶다.

2-2. 단점 (Cons)

AWS 는 바보가 아니다. 이렇게 편리한 서비스 옵션들, 모듈들을 무료로 푸는 데는 나름의 악랄한 부분이 있지 않으면 안된다(…) 등가교환 법칙…

우선 핵심은 모듈화 하는 과정에서 Fargate 의 구성 난이도가 오히려 무지하게 높다는(!) 생각을 할 수 있었다. 해당 서비스의 의도와 구성에 대한 이해도가 없지만 AI 를 활용해서 그나마 해결했지, 이걸 모르는 사람이 혼자 해본다? 과연 될까 싶은게 솔직한 감상이었다(…)

뿐만 아니라, SMS 로 알림을 보내는 게 핵심인데, 그걸 위해 하루 정도 소요되는 시간을 들여 만든다는 것은, 어떤 점에선 최악의 시간 낭비일 수도 있지 않나 싶다. 서버 상태의 알림은 주의-경보 이정도만 해도 되는데, 그걸 위해 이렇게까지 투자하는 것은 어쩌면 라이브 서비스에서도 어느정도 운영을 했다고 생각하니까지, 초기 팀이나, 초기 서비스에 이렇게 알림에 하루 쓴다는건 그게 맞나? 싶은 생각을 할 수 있었다.

모듈화가 장점이지만, 동시에 차라리 이렇게 안만들고 통짜로 만들어도 되는거 아닌가? 생각도 문뜩 들었다.

마지막으로 가장 최악은, 비용에 대한 부분이다. SMS 쓴다고 돈을 써야 하는 부분을 제외하면 이번 작업 과정 전체는 충분히 경제적이었다. 0.25 개의 코어 활용, 0.5GB 메모리, 일반 인스턴스론 불가능할 수준으로 작은 수준이다보니, 역할조차 작으니 말이 안되게 작은 스펙이지만 충분히 잘 돌아갔다.

하지만, Fargate, Lambda 등은 문제가 아닌데, 이를 통신하기 위해 연결해야 하고, 그렇게 되는 과정에서 ELB(Elastic Load Balancer)를 비롯한 각종 네트워킹 서비스들의 비용은 당연히 부과 되는 것이었고, 그냥 단순히 Fargate 의 컨테이너가 돌리기만 한다면 좋았겠지만 그렇지 못하다보니 계산 시 한달에 3만원 ~ 많게는 4.5 만원까지도 나올 수 있다는 계산을 다 만들고 했다(…)

아니, 인스턴스 24시간 켜놓고 3-4만원 나오는게 맞추면, 이것저것 다할 수 있는데? 전형적인 조삼모사

… 여하튼 할 말은 많지만 V1 버전은 그리하여 마무리하고 나니 아쉬운 점, 특히 투머치 한 점을 보완해야 한다는 점을 깨달을 수 있었다

3. 결론

요즘 키워드 DevOps, GitOps 가 특히 중요하다는데, 확실히 재미있는 영역이다. 그리고 더불어 AWS 와 같은 서비스에 대해 개념의 이해도 착실히 늘어가고 있다는 점에서 자신감이 붙는 포인트가 아닐까 싶다.

특히나 서버리스는 말로만 서버리스라고 했지, 막상 테스트 삼아 만들어 본 적은 없었는데, 만들면서 왜 필요한지 확실히 체감을 할 수 있었다. 서버라는 어떤 스테레오 타입 구축을 위한 노력 없이, 기능적 최소화, 자원 사용의 최소화라는 관점에선 꽤나 멋진 기능이고, 활용도가 상당하다는 점에서 괜찮다는 생각이 들었다.

다만, 역시나. 이렇게 편하면 괜한게 아닌것 처럼(…) 그 서비스 자체는 괜찮지만, 사실상 반 필수로 써야 하는 다른 서비스들에 대해 요금설계가 확실히 되어 있다는 점에서, “이럴 거면 그냥 EC2 연결하지 왜…?” 라는 말이 튀어나오게 되는게 사실이다(개발자 입장에선).

그리하여 바로 V2 형태로 개선을 했었는데, 그 내용도 조만간 정리해야 겠다.