동작 원리¶
이 문서는 판정 기준과 쓰기 순서를 설명합니다. 명령 사용법은 명령에 있습니다.
변경 판정¶
블록을 같은 파이프라인으로 정규화한 내용으로 비교합니다. 해시는 색인일 뿐, 판정 근거가 아닙니다.
flowchart TB
M[마크다운] --> IR1[NML IR]
N[노션 블록] --> IR2[NML IR]
IR1 --> C[정규 내용]
IR2 --> C
C --> E{같은가}
E -->|같음| Z[변화 없음]
E -->|다름| A[정렬·판정]
- 정규화: 공백을 합치고,
그림:같은 자리표시자 접두를 떼고, 같은 속성의 인라인 조각을 병합합니다. - 정규 내용: 글자뿐 아니라 굵게·기울임·링크·수식·체크 여부·표 칸·그림 캡션까지 비교합니다.
- 정렬: 정확히 같은 내용 → 블록 id → 유사도 순으로 짝을 찾습니다.
- 판정: 같음, 수정, 표현 차이, 종류 변경, 넘기기로 나눕니다.
표현 차이는 «종류만 다르고 내용이 같은» 경우입니다. 예를 들어 로컬에는 그림: … 문단이 있고 노션에는 같은 캡션의 그림 블록이 있는 자리입니다. 쓰지 않고 넘깁니다.
세 가지 상태¶
| 상태 | 조건 | 처리 |
|---|---|---|
local_ahead |
로컬만 바뀜, 또는 마지막 편집자가 우리 통합 | 반영 가능 |
remote_ahead |
사람이 마지막에 고침 | 막음 · pull/keep |
conflict |
양쪽이 같은 블록을 고침 | 막음 · pull/keep |
base(마지막 동기화 기록)가 있으면 세 값을 비교합니다: base, 로컬, 원격. 없으면 노션이 주는 last_edited_by로 판정합니다. last_edited_by는 마지막 편집자 한 명만 남고 분 단위로 반올림되므로 보조 신호입니다.
쓰기 순서¶
sequenceDiagram
participant U as nl push --apply
participant L as 잠금
participant S as 스냅숏
participant N as 노션 API
U->>L: records/.lock 획득
U->>S: 페이지 전체 저장
U->>N: 블록별 PATCH / children / DELETE
U->>N: 재조회
U->>U: 검증·저널 마감·base 갱신
| 단계 | 실패 시 |
|---|---|
| 잠금 | 다른 실행이 쓰는 중이면 즉시 종료 |
| 스냅숏 | 저장 실패하면 쓰지 않음 |
| 블록 적용 | 댓글 블록은 건너뛰고 목록에 남김 |
| 재조회 | 계획과 다르면 저널에 verify_failed 기록, 스냅숏 경로 출력 |
안전 게이트¶
다음 중 하나라도 있으면 push는 아무것도 쓰지 않고 종료 코드 3을 냅니다.
| 게이트 | 이유 |
|---|---|
| 블록 종류 불일치·종류 변경 | 자동 변환은 블록을 지우고 다시 만들어야 함 |
| 표 열 수 변경 | 표 재생성 필요, 댓글 앵커 위험 |
| 소스 타입 오류 | 깨진 소스를 반영하면 노션도 깨짐 |
| 로컬 재정렬 | 노션 API에 블록 이동이 없음 |
댓글이 달린 블록과 표 행은 별도로 건너뜁니다. 확인에 실패하면 «댓글 있음»으로 간주합니다.
저장 위치¶
store/
notes/… NML 원본
records/
snapshots/ 쓰기 전 페이지 사본
diffs/ 비교 결과
stage/ 반영할 변경 선택
base/ 마지막 동기화 상태(선택)
apply-journal/ 쓰기 저널(planned→committed)
노션 API 한도¶
| 항목 | 값 |
|---|---|
| 블록 생성 | 요청당 100개, 중첩 2단계 |
| 블록 수정·삭제 | 1개씩 |
| 페이로드 | 1,000 블록 요소, 500 KB |
| 속도 | 분당 180회(일반), 600회(Business/Enterprise) |
429 응답은 Retry-After만큼 기다린 뒤 재시도합니다.
알려진 한계¶
- 같은 글자 블록이 여러 개면 순서로만 짝을 정합니다. 고유 앵커 정렬은 미구현입니다.
- 블록 이동은 감지만 하고 실행하지 않습니다.
- 그림 업로드가 없어 그림 블록은 «표현 차이»로만 처리됩니다.
- 댓글 API는 해석된 스레드를 주지 않으므로 보호 확인에 사각지대가 있습니다.