// FlightDetailController.kt:19-23class FlightDetailController( private val flightSearchService: FlightSearchService, private val flightDetailService: FlightDetailService, private val fareRuleService: FareRuleService,)
2. 엔드포인트별 상세 분석
2.1 항공편 상세 조회 (by detailKey)
기본 정보
항목
값
HTTP Method
GET
URL
/flights/detail/{detailKey}
메서드명
getFlightDetail
소스 위치
FlightDetailController.kt:24-44
Request 파라미터
파라미터
타입
필수
기본값
설명
detailKey
String
O
-
Path Variable. 항공편 상세 조회 키
adult
Int
O
-
성인 승객 수
child
Int
X
0
소아 승객 수
infant
Int
X
0
유아 승객 수
promotionPrincipleId
Long?
X
null
프로모션 정책 ID
Response
// FlightDetailView.kt:8-59data class FlightDetailView( val detailKey: String, // 상세 조회 키 val id: String, // 항공편 ID val passengerFares: List<PassengerFareView>, // 승객별 운임 val validatingCarrier: AirlineView, // 발권 항공사 val fares: List<FlightFareDetailView>, // 운임 상세 목록 val schedules: List<ScheduleView>, // 스케줄 목록 val avail: Int, // 잔여 좌석 val passengerChangeable: Boolean,// 승객 변경 가능 여부 val cardPromotionName: String?, // 카드 프로모션명 val promotionPrincipleId: Long?, // 프로모션 정책 ID val tags: List<String>, // 태그 목록 val tripType: TripType, // 여정 타입 (ONE_WAY, ROUND_TRIP, MULTI_CITY))
파생 속성 (계산 필드)
// FlightDetailView.kt:22-32val totalPrice: Long // 총 운임 = passengerFares.sumOf { it.total * it.count }val totalDiscount: Long? // 총 할인액 (0보다 큰 경우만)val adultPrice: Long // 성인 1인 운임val originalAdultPrice: Long // 성인 1인 할인 전 운임
// FareRulesView.kt:14-25data class FareRuleView( val groupTitle: String, // 그룹 제목 (예: "규정 1") val rules: List<FareRuleItemView> // 규정 항목 목록)data class FareRuleItemView( val type: FareRuleType?, // 규정 타입 val title: String, // 규정 제목 val content: String?, // 규정 내용 val order: Int, // 정렬 순서)
// CardBenefitView.kt:6-27data class CardBenefitView( val title: String, // 혜택 제목 val period: String, // 혜택 기간 val interestFreeCards: List<InterestFreeView>, // 무이자 카드 목록 val cautions: List<String>, // 주의사항)data class InterestFreeView( val cardCompanies: List<String>, // 카드사 목록 val installments: String, // 할부 개월 (예: "2-6" 또는 "3"))
3. Request/Response 구조 상세
3.1 PassengerFareView (승객별 운임)
// FlightDetailView.kt:80-105data class PassengerFareView( val type: PassengerType, // ADULT, CHILD, INFANT val count: Int, // 승객 수 val airPrice: Long, // 항공료 (카드/판매사 프로모션 할인 차감) val fuelCharge: Long, // 유류할증료 val otherTax: Long, // 기타 세금 val ticketingFee: Long, // 발권 수수료 val discount: Long, // 판매사 할인) { val total: Long // 총액 = airPrice + fuelCharge + otherTax + ticketingFee - discount}
3.2 ScheduleView (스케줄)
// FlightDetailView.kt:107-145data class ScheduleView( val title: String, // "가는편", "오는편", "여정 N" val totalFlightTime: String, // 총 비행 시간 val departure: LocationView, // 출발지 val arrival: LocationView, // 도착지 val segments: List<SegmentView>, // 구간 목록 val stop: Int, // 경유 횟수) { val mainCarrier: AirlineView // 주 운항 항공사 val carrierText: String // 항공사 텍스트 (공동운항 표시 포함)}
3.3 SegmentView (구간)
// FlightDetailView.kt:147-183data class SegmentView( val departure: LocationView, // 출발지 val arrival: LocationView, // 도착지 val marketingCarrier: AirlineView, // 마케팅 항공사 val operatingCarrier: AirlineView?, // 운항 항공사 (공동운항시) val flightNumber: String, // 편명 (3자리 패딩) val cabin: CabinType, // 좌석 등급 val bookingClass: String, // 예약 클래스 val freeBaggage: FreeBaggageView?, // 무료 수하물 (volume > 0인 경우만) val legs: List<LegView>, // 레그 목록 val amenity: AmenityView?, // 기내 편의시설)
3.4 LocationView (위치)
// LocationView.kt:6-24data class LocationView( val code: String, // 공항 IATA 코드 val name: String?, // 공항명 (한글) val dateTime: LocalDateTime,// 날짜/시간 val cityName: String?, // 도시명 (한글) val terminal: String?, // 터미널)
3.5 AirlineView (항공사)
// AirlineView.kt:6-19data class AirlineView( val code: String, // IATA 코드 val name: String?, // 항공사명 val logoUrl: String?, // 로고 URL)
4. 비즈니스 로직 흐름
4.1 getFlightDetail 처리 흐름
sequenceDiagram
participant C as Controller
participant V as Validator
participant S as FlightDetailService
participant A as AdapterClient
participant P as PricingClient
participant Air as AirlineService
participant Apt as AirportService
participant B as BookableDateService
participant FS as FlightSearchService
C->>V: checkSearchablePassengers(adult, child, infant)
V-->>C: 유효성 통과
C->>S: getFlightDetail(detailKey, adult, child, infant, promotionPrincipleId)
S->>S: destructDetailKey() -> (listKey, key, supplier)
S->>A: getFareItinerary(key, supplier, adult, child, infant)
A-->>S: FareItineraryDetailResponse
S->>B: getBookableDateRange().validateBookable(departureDate)
Note over S: 가용 좌석 검증 (avail < adult + child)
S->>Air: getAirlinesMap(airlineSet)
S->>Apt: getAirportsMap(airportSet)
S->>P: getMatchedFarePrinciple(fareItinerary, airportMap)
Note over S: farePrinciple.display == false 검증
S->>P: getMatchedTasfPrinciplesByPassengerType(...)
S->>P: getMatchedSellerDiscountsAndPromotions(...)
S->>S: FlightDetail.of(...) 생성
S->>FS: getFlightItem(detailKey)
Note over S: 가격 변동 검증 (|originTotalPrice - totalPrice| > 1000)
S-->>C: FlightDetail
C->>C: FlightDetailView.of(flightDetail, detailKey)
C-->>Client: ResponseEntity<FlightDetailView>
4.2 주요 처리 단계 (FlightDetailService.getFlightDetail)
// FlightDetail.kt:14-25data class FlightDetail( val key: String, val id: String, val validatingCarrier: Airline, val schedules: List<ScheduleDetail>, val avail: Int, val fares: List<FlightFareDetail>, val cardPromotionName: String?, val promotionPrincipleId: Long? = null, val tags: List<String>, val tripType: TripType,)
8.2 FlightDetail 계산 속성
// FlightDetail.kt:26-51val totalPrice: Long // passengerFares 합계val passengerFares: List<PassengerFareDetail> // 프로모션 적용된 운임 또는 일반 운임val adult: Int // 성인 수val child: Int // 소아 수val infant: Int // 유아 수val adultIdentityType: IdentityType? // 성인 신분 타입
8.3 PassengerFareDetail 도메인 모델
// FlightDetail.kt:361-391data class PassengerFareDetail( val passengerType: PassengerType, val count: Int, val airPrice: Long, val tax: Long, val fuelCharge: Long, val qCharge: Long, val discounts: List<DiscountDetail>, val ticketingFee: Long, val carrierFee: Long, val identityCode: String, val identityType: IdentityType?,) { val otherTax: Long // tax - fuelCharge val total: Long // airPrice + tax - discounts + ticketingFee val cardPromotionDiscount: Long // 카드 프로모션 할인 val sellerPromotionDiscount: Long // 판매사 프로모션 할인 val sellerDiscount: Long // 판매사 할인 val naverDiscount: Long // 네이버 할인}
8.4 TripType Enum
// TripType.kt:3-18enum class TripType { ONE_WAY, // 편도 ROUND_TRIP, // 왕복 MULTI_CITY; // 다구간 fun getScheduleTitle(scheduleIndex: Int): String { return when (this) { ONE_WAY -> "가는편" ROUND_TRIP -> if (scheduleIndex == 0) "가는편" else "오는편" MULTI_CITY -> "여정 ${scheduleIndex + 1}" } }}
// BillingResponse.kt:3-14data class CardBenefit( val prepayment: Boolean, val title: String, val period: String, val interestFreeCards: List<InterestFreeCard>, val cautions: List<String>,)data class InterestFreeCard( val cardCompany: String, val installments: List<Int>,)
9. MetaFareStrategy (운임 조회 전략)
9.1 전략 종류
// MetaFareStrategy.kt:3-8enum class MetaFareStrategy { LOWEST_FARE, // 최저가 LOWEST_REPRESENTATIVE_PROMOTION_FARE, // 대표카드 프로모션 중 최저가 LOWEST_FARE_WITH_CARD_PROMOTION, // 특정 카드 프로모션 중 최저가 MATCHED_FARE_WITH_PROMOTION_PRINCIPLE // 특정 프로모션 정책 운임}