Singapore Airlines — 개요
module-singaporeair arch-supplier api-ndc pattern-soap
한 줄 요약
Singapore Airlines(SQ)는 NDC(EDIST 18.1) 표준 메시지를 사용하지만, 실제 통신은 Amadeus NDC SOAP 게이트웨이를 경유한다. 즉 “NDC 페이로드를 SOAP 봉투에 싸서 Amadeus 인프라로 보내는” 하이브리드 연동이다. 이 사실은 코드에서
SOAPAction헤더와 보안 헤더 네임스페이스로 명확히 증명된다.
1. 공급사 특징 — LCC/FSC/GDS 정체성과 시장 맥락
1-1. SQ는 FSC(풀서비스 항공사)이며 연동 방식은 NDC
| 항목 | 값 | 근거(코드) |
|---|---|---|
| 항공사 | Singapore Airlines (IATA: SQ) | SingaporeairClient.isNonSingaporeAirlineSchedule() 에서 marketingCarrier != "SQ" 필터 (SingaporeairClient.kt:134-138) |
| 비즈니스 타입 | FSC (Full Service Carrier) | NDC 부가서비스(좌석/수하물) 지원, OfferPrice/OrderReshop 기반 정밀 운임 |
| 연동 프로토콜 | NDC EDIST 18.1 over SOAP | action = "http://webservices.amadeus.com/NDC_AirShopping_18.1" 외 9종 (아래 표) |
| 전송 계층 | SOAP 1.x (text/xml) + WS-Security | getHeaderMap() 의 Content-Type: text/xml, SOAPAction 헤더 (SingaporeairClient.kt:898-904) |
| 게이트웨이 | Amadeus 인프라 경유 | 보안 헤더 AMA_SecurityHostedUser 네임스페이스 http://xml.amadeus.com/... (SingaporeairSoapHeaderNamespace.kt:12-14) |
"NDC인데 왜 SOAP인가?"
NDC는 본래 항공사-여행사 간 **메시지 표준(XML 스키마)**일 뿐, 전송 방식을 강제하지 않는다. SQ는 자체 NDC 엔드포인트를 직접 노출하는 대신 **Amadeus의 NDC 애그리게이터(ART/웹서비스)**를 통해 노출한다. 그래서:
- 메시지 본문(body)은 EDIST 18.1 NDC 스키마(
AirShoppingRQ,OfferPriceRQ,OrderCreateRQ…)- 봉투(envelope)와 인증은 Amadeus SOAP 규약(
AMA_SecurityHostedUser+ WS-SecurityUsernameToken+PasswordDigest)이 하이브리드 구조 때문에 JSON)·루프트한자와 달리 SQ는 SOAP 직렬화 + XML ObjectMapper를 쓴다.
1-2. SOAPAction 값으로 본 “Amadeus 경유 NDC” 증거
코드에서 각 요청 DTO가 선언한 action(= SOAPAction 헤더)은 전부 webservices.amadeus.com/NDC_*_18.1 형태다.
| 요청 DTO | SOAPAction (action) | 오퍼레이션 의미 |
|---|---|---|
AirShoppingRQ | .../NDC_AirShopping_18.1 | 항공편/운임 검색 |
OfferPriceRQ | .../NDC_OfferPrice_18.1 | 운임 확정(pricing)·미니룰 조회 |
OrderCreateRQ | .../NDC_OrderCreate_18.1 | 예약 생성(PNR) |
OrderRetrieveRQ | .../NDC_OrderRetrieve_18.1 | 예약 조회 |
OrderChangeRQ | .../NDC_OrderChange_18.1 | 결제/재발행/좌석/부가/분리(divide) |
OrderCancelRQ | .../NDC_OrderCancel_18.1 | 취소/환불 |
OrderReshopRQ | .../NDC_OrderReshop_18.1 | 재발행 검색/리프라이싱/환불액 계산 |
ServiceListRQ | .../NDC_ServiceList_18.1 | 부가서비스(수하물 등) 가용 조회 |
SeatAvailabilityRQ | .../NDC_SeatAvailability_18.1 | 좌석 가용 조회 |
출처 추적
위 값은
infrastructure/request/*.kt의override val action선언에서 직접 확인했다. 예:AirShoppingRQ.kt:27,OfferPriceRQ.kt:25,OrderChangeRQ.kt:28,OrderReshopRQ.kt:30,ServiceListRQ.kt:27,SeatAvailabilityRQ.kt:23.
1-3. EDIST 스키마 / 재발행 샘플 존재
| 자산 | 위치 | 비고 |
|---|---|---|
| EDIST 18.1 XSD 스키마 (52개) | src/test/schema/singaporeair/18_1_EDIST_schemas/*.xsd | AirShoppingRQ.xsd, OfferPriceRS.xsd, OrderCreateRQ.xsd 등 NDC 18.1 표준 정의 |
| 재발행 샘플 XML (14단계) | src/test/schema/singaporeair/example/재발행Sample/ | 1_AirShoppingRQ.xml → … → 14_OrderViewRS-AfterChange.xml. 재발행 전 과정의 실제 메시지 흐름 예시 |
재발행 샘플이 알려주는 흐름
샘플 파일명 순서가 곧 재발행 시퀀스다:
AirShopping(검색) →OfferPrice(가격) →OrderCreate(신규예약) →OrderRetrieve(조회) →OrderReshop(재발행 검색/리프라이싱) →OrderChange(변경 확정) →OrderView(변경 후 상태). 코드의 재발행 경로(reissueSearch/repricingWithReissue/reissue)와 1:1로 대응된다. 자세한 동작은 singaporeair-operations 참고.
2. 모듈 규모와 서브패키지 구조
2-1. 11개 공급사 중 규모 위치
| 공급사 | 파일 수 | 라인 수 | 비고 |
|---|---|---|---|
| amadeus | 873 | 23,716 | 최대 |
| sabre | 882 | 24,964 | 최대 |
| galileo | 525 | 27,109 | 스키마 방대 |
| amadeusndc | 357 | 12,444 | |
| tway | 253 | 10,659 | |
| koreanair | 213 | 8,528 | |
| jinair | 114 | 9,249 | |
| jejuair | 99 | 5,131 | |
| singaporeair | 88 | 7,778 | 중간 규모 |
| lufthansa | 87 | 8,968 | |
| groupair | 34 | 1,921 | 최소 |
규모 해석
파일 수 88개는 11개 모듈 중 9위(작은 편)이지만 라인 수 7,778은 중위권이다. 파일은 적은데 라인이 많다는 것은 infrastructure(요청/응답 DTO)에 코드가 몰려 있고 NDC 메시지 매핑 로직이 큰 함수에 집중됨을 뜻한다. 실제로 88개 중 55개(62.5%)가
infrastructure하위다.
2-2. 실제 디렉터리 트리
supplier/singaporeair/
├── application/ (6) 유즈케이스 서비스 — 트랜잭션·조합·검증
│ ├── SingaporeairFlightSearchService.kt
│ ├── SingaporeairBookingService.kt
│ ├── SingaporeairTicketingService.kt
│ ├── SingaporeairCancelService.kt
│ ├── SingaporeairAncillaryService.kt
│ └── SingaporeairFareRuleService.kt
├── configuration/ (1) 모듈 전용 빈 정의
│ └── RedisConfiguration.kt (class 이름은 SingaporeairRedisConfiguration)
├── domain/ (3) 핵심 도메인 모델 + 캐시 레포지토리
│ ├── model/
│ │ ├── FareItinerary.kt (※ 파일명 SingaporeairFlightSearch.kt)
│ │ └── MiniRule.kt
│ └── repository/
│ └── SingaporeairFareItineraryRepository.kt
├── infrastructure/ (55) 외부 API 클라이언트 + 요청/응답 DTO
│ ├── SingaporeairClient.kt ← 단일 통신 진입점(905 라인)
│ ├── request/ (AirShoppingRQ, OfferPriceRQ, OrderCreateRQ, OrderChangeRQ,
│ │ OrderCancelRQ, OrderReshopRQ, OrderRetrieveRQ,
│ │ ServiceListRQ, SeatAvailabilityRQ, + 하위 NDC 노드 DTO 다수)
│ └── response/ (AirShoppingRS, OfferPriceRS, OrderViewRS, OrderCancelRS,
│ OrderReshopRS, ServiceListRS, SeatAvailabilityRS, + 노드 DTO)
├── interfaces/ (5) Triple 예약시스템에 노출하는 내부 REST 컨트롤러
│ └── controller/internals/
│ ├── SingaporeairSearchController.kt
│ ├── SingaporeairBookingController.kt
│ ├── SingaporeairTicketingController.kt
│ ├── SingaporeairFareRuleController.kt
│ └── SingaporeairAncillaryController.kt
└── support/ (18) 모듈 전용 enum·도메인 모델·유틸
├── enums/ (CabinType, CommissionType, PassengerTypeCode, RuleTitle,
│ SingaporeairSoapHeaderNamespace)
├── model/ (Booking, Passenger, Fare, Schedule, Payment, SeatAvailability,
│ Ancillary, ReservationUser, PnrTicketDocument …)
└── util/ (AirportUtils.kt)
패키지 네이밍 함정
디렉터리 가이드는
domain/support등 표준 레이어를 가정하지만 SQ는support/model과domain/model이 모두 존재한다.
domain/model/FareItinerary= 검색 결과(캐시 직렬화 대상,Serializable)support/model/Booking·Passenger= 예약/결제 도메인 객체또한
domain/model/SingaporeairFlightSearch.kt파일 안의 클래스는FareItinerary이고,configuration/RedisConfiguration.kt파일 안의 클래스는SingaporeairRedisConfiguration이다. 파일명≠클래스명 이므로 IDE 검색 시 클래스명으로 찾아라.
3. 핵심 파일 표
파일 (상대경로 supplier/singaporeair/ 기준) | 역할 | 핵심 메서드 / 비고 |
|---|---|---|
infrastructure/SingaporeairClient.kt | 단일 외부 통신 게이트웨이(905라인). 모든 SOAP/NDC 호출·인증헤더·에러분기 집중 | search, pricing, getMiniRules, book, retrieve, cancel, savePayment, refundCalculate, reissueSearch, repricingWithReissue, reissue, searchAvail* |
application/SingaporeairFlightSearchService.kt | 검색 유즈케이스. 캐시키→캐시조회, OD조합 병렬검색(pmap), 재발행 검색/상세 | search, getFareItinerary, reissueSearch, reissueDetail |
application/SingaporeairBookingService.kt | 예약: pricing→book→retrieve 재조회, 분리(divide) 검증 | book, retrieve, divide (book은 pricing 후 retrieve 재조회로 탑승객번호 보정) |
application/SingaporeairTicketingService.kt | 발권(결제 저장)·재발행 발권. 실패 시 비동기 취소·Slack 경보 | ready, issue, reissue, cancelAsync |
application/SingaporeairCancelService.kt | 취소 가능성 판단(void vs refund), 환불액 계산 | cancel, expectedCancel, cancelable |
application/SingaporeairAncillaryService.kt | 부가서비스(좌석/수하물) 가용 조회. 좌석/수하물 병렬(pmap) | searchAvailAncillary, searchBaggage, searchSeat |
application/SingaporeairFareRuleService.kt | 미니룰(요금규정) 조회. OfferPrice 결과를 FareRule로 변환 | findFareRules (CMS 누락 시 Slack 경보) |
domain/model/SingaporeairFlightSearch.kt | FareItinerary 모델 — 검색결과 캐시 단위. Serializable, responseId(ShoppingResponseRefID) 보유 | validatingCarrier="SQ" 기본값 |
domain/repository/SingaporeairFareItineraryRepository.kt | Redis Hash 기반 FareItinerary 캐시 | saveFareItineraries, getFareItineraries, getFareItinerary |
configuration/RedisConfiguration.kt | FareItinerary 전용 Redis 템플릿(Gzip+JSON 직렬화) | bean singaporeairFareItineraryRedisTemplate |
support/enums/SingaporeairSoapHeaderNamespace.kt | SOAP 헤더 네임스페이스(Amadeus 보안/WS-Security/Addressing) | Amadeus 경유 증거 |
support/util/AirportUtils.kt | OD(출발-도착) 조합/필터 유틸 | makeOriginDestinationsFilterByRoutes |
단일 클라이언트 패턴
다른 GDS 모듈(amadeus-overview·sabre-overview)이 오퍼레이션별로 클라이언트를 쪼개는 것과 달리, SQ는
SingaporeairClient하나에 모든 NDC 메시지 호출이 모여 있다. SOAP 봉투 생성(soapRequestBodyConverter)·WS-Security 인증(PasswordDigest)·에러코드 분기가 전부 이 한 파일이라, 이 파일만 읽으면 SQ 연동 전체를 파악할 수 있다(반대로 905라인이라 변경 시 영향범위 주의). 프로토콜 디테일은 singaporeair-protocol.
4. 공개 인터페이스 (컨트롤러 + application 서비스)
4-1. 컨트롤러별 엔드포인트
모든 컨트롤러는 @RestController 이며 베이스 경로는 /internals/SINGAPOREAIR/... (Triple 예약시스템 내부 API). 중앙 디스패처 없이 공급사명이 URL에 박힌 구조다(system-architecture 참고).
SingaporeairSearchController — /internals/SINGAPOREAIR/search
| 메서드 | 매핑 | 설명 |
|---|---|---|
search | @PostMapping | 항공편/운임 검색. @CircuitBreaker(name="singaporeSearch", fallbackMethod="searchFallback") — 회로 OPEN 시 빈 리스트 반환 + Datadog 스팬 태깅 |
detail | @GetMapping | 운임 상세 + 어메니티(amenity) 조회 |
reissueSearch | @PostMapping("/reissue") | 재발행용 검색(PNR 기반) |
reissueDetail | @GetMapping("/reissue") | 재발행 상세(리프라이싱 검증) |
SingaporeairBookingController — /internals/SINGAPOREAIR/bookings
| 메서드 | 매핑 | 설명 |
|---|---|---|
create | @PostMapping | 예약 생성 |
cancel | @PutMapping("/{pnr}/cancel") | 취소(void/refund) |
expectedCancel | @GetMapping("/{pnr}/expected-cancel") | 취소 예상(환불액 미리보기) |
cancelable | @GetMapping("/{pnr}/cancelable") | 취소 가능 타입(VOID/REFUND) 판정 |
retrieve | @GetMapping("/{pnr}") | 예약 조회 |
divide | @PostMapping("/{pnr}/divide") | 탑승객 분리(PNR split) |
checkPnr | @GetMapping("/{pnr}/check-pnr") | 항상 true 반환(no-op) |
confirm | @GetMapping("/{pnr}/confirm") | retrieve 위임 |
repricing | @GetMapping("/{pnr}/repricing") | retrieve 후 RepricingView 변환 |
SingaporeairTicketingController — /internals/SINGAPOREAIR/ticketing
| 메서드 | 매핑 | 설명 |
|---|---|---|
ready | @PostMapping("/ready") | 발권 준비(스케줄 조회) |
issue | @PostMapping | 발권(=결제 저장). prepayment 아니면 거부 |
reissue | @PostMapping("/addition") | 재발행 발권 — 비동기 폴링(polling(), 202 ACCEPTED + 폴링키) |
checkReissue | @GetMapping("/addition/{reissueKey}") | 재발행 결과 폴링(poller, PENDING/ERROR/COMPLETE) |
재발행 발권은 동기가 아니다
reissue는 Redis 기반 deferred 폴링(polling/poller,CacheSet.REISSUE)으로 처리된다. 호출측은202+pollingKey를 받고/addition/{reissueKey}를 반복 조회해 완료를 기다려야 한다. 비동기 메커니즘은 async-coroutines·resilience-and-events 참고.
SingaporeairFareRuleController — /internals/SINGAPOREAIR/fare-rules
| 메서드 | 매핑 | 설명 |
|---|---|---|
getFareRules | @GetMapping | 미니룰(OfferPrice 기반) 조회 |
getStructuredFareRules | @GetMapping("/structured") | 구조화 운임규정 뷰 |
SingaporeairAncillaryController — /internals/SINGAPOREAIR/ancillaries
| 메서드 | 매핑 | 설명 |
|---|---|---|
searchAvail (key) | @GetMapping("/avail/key") | 검색키 기반 부가 가용 |
searchAvail (pnr) | @GetMapping("/avail/pnr") | PNR 기반 부가 가용 |
searchBaggage (key) | @GetMapping("/baggage/key") | 검색키 기반 수하물 |
searchBaggage (pnr) | @GetMapping("/baggage/pnr") | PNR 기반 수하물 |
searchSeat | @GetMapping("/seat/pnr") | PNR 기반 좌석 가용 |
지원 오퍼레이션 매핑 (Search / Booking / Ticketing / FareRule / Ancillary)
오퍼레이션 컨트롤러 NDC 메시지 Search SearchController AirShopping(+OfferPrice상세)Booking BookingController OrderCreate/OrderRetrieve/OrderCancel/OrderChange/OrderReshopTicketing TicketingController OrderChange(결제) / 재발행OrderReshop+OrderChangeFareRule FareRuleController OfferPrice(미니룰)Ancillary AncillaryController ServiceList/SeatAvailability
4-2. application 서비스 목록 (6개)
| 서비스 | 의존 클라이언트 메서드 | 동시성 | 부수효과 |
|---|---|---|---|
SingaporeairFlightSearchService | search, reissueSearch, repricingWithReissue, retrieve | withBlocking(Dispatchers.IO) + cartesianProduct().pmap | 검색결과 Redis 캐시 |
SingaporeairBookingService | pricing, book, retrieve, divide | CoroutineScope(IO).withLaunch (검색키 비동기 삭제) | — |
SingaporeairTicketingService | savePayment, repricingWithReissue, reissue, cancel | cancelAsync = CoroutineScope(IO).withLaunch + delay(5000) | 실패 시 Slack 경보, 보상 취소 |
SingaporeairCancelService | retrieve, cancel, refundCalculate | — | 체크인 시 예외 |
SingaporeairAncillaryService | searchAvailAncillary, searchAvailBaggage, isSeatAvail, searchAvailSeat, retrieve | withBlocking(IO) + pmap (좌석/수하물 병렬) | — |
SingaporeairFareRuleService | getMiniRules | — | CMS 누락 시 Slack 경보 |
코루틴/동시성 위치
SQ에서 코루틴은 (1) 검색의 OD 조합 병렬화(
pmap), (2) 부가서비스 좌석·수하물 병렬화, (3) 발권 실패 후 보상 취소(cancelAsync, 5초 지연), (4) 예약 후 검색키 비동기 삭제에 쓰인다. 모두support/util의withBlocking/pmap/withLaunch확장에 의존한다. 자세한 내용은 async-coroutines.
5. 중요도 별점
중요도: ★★ (중간-높음)
평가축 점수 근거 코드 규모 ★★ 88파일/7,778라인, 11개 중 중위. amadeus/sabre/galileo보다 훨씬 작음 학습 난이도 ★★★ NDC over SOAP 하이브리드 + Amadeus 경유라는 독특한 구조. 단일 905라인 클라이언트에 인증·직렬화·에러분기 집중 비즈니스 중요도 ★★ SQ는 인기 FSC 노선이나 단일 항공사(11개 공급사 중 1개) 패턴 학습 가치 ★★★ 재발행(reissue) 전 과정(검색→리프라이싱→발권)과 비동기 폴링 발권, 취소 void/refund 분기, 보상 트랜잭션( cancelAsync)을 한 모듈에서 모두 학습 가능종합 ★★: “꼭 알아야 하는 핵심”은 아니지만, NDC·SOAP·재발행·비동기 발권·서킷브레이커를 압축적으로 담고 있어 중급 학습 표본으로 매우 가치 높다. Amadeus NDC·Lufthansa·Korean Air와 비교하면 “NDC를 SOAP로 감싸는” 변형을 이해하는 데 최적.
다음에 읽을 노트
- SQ 오퍼레이션 상세 — Search/Booking/Ticketing/FareRule/Ancillary 시퀀스
- SQ 프로토콜 — SOAP 봉투·WS-Security·NDC 18.1 메시지 직렬화
- 주의점 — 재발행 마이너스 운임, 발권 보상 취소, no-op 엔드포인트
- 시스템 아키텍처 — 중앙 디스패처 없는 공급사별 컨트롤러 구조
- 요청 흐름 — Triple → 컨트롤러 → 서비스 → 클라이언트 → 외부 API
- 기존 심층 분석: singaporeair-ndc