import React, { useEffect, useRef } from 'react'; export type SyncHelpAnchor = 'main-conflict' | 'auto' | 'silent' | 'setup'; interface Props { onClose: () => void; initialAnchor?: SyncHelpAnchor; } const overlayStyle: React.CSSProperties = { position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh', background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 110 }; const modalStyle: React.CSSProperties = { background: '#fff', borderRadius: 8, padding: 20, width: 640, maxHeight: '80vh', overflow: 'auto', boxShadow: '0 4px 16px rgba(0,0,0,0.2)' }; const sectionStyle: React.CSSProperties = { marginTop: 18, paddingTop: 12, borderTop: '1px solid #eee' }; const h4Style: React.CSSProperties = { fontSize: 14, margin: '0 0 8px 0' }; const pStyle: React.CSSProperties = { fontSize: 12, color: '#444', lineHeight: 1.6, margin: '4px 0' }; const liStyle: React.CSSProperties = { fontSize: 12, color: '#444', lineHeight: 1.6, marginBottom: 4 }; const codeStyle: React.CSSProperties = { background: '#f4f4f4', padding: '1px 4px', borderRadius: 3, fontSize: 11 }; export function SyncHelpModal({ onClose, initialAnchor }: Props): React.ReactElement { const bodyRef = useRef(null); useEffect(() => { if (!initialAnchor) return; const el = bodyRef.current?.querySelector(`#${initialAnchor}`); if (el) el.scrollIntoView({ behavior: 'auto', block: 'start' }); }, [initialAnchor]); // Escape key 로 닫기. useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === 'Escape') onClose(); } document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [onClose]); return (
e.stopPropagation()}>

동기화 도움말

1. 충돌 해결 (메인 시나리오)

같은 노트를 두 기기에서 동시에 수정하면 충돌이 발생한다. "충돌 해결…" 버튼이 활성화되면 ConflictModal 이 열려 path 별 결정 (내 것 사용 / 원격 사용) 을 받는다.

편집/편집 — 가장 흔한 경우

  • 두 기기에서 같은 노트 본문 수정 → 양 텍스트가 ConflictModal 에 좌우로 나란히 표시
  • 결정 트리: 어느 쪽 변경이 더 새롭고 완전한지 비교 → 더 나은 쪽 선택
  • 둘 다 보존하려면? 현재 'both' 미지원 — 한쪽 선택 후 사후 수동 병합 (다른 쪽 텍스트 메모 → 모달 닫고 노트 편집)

삭제/편집

  • 한쪽에서 trash 처리, 다른 쪽에서 같은 노트 본문 수정
  • "삭제가 의도였다" → 원격 사용 (trash 측 적용)
  • "수정이 더 중요" → 내 것 사용 (편집 측 적용 = trash 취소)

AI 결과 충돌

  • 양 기기에서 AI 자동 처리 결과 (태그 / 주제 / 요약) 가 다름
  • 대부분 어느 쪽이든 무관 → 한쪽 선택 후 AI 재실행 권장 (가장 최신 모델 결과로 통일)

2. 자동 처리 (내가 안 해도 되는 일)

  • fetch + rebase: sync 시작 시 원격 변경을 가져와 내 변경 위에 다시 쌓음 (linear history). conflict 없으면 자동 진행
  • 첫 sync 순서: 빈 원격에는 어느 기기든 먼저 push 가능. 두 번째 기기는 fetch 후 자동 rebase
  • push 거부 (non-fast-forward): 다른 기기가 먼저 push 했어도 자동 fetch + rebase + 재시도. 사용자 개입은 rebase conflict 발생 시에만
  • 자동 sync 주기: 기본 30분 (설정에서 변경). 앱 종료 시 자동 1회 추가

3. 조용히 잘못될 수 있는 케이스 (silent risk)

  • 시계 어긋남 (NTP): 양 기기 시계가 다르면 timestamp 기반 merge 가 잘못된 결과를 낼 수 있음. macOS / Windows 모두 기본 NTP 동기화 켜져 있음 — 수동으로 끄지 말 것
  • 두 기기 동시 수정 회피: 같은 노트를 동시에 수정하면 conflict 가 더 자주 발생. 한 기기에서 작업 중이면 다른 기기에서 같은 노트 수정 자제
  • 자동 sync 실패 silent: 주기적 sync 실패 시 토스트 안 뜸. 마지막 sync 시각 / 결과는 설정 페이지에서 확인 — 주 1회 점검 권장

4. Setup / 인증 (troubleshoot)

URL 형식 (둘 중 하나)

  • SSH: git@host:user/repo.git
  • HTTPS: https://host/user/repo.git
  • 잘못된 형식: git@https://... 같은 혼합 형식 ✗

인증

  • SSH: 기기에 SSH key 등록 + 원격에 public key 추가
  • HTTPS: OS credential helper (Windows Credential Manager / macOS Keychain) 가 첫 push 시 token 입력받아 저장. 매 push 마다 재입력 X

"연결 테스트" 실패 시

  • 네트워크: 원격 host 에 브라우저로 접속해 응답 확인
  • 인증: 위 인증 절차 점검
  • URL: 형식 (SSH/HTTPS) + 오타 점검

재설정

  • URL 변경 시 설정 → 동기화 저장소에서 새 URL 입력 → 저장. 내부적으로 git remote set-url origin 자동 처리
); }