26년 반기를 돌아보면서
ONTHEBLOCK App Gateway 설명
결론
app-gateway-service는 여러 서비스를 억지로 한곳에 묶는 서비스가 아니다.
목적은 Flutter 앱이 안정적인 gRPC 진입점 하나를 바라보게 하고, 내부의
authorization-service, recommendation-service, chatbot-service 같은
도메인 서비스를 private하게 보호하면서 요청을 올바른 서비스로 전달하는
모바일용 gRPC gateway/BFF를 만드는 것이다.
즉, gateway는 다음 문제를 해결하기 위해 만든다.
- Flutter가 내부 서비스별 URL, IAM, metadata 규칙을 모두 알 필요가 없게 한다.
- 내부 Cloud Run 서비스는 private하게 유지한다.
- 사용자 인증, request_id, timeout, logging, metadata forwarding을 한곳에서 관리한다.
- Flutter-facing proto를 안정적으로 유지하고, 내부 서비스 변경을 앱에 바로 노출하지 않는다.
- chatbot 경로와 recommendation 직접 경로를 명확히 분리한다.
왜 gateway가 필요한가
ONTHEBLOCK은 기능별로 서비스가 나뉘어 있다.
authorization-service: 로그인, 토큰 검증, 사용자 인증recommendation-service: 추천 프로필, 추천 순위, 추천 이유, 추천 이벤트chatbot-service: 챗봇 orchestration, 추천 context 구성, LLM 호출, guardrailllm-serving-service: 모델 inference만 담당map-service,survey-service,board-service: 각자 도메인 기능 담당
Flutter가 이 모든 서비스를 직접 호출하면 앱이 너무 많은 책임을 갖게 된다.
예를 들어 Flutter가 직접 해야 할 일이 많아진다.
- 어떤 화면에서 어떤 backend service를 호출할지 판단
- 서비스별 gRPC address 관리
- private Cloud Run 호출용 Google ID token 처리
- 사용자 bearer token과 Cloud Run IAM token 구분
- 서비스별 timeout 설정
- 서비스별 장애 fallback 처리
- proto 변경에 따른 앱 수정
- request_id, logging, observability 기준 맞추기
이건 모바일 앱이 맡기에는 무겁다. 앱은 사용자 경험과 화면 상태에 집중해야 하고, backend routing과 service-to-service 인증은 서버에서 처리하는 것이 맞다.
그래서 app-gateway-service를 둔다.
gateway가 하는 일
gateway는 Flutter-facing gRPC API를 제공한다.
Flutter는 내부 서비스들을 직접 알지 않고 gateway만 호출한다.
Flutter
-> app-gateway-service
-> internal backend services
gateway는 요청을 받은 뒤 다음 일을 한다.
- Flutter의
authorization: Bearer <user_access_token>metadata를 받는다. - access token을 검증하거나 authorization-service를 통해 사용자 context를 확인한다.
- trusted user context를 만든다.
- 필요한 downstream service를 선택한다.
- 사용자 bearer token을 downstream service에 그대로 전달한다.
- private Cloud Run service에는 별도로 Google ID token을
x-serverless-authorizationmetadata로 붙인다. - gRPC response를 Flutter가 쓰기 좋은 app-facing response로 반환한다.
gateway가 하지 말아야 하는 일
gateway는 도메인 서비스가 아니다.
따라서 gateway는 다음 일을 하면 안 된다.
- 술 추천 점수 계산
- 추천 순위 변경
- 추천 후보 필터링
- 추천 이유 생성
- 설문 데이터 해석
- 지도 DB 또는 추천 DB 직접 조회
- 챗봇 답변 생성
- LLM 직접 호출
- client가 보낸
user_id신뢰
특히 LLM 호출은 절대 gateway가 직접 하면 안 된다.
LLM 호출은 chatbot-service 책임이다.
Flutter
-> app-gateway-service
-> chatbot-service
-> llm-serving-service
gateway가 LLM을 직접 호출하기 시작하면 chatbot-service의 guardrail, grounded context, verifier 책임이 gateway로 새어 들어오게 된다. 그러면 서비스 경계가 무너진다.
chatbot 경로와 recommendation 직접 경로
gateway에는 크게 두 종류의 routing이 생긴다.
1. Chatbot path
챗봇 질문은 gateway가 chatbot-service로 보낸다.
Flutter
-> app-gateway-service
-> chatbot-service
-> recommendation-service
-> llm-serving-service
이 흐름에서 gateway는 챗봇 내용을 해석하지 않는다.
gateway의 역할은 다음뿐이다.
- 인증 확인
- metadata forwarding
- private Cloud Run IAM token 추가
- timeout 적용
- response 전달
질문 의도 분류, 추천 context 구성, LLM 호출, hallucination 차단은
chatbot-service가 담당한다.
2. Recommendation direct path
추천 화면, 홈 hero, 추천 리스트, 추천 이벤트 같은 UI는 gateway가 recommendation-service로 직접 보낼 수 있다.
Flutter
-> app-gateway-service
-> recommendation-service
하지만 이때도 gateway는 추천을 만들지 않는다.
gateway는 recommendation-service가 준 순서와 값을 그대로 앱에 전달해야 한다.
gateway가 하면 안 되는 일:
- 추천 결과 재정렬
- score 재계산
- 가격/장소/재고 추측
- reason code 해석으로 새로운 이유 생성
- survey DB 또는 recommendation DB 직접 조회
authorization과 x-serverless-authorization 차이
이 부분이 가장 중요하다.
Flutter에서 gateway로 들어오는 metadata:
authorization: Bearer <user_access_token>
gateway가 private downstream Cloud Run service를 호출할 때:
authorization: Bearer <user_access_token>
x-serverless-authorization: Bearer <google_id_token_for_downstream_service>
두 token은 목적이 다르다.
authorization: 앱 사용자 인증과 user context용x-serverless-authorization: Google Cloud Run IAM 통과용
절대 user access token을 Google ID token으로 대체하면 안 된다.
recommendation-service나 chatbot-service는 authorization metadata를 보고
사용자를 판단해야 한다. Cloud Run IAM은 x-serverless-authorization으로만
처리한다.
왜 Flutter가 private service를 직접 호출하지 않는가
Flutter가 private Cloud Run service를 직접 호출하려면 앱이 Google ID token을 다뤄야 한다.
이건 좋지 않다.
- 모바일 앱에 server-to-server IAM 책임이 생긴다.
- private backend topology가 앱에 노출된다.
- 서비스 URL 변경 시 앱 수정이 필요하다.
- 권한 관리가 복잡해진다.
- 보안 경계가 흐려진다.
따라서 Flutter는 public gateway만 호출한다.
Flutter -> app-gateway-service
그리고 gateway가 private downstream service를 호출한다.
app-gateway-service -> private chatbot-service
app-gateway-service -> private recommendation-service
Cloud Run 배포 관점
Flutter-facing gateway는 앱에서 호출해야 하므로 public invoker가 될 수 있다.
app-gateway-service: --allow-unauthenticated
하지만 내부 서비스는 private하게 유지한다.
chatbot-service: --no-allow-unauthenticated
recommendation-service: --no-allow-unauthenticated
llm-serving-service: --no-allow-unauthenticated
gateway runtime service account에는 downstream private service 호출 권한을 준다.
roles/run.invoker
그리고 gRPC Cloud Run 배포에서는 HTTP/2가 필요하다.
--use-http2
--port=8080
서버는 반드시 PORT 환경변수로 받은 port에서 listen해야 한다.
비용과 성능 관점
gateway를 추가하면 네트워크 hop이 하나 생긴다.
Flutter -> gateway -> service
하지만 이 비용은 보통 작다.
반대로 gateway가 없으면 Flutter가 여러 backend service와 직접 연결해야 하므로 운영 비용과 복잡도가 커진다.
gateway가 주는 이점은 다음과 같다.
- 앱 release 없이 backend routing 변경 가능
- downstream timeout과 retry 정책 중앙화
- request logging과 tracing 중앙화
- private service 보호
- Flutter proto 안정성 유지
- 서비스별 장애 fallback 설계 가능
따라서 약간의 hop 비용보다 구조적 이점이 더 크다.
MSA 관점에서의 의미
MSA에서 gateway는 도메인 로직을 모으는 곳이 아니다.
gateway는 service boundary를 지키기 위한 외부 진입점이다.
좋은 gateway:
- 얇다.
- 인증과 routing에 집중한다.
- domain service의 책임을 침범하지 않는다.
- API contract를 안정화한다.
- 내부 service topology를 숨긴다.
나쁜 gateway:
- 추천을 직접 계산한다.
- chatbot 답변을 직접 만든다.
- 여러 DB를 직접 읽는다.
- 모든 비즈니스 로직을 한곳에 모은다.
- 내부 서비스 변경을 임시로 때우는 장소가 된다.
우리가 만들 gateway는 첫 번째 방향이어야 한다.
현재 ONTHEBLOCK에서 gateway가 필요한 직접적인 이유
현재 챗봇 시스템은 여러 서비스가 순서대로 연결된다.
Flutter
-> chatbot-service
-> recommendation-service
-> llm-serving-service
그리고 추천 UI는 별도 흐름도 필요하다.
Flutter
-> recommendation-service
Flutter가 이 둘을 모두 직접 관리하면 앱 구조가 복잡해진다.
gateway를 두면 Flutter는 다음처럼 단순해진다.
Flutter
-> app-gateway-service
그리고 gateway 내부에서 분기한다.
chatbot request -> chatbot-service
recommendation request -> recommendation-service
auth request -> authorization-service
이렇게 하면 앱은 하나의 backend contract를 사용하고, backend는 각 서비스의 책임을 유지할 수 있다.
첫 구현 대상
처음부터 모든 서비스를 gateway로 옮길 필요는 없다.
big-bang migration은 위험하다.
추천 순서:
- gRPC health check
- auth metadata resolver
- chatbot gRPC proxy
- recommendation profile status proxy
- beverage recommendation proxy
- venue recommendation proxy
- feedback/event proxy
- Flutter staging integration
먼저 chatbot과 recommendation만 gateway로 연결해도 충분히 의미가 있다.
최종 정리
app-gateway-service는 “엮이는 서비스들을 하나로 합치는 서비스”가 아니다.
정확한 목적은 다음이다.
Flutter-facing gRPC contract를 안정화하고,
내부 private services로 안전하게 routing하는 얇은 BFF/gateway
gateway는 앱과 내부 MSA 사이의 경계다.
이 경계를 두면 Flutter는 단순해지고, 내부 서비스는 private하게 보호되며, recommendation-service와 chatbot-service의 책임도 명확하게 유지된다.
댓글