Skip to main content
Glama

Naver Flight MCP

by InSIkHwang
NAVER_FLIGHT_API_ANALYSIS.md14.3 kB
# 네이버 항공권 API 분석 보고서 ## 개요 네이버 항공권 검색 시스템은 **REST API**를 사용하여 항공권 정보를 가져옵니다. GraphQL이 아닌 **Server-Sent Events (SSE) 방식**의 일반 REST API입니다. ## API 엔드포인트 ### 항공권 검색 API - **URL**: `https://flight-api.naver.com/flight/international/searchFlights` - **Method**: `POST` - **Content-Type**: `application/json` - **Accept**: `text/event-stream` (SSE 방식) - **응답 상태**: `201 Created` ## 요청 페이로드 구조 ```json { "adultCount": 1, "childCount": 0, "infantCount": 0, "device": "pc", "isNonstop": true, "itineraries": [ { "departureLocationCode": "PUS", "departureLocationType": "airport", "arrivalLocationCode": "TYO", "arrivalLocationType": "airport", "departureDate": "20251202" }, { "departureLocationCode": "TYO", "departureLocationType": "airport", "arrivalLocationCode": "PUS", "arrivalLocationType": "airport", "departureDate": "20251209" } ], "openReturnDays": 0, "seatClass": "Y", "tripType": "RT", "flightFilter": { "filter": { "airlines": [], "departureAirports": [["PUS"], []], "arrivalAirports": [[], ["PUS"]], "departureTime": [], "fareTypes": [], "flightDurationSeconds": [], "hasCardBenefit": true, "isIndividual": false, "isLowCarbonEmission": false, "isSameAirlines": false, "isSameDepArrAirport": true, "isTravelClub": false, "minFare": {}, "viaCount": [], "selectedItineraries": [] }, "limit": 200, "skip": 0, "sort": { "adultMinFare": 1 } }, "initialRequest": true } ``` ### 주요 파라미터 설명 | 파라미터 | 설명 | 예시 값 | | ------------------------------------- | ------------ | ----------------------- | | `adultCount` | 성인 인원 수 | `1` | | `childCount` | 아동 인원 수 | `0` | | `infantCount` | 유아 인원 수 | `0` | | `isNonstop` | 직항 여부 | `true` (직항만) | | `seatClass` | 좌석 등급 | `"Y"` (이코노미) | | `tripType` | 여행 유형 | `"RT"` (왕복) | | `itineraries[].departureLocationCode` | 출발지 코드 | `"PUS"` (부산) | | `itineraries[].arrivalLocationCode` | 도착지 코드 | `"TYO"` (도쿄) | | `itineraries[].departureDate` | 출발일 | `"20251202"` (YYYYMMDD) | ### 좌석 등급 코드 - `Y`: 이코노미 (Economy) - `C`: 비즈니스 (Business) - `F`: 일등석 (First Class) ### 여행 유형 코드 - `RT`: Round Trip (왕복) - `OW`: One Way (편도) ## 응답 데이터 구조 응답은 **Server-Sent Events (SSE)** 형식으로 전달됩니다. ``` data: {JSON_DATA} ``` ### 응답 JSON 구조 ```json { "uniqueId": "40ed2dc0-2e82-416f-948e-03cad42bd985", "status": { "searchKey": "RTY100PY020251202PUSATYOA20251209TYOAPUSA", "__v": 16, "requestedPartnerCount": 20, "completedPartnerCount": 20, "isCompleted": true, "airlinesCodeMap": { ... }, "itineraryAirports": [ ... ], "airportsCodeMap": { ... }, "fareTypesCodeMap": { ... }, "durationSecondsRanges": [ ... ], "lowestFare": { "direct": 223500, "a01": 230200 }, "priceRange": { "min": 223500, "max": 1252400 }, "expireAt": "2025-10-22T01:50:56.373Z", "hasCarbonEmission": true }, "itineraries": [ ... ], "fareMappings": [ ... ], "isExpired": false, "popularFlights": [ ... ] } ``` ### 주요 데이터 필드 #### 1. `status` - 검색 상태 정보 ```json { "searchKey": "검색 고유 키", "isCompleted": true, "lowestFare": { "direct": 223500, "a01": 230200 }, "priceRange": { "min": 223500, "max": 1252400 } } ``` #### 2. `itineraries` - 항공편 정보 배열 각 항공편(leg)의 상세 정보를 포함합니다. ```json { "itineraryId": "20251202PUSNRT7C1151", "duration": 7500, "sequence": 1, "segments": [ { "departure": { "airportCode": "PUS", "date": "20251202", "time": "0720", "terminal": "I" }, "arrival": { "airportCode": "NRT", "date": "20251202", "time": "0925", "terminal": "3" }, "marketingCarrier": { "airlineCode": "7C", "flightNumber": "1151" }, "operatingCarrier": { "airlineCode": "7C", "flightNumber": "1151" }, "flightDuration": 7500, "groundDuration": 0, "aircraftCode": "738" } ], "carbonEmission": -1 } ``` **필드 설명:** - `itineraryId`: 항공편 고유 ID (형식: `YYYYMMDD출발공항도착공항항공사편명`) - `duration`: 총 비행 시간 (초 단위) - `sequence`: 1 = 가는 편, 2 = 오는 편 - `segments`: 구간별 상세 정보 (경유가 있으면 여러 개) - `time`: HHMM 형식 (예: "0720" = 07시 20분) - `flightDuration`: 실제 비행 시간 (초) - `groundDuration`: 지상 대기 시간 (초) #### 3. `fareMappings` - 가격 정보 배열 항공편 조합과 각 OTA(온라인 여행사)별 가격 정보를 포함합니다. ```json { "itineraryIds": "20251202PUSNRT7C1151-20251209NRTPUS7C1152", "fares": [ { "partnerCode": "TBK033", "fareType": "A01/B16", "adult": { "totalFare": 223500, "qCharge": 0, "tax": 55000 }, "isConfirmed": true, "baggageFeeType": "FREE" } ], "carbonEmission": -3, "curation": ["MIN_PRICE", "NONSTOP_MIN_PRICE"] } ``` **필드 설명:** - `itineraryIds`: 가는편-오는편 항공편 ID 조합 (`-`로 연결) - `fares`: 해당 조합에 대한 판매 파트너별 요금 정보 - `partnerCode`: OTA 파트너 코드 - `fareType`: 요금 유형 (카드 할인 등) - `totalFare`: 총 요금 (원 단위) - `qCharge`: Q-Charge (유류할증료 등) - `tax`: 세금 - `baggageFeeType`: 수하물 정책 (`FREE`, `PAID` 등) ## 실제 검색 결과 예시 ### 최저가 TOP 10 항공권 (PUS-TYO, 2025.12.02~12.09) | 순위 | 출발일 | 복귀일 | 가는편 | 오는편 | 총요금 | 가는편 출발 | 가는편 도착 | 가는편 소요시간 | 오는편 출발 | 오는편 도착 | 오는편 소요시간 | | ---- | -------- | -------- | ------ | ------ | --------- | ----------- | ----------- | --------------- | ----------- | ----------- | --------------- | | 1 | 20251202 | 20251209 | 7C1151 | 7C1152 | 223,500원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 2 | 20251202 | 20251209 | 7C1151 | 7C1152 | 223,500원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 3 | 20251202 | 20251209 | 7C1151 | 7C1152 | 223,900원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 4 | 20251202 | 20251209 | 7C1151 | 7C1152 | 224,500원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 5 | 20251202 | 20251209 | 7C1151 | 7C1152 | 225,900원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 6 | 20251202 | 20251209 | 7C1151 | 7C1152 | 225,900원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 7 | 20251202 | 20251209 | 7C1151 | 7C1152 | 226,000원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 8 | 20251202 | 20251209 | 7C1151 | 7C1152 | 226,000원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 9 | 20251202 | 20251209 | 7C1151 | 7C1152 | 226,100원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | | 10 | 20251202 | 20251209 | 7C1151 | 7C1152 | 226,400원 | 0720 | 0925 | 125분 | 1015 | 1300 | 165분 | **참고:** 같은 항공편 조합이 여러 번 나타나는 이유는 각기 다른 카드 할인 혜택이나 판매 파트너에 따라 가격이 다르기 때문입니다. ## MCP 서버 구현을 위한 핵심 정보 ### 1. 필수 입력 파라미터 - `departureCode`: 출발지 공항/도시 코드 (예: "PUS", "ICN") - `arrivalCode`: 도착지 공항/도시 코드 (예: "TYO", "NRT") - `departureDate`: 출발일 (YYYYMMDD 형식) - `returnDate`: 복귀일 (YYYYMMDD 형식) ### 2. 고정 파라미터 ```javascript { adultCount: 1, // 성인 1명 childCount: 0, infantCount: 0, isNonstop: true, // 직항만 seatClass: "Y", // 이코노미 tripType: "RT" // 왕복 } ``` ### 3. 응답 데이터 처리 #### 항공편 정보 추출 ```javascript const itineraries = response.itineraries; // sequence: 1 = 가는편, 2 = 오는편 ``` #### 가격 정보 추출 및 최저가 계산 ```javascript const allFares = response.fareMappings.flatMap((mapping) => mapping.fares.map((f) => ({ itineraryIds: mapping.itineraryIds.split("-"), totalFare: f.adult.totalFare, partnerCode: f.partnerCode, })) ); // 가격 순 정렬 const sorted = allFares.sort((a, b) => a.totalFare - b.totalFare); ``` #### 항공편-가격 조합 ```javascript const flightWithPrice = allFares.map((fare) => { const outbound = itineraries.find( (it) => it.itineraryId === fare.itineraryIds[0] ); const inbound = itineraries.find( (it) => it.itineraryId === fare.itineraryIds[1] ); return { price: fare.totalFare, outbound: { flightNumber: outbound.segments[0].marketingCarrier.airlineCode + outbound.segments[0].marketingCarrier.flightNumber, departure: outbound.segments[0].departure.time, arrival: outbound.segments[0].arrival.time, duration: Math.floor(outbound.duration / 60), // 초 → 분 }, inbound: { flightNumber: inbound.segments[0].marketingCarrier.airlineCode + inbound.segments[0].marketingCarrier.flightNumber, departure: inbound.segments[0].departure.time, arrival: inbound.segments[0].arrival.time, duration: Math.floor(inbound.duration / 60), }, }; }); ``` ### 4. 출력 형식 최종 MCP 도구는 다음 형식으로 데이터를 반환해야 합니다: | 순위 | 출발일 | 복귀일 | 가는편 | 오는편 | 총요금 | 출발시간 | 도착시간 | 소요시간 (가는편) | 출발시간 | 도착시간 | 소요시간 (오는편) | | ---- | ------ | ------ | ------ | ------ | ------ | -------- | -------- | ----------------- | -------- | -------- | ----------------- | ## 추가 참고사항 ### 시간 형식 변환 - API 응답: `"0720"` (문자열) - 표시 형식: `"07:20"` 또는 그대로 사용 ### Duration 변환 - API 응답: 초 단위 (예: 7500초) - 표시 형식: 분 단위 (예: 125분) 또는 시간:분 (예: 2시간 5분) ### 공항 코드 매핑 응답의 `status.airportsCodeMap`에서 공항 이름 확인 가능: ```json { "NRT": { "airportName": "나리타국제공항", "cityName": "도쿄" }, "PUS": { "airportName": "김해국제공항", "cityName": "부산" } } ``` ### 항공사 코드 매핑 응답의 `status.airlinesCodeMap`에서 항공사 이름 확인 가능: ```json { "7C": "제주항공", "LJ": "진에어", "KE": "대한항공", "OZ": "아시아나항공" } ``` ## 구현 예시 (의사 코드) ```javascript async function searchNaverFlights(departure, arrival, departDate, returnDate) { const payload = { adultCount: 1, childCount: 0, infantCount: 0, device: "pc", isNonstop: true, seatClass: "Y", tripType: "RT", itineraries: [ { departureLocationCode: departure, departureLocationType: "airport", arrivalLocationCode: arrival, arrivalLocationType: "airport", departureDate: departDate, // "YYYYMMDD" }, { departureLocationCode: arrival, departureLocationType: "airport", arrivalLocationCode: departure, arrivalLocationType: "airport", departureDate: returnDate, // "YYYYMMDD" }, ], openReturnDays: 0, flightFilter: { filter: { airlines: [], departureAirports: [[departure], []], arrivalAirports: [[], [departure]], departureTime: [], fareTypes: [], flightDurationSeconds: [], hasCardBenefit: true, isIndividual: false, isLowCarbonEmission: false, isSameAirlines: false, isSameDepArrAirport: true, isTravelClub: false, minFare: {}, viaCount: [], selectedItineraries: [], }, limit: 200, skip: 0, sort: { adultMinFare: 1 }, }, initialRequest: true, }; const response = await fetch( "https://flight-api.naver.com/flight/international/searchFlights", { method: "POST", headers: { "Content-Type": "application/json", Accept: "text/event-stream", "User-Agent": "Mozilla/5.0 ...", Referer: "https://flight.naver.com/", }, body: JSON.stringify(payload), } ); const text = await response.text(); // SSE 형식 파싱: "data: {...}" → {...} const dataLine = text.split("\n").find((line) => line.startsWith("data: ")); const jsonData = JSON.parse(dataLine.substring(6)); // 데이터 가공 및 반환 return processFlightData(jsonData); } ``` ## 결론 네이버 항공권 API는 GraphQL이 아닌 **REST API with SSE (Server-Sent Events)** 방식을 사용합니다. 핵심 포인트: 1. ✅ 단일 엔드포인트: `/flight/international/searchFlights` 2. ✅ POST 요청으로 검색 조건 전송 3. ✅ SSE 형식으로 실시간 응답 수신 4. ✅ `itineraries`와 `fareMappings`를 조합하여 최저가 항공편 정보 추출 5. ✅ 항공편 ID로 매칭하여 상세 정보 조회 가능

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/InSIkHwang/Naver-Flight-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server