개발/주니어 개발자의 캐시백 앱 단독 개발기

4. 갑자기 해외 서비스도 추가된다고?

hyjoo1226 2026. 1. 19. 14:22

로그인 기능을 구현한 뒤, 갑자기 이런 이야기를 들었습니다.

 

“이 앱, 해외 서비스도 가능하게 해야 할 것 같아요.”

 

 

 

 

 

1. 예상치 못한 방향 전환

 

"지금 기본 기능도 안 만들었는데..."

솔직히 당황스러웠습니다. 3개월 안에 MVP를 출시해야 하는데, 갑자기 해외 확장까지 고려하라니.

하지만 회사 입장에서는 합리적인 요구였습니다.

투자 유치를 위해 글로벌 시장 가능성을 어필할 필요가 있었고, 실제로도 해외 바이어들과 잦은 미팅을 가지고 있었기 때문이죠.

 

이미 캐시백 앱의 기본 구조를 잡고 기능 개발에 들어간 상태였기 때문에, 이 말은 단순한 요구사항 추가가 아니라 아키텍처 전반을 다시 고민해야 한다는 신호처럼 느껴졌습니다.

 

 

 

 

 

2. 해외 서비스를 위한 설계 고민

 

해외 서비스를 한다는 말 한마디에, 고민해야 할 부분이 생각보다 많았습니다.

 

1. 해외 앱스토어 등록 여부

 

국내 앱스토어에서 등록한 단일 앱으로 노출할 국가를 선택해 운영하는 것은 가능합니다.

하지만 제가 개발중인 서비스처럼 현지의 법/약관/세금/결제 등이 요구되는 경우, 각 국가별 앱스토어를 별도로 등록하는게 안전합니다.

 

각 국가의 법규와 정책이 다른 만큼, 앱을 국가별로 분리해서 출시하기로 결정했습니다.

 

 

2. 다국어 지원, 콘텐츠 현지화

 

앱 자체의 다국어 지원은 그렇다 쳐도, 진짜 문제는 따로 있었습니다.

바로 인앱 브라우저로 접속하게 될 브랜드몰들이었습니다.

입점 예정인 브랜드몰 대부분이 한국어로만 되어 있었고, 각 브랜드사에 "다국어 지원 개발 부탁드립니다"라고 요청하기엔 현실적으로 어려웠습니다. 가장 안정적인 방법이지만, 브랜드 입장에서는 비용이 부담되기 때문이죠.

 

그래서 인앱 브라우저에 실시간 자동 번역 기능을 제공해주되, 민감정보인 약관 및 결제는 따로 개발하거나 전문 번역본을 제공하기로 결정했습니다. 완벽하지 않은 번역으로 인한 법적 리스크는 최소화해야 했습니다.

 

 

3. DB 구조

 

다음 고민은 데이터를 어떻게 관리할지였습니다. 

 

중앙 집중식(한국 본사에서 모든 데이터 관리)

  • 장점: 관리가 쉬움
  • 단점: 데이터 현지화 법규 위반 가능성(GDPR 등)

국가별 분산식(각 나라의 DB 분리)

  • 장점: 법규 준수, 속도 향상
  • 단점: 관리 포인트 증가, 복잡도 상승   

 

저는 둘의 장점을 섞은 하이브리드 + 샤딩 구조를 선택했습니다.

  • Central DB: 브랜드, 카테고리, 글로벌 정책처럼 모든 국가에서 공통으로 쓰는 데이터
  • Local DB: 각 나라 사용자의 개인 정보,  주문·결제 트랜잭션처럼 그 나라에 묶여야 하는 데이터

여기서 Local DB는 단순히 나라 별로 나눈 DB 수준이 아니라, 국가를 기준으로 샤딩된 DB에 가깝습니다.

 

예를 들어

Local_kr(한국 사용자 데이터), Local_vn(베트남 사용자 데이터)처럼 국가 코드로 샤드를 나눠 두고,

API 요청 헤더의 x-country-code를 기준으로 어떤 샤드(Local DB)에 읽고 쓸지 라우팅하는 방식을 고려했습니다.

 

이 구조를 쓰면 GDPR같은 데이터 현지화 법규를 준수합니다. 또한 특정 국가 트래픽이 급증하더라도, 그 나라 Local DB만 수평 확장하는 식으로 샤드 단위 스케일링이 가능합니다.

반면 브랜드 정보나 전역 정책처럼 국가와 상관없이 동일해야 하는 데이터는 Central DB에만 저장해서, 중복과 동기화 문제를 줄이도록 설계했습니다.

 

다만 어떤 브랜드가 어느 나라에서 서비스하는지 등은 관리가 필요했습니다.

그래서 Central DB에 BrandMarket 테이블을 둬서 아래처럼 브랜드와 국가를 매핑하도록 설계했습니다.

 

[BrandMarket]

brand_id country_code is_active
101 (이니스프리) KR true
101 (이니스프리) VN true
102 (설화수) KR true

 

 

4. 국가별 인증, 결제

 

해외 서비스에서 가장 머리아팠던 부분이 인증과 결제였습니다.

국내의 경우 일반적으로 회원가입 시 핸드폰 본인인증, 환급 시 계좌등록을 위한 계좌인증이 필요합니다.

하지만 해외의 경우 핸드폰 본인인증을 한국처럼 하는걸 본 기억이 없는 것 같았습니다. 대부분 이메일이나 간단한 SMS 인증 정도였죠.

캐시백 환급을 위한 결제 시스템도 마찬가지입니다. 한국이 토스페이먼츠같은 PG사를 쓴다 하면, 베트남이나 대만은 전혀 다른 결제 시스템을 쓸 것이 분명했습니다.

 

결국 각 나라마다 완전히 다른 외부 API를 연동하는 상황을 고려해야 했습니다.

그렇게 처음 떠올린 아이디어는 MSA(Microservices Architecture)였습니다.

 

 

 

 

 

3. MSA vs 모놀리스

 

[ MSA(Microservices Architecture) ]

 

MSA는 하나의 거대한 서비스를 여러 개의 작은 서비스로 쪼개서 각각 독립적으로 배포, 확장, 운영하는 아키텍처입니다.

예를 들어 인증 · 주문 · 결제 · 정산을 각각 다른 서비스로 분리하고, 서비스 간에는 HTTP나 메시지 큐로 통신하는 방식입니다.

 

MSA는 장점이 명확해 보였습니다.

각 서비스를 독립적으로 배포 가능하고, 특정 국가의 인증 시스템이 터지더라도 다른 나라는 정상 운영이 가능합니다.

메인 서버와 인증 서버, 결제 서버를 독립적으로 분리시킬 수 있었죠.

인증 서버 (별도)
  ├─ 한국 인증 모듈
  ├─ 베트남 인증 모듈
  └─ ...

결제 서버 (별도)
  ├─ 한국 결제 모듈
  ├─ 베트남 결제 모듈
  └─ ...

 

이론적으로는 가장 깔끔해 보였습니다.

하지만 곧 현실적인 문제들이 눈에 들어왔습니다.

개발자는 저 혼자였고, 초기 서비스 단계에 아직 비즈니스 모델도 계속 바뀌는 상황입니다. 그리고 저는 인프라, 배포, 모니터링까지 전부 감당해야합니다.

 

문제1: 트랜잭션 관리의 복잡도

 

캐시백 환급 과정을 생각해보니 문제가 보였습니다.

1. 사용자가 환급 신청
2. 계좌 인증 (인증 서버)
3. 지갑에서 캐시백 차감 (주문 서버)
4. 실제 송금 처리 (결제 서버)

 

특정 단계에서 실패했을 때 보상 트랜잭션까지도 직접 구현해야 합니다.

각 서버들을 돌면서 "이거 취소해줘"라고 전부 요청을 보내야하기에, 코드 복잡도가 기하급수적으로 늘어나는 구조였습니다.

 

문제2: 개발/운영 리소스

 

혼자 개발하는 입장에서 MSA는 부담이 컸습니다.

  • 서버별로 배포 파이프라인 구축
  • 서버 간 통신 실패 시 재시도 로직
  • 네트워크 지연으로 인한 성능 저하
  • API Gateway, 서비스 디스커버리 등 인프라 복잡도

 

3개월 안에 MVP를 만들어야 하는데, MSA 인프라를 구축하는 데만 시간을 다 쏟을 것 같았습니다.

 

이 상태에서 MSA를 선택하는 건

확장성을 얻는 대신 개발 속도와 안정성을 포기하는 선택처럼 느껴졌습니다. MSA는 너무 무거워보였습니다.

그래서 내린 결론은 모듈러 모놀리스였습니다.

 

 

[ 모듈러 모놀리스 (Modular Monolith) ]

 

모놀리스는 물리적으로 하나의 애플리케이션으로 배포되지만, 내부 구조를 잘게 나눈 모듈들로 나누어 설계하는 방식입니다.

모듈러 모놀리스는 모듈 간 의존성을 명확히 관리해서, 겉으로 보이게는 하나의 서버지만 내부적으로는 MSA처럼 역할이 나뉜 구조를 가지도록 만드는 아키텍처입니다.

 

일반적인 모놀리스와 비교했을 때 둘 다 배포 단위는 하나라는 점은 같습니다.

하지만 모놀리스가 기능 간 경계가 느슨해서 한 모듈이 다른 모듈의 내부까지 참조하기 쉽다면, 모듈러 모놀리스는 내부를 기능별 모듈로 강하게 나눠 낮은 결합도와 높은 응집도를 지닙니다.

 

마침 제가 사용하던 NestJS의 기본 철학도 '모듈 단위 설계'였습니다.

NestJS는 각 모듈이 providers, controllers, services 등을 가지고 있고, 외부에 내보낼 것만 exports로 노출합니다.

이러한 구조는 모듈별 경계를 세우는 모듈러 모놀리스와 굉장히 잘 맞습니다.

// 모듈러 모놀리스 구조

src/
 ├── auth/	# 인증 모듈
 ├── order/	# 주문 모듈
 ├── cashback/	# 캐시백 모듈
 │ 	├── cashback.module.ts		# 모듈 정의
 │ 	├── cashback.controller.ts	# API 컨트롤러
 │ 	├── cashback.service.ts		# 비즈니스 로직
 │ 	├── dto/			# 요청/응답 DTO
 │ 	└── factories/			# 국가별 전략 팩토리
 ├── entities/	# DB 엔티티

 

그래서 모듈러 모놀리스로 코어 로직은 공통으로 유지하되, 인증 · 주문 · 결제 등은 국가별로 갈아끼우기 위해 전략 패턴(Strategy Pattern)을 사용했습니다.

디자인 패턴 중 전략 패턴을 사용하면 새로운 나라 추가 시 기존 코드를 건드리지 않고, 각 나라별 로직을 독립적으로 관리할 수 있습니다.

실제 구현 과정은 추후 별도 글에서 자세히 다루겠습니다.

 

 

 

 

 

4. 현실적 선택: 일단 한국 서비스부터

모듈러 모놀리스와 전략 패턴을 선택했다고 해서 처음부터 모든 걸 국가별로 완벽하게 분리해 구현한 건 아니었습니다.

3개월 안에 MVP를 만들어야 했고, 해외 서비스가 가능한 구조인지가 중요하지 당장 출시하는 것이 아니었기 때문입니다.

그래서 저는 이렇게 결론을 내렸습니다.

 

아키텍처는 해외 서비스를 고려해 설계하되, 실제 구현은 한국 서비스 기준의 단일 구조로 먼저 완성한다.

대신, 나중에 확장할 수 있도록 모듈 경계와 책임만큼은 명확히 나눈다.

 

지금 당장 필요하지 않은 복잡도는 과감히 미루고, 미래에 감당 가능한 형태로만 준비해두는 전략이었습니다.

실제 국가별 분기 처리나 전략 패턴 도입은 한국 서비스가 어느 정도 안정된 후 리팩토링 단계에서 진행했습니다.

결과적으로 이 선택은 개발 속도를 지키면서 확장 가능성을 가진 구조로, 지금의 제 상황에서 가장 현실적인 선택이었습니다.

 

해외 확장에 대한 고민과 아키텍처 설계는 이렇게 정리됐습니다.

다음 글에서는 HTTPS를 위한 로드밸런서 구성 등 인프라 구축 과정에서 겪은 시행착오들을 정리해보려고 합니다.