# 이벤트 데이터 구성

## Content

원본 데이터의 각 필드를 스케줄러의 이벤트 필드에 매핑하여 애플리케이션 데이터와 연결합니다. 원본 컬렉션은 기준 데이터로 유지하고, 화면에 표시되는 이벤트 요소는 해당 데이터를 시각적으로 나타내는 역할만 합니다.

## 이벤트 데이터 정의

각 이벤트에는 시작 날짜와 종료 날짜가 있어야 합니다. 사용자가 이벤트를 식별할 수 있도록 제목(subject)도 제공하는 것이 좋습니다.

```ts
interface Appointment {
  title: string;
  beginsAt: Date;
  endsAt: Date;
  notes?: string;
  roomId?: string;
}

const appointments: Appointment[] = [
  {
    title: 'Project planning',
    beginsAt: new Date(2026, 7, 24, 9, 0),
    endsAt: new Date(2026, 7, 24, 10, 30),
    notes: 'Review milestones and ownership.',
    roomId: 'room-a',
  },
];
```

스케줄러는 원본 객체를 `itemsSource`에 그대로 유지합니다. 애플리케이션에서 기존 필드명을 변경할 필요는 없습니다.

## 애플리케이션 필드 매핑

`eventFields`를 사용하여 스케줄러 이벤트 필드를 원본 데이터의 속성과 연결합니다.

```ts
import * as wjScheduler from '@mescius/wijmo.scheduler';

const eventFields: wjScheduler.SchedulerEventFields = {
  subject: 'title',
  start: 'beginsAt',
  end: 'endsAt',
  description: 'notes',
};

const scheduler = new wjScheduler.FlexScheduler('#scheduler', {
  itemsSource: appointments,
  eventFields,
  displayDate: new Date(2026, 7, 24),
});
```

`eventFields`에 할당하는 값은 이벤트 값 자체가 아니라 원본 속성명입니다. 예를 들어 `subject: 'title'`은 스케줄러가 `item.title`에서 이벤트 제목을 읽도록 지정합니다.

| 이벤트 필드 | 용도 | 필수 여부 |
| ------ | --- | ----- |
| `start` | 시작 날짜 및 시간 | 필수 |
| `end` | 종료 날짜 및 시간 | 필수 |
| `subject` | 이벤트에 표시되는 텍스트 | 권장 |
| `description` | 추가 이벤트 정보 또는 편집기 콘텐츠 | 선택 사항 |

대부분의 애플리케이션은 위 이벤트 필드를 매핑합니다. 렌더링에 `subject`가 필수는 아니지만, 사용자가 이벤트를 식별할 수 있도록 제공하는 것이 좋습니다. 값이 비어 있으면 스케줄러는 현지화된 기본 제목을 사용합니다. `SchedulerEventFields`의 전체 정의와 추가 편집기 필드는 API 참조를 확인하세요.

>type=note
> 원본 데이터에서 기본 이벤트 속성명을 이미 사용하는 경우 해당 속성에 대한 명시적 매핑은 필요하지 않습니다.

### 편집기에서 추가되는 필드 이해하기

기본 제공 편집기에서 이벤트를 저장하면 스케줄러가 편집기에서 입력한 값을 원본 데이터에 반영합니다. 제목이나 설명처럼 선택적으로 매핑하는 속성이 원본 객체에 없는 경우, 저장할 때 해당 속성이 새로 추가될 수 있습니다.
`eventFields`를 사용해 편집기 필드와 애플리케이션 속성을 매핑하면 스케줄러는 매핑된 속성에 값을 저장합니다. 예를 들어 `subject: 'title'`로 설정하면 이벤트 제목은 `item.title`에 저장됩니다. 따라서 애플리케이션에서 데이터를 검증하거나 변경 사항을 직렬화하거나 서비스에 부분 업데이트를 전송할 때는 이러한 속성도 고려해야 합니다.

## 유효한 날짜 범위 사용

매핑된 시작 및 종료 필드에는 JavaScript `Date` 객체를 할당해야 합니다. 종료 시간은 시작 시간보다 늦어야 합니다.

```ts
const event = {
  title: 'Design review',
  beginsAt: new Date(2026, 7, 24, 13, 0),
  endsAt: new Date(2026, 7, 24, 14, 0),
};
```

JavaScript의 월 값은 0부터 시작하므로 `7`은 8월을 의미합니다. 서버에서 날짜를 가져오는 경우 컬렉션에 할당하기 전에 직렬화된 값을 변환합니다.

```ts
const appointments = response.items.map(item => ({
  ...item,
  beginsAt: new Date(item.beginsAt),
  endsAt: new Date(item.endsAt),
}));
```

컬렉션 전체에서 일관된 시간대 처리 방식을 사용하세요. 애플리케이션에서 명시적으로 변환하지 않는 한 현지 시간으로 해석되는 값과 UTC 시점을 나타내는 값을 혼합하지 마세요.

## 날짜를 넘기는 이벤트 바인딩

이벤트는 하루의 경계를 넘어 이어질 수 있습니다. 이 경우 하나의 연속된 날짜 범위를 가진 단일 원본 항목으로 저장합니다.

```ts
const overnightMaintenance = {
  title: 'System maintenance',
  beginsAt: new Date(2026, 7, 24, 22, 0),
  endsAt: new Date(2026, 7, 25, 2, 0),
};
```

활성 뷰에 따라 스케줄러는 이벤트를 여러 조각으로 렌더링할 수 있습니다. 이 조각들은 여전히 동일한 원본 항목을 참조합니다. 표시 목적으로만 여러 날짜에 걸친 이벤트를 별도의 레코드로 분할하지 마세요.

## 이벤트 항목에 리소스 값 추가

리소스 할당은 이벤트 객체의 일반 속성으로 지정합니다. 리소스 설명자의 `fieldExpr`는 해당 원본 속성을 식별합니다.

```ts
const event = {
  title: 'Customer workshop',
  beginsAt: new Date(2026, 7, 25, 9, 0),
  endsAt: new Date(2026, 7, 25, 12, 0),
  roomId: 'room-a',
};
```

리소스 설명자의 `fieldExpr`는 `roomId` 필드와 일치해야 합니다. 하나의 값만 지정하는 리소스에는 단일 값을 사용하고, 여러 값을 지정할 수 있는 리소스에는 배열을 사용합니다. 각 이벤트에 설정된 값은 리소스 데이터 소스에 정의된 항목과 일치해야 합니다. 자세한 설정 방법은 리소스 구성을 참조하세요.

## CollectionView 사용

컬렉션 변경 알림, 정렬, 필터링 또는 변경 사항 추적이 필요한 경우 `CollectionView`를 바인딩합니다.

```ts
import * as wjCore from '@mescius/wijmo';

const events = new wjCore.CollectionView(appointments);

scheduler.itemsSource = events;
scheduler.eventFields = eventFields;
```

바인딩된 컬렉션을 사용하여 스케줄러에서 발생한 변경 사항을 확인하고 관리합니다. 애플리케이션 코드에서도 DOM의 이벤트 요소를 직접 조작하는 대신 컬렉션을 통해 원본 항목을 추가, 편집 및 제거해야 합니다.

## 바인딩 전 데이터 검증

서비스에서 반환된 데이터를 `itemsSource`에 할당하기 전에 검증합니다. 최소한 다음 사항을 확인하세요.

* 모든 항목에 필수 매핑에서 참조하는 속성이 포함되어 있는지 확인합니다.
* 시작 및 종료 값이 유효한 `Date` 객체인지 확인합니다.
* 종료 값이 시작 값보다 늦은지 확인합니다.
* 리소스 값이 구성된 리소스 데이터 소스와 일치하는지 확인합니다.

다음 예제는 `CollectionView`를 생성하기 전에 유효하지 않은 레코드를 제외하는 애플리케이션 수준의 방법을 보여 줍니다.

```ts
const validAppointments = appointments.filter(item =>
  item.beginsAt instanceof Date &&
  !Number.isNaN(item.beginsAt.getTime()) &&
  item.endsAt instanceof Date &&
  !Number.isNaN(item.endsAt.getTime()) &&
  item.endsAt > item.beginsAt
);

const events = new wjCore.CollectionView(validAppointments);
scheduler.itemsSource = events;
```

새로 생성하거나 수정한 이벤트에도 동일한 검증을 적용해야 합니다. 비즈니스 규칙에 따른 검증과 서버 측 검증은 애플리케이션에서 직접 처리해야 합니다.