> ## Documentation Index
> Fetch the complete documentation index at: https://api-docs.vpms.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 예약 관리 (Reservation)

> 숙박 및 대실 예약의 생성, 수정, 조회, 상태 관리 API

## 개요

Core 서비스의 예약 도메인은 숙박시설의 예약 전체 생명주기를 관리합니다. 예약 조회, 체크인/체크아웃, 객실 배정, 설정 관리 등의 기능을 제공합니다.

## Query

### getAccommodationReservations

숙박시설의 예약 목록을 페이지네이션하여 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetAccommodationReservations(
  $accommodationId: ULID!
  $filter: AccommodationReservationsFilter
  $first: Int
  $after: String
) {
  getAccommodationReservations(
    accommodationId: $accommodationId
    filter: $filter
    first: $first
    after: $after
  ) {
    edges {
      node {
        id
        status
        useStartAt
        useExpireAt
        roomType {
          id
          name
        }
        room {
          id
          roomNumber
        }
        primaryGuestProfile {
          name
          contacts {
            contactType
            contactValue
          }
        }
      }
      cursor
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  조회할 숙박시설 ID
</ParamField>

<ParamField path="filter" type="AccommodationReservationsFilter">
  필터 옵션:

  * `keyword`: 예약 검색 키워드 (예약ID, 고객명, 전화번호, 차량번호, 신분증번호)
  * `dateFilterType`: 날짜 필터 타입 (`createdAt`, `inHouseDate`, `intendedUseStartAt`, `intendedUseExpireAt`)
  * `includeMultipleNights`: 다박 예약 포함 여부
  * `sortKey`: 정렬 기준 (기본값: `createdAt`)
  * `sortDirection`: 정렬 방향 (`asc`, `desc` - 기본값: `desc`)
</ParamField>

<ParamField path="first" type="Int">
  한 번에 조회할 예약 수 (기본값: 20)
</ParamField>

<ParamField path="after" type="String">
  페이지네이션 커서
</ParamField>

#### 응답

<ResponseField name="edges" type="Array<Edge>">
  예약 목록

  * `node`: 예약 상세 정보
  * `cursor`: 페이지네이션 커서
</ResponseField>

<ResponseField name="pageInfo" type="PageInfo">
  페이지네이션 정보

  * `hasNextPage`: 다음 페이지 존재 여부
  * `endCursor`: 마지막 커서 값
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getAccommodationReservations(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      filter: {
        keyword: "홍길동"
        sortKey: "useStartAt"
        sortDirection: "desc"
      }
      first: 10
    ) {
      edges {
        node {
          id
          status
          useStartAt
          primaryGuestProfile {
            name
          }
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getAccommodationReservations": {
        "edges": [
          {
            "node": {
              "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
              "status": "CHECKED_IN",
              "useStartAt": "2025-12-23T15:00:00Z",
              "primaryGuestProfile": {
                "name": "홍길동"
              }
            }
          }
        ],
        "pageInfo": {
          "hasNextPage": false,
          "endCursor": "01HQKS9V8X2N3P4Q5R6S7T8U9V"
        }
      }
    }
  }
  ```
</CodeGroup>

#### 키워드 검색

`keyword` 필터는 다음 정보를 검색합니다:

* **예약 ID**: ULID 형식의 예약 고유번호
* **고객명**: 예약자 이름
* **전화번호**: 연락처 정보
* **차량번호**: 차량 등록번호
* **신분증번호**: 여권번호 등 신분증 정보

<Warning>
  `includeMultipleNights`와 `dateFilterType`은 함께 사용할 수 없습니다.
</Warning>

***

### getAccommodationReservationsAggregate

예약 통계 정보를 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetAccommodationReservationsAggregate(
  $accommodationId: ULID!
  $filter: AccommodationReservationsFilter
) {
  getAccommodationReservationsAggregate(
    accommodationId: $accommodationId
    filter: $filter
  ) {
    count
    totalSleeps
    totalChildSleeps
    totalInfantSleeps
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  통계를 조회할 숙박시설 ID
</ParamField>

<ParamField path="filter" type="AccommodationReservationsFilter">
  집계 필터 조건 (getAccommodationReservations와 동일)
</ParamField>

#### 응답

<ResponseField name="count" type="Int">
  총 예약 수
</ResponseField>

<ResponseField name="totalSleeps" type="Int">
  총 성인 투숙객 수
</ResponseField>

<ResponseField name="totalChildSleeps" type="Int">
  총 어린이 투숙객 수
</ResponseField>

<ResponseField name="totalInfantSleeps" type="Int">
  총 유아 투숙객 수
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getAccommodationReservationsAggregate(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      filter: {
        dateFilterType: "useStartAt"
        includeMultipleNights: false
      }
    ) {
      count
      totalSleeps
      totalChildSleeps
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getAccommodationReservationsAggregate": {
        "count": 42,
        "totalSleeps": 95,
        "totalChildSleeps": 12
      }
    }
  }
  ```
</CodeGroup>

***

### getSingleReservation

특정 예약의 상세 정보를 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetSingleReservation($reservationId: ULID!) {
  getSingleReservation(reservationId: $reservationId) {
    id
    status
    type
    useStartAt
    useExpireAt
    sleeps
    childSleeps
    infantSleeps
    checkInCode
    memo {
      content
    }
    request {
      content
    }
    roomType {
      id
      name
    }
    room {
      id
      roomNumber
    }
    primaryGuestProfile {
      name
      contacts {
        contactType
        contactValue
      }
    }
    guestProfiles {
      name
    }
    folio {
      id
      totalPrice
    }
    bills {
      id
      amount
    }
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  조회할 예약 ID
</ParamField>

#### 권한

다음 중 하나의 권한이 필요합니다:

* 숙박시설 관리 권한
* 서드파티 읽기 권한 (`ACC_RESERVATION_READ`)
* 관리자 영구 토큰

#### 응답

<ResponseField name="Reservation" type="Reservation">
  예약 전체 정보

  * `status`: 예약 상태 (`INQUIRY`, `TENTATIVE`, `DEFINITE`, `HOLDING`, `CHECKED_IN`, `CHECKED_OUT`)
  * `type`: 예약 타입 (`lodge`: 숙박, `rent`: 대실)
  * `sleeps/childSleeps/infantSleeps`: 투숙 인원
  * `checkInCode`: 체크인 코드
  * `memo`: 관리자 메모
  * `request`: 고객 요청사항
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getSingleReservation(
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
    ) {
      id
      status
      useStartAt
      useExpireAt
      primaryGuestProfile {
        name
      }
      room {
        roomNumber
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getSingleReservation": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "status": "CHECKED_IN",
        "useStartAt": "2025-12-23T15:00:00Z",
        "useExpireAt": "2025-12-24T11:00:00Z",
        "primaryGuestProfile": {
          "name": "홍길동"
        },
        "room": {
          "roomNumber": "201"
        }
      }
    }
  }
  ```
</CodeGroup>

***

### getReservationReadyToCheckIn

체크인 코드로 체크인 가능한 예약을 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetReservationReadyToCheckIn(
  $accommodationId: ULID!
  $checkInCode: String!
) {
  getReservationReadyToCheckIn(
    accommodationId: $accommodationId
    checkInCode: $checkInCode
  ) {
    id
    status
    useStartAt
    useExpireAt
    roomId
    primaryGuestProfile {
      name
    }
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  숙박시설 ID
</ParamField>

<ParamField path="checkInCode" type="String!" required>
  체크인 코드
</ParamField>

#### 체크인 시간 제한 없음

예약 설정에서 `unlimitedCheckIn`이 활성화된 경우:

* 숙박 이용 시 입실 시간을 제한하지 않아 언제든지 입실할 수 있습니다.
  (키오스크 이용 시 추가 요금 없이 입실 가능)

#### 응답

<ResponseField name="reservations" type="Array<Reservation>">
  체크인 가능한 예약 목록

  * 상태: `CHECKED_IN`, `CHECKED_OUT` 제외
  * 기간: 조회일 기준 전후 3일 범위
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getReservationReadyToCheckIn(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      checkInCode: "1234"
    ) {
      id
      status
      roomId
      primaryGuestProfile {
        name
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getReservationReadyToCheckIn": [
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
          "status": "DEFINITE",
          "roomId": "01HQKS9V8X2N3P4Q5R6S7T8U9W",
          "primaryGuestProfile": {
            "name": "홍길동"
          }
        }
      ]
    }
  }
  ```
</CodeGroup>

***

### getAccommodationGuestReservations

고객이 자신의 예약을 조회합니다 (SMS 인증 필요).

#### GraphQL Signature

```graphql theme={null}
query GetAccommodationGuestReservations(
  $accommodationId: ULID!
  $guestName: String!
  $phone: String!
  $authToken: String!
) {
  getAccommodationGuestReservations(
    accommodationId: $accommodationId
    guestName: $guestName
    phone: $phone
    authToken: $authToken
  ) {
    id
    status
    useStartAt
    useExpireAt
    roomType {
      name
    }
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  숙박시설 ID
</ParamField>

<ParamField path="guestName" type="String!" required>
  고객 이름
</ParamField>

<ParamField path="phone" type="String!" required>
  고객 전화번호
</ParamField>

<ParamField path="authToken" type="String!" required>
  SMS 인증 토큰 (`requestMileageSMSAuth` → `confirmMileageSMSAuth`로 발급)
</ParamField>

#### 인증 흐름

1. `requestMileageSMSAuth(phone, type: "reservation")`로 SMS 발송
2. `confirmMileageSMSAuth(phone, number)`로 인증 토큰 발급
3. 발급받은 토큰으로 예약 조회

#### 응답

<ResponseField name="reservations" type="Array<Reservation>">
  고객의 예약 목록

  * 기간: 최근 30일 \~ 향후 1년
  * 상태: 삭제되지 않은 예약만
</ResponseField>

#### 에러 처리

<ResponseField name="AUTH_NUMBER_NOT_MATCH">
  인증 토큰이 유효하지 않거나 만료되었습니다.
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  query {
    getAccommodationGuestReservations(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      guestName: "홍길동"
      phone: "01012345678"
      authToken: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
    ) {
      id
      status
      useStartAt
      roomType {
        name
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getAccommodationGuestReservations": [
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
          "status": "DEFINITE",
          "useStartAt": "2025-12-25T15:00:00Z",
          "roomType": {
            "name": "디럭스룸"
          }
        }
      ]
    }
  }
  ```
</CodeGroup>

***

### getLodgeReservationsInDate

특정 기간의 숙박 예약을 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetLodgeReservationsInDate(
  $accommodationId: ULID!
  $startDate: DateTime!
  $endDate: DateTime!
) {
  getLodgeReservationsInDate(
    accommodationId: $accommodationId
    startDate: $startDate
    endDate: $endDate
  ) {
    id
    useStartAt
    useExpireAt
    type
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  숙박시설 ID
</ParamField>

<ParamField path="startDate" type="DateTime!" required>
  조회 시작 날짜
</ParamField>

<ParamField path="endDate" type="DateTime!" required>
  조회 종료 날짜
</ParamField>

#### 응답

<ResponseField name="reservations" type="Array<Reservation>">
  숙박(`lodge`) 타입 예약 목록

  * 조건: `useStartAt <= endDate` AND `useExpireAt >= startDate`
</ResponseField>

***

### getRentReservationsInDate

특정 기간의 대실 예약을 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetRentReservationsInDate(
  $accommodationId: ULID!
  $startDate: DateTime!
  $endDate: DateTime!
) {
  getRentReservationsInDate(
    accommodationId: $accommodationId
    startDate: $startDate
    endDate: $endDate
  ) {
    id
    useStartAt
    useExpireAt
    type
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  숙박시설 ID
</ParamField>

<ParamField path="startDate" type="DateTime!" required>
  조회 시작 날짜
</ParamField>

<ParamField path="endDate" type="DateTime!" required>
  조회 종료 날짜
</ParamField>

#### 응답

<ResponseField name="reservations" type="Array<Reservation>">
  대실(`rent`) 타입 예약 목록

  * 조건: `useStartAt >= startDate` AND `useStartAt <= endDate`
</ResponseField>

***

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  숙박시설 ID
</ParamField>

<ParamField path="otaCodes" type="[String!]!" required>
  검사할 OTA 예약번호 목록
</ParamField>

#### 응답

<ResponseField name="duplicates" type="Array<String>">
  중복된 OTA 예약번호 목록
</ResponseField>

***

## Mutation

### createReservationMemo

예약에 메모 또는 요청사항을 추가합니다.

#### GraphQL Signature

```graphql theme={null}
mutation CreateReservationMemo($input: CreateReservationMemoInput!) {
  createReservationMemo(input: $input) {
    id
    reservationId
    type
    content
  }
}
```

#### 파라미터

<ParamField path="input.reservationId" type="ULID!" required>
  메모를 추가할 예약 ID
</ParamField>

<ParamField path="input.type" type="ReservationMemoType!" required>
  메모 타입:

  * `MEMO`: 관리자 메모
  * `REQUEST`: 고객 요청사항
</ParamField>

<ParamField path="input.content" type="String!" required>
  메모 내용
</ParamField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    createReservationMemo(input: {
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      type: MEMO
      content: "체크인 시 웰컴 선물 제공"
    }) {
      id
      content
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "createReservationMemo": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9X",
        "content": "체크인 시 웰컴 선물 제공"
      }
    }
  }
  ```
</CodeGroup>

***

### updateReservationMemo

예약 메모를 수정합니다.

#### GraphQL Signature

```graphql theme={null}
mutation UpdateReservationMemo($input: UpdateReservationMemoInput!) {
  updateReservationMemo(input: $input) {
    id
    type
    content
  }
}
```

#### 파라미터

<ParamField path="input.id" type="ULID!" required>
  수정할 메모 ID
</ParamField>

<ParamField path="input.type" type="ReservationMemoType">
  변경할 메모 타입
</ParamField>

<ParamField path="input.content" type="String">
  변경할 메모 내용
</ParamField>

***

### deleteReservationMemo

예약 메모를 삭제합니다.

#### GraphQL Signature

```graphql theme={null}
mutation DeleteReservationMemo($id: ULID!) {
  deleteReservationMemo(id: $id)
}
```

#### 파라미터

<ParamField path="id" type="ULID!" required>
  삭제할 메모 ID
</ParamField>

#### 응답

<ResponseField name="success" type="Boolean!">
  삭제 성공 여부
</ResponseField>

***

### reservationCheckIn

예약을 체크인 상태로 변경합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ReservationCheckIn(
  $reservationId: ULID!
  $useExpireAtLimit: String
) {
  reservationCheckIn(
    reservationId: $reservationId
    useExpireAtLimit: $useExpireAtLimit
  ) {
    id
    status
    useStartAt
    useExpireAt
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  체크인할 예약 ID
</ParamField>

<ParamField path="useExpireAtLimit" type="String">
  체크아웃 제한 시간 (HH:mm 형식, 대실 무제한 체크인 설정 시 사용)
</ParamField>

#### 전제조건

* 예약에 객실이 배정되어 있어야 함 (`roomId != null`)
* 이미 체크인 이상 상태가 아니어야 함

#### 무제한 체크인 (대실)

예약 설정에서 `unlimitedRentCheckIn`이 활성화된 경우:

* 현재 시각을 체크인 시간으로 설정
* 원래 예약 기간만큼 연장하여 체크아웃 시간 계산
* `useExpireAtLimit`이 제공되면 해당 시간을 초과하지 않도록 제한

#### 에러 처리

<ResponseField name="ROOM_UNASSIGNED">
  객실이 배정되지 않은 예약입니다.
</ResponseField>

<ResponseField name="ALREADY_CHECKED_IN">
  이미 체크인 완료된 예약입니다.
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    reservationCheckIn(
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      useExpireAtLimit: "20:00"
    ) {
      id
      status
      useStartAt
      useExpireAt
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "reservationCheckIn": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "status": "CHECKED_IN",
        "useStartAt": "2025-12-23T06:30:00Z",
        "useExpireAt": "2025-12-23T11:00:00Z"
      }
    }
  }
  ```
</CodeGroup>

***

### reservationCheckOut

예약을 체크아웃 상태로 변경합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ReservationCheckOut($reservationId: ULID!) {
  reservationCheckOut(reservationId: $reservationId) {
    id
    status
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  체크아웃할 예약 ID
</ParamField>

#### 자동 처리

체크아웃 시 자동으로 다음 작업을 수행합니다:

* 객실 상태를 `NOT_USING`으로 변경
* 객실 청소 상태를 `DIRTY`로 변경

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    reservationCheckOut(
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
    ) {
      id
      status
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "reservationCheckOut": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "status": "CHECKED_OUT"
      }
    }
  }
  ```
</CodeGroup>

***

### roomCheckIn

객실 번호로 해당 객실의 예약을 체크인합니다.

#### GraphQL Signature

```graphql theme={null}
mutation RoomCheckIn($roomId: ULID!) {
  roomCheckIn(roomId: $roomId) {
    id
    status
  }
}
```

#### 파라미터

<ParamField path="roomId" type="ULID!" required>
  체크인할 객실 ID
</ParamField>

#### 처리 로직

* 해당 객실의 현재 또는 예정된 예약 중 가장 빠른 예약을 체크인
* 조회 조건: `useExpireAt >= 현재시각`
* 이미 체크인된 예약은 제외

#### 에러 처리

<ResponseField name="ALREADY_CHECKED_IN">
  해당 예약이 이미 체크인 상태입니다.
</ResponseField>

***

### roomCheckOut

객실 번호로 해당 객실의 예약을 체크아웃합니다.

#### GraphQL Signature

```graphql theme={null}
mutation RoomCheckOut($roomId: ULID!) {
  roomCheckOut(roomId: $roomId) {
    id
    status
    useExpireAt
  }
}
```

#### 파라미터

<ParamField path="roomId" type="ULID!" required>
  체크아웃할 객실 ID
</ParamField>

#### 처리 로직

* 해당 객실의 현재 사용 중인 예약을 체크아웃
* 조회 조건: `useStartAt <= 현재시각` AND `useExpireAt >= 현재시각`
* `useExpireAt`을 현재 시각으로 업데이트

***

### assignRoomToReservation

예약에 객실을 배정합니다.

#### GraphQL Signature

```graphql theme={null}
mutation AssignRoomToReservation($input: AssignRoomInput!) {
  assignRoomToReservation(input: $input) {
    id
    roomId
    roomTypeId
    room {
      id
      roomNumber
    }
  }
}
```

#### 파라미터

<ParamField path="input.reservationId" type="ULID!" required>
  객실을 배정할 예약 ID
</ParamField>

<ParamField path="input.roomId" type="ULID!" required>
  배정할 객실 ID
</ParamField>

<ParamField path="input.roomTypeId" type="ULID">
  객실 타입 ID (미제공 시 예약의 intendedRoomTypeId 사용)
</ParamField>

<ParamField path="input.roomName" type="String">
  객실 이름
</ParamField>

<ParamField path="input.isManualAssign" type="Boolean">
  수동 배정 여부 (자동 시간 조정 활성화 시 사용)
</ParamField>

#### 객실 가용성 검사

배정 전 다음을 확인합니다:

* 해당 객실이 예약 기간 동안 사용 가능한지
* 다른 예약과 겹치지 않는지

#### 자동 시간 조정

예약 설정에서 `useManualAssignAutoAdjustment`가 활성화되고 `isManualAssign: true`인 경우:

* **숙박**: `useStartAt`을 현재 시각으로 조정
* **대실**: `useStartAt`을 현재 시각으로, `useExpireAt`을 원래 기간만큼 연장

#### 자동 처리

객실 배정 시 자동으로:

* 객실 상태를 `USING`으로 변경
* 감사 로그에 객실 상태 변경 기록

#### 에러 처리

<ResponseField name="RESERVATION_NOT_FOUND">
  예약을 찾을 수 없습니다.
</ResponseField>

<ResponseField name="ROOM_NOT_AVAILABLE">
  해당 기간에 객실을 사용할 수 없습니다.
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    assignRoomToReservation(input: {
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      roomId: "01HQKS9V8X2N3P4Q5R6S7T8U9W"
      isManualAssign: true
    }) {
      id
      roomId
      room {
        roomNumber
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "assignRoomToReservation": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "roomId": "01HQKS9V8X2N3P4Q5R6S7T8U9W",
        "room": {
          "roomNumber": "201"
        }
      }
    }
  }
  ```
</CodeGroup>

***

### cancelAssignReservation

예약의 객실 배정을 취소합니다.

#### GraphQL Signature

```graphql theme={null}
mutation CancelAssignReservation($reservationId: ULID!) {
  cancelAssignReservation(reservationId: $reservationId) {
    id
    roomId
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  배정을 취소할 예약 ID
</ParamField>

#### 처리 로직

체크인 전 예약인 경우:

* 객실 상태를 `NOT_USING`으로 변경
* 예약의 `roomId`를 `null`로 설정

#### 전제조건

* 예약에 객실이 배정되어 있어야 함
* 체크인 상태가 아니어야 함

#### 에러 처리

<ResponseField name="ROOM_UNASSIGNED">
  객실이 배정되지 않은 예약입니다.
</ResponseField>

***

### changeReservationRoom

예약의 배정 객실을 변경합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ChangeReservationRoom($input: ChangeReservationRoomInput!) {
  changeReservationRoom(input: $input) {
    id
    roomId
    room {
      id
      roomNumber
    }
  }
}
```

#### 파라미터

<ParamField path="input.reservationId" type="ULID!" required>
  객실을 변경할 예약 ID
</ParamField>

<ParamField path="input.roomId" type="ULID!" required>
  새로 배정할 객실 ID
</ParamField>

<ParamField path="input.roomTypeId" type="ULID">
  객실 타입 ID (미제공 시 기존 타입 유지)
</ParamField>

<ParamField path="input.roomName" type="String">
  객실 이름
</ParamField>

#### 처리 로직

1. 새 객실 상태를 `USING`으로 변경
2. 예약의 객실 정보 업데이트 (`isReAssign: true`)
3. 감사 로그에 객실 변경 기록

#### 전제조건

* 예약에 기존 객실이 배정되어 있어야 함

#### 에러 처리

<ResponseField name="ROOM_UNASSIGNED">
  객실이 배정되지 않은 예약입니다. `assignRoomToReservation`을 사용하세요.
</ResponseField>

***

### modifyReservation

예약 정보를 수정합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ModifyReservation($input: ModifyReservationInput!) {
  modifyReservation(input: $input) {
    id
    status
    useStartAt
    useExpireAt
    sleeps
    childSleeps
    infantSleeps
    memo {
      content
    }
    request {
      content
    }
  }
}
```

#### 파라미터

<ParamField path="input.id" type="ULID!" required>
  수정할 예약 ID
</ParamField>

<ParamField path="input.useStartAt" type="DateTime">
  체크인 시간
</ParamField>

<ParamField path="input.useExpireAt" type="DateTime">
  체크아웃 시간
</ParamField>

<ParamField path="input.sleeps" type="Int">
  성인 투숙객 수
</ParamField>

<ParamField path="input.childSleeps" type="Int">
  어린이 투숙객 수
</ParamField>

<ParamField path="input.infantSleeps" type="Int">
  유아 투숙객 수
</ParamField>

<ParamField path="input.memoContent" type="String">
  관리자 메모 내용 (메모가 없으면 생성, 있으면 업데이트)
</ParamField>

<ParamField path="input.requestContent" type="String">
  고객 요청사항 내용 (요청사항이 없으면 생성, 있으면 업데이트)
</ParamField>

#### 권한

다음 중 하나의 권한이 필요합니다:

* 숙박시설 관리 권한
* 서드파티 쓰기 권한 (`ACC_RESERVATION_WRITE`)

#### 메모 처리

* `memoContent`: MEMO 타입 메모를 생성/수정
* `requestContent`: REQUEST 타입 메모를 생성/수정
* 각 메모는 예약당 1개씩만 존재

#### 키오스크 수정

키오스크를 통한 수정인 경우 (`context.user.kioskId` 존재):

* 스케줄 재조정 방지 (`preventReschedule: true`)

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    modifyReservation(input: {
      id: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      sleeps: 2
      childSleeps: 1
      memoContent: "조식 추가 요청"
      requestContent: "높은 층 선호"
    }) {
      id
      sleeps
      childSleeps
      memo {
        content
      }
      request {
        content
      }
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "modifyReservation": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "sleeps": 2,
        "childSleeps": 1,
        "memo": {
          "content": "조식 추가 요청"
        },
        "request": {
          "content": "높은 층 선호"
        }
      }
    }
  }
  ```
</CodeGroup>

***

### deleteReservation

예약을 삭제(취소)합니다.

#### GraphQL Signature

```graphql theme={null}
mutation DeleteReservation($id: ULID!, $cancelReason: String) {
  deleteReservation(id: $id, cancelReason: $cancelReason) {
    id
    deletedAt
  }
}
```

#### 파라미터

<ParamField path="id" type="ULID!" required>
  삭제할 예약 ID
</ParamField>

<ParamField path="cancelReason" type="String">
  취소 사유
</ParamField>

#### 권한

다음 중 하나의 권한이 필요합니다:

* 숙박시설 관리 권한
* 서드파티 쓰기 권한 (`ACC_RESERVATION_WRITE`)

#### 삭제 정보

삭제 시 다음 정보가 기록됩니다:

* `deletedAt`: 삭제 시각
* `reason`: "DELETED\_BY\_USER"
* `cancelledBy`: 삭제 수행자 이름
* `cancelReason`: 취소 사유

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    deleteReservation(
      id: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      cancelReason: "고객 일정 변경"
    ) {
      id
      deletedAt
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "deleteReservation": {
        "id": "01HQKS9V8X2N3P4Q5R6S7T8U9V",
        "deletedAt": "2025-12-23T12:34:56Z"
      }
    }
  }
  ```
</CodeGroup>

***

### restoreReservation

삭제된 예약을 복구합니다.

#### GraphQL Signature

```graphql theme={null}
mutation RestoreReservation($id: ULID!) {
  restoreReservation(id: $id) {
    id
    deletedAt
  }
}
```

#### 파라미터

<ParamField path="id" type="ULID!" required>
  복구할 예약 ID
</ParamField>

#### 권한

다음 중 하나의 권한이 필요합니다:

* 숙박시설 관리 권한
* 서드파티 쓰기 권한 (`ACC_RESERVATION_WRITE`)

#### 복구 처리

* `deletedAt`을 `null`로 설정
* 예약 상태는 삭제 전 상태로 복구

***

### setReservationSettings

예약 관련 설정을 저장합니다.

#### GraphQL Signature

```graphql theme={null}
mutation SetReservationSettings(
  $accommodationId: ULID!
  $inputs: [SettingInput!]!
) {
  setReservationSettings(
    accommodationId: $accommodationId
    inputs: $inputs
  ) {
    id
    key
    value
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  설정을 저장할 숙박시설 ID
</ParamField>

<ParamField path="inputs" type="[SettingInput!]!" required>
  설정 목록 (key-value 쌍)
</ParamField>

#### 주요 설정

* `unlimitedCheckIn`: 무제한 체크인 활성화
* `unlimitedRentCheckIn`: 대실 무제한 체크인 활성화
* `unlimitedRentCheckInDays`: 무제한 체크인 가능 요일 (0=일요일)
* `useManualAssignAutoAdjustment`: 수동 배정 시 자동 시간 조정
* `useAutoStopSale`: 자동 판매 중지 활성화
* `autoStopSaleUntil`: 자동 판매 중지 종료 시각

#### 자동 판매 중지

`useAutoStopSale` 또는 `autoStopSaleUntil` 변경 시:

* 감사 로그에 해당 필드를 강조 표시 (`highlightFields`)

#### 캐시 관리

설정 변경 시 자동으로 캐시를 무효화합니다 (`reservationSettingCache.flush`)

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    setReservationSettings(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      inputs: [
        { key: "unlimitedCheckIn", value: "true" }
        { key: "useManualAssignAutoAdjustment", value: "true" }
      ]
    ) {
      id
      key
      value
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "setReservationSettings": [
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9X",
          "key": "unlimitedCheckIn",
          "value": "true"
        },
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9Y",
          "key": "useManualAssignAutoAdjustment",
          "value": "true"
        }
      ]
    }
  }
  ```
</CodeGroup>

***

### setRoomSettings

객실 관련 설정을 저장합니다.

#### GraphQL Signature

```graphql theme={null}
mutation SetRoomSettings(
  $accommodationId: ULID!
  $inputs: [SettingInput!]!
) {
  setRoomSettings(
    accommodationId: $accommodationId
    inputs: $inputs
  ) {
    id
    key
    value
  }
}
```

#### 파라미터

<ParamField path="accommodationId" type="ULID!" required>
  설정을 저장할 숙박시설 ID
</ParamField>

<ParamField path="inputs" type="[SettingInput!]!" required>
  설정 목록 (key-value 쌍)
</ParamField>

#### 주요 설정

* `useAssignRoom`: 자동 객실 배정 활성화
* `assignRoomBefore`: 체크인 몇 시간 전에 자동 배정할지 (시간 단위)
* `roomAssignOrderBy`: 객실 배정 순서 기준

#### 자동 배정 옵션 변경

`assignRoomBefore` 또는 `useAssignRoom` 변경 시:

* 이벤트 스토어에 `ASSIGN_ROOM_OPTION_CHANGED` 이벤트 저장

#### 캐시 관리

설정 변경 시 자동으로 캐시를 무효화합니다 (`roomSettingCache.flush`)

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    setRoomSettings(
      accommodationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      inputs: [
        { key: "useAssignRoom", value: "true" }
        { key: "assignRoomBefore", value: "2" }
      ]
    ) {
      id
      key
      value
    }
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "setRoomSettings": [
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9X",
          "key": "useAssignRoom",
          "value": "true"
        },
        {
          "id": "01HQKS9V8X2N3P4Q5R6S7T8U9Y",
          "key": "assignRoomBefore",
          "value": "2"
        }
      ]
    }
  }
  ```
</CodeGroup>

***

## 게스트 세션 API

게스트 세션 토큰을 통해 고객이 직접 예약을 관리할 수 있습니다.

### getReservationGuestSessionToken

PIN 코드 검증 후 게스트 세션 토큰을 발급합니다.

#### GraphQL Signature

```graphql theme={null}
mutation GetReservationGuestSessionToken(
  $reservationId: ULID!
  $pinCode: String!
) {
  getReservationGuestSessionToken(
    reservationId: $reservationId
    pinCode: $pinCode
  )
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  예약 ID
</ParamField>

<ParamField path="pinCode" type="String!" required>
  예약에 설정된 PIN 코드
</ParamField>

#### 응답

<ResponseField name="sessionToken" type="String!">
  게스트 세션 토큰 (이후 게스트 전용 API 호출 시 사용)
</ResponseField>

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    getReservationGuestSessionToken(
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      pinCode: "1234"
    )
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "getReservationGuestSessionToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }
  }
  ```
</CodeGroup>

***

### getSingleReservationByGuestSession

게스트 세션으로 예약 정보를 조회합니다.

#### GraphQL Signature

```graphql theme={null}
query GetSingleReservationByGuestSession(
  $reservationId: ULID!
  $sessionToken: String!
) {
  getSingleReservationByGuestSession(
    reservationId: $reservationId
    sessionToken: $sessionToken
  ) {
    id
    status
    useStartAt
    useExpireAt
    room {
      roomNumber
    }
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  조회할 예약 ID
</ParamField>

<ParamField path="sessionToken" type="String!" required>
  게스트 세션 토큰
</ParamField>

***

### checkInReservationByGuestSession

게스트가 직접 체크인합니다.

#### GraphQL Signature

```graphql theme={null}
mutation CheckInReservationByGuestSession(
  $reservationId: ULID!
  $sessionToken: String!
) {
  checkInReservationByGuestSession(
    reservationId: $reservationId
    sessionToken: $sessionToken
  ) {
    id
    status
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  체크인할 예약 ID
</ParamField>

<ParamField path="sessionToken" type="String!" required>
  게스트 세션 토큰
</ParamField>

#### 전제조건

* 객실이 배정되어 있어야 함
* 체크인 또는 체크아웃 상태가 아니어야 함

***

### checkOutReservationByGuestSession

게스트가 직접 체크아웃합니다.

#### GraphQL Signature

```graphql theme={null}
mutation CheckOutReservationByGuestSession(
  $reservationId: ULID!
  $sessionToken: String!
) {
  checkOutReservationByGuestSession(
    reservationId: $reservationId
    sessionToken: $sessionToken
  ) {
    id
    status
  }
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  체크아웃할 예약 ID
</ParamField>

<ParamField path="sessionToken" type="String!" required>
  게스트 세션 토큰
</ParamField>

#### 자동 처리

체크아웃 시 자동으로:

* 객실 상태를 `USING`으로 변경 (체크아웃 직후 상태)
* 객실 청소 상태를 `DIRTY`로 변경

***

### controlRoomDeviceByGuestSession

게스트가 객실 디바이스를 제어합니다.

#### GraphQL Signature

```graphql theme={null}
mutation ControlRoomDeviceByGuestSession(
  $reservationId: ULID!
  $sessionToken: String!
  $deviceType: String!
  $data: JSON
) {
  controlRoomDeviceByGuestSession(
    reservationId: $reservationId
    sessionToken: $sessionToken
    deviceType: $deviceType
    data: $data
  )
}
```

#### 파라미터

<ParamField path="reservationId" type="ULID!" required>
  예약 ID
</ParamField>

<ParamField path="sessionToken" type="String!" required>
  게스트 세션 토큰
</ParamField>

<ParamField path="deviceType" type="String!" required>
  디바이스 타입:

  * `"doorlock"`: 도어락 제어
</ParamField>

<ParamField path="data" type="JSON">
  디바이스별 추가 데이터
</ParamField>

#### 전제조건

* 세션 토큰이 유효해야 함 (`requireActive: true`)
* 예약에 객실이 배정되어 있어야 함

#### 도어락 제어

`deviceType: "doorlock"`인 경우:

* 예약 데이터의 `keyId`를 사용하여 스마트 도어락 개방
* SmartAccess 어댑터를 통해 실제 도어락 제어

#### 예제

<CodeGroup>
  ```graphql Request theme={null}
  mutation {
    controlRoomDeviceByGuestSession(
      reservationId: "01HQKS9V8X2N3P4Q5R6S7T8U9V"
      sessionToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
      deviceType: "doorlock"
    )
  }
  ```

  ```json Response theme={null}
  {
    "data": {
      "controlRoomDeviceByGuestSession": true
    }
  }
  ```
</CodeGroup>

***

## 예약 상태

예약은 다음과 같은 상태를 가집니다:

| 상태            | 설명      |
| ------------- | ------- |
| `INQUIRY`     | 문의      |
| `TENTATIVE`   | 임시 예약   |
| `DEFINITE`    | 확정됨     |
| `HOLDING`     | 보류 중    |
| `CHECKED_IN`  | 체크인 완료  |
| `CHECKED_OUT` | 체크아웃 완료 |

상태 레벨: `INQUIRY` \< `TENTATIVE` \< `DEFINITE` \< `HOLDING` \< `CHECKED_IN` \< `CHECKED_OUT`

## 예약 타입

| 타입      | 설명            |
| ------- | ------------- |
| `lodge` | 숙박 예약 (1박 이상) |
| `rent`  | 대실 예약 (시간 단위) |

## Intended Fields (계획 필드)

예약 생성 시 원래 의도했던 정보를 저장하는 필드입니다. 실제 체크인/체크아웃 시간이 변경되더라도 원래 계획을 추적할 수 있습니다.

| 필드                    | 타입                 | 설명                     |
| --------------------- | ------------------ | ---------------------- |
| `intendedType`        | `ReservationType!` | 계획된 예약 타입 (lodge/rent) |
| `intendedUseStartAt`  | `Date`             | 계획된 체크인 날짜/시간          |
| `intendedUseExpireAt` | `Date`             | 계획된 체크아웃 날짜/시간         |
| `intendedRoomTypeId`  | `ID`               | 계획된 객실 타입 ID           |
| `intendedRoomType`    | `RoomType`         | 계획된 객실 타입 (resolver)   |

### 사용 목적

* 예약 변경 전후 비교 분석
* 날짜 필터링 시 계획 기준 조회 (`intendedUseStartAt`, `intendedUseExpireAt`)
* 리포팅 및 통계 분석

## 날짜 필터 타입 (DateFilterType)

예약 목록 조회 시 날짜 필터링에 사용됩니다:

| 값                     | 설명             |
| --------------------- | -------------- |
| `createdAt`           | 예약 생성 날짜 기준    |
| `inHouseDate`         | 실제 체류 날짜 기준    |
| `intendedUseStartAt`  | 계획된 체크인 날짜 기준  |
| `intendedUseExpireAt` | 계획된 체크아웃 날짜 기준 |

## 사용 흐름

### 일반 예약 프로세스

1. **예약 생성**: 외부 시스템에서 예약 데이터 전송
2. **객실 배정**: `assignRoomToReservation`으로 객실 배정
3. **체크인**: `reservationCheckIn` 또는 `roomCheckIn`
4. **체크아웃**: `reservationCheckOut` 또는 `roomCheckOut`

### 무인 체크인 프로세스 (게스트)

1. **인증번호 요청**: `requestMileageSMSAuth(type: "reservation")`
2. **인증 확인**: `confirmMileageSMSAuth`로 토큰 발급
3. **예약 조회**: `getAccommodationGuestReservations`로 예약 확인
4. **세션 토큰 발급**: `getReservationGuestSessionToken`
5. **체크인**: `checkInReservationByGuestSession`
6. **도어락 개방**: `controlRoomDeviceByGuestSession`

### 키오스크 체크인 프로세스

1. **체크인 코드 입력**: 고객이 키오스크에 코드 입력
2. **예약 조회**: `getReservationReadyToCheckIn`
3. **자동 배정**: 객실 미배정 시 자동 배정
4. **체크인**: `reservationCheckIn`

## 관련 API

* [인증 API](/api-reference/core-svc/auth) - SMS 기반 고객 인증
* [객실 API](/api-reference/core-svc/room) - 객실 관리 및 상태 변경
* [요금 API](/api-reference/core-svc/folio) - 예약 요금 및 청구서 관리
