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 SOAPaction = "http://webservices.amadeus.com/NDC_AirShopping_18.1" 외 9종 (아래 표)
전송 계층SOAP 1.x (text/xml) + WS-SecuritygetHeaderMap()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-Security UsernameToken + PasswordDigest)

이 하이브리드 구조 때문에 JSON)·루프트한자와 달리 SQ는 SOAP 직렬화 + XML ObjectMapper를 쓴다.

1-2. SOAPAction 값으로 본 “Amadeus 경유 NDC” 증거

코드에서 각 요청 DTO가 선언한 action(= SOAPAction 헤더)은 전부 webservices.amadeus.com/NDC_*_18.1 형태다.

요청 DTOSOAPAction (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/*.ktoverride 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/*.xsdAirShoppingRQ.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개 공급사 중 규모 위치

공급사파일 수라인 수비고
amadeus87323,716최대
sabre88224,964최대
galileo52527,109스키마 방대
amadeusndc35712,444
tway25310,659
koreanair2138,528
jinair1149,249
jejuair995,131
singaporeair887,778중간 규모
lufthansa878,968
groupair341,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/modeldomain/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.ktFareItinerary 모델 — 검색결과 캐시 단위. Serializable, responseId(ShoppingResponseRefID) 보유validatingCarrier="SQ" 기본값
domain/repository/SingaporeairFareItineraryRepository.ktRedis Hash 기반 FareItinerary 캐시saveFareItineraries, getFareItineraries, getFareItinerary
configuration/RedisConfiguration.ktFareItinerary 전용 Redis 템플릿(Gzip+JSON 직렬화)bean singaporeairFareItineraryRedisTemplate
support/enums/SingaporeairSoapHeaderNamespace.ktSOAP 헤더 네임스페이스(Amadeus 보안/WS-Security/Addressing)Amadeus 경유 증거
support/util/AirportUtils.ktOD(출발-도착) 조합/필터 유틸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 메시지
SearchSearchControllerAirShopping (+ OfferPrice 상세)
BookingBookingControllerOrderCreate/OrderRetrieve/OrderCancel/OrderChange/OrderReshop
TicketingTicketingControllerOrderChange(결제) / 재발행 OrderReshop+OrderChange
FareRuleFareRuleControllerOfferPrice(미니룰)
AncillaryAncillaryControllerServiceList / SeatAvailability

4-2. application 서비스 목록 (6개)

서비스의존 클라이언트 메서드동시성부수효과
SingaporeairFlightSearchServicesearch, reissueSearch, repricingWithReissue, retrievewithBlocking(Dispatchers.IO) + cartesianProduct().pmap검색결과 Redis 캐시
SingaporeairBookingServicepricing, book, retrieve, divideCoroutineScope(IO).withLaunch (검색키 비동기 삭제)
SingaporeairTicketingServicesavePayment, repricingWithReissue, reissue, cancelcancelAsync = CoroutineScope(IO).withLaunch + delay(5000)실패 시 Slack 경보, 보상 취소
SingaporeairCancelServiceretrieve, cancel, refundCalculate체크인 시 예외
SingaporeairAncillaryServicesearchAvailAncillary, searchAvailBaggage, isSeatAvail, searchAvailSeat, retrievewithBlocking(IO) + pmap (좌석/수하물 병렬)
SingaporeairFareRuleServicegetMiniRulesCMS 누락 시 Slack 경보

코루틴/동시성 위치

SQ에서 코루틴은 (1) 검색의 OD 조합 병렬화(pmap), (2) 부가서비스 좌석·수하물 병렬화, (3) 발권 실패 후 보상 취소(cancelAsync, 5초 지연), (4) 예약 후 검색키 비동기 삭제에 쓰인다. 모두 support/utilwithBlocking/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