본문으로 건너뛰기

Gateway Org Path 동작 이해하기

LinkPie가 browserAuthMode=GATEWAY로 동작할 때는 URL의 /org/{slug}가 단순 장식이 아닙니다.
운영 Gateway가 이 path를 보고 aipgw-ctx-* header를 붙이므로, LinkPie는 페이지 이동과 보호 API 호출에서 org slug를 잃지 않아야 합니다.

이 문서에서는 아래 역할명을 기준으로 설명합니다.

  • LinkPie Browser App: 브라우저 안에서 동작하는 LinkPie 프런트엔드
  • AIP Gateway: 앞단 인증 게이트웨이
  • Org Path Canonicalization Filter: /org/{slug}/api/**/api/**로 되돌리는 backend filter
  • Gateway Auth Bridge: aipgw-ctx-*를 읽어 AIP gateway context를 gateway user principal로 연결하는 backend 인증 체인
  • LinkPie API: 실제 backend API/controller 계층

왜 필요한가

  • 운영 Gateway는 org path가 있는 요청에만 인증 header를 붙입니다.
  • linkpie만 Gateway 모드를 사용하고, appdeskpie는 기존 local path를 유지합니다.
  • Edge/Gateway는 브라우저 내부에서 만들어지는 링크나 API URL을 고칠 수 없습니다.

그래서 LinkPie는 브라우저 안에서 URL을 만들 때 직접 /org/{slug}를 관리하고, backend는 받은 /org/{slug}/api/**를 다시 원래 /api/**로 되돌립니다.

한눈에 보는 흐름

1. 최초 페이지 로드

2. 내부 이동 + 보호 API 호출

누가 무엇을 담당하나

역할명실제 클래스/파일설명
AIP Gateway Org Path ContextcreateAipGatewayOrgPathContext현재 브라우저 URL에서 orgSlug, 외부 path, canonical path를 계산합니다.
AIP Gateway Org Path RoutercreateAipGatewayOrgPathRouterpage/API path를 public인지 protected인지 분류하고, 외부 path와 내부 path를 서로 변환합니다.
AIP Gateway Org Path Routing RegistrationregisterAipGatewayOrgPathRoutingLinkPie 시작 시 route interceptor와 API request interceptor를 등록합니다.
Route Interceptor SeamrouteInterceptorspage ingress/egress를 위한 공용 seam입니다. app은 여기까지만 알고 org 규칙은 모릅니다.
API Interceptor SeamapiInterceptorsHTTP client request를 전송 직전에 재작성하는 공용 seam입니다.
Org Path Canonicalization FilterAipGatewayOrgPathCanonicalizationFilter/org/{slug}/api/** 요청을 backend 내부 canonical /api/**로 되돌립니다.
AIP Gateway Context ResolverAipGatewayContextResolveraipgw-ctx-* header를 읽어 AIP gateway context를 만듭니다.
Gateway Auth Bridge FilterAipGatewayAuthBridgeFilter보호 API 요청에서 AIP gateway context가 있을 때만 인증 bridge를 태웁니다.
Gateway User Details ServiceAipGatewayUserDetailsServicegateway 외부 사용자 정보를 gateway user principal로 복원합니다.

클래스 기준으로 보면

Frontend

Backend

위 순서도에서 쓴 이름은 이해를 돕기 위한 역할명이고, 실제 클래스명은 바로 위 표를 기준으로 보면 됩니다.

프런트는 왜 interceptor 방식인가

프런트는 공용 코드와 LinkPie 전용 정책을 분리해야 했습니다.

  • apporg slug를 몰라야 합니다.
  • linkpie/org/{slug} 규칙을 알아야 합니다.
  • navigate(), pushUrl(), replaceUrl(), http.get()가 모두 같은 정책을 타야 합니다.

그래서 공용 계층에는 interceptor seam만 두고, LinkPie가 시작 시 자기 router를 등록하는 구조로 정리했습니다.

백엔드는 왜 filter 방식인가

백엔드는 controller보다 앞에서 path를 바꿔야 했습니다.

  • controller/service는 계속 /api/v1/**만 알게 두고 싶었습니다.
  • security matcher도 canonical API path를 기준으로 유지하고 싶었습니다.

그래서 gateway mode일 때만 pre-security filter를 등록해서 /org/{slug}/api/**/api/**로 canonicalize합니다.

OAuth calendar 연결은 예외가 아니라 포함 범위다

calendar 연결은 gateway mode에서도 계속 동작해야 합니다.
다만 이 흐름은 보호 API공용 OAuth 경로가 섞여 있어서 구분해서 봐야 합니다.

  • OAuth 시작/콜백 경로는 public path로 그대로 둡니다.
    • 예: /oauth2/**
  • OAuth intent API는 인증된 보호 API로 다룹니다.
    • 예: /api/v1/auth/oauth2/intent
  • 대신 OAuth provider 시작/콜백 자체는 public path로 유지하고, 돌아올 위치는 backend continue cookie가 보존합니다.

즉 calendar OAuth는 "org path를 안 써도 되는 예외"가 아니라,
"공용 OAuth 경로는 그대로 두되, 보호 API와 복귀 위치 보존은 org path 체인을 유지해야 하는 흐름"으로 이해하면 됩니다.

실제 순서

1. 최초 진입

  1. 사용자가 /org/acme/console/contacts로 들어옵니다.
  2. LinkPie는 현재 URL에서 orgSlug=acme를 읽습니다.
  3. 내부에서는 /console/contacts를 canonical path로 사용합니다.

2. 내부 페이지 이동

  1. 코드가 /settings/account/profile 같은 canonical page path로 이동합니다.
  2. route egress interceptor가 이를 /org/acme/settings/account/profile로 바꿉니다.
  3. 브라우저 URL은 계속 org slug를 유지합니다.

3. 보호 API 호출

  1. 코드가 canonical API path를 사용해 요청을 만듭니다.
  2. API request interceptor가 이를 /org/acme/api/v1/...로 바꿉니다.
  3. Gateway가 path를 보고 aipgw-ctx-* header를 붙입니다.
  4. backend filter가 다시 /api/v1/...로 되돌립니다.
  5. 인증 bridge가 header를 읽어 사용자 principal을 복원합니다.

4. OAuth calendar 연결

  1. 사용자가 /org/acme/console/calendar-integrations 같은 org-scoped page에 있습니다.
  2. 보호 API가 현재 요청의 org-scoped path를 기준으로 backend continue cookie를 저장합니다.
  3. OAuth 시작은 public path로 나갑니다.
  4. provider 인증이 끝나면 callback도 public path로 돌아옵니다.
  5. 완료 후 다시 /org/acme/... 형태의 원래 화면으로 복귀합니다.

현재 구현 상태

이미 전역으로 들어간 것

  • protected page의 /org/{slug} 유지
  • backend의 /org/{slug}/api/** 수용과 canonicalization
  • frontend protected API 기본 org-prefix
  • raw fetch()와 MCP fallback의 helper 연결

확인할 때 보면 좋은 포인트

  • URL이 /org/{slug}를 유지한 채 페이지 이동하는지
  • 보호 API가 /org/{slug}/api/v1/**로 나가는지
  • /api/v1/auth/config, /api/v1/auth/password-change-context 같은 공용 API는 그대로 남는지
  • /api/v1/auth/me, /api/v1/auth/oauth2/intent 같은 보호 API는 /org/{slug}/api/**로 나가는지
  • backend가 /org/{slug}/api/**를 받아도 controller는 기존 /api/**로 처리하는지
노트

로컬 gateway-mock은 편의상 slug 없이도 header를 붙일 수 있습니다.
로컬에서 slug 없이 동작했다고 해서 운영 Gateway도 같다고 보면 안 됩니다.

더 자세한 기술 문서

  • docs/reference/aip-gateway-org-path-routing.md
  • docs/reference/backend/gateway-session-bridge.md