Phase 2: 아마데우스 예약 API 심층 분석

1. API 엔드포인트 개요

1.1 예약 관련 엔드포인트

엔드포인트메서드기능위치
/internals/AMADEUS/bookingsPOST예약 생성AmadeusBookingController.kt:25-38
/internals/AMADEUS/bookings/{pnr}GET예약 조회AmadeusBookingController.kt:107-116
/internals/AMADEUS/bookings/{pnr}PUTAPIS 정보 변경AmadeusBookingController.kt:40-56
/internals/AMADEUS/bookings/{pnr}/cancelPUT예약 취소AmadeusBookingController.kt:58-75
/internals/AMADEUS/bookings/{pnr}/confirmGET예약 확정AmadeusBookingController.kt:124-127
/internals/AMADEUS/bookings/{pnr}/repricingGET재운임 계산AmadeusBookingController.kt:129-138
/internals/AMADEUS/bookings/{pnr}/dividePOST예약 분리AmadeusBookingController.kt:140-149

2. 예약 생성 (Create Booking)

2.1 전체 예약 플로우

flowchart TD
    A[예약 요청 수신] --> B[운임 정보 조회<br/>flightSearchService.getFareItinerary]
    B --> C{Stateful Session<br/>시작}

    C --> D[좌석 마킹<br/>markSeat]
    D --> E[예약 정보 저장<br/>saveReservationInfo]
    E --> F[운임 조회<br/>getPnrFares]
    F --> G[PNR 저장 및 경고 확인<br/>savePnrWithShowWarnings]

    G --> H{경고 메시지<br/>확인}
    H -->|최소 연결시간 오류| I[예외 발생<br/>MINIMUM_CONNECTION_TIME]
    H -->|일반 경고| J[재저장 시도]
    H -->|경고 없음| K[PNR 확정]

    J --> K
    K --> L[PNR 정보 조회<br/>getPnrInfoAndCheckInfantSoldOut]

    L --> M{Carrier Time<br/>Limit 확인}
    M -->|null| N[3초 대기 후<br/>재조회]
    M -->|존재| O[시간 계산]

    N --> O
    O --> P{스케줄 상태<br/>검증}
    P -->|HK/KK 아님| Q[SOLD_OUT<br/>예외]
    P -->|정상| R[세션 종료<br/>signOut]

    R --> S[예약 완료<br/>Booking 반환]

     다크/라이트 모드 호환 색상
    style C fill:#A8D5BA,stroke:#333,stroke-width:2px,color:#000
    style H fill:#F4E4B1,stroke:#333,stroke-width:2px,color:#000
    style I fill:#F39C9C,stroke:#333,stroke-width:2px,color:#000
    style Q fill:#F39C9C,stroke:#333,stroke-width:2px,color:#000
    style ERR fill:#E8B4B8,stroke:#333,stroke-width:2px,color:#000
    style S fill:#95D5A6,stroke:#333,stroke-width:2px,color:#000

2.2 Stateful 세션 관리 패턴

위치: AmadeusBookingService.kt:65-178

stateDiagram-v2
    [*] --> Start: 세션 시작
    Start --> InSeries: 좌석 마킹 완료
    InSeries --> InSeries: 연속 작업
    InSeries --> End: 모든 작업 완료
    End --> [*]: 세션 종료

    InSeries --> Error: 예외 발생
    Error --> Rollback: 세션 롤백
    Rollback --> [*]: PNR 취소

2.3 핵심 처리 단계

Step 1: 좌석 마킹

위치: AmadeusBookingService.kt:67-73

start {
    amadeusClient.markSeat(
        fareItinerary = fareItinerary,
        seatCount = passengers.count { it.type != PassengerType.INFANT },
        statefulBuilder = this
    )
}
  • 목적: 좌석 예약 및 가용성 확인
  • 조건: 유아(INFANT)는 좌석 카운트에서 제외

Step 2: 예약 정보 저장

위치: AmadeusBookingService.kt:75-92

val pnrPassengers = inSeries {
    amadeusClient.saveReservationInfo(
        fareItinerary = fareItinerary,
        reservationUser = reservationUser,
        passengers = passengers,
        corporateId = fareItinerary.passengerFares.first().option.corporateId,
        statefulBuilder = this
    )
}
  • 포함 정보: 승객, 연락처, 기업 ID
  • 반환값: PNR과 승객 정보

Step 3: 최소 연결시간 체크

위치: AmadeusBookingService.kt:100-104

warnings.forEach {
    if (it.contains("CHECK MINIMUM CONNECTION TIME") ||
        it.contains("CHECK ARRIVAL/DEPARTURE")) {
        throw InternationalAdapterException(
            ErrorMessage.MINIMUM_CONNECTION_TIME,
            it
        )
    }
}
  • 조건: 경고 메시지에 최소 연결시간 관련 문구 포함
  • 처리: 즉시 예외 발생, 예약 중단

Step 4: Carrier Time Limit 처리

위치: AmadeusBookingService.kt:128-139

val calculatedCarrierTimeLimit =
    if ((savedPnrInfo.carrierTimeLimit ?: savedPnrInfo.ssrCarrierTimeLimit) == null) {
        withBlocking {
            logger.info("[AMADEUS] carrierTimeLimit is null")
            delay(3000)  // 3초 대기
            amadeusClient.getPnrInfo(pnr = pnr, statefulBuilder = this@stateful)
        }
    } else {
        savedPnrInfo
    }.let {
        calculateCarrierTimeLimit(it)
    }
  • 문제: 발권 시한이 즉시 설정되지 않는 경우
  • 해결: 3초 대기 후 재조회

3. 예외 처리 및 롤백

3.1 예외 처리 플로우

flowchart TD
    A[예외 발생] --> B{세션 상태<br/>확인}
    B -->|InSeries| C[세션 종료<br/>signOut]
    B -->|기타| D[세션 유지]

    C --> E{예외 타입<br/>판단}
    D --> E

    E --> F{isUnexposedFareItinerary}
    F -->|SOLD_OUT| G[운임 정보 제거<br/>캐시에서 삭제]
    F -->|INFANT_SOLD_OUT| G
    F -->|MINIMUM_CONNECTION_TIME| G
    F -->|기타| H[일반 처리]

    G --> I[unexposed 저장]
    H --> J{PNR 존재}
    I --> J

    J -->|있음| K[PNR 취소<br/>비동기]
    J -->|없음| L[예외 재발생]
    K --> L

     다크/라이트 모드 호환 색상
    style B fill:#F4E4B1,stroke:#333,stroke-width:2px,color:#000
    style F fill:#F4E4B1,stroke:#333,stroke-width:2px,color:#000
    style K fill:#F4E4B1,stroke:#333,stroke-width:2px,color:#000
    style P fill:#95D5A6,stroke:#333,stroke-width:2px,color:#000

4.2 티켓 문서 처리

위치: AmadeusBookingService.kt:210-245

val (emdTickets, eTickets) = tickets.partition { it.type == TicketType.EMD }
  • EMD: Electronic Miscellaneous Document (부가 서비스)
  • E-Ticket: 전자 항공권
  • 처리 차이: EMD는 별도 검증 (officeId 확인)

5. 예약 변경 (Modify)

5.1 APIS 정보 변경

엔드포인트: PUT /internals/AMADEUS/bookings/{pnr} 위치: AmadeusBookingController.kt:40-56

flowchart LR
    A[APIS 변경 요청] --> B[승객 정보 변환<br/>Passenger.of]
    B --> C[passengerService.changeApis]
    C --> D[변경된 승객 정보]
    D --> E[응답 반환]

     다크/라이트 모드 호환 색상
    style B fill:#F4E4B1,stroke:#333,stroke-width:2px,color:#000
    style C fill:#9FB4CE,stroke:#333,stroke-width:2px,color:#000
    style D fill:#9FB4CE,stroke:#333,stroke-width:2px,color:#000
    style E fill:#9FB4CE,stroke:#333,stroke-width:2px,color:#000

8.2 비동기 처리

  • 캐시 제거: AmadeusBookingService.kt:193-196
  • Unexposed 저장: AmadeusBookingService.kt:187-191
  • PNR 취소: cancelService.pnrCancelAsync

9. 트러블슈팅

9.1 일반적인 문제

문제증상원인해결 방법
Carrier Time Limit null발권 시한 미설정GDS 응답 지연3초 대기 후 재조회
최소 연결시간 오류예약 실패환승 시간 부족다른 스케줄 선택
스케줄 미확정SOLD_OUT 예외좌석 부족다른 항공편 검색
유아 좌석 부족INFANT_SOLD_OUT유아 제한 초과항공사 문의 필요

9.2 세션 관리 주의사항

  • InSeries 상태에서 예외 발생 시 반드시 세션 종료
  • PNR 생성 후 실패 시 비동기 취소 처리
  • 트랜잭션 무결성 보장을 위한 stateful 패턴 사용

10. 주요 코드 위치 참조

기능파일라인
예약 생성AmadeusBookingService.kt53-178
예약 조회AmadeusBookingService.kt199-252
예약 확정AmadeusBookingService.kt254-268
재운임 계산AmadeusBookingService.kt270-277
예약 분리AmadeusBookingService.kt279-289
PNR 체크AmadeusBookingService.kt291-295
예외 처리AmadeusBookingService.kt167-177
경고 필터링AmadeusBookingService.kt110-122

다음 Phase

Phase 3: 발권 API 심층 분석으로 진행