새로운 출발을 위한 점검 — 4년의 코드를 다시 열어보다
요약: 기존 AWS 싱가포르 기반의 React + Java 구조를 완전히 해체하고, Tencent Cloud로 전환했다. 프런트는 Nuxt + Vue3로 재구성, 배포는 Drone CI/CD와 COS/CDN 자동화로 처리한다. 백엔드는 내부 전용 서비스로 비공개 운영한다.
1. 초기 상황 — AWS 싱가포르, 복잡하게 얽힌 실험의 흔적
REINDEERS 초기 버전은 AWS ap-southeast-1 (Singapore) 리전 위에서 운영되었다. 하지만 이는 서비스 운영을 위한 구조가 아니라 “학습” 중심의 환경에 가까웠다. EC2, S3, RDS, CloudFront, Lambda 등 AWS 주요 기능이 모두 혼재했지만, 어떤 구성요소도 서로 명확히 연결되어 있지 않았다.
인프라 구성 예시 (기존 AWS)
VPC (10.0.0.0/16)
├── Subnet-Public (10.0.1.0/24)
│ ├── EC2: react-build-server
│ └── EC2: spring-api-server
├── Subnet-Private (10.0.2.0/24)
│ ├── RDS: mysql-v1
│ └── ElasticCache: disabled
└── SecurityGroup: default (80/443 open to 0.0.0.0/0)
IAM은 Root Key로 접근했고, Auto Scaling Group은 설정만 되어 있을 뿐 실제 트리거가 비활성화되어 있었다. CloudFront는 S3 버킷에 연결되어 있었지만, TTL(캐시 수명) 설정이 24시간으로 고정되어 실시간 반영이 어려웠다. API 응답 속도는 태국에서 평균 620ms, 말레이시아에서 710ms로 측정됐다. 이 수치는 글로벌 B2B 플랫폼으로는 치명적이었다.
2. 기술 스택 한계 — React + Java 구조의 제약
프런트엔드는 React, 백엔드는 Java(Spring Boot) 조합이었다. 초기 구축 목적은 “기능 구현 속도”였으나, 이 구조는 시간이 지날수록 관리 복잡성이 기하급수적으로 커졌다.
문제점 요약
- React: SSR(Server-Side Rendering) 미지원 → SEO 불가, 첫 페이지 로딩 지연
- Java(Spring Boot): API 버전별 배포가 불가, 빌드 속도 느림(평균 4~6분)
- 프런트와 백이 강하게 결합되어, 코드 한 줄 수정에도 전체 배포 필요
- 다국가 언어(i18n), 화폐, 세율 관리가 하드코딩 상태
당시 코드는 기능 단위로는 “완성”되어 있었지만, 운영 단위로는 “분리 불가능한 덩어리”였다. 서버 장애가 발생하면 전체 서비스가 멈췄다.
3. 백엔드 정책 — 내부 전용(Private Service Layer)
이번 개편에서 우리는 백엔드를 완전히 비공개 구조로 전환했다. 외부에서 접근할 수 있는 공개 API는 존재하지 않으며, 모든 통신은 내부 게이트웨이를 거쳐야 한다. 내부 인증은 JWT + IAM Key로 제한되며, 외부 방화벽(WAF)과 보안그룹으로 이중 보호된다.
백엔드 접근 구조 (Simplified)
[ Client (Nuxt/Vue3) ]
↓ HTTPS (443)
[ CDN Edge (COS/CDN) ]
↓ API Proxy (Internal Gateway)
[ Private Network ]
↓
[ API Services - Internal Only ]
├── User Service (Python FastAPI)
├── Order Service (Node.js)
├── Logistics Service (Go)
└── Payment Service (Private Endpoint)
모든 API는 서브도메인 구분 없이 내부 DNS로만 라우팅된다. 외부에서 백엔드 서버에 직접 접근하는 것은 기술적으로 불가능하다.
4. 프런트엔드 전면 교체 — Nuxt + Vue3로 재구성
새로운 프런트엔드는 Nuxt (3.x) + Vue3를 기반으로 했다. 이 조합은 SSR, CSR, SSG를 모두 지원하며, 글로벌 환경에 적합하다.
Nuxt 설정 예시
// nuxt.config.ts
export default defineNuxtConfig({
ssr: true,
modules: ['@nuxtjs/i18n', '@pinia/nuxt', '@nuxt/image'],
i18n: {
locales: ['ko', 'en', 'th', 'zh', 'ms'],
defaultLocale: 'en',
vueI18n: './i18n.config.ts'
},
runtimeConfig: {
public: {
apiBase: '/api'
}
}
})
빌드 후 생성된 정적 자산은 `/dist` 또는 `/.output/public` 경로에 저장되고, Tencent Cloud COS로 업로드된다. 이후 CDN Edge에서 자동 캐싱되어 지역별로 빠르게 반영된다.
5. CI/CD 전환 — Drone + COS + CDN 완전 자동화
기존 Shell Script 기반의 배포는 유지할 수 없었다. Drone을 도입해 완전 자동화된 파이프라인을 구축했다. 빌드-테스트-배포-캐시 무효화까지 단일 YAML로 관리된다.
Drone 파이프라인 (요약)
kind: pipeline
type: docker
name: nuxt-deploy
steps:
- name: build
image: node:20
commands:
- npm ci
- npm run build
- name: upload
image: tencentcloud/tencentcloud-cli
environment:
COS_SECRET_ID: <secret_id>
COS_SECRET_KEY: <secret_key>
COS_BUCKET: reindeers-fe
COS_REGION: ap-hongkong
commands:
- tccli cos PutObject --bucket $COS_BUCKET --region $COS_REGION --file ./.output/public --key /
- name: purge-cache
image: alpine/curl
commands:
- curl -X POST https://cdn.tencentcloudapi.com/purge?path=https://static.reindeers.com/*
Drone은 Git push 이벤트에 의해 자동으로 트리거되며, COS 업로드 후 CDN 캐시를 무효화한다. 전체 배포 소요 시간은 평균 10~12초 이내다.
6. 클라우드 전환 — AWS → Tencent Cloud
Tencent Cloud로의 전환은 단순한 클라우드 이동이 아니라, 글로벌 네트워크 성능과 운영 효율성의 재설계였다.
| 항목 | AWS (기존) | Tencent Cloud (현재) |
|---|---|---|
| 리전 | ap-southeast-1 (Singapore) | ap-hongkong (Main) / ap-seoul (DR) |
| 스토리지 | S3 + 수동 업로드 | COS + Drone 자동 업로드 |
| CDN | CloudFront 수동 캐시 | Tencent CDN 자동 캐시 무효화 |
| DB | RDS (단일) | TencentDB + DTS 복제 |
| DNS | Route53 | DNSPod Geo Routing |
| 배포 | Shell Script | Drone CI/CD 자동화 |
결과적으로 태국과 말레이시아 지역의 평균 응답 속도는 650ms → 180ms로 단축되었다. 또한 데이터베이스 복제와 DR 구조를 통해 장애 복구 시간을 1분 이내로 줄였다.
7. 결론 — 5%의 현실, 95%의 가능성
점검을 통해 드러난 현실은 냉정했다. 우리가 가진 것은 완성된 플랫폼이 아니라, 실험의 결과였다. 그러나 그 5%의 코드가 나머지 95%를 설계할 수 있는 기반이 되었다.
- 클라우드: AWS 싱가포르 → Tencent Cloud (홍콩/서울)
- 프런트: React → Nuxt + Vue3
- 배포: Shell Script → Drone CI/CD
- 스토리지: S3 → COS + CDN 자동화
- DB: RDS → TencentDB + DTS 복제
- DNS: Route53 → DNSPod Geo Routing
- 백엔드: Private Service Layer, 공개 API 없음
결론: 코드를 바꾼 게 아니라, 운영 기준을 새로 세웠다.
이제는 기능이 아니라 시스템을 만든다.
Comments
Post a Comment