@dayflow/sync-core
@dayflow/sync-core์๋ DayFlow ๋๊ธฐํ ์ฐ๋์์ ์ฌ์ฉํ๋ ์ ๊ณต์ ์ค๋ฆฝ์ ์ธ ์กฐ์ (reconciliation) ํฌํผ๊ฐ ๋ค์ด ์์ต๋๋ค. ๋๋ถ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ด ํจํค์ง๋ฅผ ์ง์ ๊ฐ์ ธ์ฌ ํ์๊ฐ ์์ต๋๋ค. ๋จผ์ ์ ๊ณต์ ํจํค์ง๋ฅผ ์ฌ์ฉํ์ธ์:
- CalDAV ์๋ฒ์๋
@dayflow/caldav - Google ์บ๋ฆฐ๋์๋
@dayflow/google-sync - Outlook ์บ๋ฆฐ๋์๋
@dayflow/outlook-sync
์ง์ ์ ๊ณต์๋ฅผ ๋ง๋ค๊ฑฐ๋, ๋ฐฑ์๋ ์์
์ผ๋ก ๋๊ธฐํํ๊ฑฐ๋, ์ ํ ์ ์ฉ ์บ๋ฆฐ๋ยท์ด๋ฒคํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์๊ฒฉ ์ ๊ณต์ ๋ ์ฝ๋์ ๋ง์ถฐ์ผ ํ ๋ @dayflow/sync-core๋ฅผ ์ฌ์ฉํ์ธ์.
์ค์น
๋ด๋น ๋ฒ์
@dayflow/sync-core๋ ์ ๊ณต์์ ์ข
์๋์ง ์๋ diffยท์ ์ฉ ๊ณ์ธต์ ๋ด๋นํฉ๋๋ค:
| ํฌํผ | ์ฌ์ฉํ๋ ์ํฉ |
|---|---|
applyProviderEventsToDayFlow | ์ ๊ณต์ ๋ฐ์ธ๋ฉ์ด ์ด๋ฏธ DayFlow ์ด๋ฒคํธ๋ก ๋งคํํ๊ณ ์ญ์ ๋ ์๊ฒฉ ์ฐธ์กฐ๋ฅผ ํ๋ณดํ ๊ฒฝ์ฐ |
applyRemoteSnapshot | ์ ์ฒด ๋๋ ์ผ๋ถ ์ ๊ณต์ ์ค๋
์์ด ์๊ณ ์ด๋ฅผ CalendarApp์ ์ ์ฉํ๋ ค๋ ๊ฒฝ์ฐ |
reconcileProviderCalendars | ์๊ฒฉ ์บ๋ฆฐ๋๋ฅผ ์์ฒด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ์ฝ๋๋ก ๋๊ธฐํํ๋ ๊ฒฝ์ฐ |
reconcileProviderEvents | ์๊ฒฉ ์ด๋ฒคํธ๋ฅผ ์์ฒด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๋๊ธฐํํ๋ฉด์ ์ ๊ทํ๋ ๋ณ๊ฒฝ ๋ ์ฝ๋๊ฐ ํ์ํ ๊ฒฝ์ฐ |
์ ๊ณต์ ์ด๋ํฐ, ์ธ์ฆ ํ๋ฆ, ํ ํฐ ๊ฐฑ์ , ์๊ฒฉ ์ฆ๋ช ์ ์ฅ, ํ์คํ ๋ฆฌ ์์ํ, ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ , ์ฌ์๋ ํ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง๋ ์ ๊ณตํ์ง ์์ต๋๋ค.
์ ๊ณต์ ์ด๋ฒคํธ๋ฅผ DayFlow์ ์ ์ฉํ๊ธฐ
์ ๊ณต์ ํจํค์ง๋ ์๊ฒฉ ์ด๋ฒคํธ๋ฅผ DayFlow Event ๊ฐ์ฒด๋ก ๋ณํํ ๋ค applyProviderEventsToDayFlow๋ฅผ ํธ์ถํฉ๋๋ค. ๊ธฐ์กด ์ด๋ฒคํธ๋ฅผ ๋จผ์ ์ ๊ณต์ ์๋ณ์๋ก, ๊ทธ๋ค์ DayFlow ID๋ก ๋์กฐํ๊ณ , ๋๊ธฐํ ๋ฃจํ๋ฅผ ๋ง๊ธฐ ์ํด source: 'remote'๋ก ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฉํฉ๋๋ค.
import { applyProviderEventsToDayFlow } from '@dayflow/sync-core';
const delta = applyProviderEventsToDayFlow({
app: calendar.app,
events: mappedRemoteEvents,
deleted: deletedRemoteRefs,
getProviderEventId: event => event.meta?.myProvider?.href,
getDeletedProviderEventId: deleted => deleted.href,
resolveUpdate: (remote, existing) => ({
...remote,
meta: {
...existing.meta,
...remote.meta,
},
}),
});
console.log(delta.added, delta.updated, delta.deleted);CalDAV์ href, Google ์ด๋ฒคํธ id, Graph ์ด๋ฒคํธ id์ฒ๋ผ ์์ ์ ์ธ ์ ๊ณต์ ์๋ณ์๋ฅผ ์ฌ์ฉํ์ธ์. ๊ทธ๋์ผ ๋์ค์ DayFlow id ์ ๋ต์ ๋ฐ๊พธ๋๋ผ๋ ๋ก์ปฌ ์ํ์ ์ด๋ฒคํธ๊ฐ ์ค๋ณต๋์ง ์์ต๋๋ค.
์๊ฒฉ ์ค๋ ์ ์ ์ฉํ๊ธฐ
applyRemoteSnapshot์ DayFlow ์บ๋ฆฐ๋์ ์ด๋ฒคํธ์ ์ค๋
์์ ์ฑ์ ๋ฐ์ํฉ๋๋ค.
import { applyRemoteSnapshot } from '@dayflow/sync-core';
const delta = await applyRemoteSnapshot(
calendar.app,
{
calendars: remoteCalendars,
events: remoteEvents,
},
{
isOwnedCalendar: calendar => calendar.source === 'Custom Provider',
isOwnedEvent: event => event.meta?.provider === 'custom',
resolveConflict: (remote, local) => ({
...remote,
meta: {
...local.meta,
...remote.meta,
},
}),
snapshotMode: 'authoritative',
}
);๊น๋ค๋ก์ด ๋ถ๋ถ์ ์ญ์ ์
๋๋ค. snapshotMode์ ๊ธฐ๋ณธ๊ฐ์ 'partial'์ด๋ฏ๋ก, ์ค๋
์์ ์๋ ๋ก์ปฌ ์์ ๋ ์ฝ๋๋ ๊ทธ๋๋ก ๋ณด์กด๋ฉ๋๋ค. ํ์ ๋ฒ์ ๋๊ธฐํ, ํํฐ๋ง๋ ์กฐํ, ํ์ด์ง ๋จ์ ์ ๊ณต์ ์๋ต์๋ ์ด ๋ฐฉ์์ด ์์ ํฉ๋๋ค:
await applyRemoteSnapshot(calendar.app, partialSnapshot, {
isOwnedCalendar,
isOwnedEvent,
snapshotMode: 'partial',
});snapshotMode: 'authoritative'๋ ์ค๋
์์ด ์ ๊ณต์๊ฐ ์์ ํ ๋ชจ๋ ์บ๋ฆฐ๋์ ์ด๋ฒคํธ๋ฅผ ์จ์ ํ ๋ด๊ณ ์์ ๋๋ง ์ฌ์ฉํ์ธ์. ์ ๊ณต์๋ณ ์ ๋ฆฌ ์ ์ฑ
์ ์ํ ํ์ ์์ค ํ๋๊ทธ deleteMissingCalendars์ deleteMissingEvents๋ ๊ทธ๋๋ก ๋จ์ ์์ต๋๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ์ฝ๋ ์กฐ์ ํ๊ธฐ
์ ํ๋ฆฌ์ผ์ด์
์ด ์ ๊ณต์ ๋ ์ฝ๋๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ ๋ค DayFlow์ ์ ์ฉํ๋ค๋ฉด reconcileProviderCalendars์ reconcileProviderEvents๋ฅผ ์ฌ์ฉํ์ธ์.
import {
reconcileProviderCalendars,
reconcileProviderEvents,
} from '@dayflow/sync-core';
const calendarResult = await reconcileProviderCalendars({
provider: 'custom',
remoteCalendars,
existingCalendars: await db.calendar.findMany({ where: { accountId } }),
getRemoteCalendarId: remote => remote.id,
getRecordId: record => record.id,
getRecordExternalCalendarId: record => record.externalCalendarId,
mapRemoteCalendar: (remote, existing) => ({
id: existing?.id ?? crypto.randomUUID(),
accountId,
provider: 'custom',
externalCalendarId: remote.id,
name: remote.name,
color: remote.color ?? null,
readOnly: remote.readOnly,
syncEnabled: existing?.syncEnabled ?? true,
syncToken: existing?.syncToken ?? null,
syncStatus: 'idle',
}),
save: records => db.calendar.upsertMany(records),
deactivate: records =>
db.calendar.updateMany(
records.map(record => ({ ...record, syncEnabled: false }))
),
});
const eventResult = await reconcileProviderEvents({
provider: 'custom',
calendarId: localCalendar.id,
remoteEvents,
deletedRemoteEvents,
existingRecords: await db.event.findMany({
where: { calendarId: localCalendar.id },
}),
getRemoteEventId: remote => remote.id,
getDeletedRemoteEventId: deleted => deleted.id,
getRecordExternalEventId: record => record.externalEventId,
isRecordDeleted: record => Boolean(record.deletedAt),
mapRemoteEvent: (remote, existing) => ({
id: existing?.id ?? crypto.randomUUID(),
accountId,
calendarId: localCalendar.id,
provider: 'custom',
externalEventId: remote.id,
title: remote.title,
description: remote.description ?? null,
startsAt: remote.startsAt,
endsAt: remote.endsAt,
timezone: remote.timezone ?? null,
allDay: remote.allDay,
location: remote.location ?? null,
rawData: remote,
externalUpdatedAt: remote.updatedAt ?? null,
localUpdatedAt: existing?.localUpdatedAt ?? null,
syncStatus: 'synced',
deletedAt: null,
}),
save: records => db.event.upsertMany(records),
softDelete: record =>
db.event.update(record.id, { deletedAt: new Date().toISOString() }),
});
await db.syncHistory.insertMany([
...calendarResult.changes,
...eventResult.changes,
]);์ด ํฌํผ๋ค์ calendar.created, event.updated, event.deleted ๊ฐ์ ์ ๊ทํ๋ ๋ณ๊ฒฝ ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค. ๊ทธ ๋ณ๊ฒฝ์ ๊ฐ์ฌ ๋ก๊ทธ๋ก ๋จ๊ธธ์ง, ์๋ฆผ์ผ๋ก ๋ณด๋ผ์ง, ๋ถ์์ ์ธ์ง, ์๋ฌด๊ฒ๋ ํ์ง ์์์ง๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ๊ฒฐ์ ํฉ๋๋ค.
์ฐ์ง ์์๋ ๋๋ ๊ฒฝ์ฐ
ํ์ค ์๊ฒฉ ์บ๋ฆฐ๋๋ฅผ ์ฐ๊ฒฐํ๋ ค๋ ๋ชฉ์ ๋ง์ผ๋ก @dayflow/sync-core๋ฅผ ์ถ๊ฐํ์ง ๋ง์ธ์. ์ ๊ณต์ ํจํค์ง๋ค์ด ์ด๋ฏธ ๋ด๋ถ์ ์ผ๋ก ์๋ง์ sync-core ํฌํผ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฉฐ, ์ ๊ณต์๋ณ ๋งคํยท๋ฉํ๋ฐ์ดํฐยท๊ถํยท์๊ฒฉ ๋ฐ์ ๋์ยทDayFlow ์ฐ๊ฒฐ ์ปจํธ๋กค๋ฌ๊น์ง ํจ๊ป ์ ๊ณตํฉ๋๋ค.
๋ค์ ๊ฒฝ๊ณ๋ฅผ ๋์ ๋๋ง ์ง์ ์ฌ์ฉํ์ธ์:
| ๊ฒฝ๊ณ | sync-core๊ฐ ๋์์ด ๋๋ ์ด์ |
|---|---|
| ์ง์ ๋ง๋๋ ์ ๊ณต์ ํจํค์ง | ๊ณต์ ์ ๊ณต์์ ๋์ผํ DayFlow ์ ์ฉ ๋์์ ๊ณต์ ํ ์ ์์ต๋๋ค. |
| ๋ฐฑ์๋ ์ฃผ๋ ๋๊ธฐํ | DayFlow๋ฅผ ๋ ๋๋งํ๊ธฐ ์ ์ ์ ๊ณต์ ๋ ์ฝ๋๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐ์ ํ ์ ์์ต๋๋ค. |
| ๊ฐ์ฌยทํ์คํ ๋ฆฌ ํ์ดํ๋ผ์ธ | ํ์คํ ๋ฆฌ๋ฅผ ์ ๊ณต์ ์ด๋ํฐ์ ๋ฌถ์ง ์๊ณ ์ ๊ทํ๋ SyncChange ๊ฐ์ฒด๋ฅผ ์๋นํ ์ ์์ต๋๋ค. |
| id ์ ๋ต ๋ง์ด๊ทธ๋ ์ด์ | DayFlow ๋ก์ปฌ id๋ฅผ ๋ฐ๊พธ๋ฉด์๋ ์์ ์ ์ธ ์ ๊ณต์ ์๋ณ์๋ก ๋์กฐํ ์ ์์ต๋๋ค. |