Story(HotelStory) CMS API 연동 개발 가이드
- Story(HotelStory) CMS API 연동 개발 가이드
- 시스템 개요
- 공통 인증
- API 목록
- API별 처리 프로세스
- API 5 - 객실 정보 API
POST /api/cms/room-types[XML] - API 6 - 상품(요금제) 정보 API
POST /api/cms/rate-plans[XML] - API 7.1.2 - 가용객실 수신 API
POST /api/cms/push-availability[XML] - API 7.1.3 - 요금 수신 API
POST /api/cms/push-rate[XML] - API 8.1 - 신규예약 API
POST /api/cms/reservation-create[JSON] - API 8.2 - 예약수정 API
PUT /api/cms/reservation-modify[JSON] - API 8.3 - 예약취소 API
POST /api/cms/reservation-cancel[JSON] - API 9 - 예약조회 API
POST /api/cms/reservation[JSON] - 예약 목록조회(대사) -
GET /api/cms/reservation/sync - GSA - 총판 숙소 업데이트
GET /api/gsa/upsert-gsa
- API 5 - 객실 정보 API
- 기술 스택
- 주요 참고사항
Story(HotelStory) CMS API 연동 개발 가이드
HotelStory HUB와 자사 플랫폼 간의 외부 API 연동 개발 내용을 정리한 문서입니다.
Spring Boot 기반으로 구현되었으며, XML/JSON 혼용 방식으로 통신합니다.
시스템 개요
| 항목 | 내용 |
|---|---|
| 연동 대상 | HotelStory HUB CMS |
| Story API Endpoint | application properties에 설정 (비공개) |
| 자사 서버 포트 | application properties에 설정 (비공개) |
| 인증 방식 | XML 요청 본문 내 <Auth> 요소 포함 (AuthId / AuthKey) |
| 인증 정보 | 설정 파일 또는 환경 변수에서 관리 (비공개) |
| DB | MariaDB |
| 이미지 스토리지 | Naver Cloud Object Storage (S3 호환) |
| 데이터 형식 | XML (객실/요금/가용 Push), JSON (예약 관련) |
공통 인증
모든 Story API 요청에는 XML 본문에 <Auth> 요소가 포함되어야 합니다.
<Auth>
<AuthId>{설정값}</AuthId>
<AuthKey>{설정값}</AuthKey>
</Auth>
StoryCallAuth: Story로 보내는 요청(예약 생성·수정·취소)의 Base 클래스RequestCommonAuth: Story에서 받는 요청(객실·요금 조회, 가용·요금 Push)의 Base 클래스- 자사 내부 API 간 인증은 JWT 토큰 사용 (
@NoAuthCheck어노테이션으로 우회 가능)
API 목록
CMS Controller (/api/cms)
| No | API 명 | Method | Endpoint | 방향 | 형식 |
|---|---|---|---|---|---|
| 5 | 객실 정보 API | POST | /api/cms/room-types |
HUB → 자사 | XML |
| 6 | 상품(요금제) 정보 API | POST | /api/cms/rate-plans |
HUB → 자사 | XML |
| 7.1.2 | 가용객실 수신 API | POST | /api/cms/push-availability |
HUB → 자사 | XML |
| 7.1.3 | 요금 수신 API | POST | /api/cms/push-rate |
HUB → 자사 | XML |
| 8.1 | 신규예약 API | POST | /api/cms/reservation-create |
자사 → HUB | JSON |
| 8.2 | 예약수정 API | PUT | /api/cms/reservation-modify |
자사 → HUB | JSON |
| 8.3 | 예약취소 API | POST | /api/cms/reservation-cancel |
자사 → HUB | JSON |
| 9 | 예약조회 API | POST | /api/cms/reservation |
자사 → HUB | JSON |
| - | 예약 목록조회(대사) | GET | /api/cms/reservation/sync |
내부 조회 | JSON |
GSA Controller (/api/gsa)
| No | API 명 | Method | Endpoint | 방향 | 형식 |
|---|---|---|---|---|---|
| - | 총판 숙소 업데이트 | GET | /api/gsa/upsert-gsa |
Story → DB 동기화 | XML |
API별 처리 프로세스
API 5 - 객실 정보 API POST /api/cms/room-types [XML]
HUB가 자사 플랫폼에 숙소의 객실 유형 목록을 조회하는 API입니다.
Request: RequestRoomTypeList
<RequestRoomTypeList>
<Auth>
<AuthId>{설정값}</AuthId>
<AuthKey>{설정값}</AuthKey>
</Auth>
<PropertyId>{숙소코드}</PropertyId>
</RequestRoomTypeList>
처리 흐름
HUB → POST /api/cms/room-types
→ CmsController.getRoomTypesXML(RequestRoomTypeList)
→ CmsService.getRoomTypeByProductId(propertyId)
→ DB 조회 (story_room_type, product_room_mapper 등)
→ ResponseRoomTypeList 반환 (XML)
API 6 - 상품(요금제) 정보 API POST /api/cms/rate-plans [XML]
HUB가 자사 플랫폼에 숙소의 요금제 목록을 조회하는 API입니다.
Request: RequestRatePlanList
<RequestRatePlanList>
<Auth>
<AuthId>{설정값}</AuthId>
<AuthKey>{설정값}</AuthKey>
</Auth>
<PropertyId>{숙소코드}</PropertyId>
</RequestRatePlanList>
처리 흐름
HUB → POST /api/cms/rate-plans
→ CmsController.getRatePlansXML(RequestRatePlanList)
→ CmsService.getRatePlanByProductId(propertyId)
→ DB 조회 (story_rateplan, product_rateplan_mapper 등)
→ ResponseRatePlanList 반환 (XML)
API 7.1.2 - 가용객실 수신 API POST /api/cms/push-availability [XML]
HUB에서 날짜별 객실 가용 수량(재고) 및 판매중단 여부를 자사 플랫폼으로 전송하는 API입니다.
Request: RequestPushAvailability
<RequestPushAvailability>
<Auth> ... </Auth>
<PropertyId>{숙소코드}</PropertyId>
<AvailabilityList>
<Availability>
<RoomTypeId>{객실유형ID}</RoomTypeId>
<Dates>
<Date Allotment="5" Close="N">2024-04-01</Date>
<Date Allotment="3" Close="N">2024-04-02</Date>
</Dates>
</Availability>
</AvailabilityList>
</RequestPushAvailability>
| 필드 | 설명 |
|---|---|
PropertyId |
숙소 코드 |
RoomTypeId |
객실 유형 ID |
Date.Allotment |
가용 객실 수량 |
Date.Close |
판매 중단 여부 (Y/N) |
Date 값 |
날짜 (yyyy-MM-dd) |
처리 흐름
HUB → POST /api/cms/push-availability
→ CmsController.pushAvailability(RequestPushAvailability)
→ CmsService.pushAvailability(request)
→ 가용객실 수량 DB 업데이트 (product_room_stock 등)
→ Push 수신 로그 저장 (push_availability_log)
→ ResponsePushAvailability 반환 (XML)
API 7.1.3 - 요금 수신 API POST /api/cms/push-rate [XML]
HUB에서 날짜별 요금 및 최소/최대 연박 조건을 자사 플랫폼으로 전송하는 API입니다.
Request: RequestPushRate
<RequestPushAvailability>
<Auth> ... </Auth>
<PropertyId>{숙소코드}</PropertyId>
<AvailabilityList>
<Availability>
<RatePlanId>{요금제ID}</RatePlanId>
<Dates>
<Date Price="100000" Close="N" MinLos="1" MaxLos="7">2024-04-01</Date>
</Dates>
</Availability>
</AvailabilityList>
</RequestPushAvailability>
| 필드 | 설명 |
|---|---|
RatePlanId |
요금제 ID |
Date.Price |
해당 날짜 요금 |
Date.Close |
판매 중단 여부 (Y/N) |
Date.MinLos |
최소 연박 수 |
Date.MaxLos |
최대 연박 수 |
처리 흐름
HUB → POST /api/cms/push-rate
→ CmsController.pushRates(RequestPushRate)
→ CmsService.pushRate(request)
→ 날짜별 요금 DB 업데이트 (product_rateplan_price 등)
→ ResponsePushAvailability 반환 (XML)
API 8.1 - 신규예약 API POST /api/cms/reservation-create [JSON]
자사 플랫폼에서 HUB로 신규 예약을 전송하는 API입니다. @NoAuthCheck (JWT 인증 없음).
Request: RequestBooking
{
"orderNum": "{예약번호}",
"productCode": "{숙소코드}",
"roomIdx": 1364,
"ratePlanIdx": 5548,
"ratePlanName": "테스트상품",
"numberRooms": 1,
"startDate": "2024-04-01",
"endDate": "2024-04-02",
"mealCode": "N",
"price": 100000,
"adultCount": 2,
"childCount": 0,
"customer": { ... },
"occupant": { ... }
}
| 필드 | 설명 |
|---|---|
orderNum (ChannelBookingId) |
자사 예약번호 |
productCode (PropertyId) |
숙소 코드 |
roomIdx (RoomTypeId) |
객실 유형 Idx |
ratePlanIdx (RatePlanId) |
요금제 Idx |
mealCode |
조식 포함 여부 (Y/N) |
price |
합계 요금 (ota_price) |
customer |
예약자 정보 |
occupant |
투숙객 정보 |
처리 흐름
자사 플랫폼 결제 완료
→ POST /api/cms/reservation-create (JSON)
→ CmsController.reservation(RequestBooking)
→ CmsService.createReservation(request)
→ Story API 호출 (XML 변환 후 전송, Endpoint는 설정값)
→ Story 응답 수신 (BookingId 포함)
→ 예약 로그 저장 (reservation_log)
→ ResponseReservation 반환 (JSON)
API 8.2 - 예약수정 API PUT /api/cms/reservation-modify [JSON]
자사 플랫폼에서 HUB로 예약 수정을 전송하는 API입니다. @NoAuthCheck.
Request: RequestModify (RequestBooking과 유사한 구조)
{
"orderNum": "{예약번호}",
"roomIdx": 1364,
"ratePlanIdx": 5548,
"ratePlanName": "테스트상품",
"numberRooms": 1,
"startDate": "2024-04-01",
"endDate": "2024-04-03",
"mealCode": "Y",
"price": 200000,
"adultCount": 2,
"childCount": 1,
"customer": { ... },
"occupant": { ... }
}
처리 흐름
자사 플랫폼 예약 수정
→ PUT /api/cms/reservation-modify (JSON)
→ CmsController.modifyReservation(RequestModify)
→ CmsService.modifyReservation(request)
→ Story API 호출 (XML 변환 후 전송, Endpoint는 설정값)
→ 예약 수정 로그 업데이트
→ ResponseModify 반환 (JSON)
API 8.3 - 예약취소 API POST /api/cms/reservation-cancel [JSON]
자사 플랫폼에서 HUB로 예약 취소를 전송하는 API입니다. @NoAuthCheck.
Request: RequestCancellation
{
"orderNum": "{예약번호}",
"bookingId": "{HUB예약번호}",
"cancellationReason": "단순취소"
}
| 필드 | 설명 |
|---|---|
orderNum (ChannelBookingId) |
자사 예약번호 |
bookingId (BookingId) |
HotelStory 예약번호 |
cancellationReason |
취소 사유 |
처리 흐름
자사 플랫폼 취소 요청
→ POST /api/cms/reservation-cancel (JSON)
→ CmsController.cancelReservation(RequestCancellation)
→ CmsService.cancelReservation(request)
→ Story API 호출 (XML 변환 후 전송, Endpoint는 설정값)
→ 취소 결과 수신 및 로그 저장
→ ResponseCancellation 반환 (JSON)
API 9 - 예약조회 API POST /api/cms/reservation [JSON]
자사 플랫폼에서 HUB로 예약 내역을 조회하는 API입니다. @NoAuthCheck.
Request: RequestBookingList
{
"orderNum": "{예약번호}",
"productCode": "{숙소코드}",
"bookingId": "",
"dateType": "B",
"startDate": "2024-04-01",
"endDate": "2024-04-30"
}
| 필드 | 설명 |
|---|---|
orderNum |
자사 예약번호 (선택) |
productCode |
숙소 코드 (선택) |
bookingId |
Story 예약번호 (선택) |
dateType |
기간 검색 타입: B=예약일, C=투숙일 |
startDate / endDate |
검색 기간 |
처리 흐름
자사 플랫폼 → POST /api/cms/reservation
→ CmsController.getReservation(RequestBookingList)
→ CmsService.getReservation(request)
→ Story API 호출 또는 로컬 DB 조회
→ ResponseBookingList 반환 (JSON)
예약 목록조회(대사) - GET /api/cms/reservation/sync
HUB 데이터와 자사 DB 데이터를 대사(reconciliation) 목적으로 조회하는 내부 API입니다. @NoAuthCheck.
처리 흐름
GET /api/cms/reservation/sync
→ CmsController.getStoryReservationList()
→ CmsService.getStoryReservationList()
→ Story reservation_log DB 전체 조회
→ 결과 반환 (JSON)
GSA - 총판 숙소 업데이트 GET /api/gsa/upsert-gsa
매일 새벽 1회 배치 실행으로 HotelStory의 전체 숙소 정보를 자사 DB에 동기화합니다.
@NoAuthCheck. WebClient를 활용한 병렬 처리(Reactor) 포함.
처리 흐름 상세
[배치 실행] GET /api/gsa/upsert-gsa
↓
1. Story PropertyList 요청
Story CMS API 호출 (Endpoint는 설정값)
RequestPropertyList (XML) → ResponsePropertyList (XML) 파싱
↓
2. 숙소 정보 upsert
story_product INSERT/UPDATE
story_product_change_log 변경이력 저장
↓
3. 숙소 설명 저장
story_description upsert
↓
4. 취소정책 저장
story_cancelpenalties upsert
↓
5. 숙소 이미지 저장
story_photo upsert
↓
6. 객실유형 & 요금제 병렬 조회 (WebClient + Reactor Mono.zip)
각 PropertyId 별로:
- POST /api/cms/room-types → PMSResponseRoomTypeList
- POST /api/cms/rate-plans → PMSResponseRatePlanList
story_room_type upsert
story_rateplan upsert
↓
7-1. 신규 숙소 → 자사 Product 생성
findAllWithoutMappingStoryProduct()
insertProduct() : product_code 채번 (건별 처리)
insertProductMapper() : external_name=STORYGSA, dis_avail=0
↓
7-2. 변경 숙소 → 자사 Product 업데이트
findAllModifyStoryProduct()
updateProduct()
↓
7-3. 연동해제 숙소 → product_mapper avail=0 처리
findAllDisableStoryProduct()
updateProductMapper()
↓
8-1. 객실유형 동기화
findAllWithoutMappingStoryProductRoom()
insertProductRoomList()
insertProductRoomMapperList()
updateProductRoomList()
updateProductRoomMapperList()
↓
8-2. 요금제 동기화
findAllWithoutMappingStoryProductRatePlan()
insertProductRatePlanList()
insertProductRatePlanMapperList()
updateProductRatePlanList()
updateProductRatePlanMapperList()
↓
9. 취소정책 동기화
insertCancelType()
upsertProductRatePlanCancelType()
↓
10. S3 이미지 동기화
story_photo ↔ upload_file 비교
- 삭제: S3 파일 삭제 → upload_file 삭제
- 신규: Naver Cloud Storage 업로드 → upload_file INSERT
- sort 번호 재정렬
관련 DB 테이블 요약
| 테이블 | 설명 |
|---|---|
story_product |
Story 숙소 원본 데이터 |
story_product_change_log |
숙소 변경 이력 |
story_description |
숙소/객실/요금제 설명 |
story_cancelpenalties |
취소 정책 |
story_photo |
숙소 이미지 URL |
story_room_type |
객실 유형 |
story_rateplan |
요금제 |
product |
자사 상품 |
product_mapper |
자사 상품-Story 매핑 |
product_room_type |
자사 객실 유형 |
product_room_mapper |
객실 유형 매핑 |
product_rateplan |
자사 요금제 |
product_rateplan_mapper |
요금제 매핑 |
product_rateplan_cancel_type |
요금제별 취소정책 |
upload_file |
이미지 파일 관리 |
reservation_log |
예약 송수신 로그 |
push_availability_log |
가용객실 수신 로그 |
기술 스택
| 항목 | 기술 |
|---|---|
| Framework | Spring Boot |
| HTTP Client | RestTemplate, WebClient (Reactor) |
| XML 직렬화 | JAXB |
| ORM | MyBatis |
| 인증 | JWT (Spring Security) |
| DB | MariaDB |
| 이미지 저장소 | Naver Cloud Object Storage (S3 호환 SDK) |
| 병렬처리 | Project Reactor (Mono.zip, Schedulers.boundedElastic) |
주요 참고사항
- XML ↔ JSON 혼용: 객실/요금/가용Push 는 XML, 예약 관련은 JSON으로 처리됩니다.
- GSA 배치: 매일 1회 전체 데이터를 수신하므로 신규/변경/삭제 모두 처리해야 합니다. product_code 채번 때문에 Product 생성은 일괄 처리 불가, 건별로 처리합니다.
- product 설명(product_detail_kr): 운영팀 논의 필요로 story_description DB 내재화만 하고 보류 상태입니다.
- WebClient 병렬:
Mono.zip을 사용하여 각 숙소의 객실유형/요금제 조회를 동시에 처리합니다. - S3 이미지 관리: story_photo ↔ upload_file 비교를 통해 증분 업로드/삭제를 처리합니다.