아키텍처#

소스 구조#

플러그인의 모든 로직은 src/ 아래에 있습니다:

파일

역할

main.ts

플러그인 진입점: 설정을 불러오고 설정 탭, 코드 블록 프로세서, 붙여넣기 핸들러, 첨부 파일 업로드 핸들러, 링크 트래커를 연결합니다. Convert selected Nextcloud internal link to a file block 명령도 추가합니다.

types.ts

공유 타입: NextcloudProfile, HarangNextcloudSettings, NextcloudFileMeta.

settings.ts

설정 탭: 프로필 목록, “새 프로필 추가” 폼, 그리고 신규/재연결 프로필 모두에 대한 Login Flow v2 핸드셰이크(시작/폴링/취소).

i18n/

공식 getLanguage() API로 현재 Obsidian UI 언어를 확인하고 en.ts/ko.ts``에서 일치하는 문자열을 반환합니다(없으면 영어로 대체). ``t(key, params)``는 HTTP 상태나 파일명 같은 값을 포함하는 메시지에 대해 ``{placeholder} 치환을 수행합니다.

nextcloud/loginFlow.ts

Nextcloud의 Login Flow v2를 구현합니다: initiateLoginFlow가 핸드셰이크를 시작해 브라우저 로그인 URL을 반환하고, pollLoginFlow는 사용자가 인증을 마칠 때까지(또는 약 20분 토큰이 만료되거나 사용자가 취소할 때까지) 폴링하며 결과 서버 URL, 로그인명, 앱 비밀번호를 반환합니다.

nextcloud/link.ts

parseInternalLink은 Nextcloud 내부 링크 URL(index.php 포함 여부와 무관하게 .../f/<fileid> 형태)을 인식해 기본 URL과 파일 id를 추출합니다. findProfileForLink는 파싱된 링크를 설정된 프로필과 매칭합니다(정확한 서버 URL 일치, 실패 시 호스트만 비교). resolveUploadTargetnc-folder 프런트매터 값(프로필이름/경로, 또는 프로필이 정확히 하나만 등록된 경우 경로만)을 프로필과 대상 폴더로 파싱합니다.

nextcloud/client.ts

플러그인이 필요로 하는 Nextcloud API 부분에 대한 WebDAV 클라이언트로, 모두 Obsidian의 requestUrl을 통해 동작합니다: fetchFileMeta``(내부 링크에는 경로가 아니라 숫자 파일 id만 담겨 있으므로 ``oc:fileid로 WebDAV SEARCH), fetchFileMetaByPath``(``PROPFIND), ensureFolder``(``MKCOL, 멱등적이며 없는 상위 폴더도 생성), findAvailableUploadName``(업로드 이름 충돌 확인), ``uploadFile``(``PUT), deleteFile``(``DELETE — Nextcloud는 이를 완전 삭제가 아니라 “휴지통으로 이동”으로 처리합니다).

block-view.ts

nextcloud-file 코드 블록 프로세서를 등록합니다. 블록의 링크를 파싱하고, 프로필을 매칭하고, 파일 메타데이터를 가져와(60초 동안 캐시) 사용법에서 설명한 정보 카드를 렌더링합니다 — 실패 시에는 재시도 버튼이 있는 오류 카드를 표시합니다.

paste-handler.ts

에디터의 붙여넣기 이벤트를 감지합니다. 붙여넣은 일반 텍스트가 인식 가능한 Nextcloud 내부 링크이면, 원본 URL 대신 nextcloud-file 코드 블록으로 바꿉니다.

attachment-upload.ts

같은 붙여넣기 이벤트를 붙여넣은 파일에 대해서도 감지합니다. 이미지는 건너뛰어(Obsidian의 일반 이미지 붙여넣기 동작을 그대로 유지) 활성 노트에 nc-folder 프런트매터 속성이 있어야 하며, 이미지가 아닌 각 파일을 해당 폴더에 업로드하고 업로드된 파일마다 nextcloud-file 블록을 삽입합니다.

link-tracker.ts

열려 있는 각 마크다운 노트의 nextcloud-file 블록 집합을 기준선(baseline)으로 추적합니다. 노트가 편집되거나 삭제되어 이전에 있던 블록이 사라지면, (confirm-delete-modal.ts를 통해) 해당 Nextcloud 파일도 휴지통으로 옮길지 물어봅니다.

confirm-delete-modal.ts

링크 트래커가 표시하는 확인 모달입니다: Nextcloud에 유지 또는 휴지통으로 이동. 명시적인 선택 없이 닫으면(Esc / 바깥 클릭) 파괴적이지 않은 “유지” 옵션이 기본으로 적용됩니다.

util.ts

errorMessage: 캐치된 값(Error이든 아니든)을 표시용 문자열로 정규화합니다.

데이터 흐름#

settings.ts               -->  NextcloudProfile[] (server URL, login name, app password)
     |                         via nextcloud/loginFlow.ts (Login Flow v2)
     v
nextcloud/link.ts          -->  parses a pasted URL / nc-folder value
     |
     v
nextcloud/client.ts          -->  SEARCH/PROPFIND/MKCOL/PUT/DELETE over requestUrl
     |
     +--> paste-handler.ts        -->  URL paste  --> ```nextcloud-file``` block
     |
     +--> attachment-upload.ts    -->  file paste  --> upload, then ```nextcloud-file``` block
     |
     +--> block-view.ts           -->  renders the block: icon, name, path, size, date, Open-in-browser
     |
     +--> link-tracker.ts         -->  block removed from a note --> confirm-delete-modal.ts
                                       --> optional deleteFile (moves to Nextcloud trash)

참조 문법#

확정된 참조는 펜스 코드 블록으로 저장됩니다:

```nextcloud-file
https://cloud.example.com/f/12345
```

블록의 내용은 수정되지 않은 원본 Nextcloud 내부 링크입니다 — 별도로 동기화해야 할 ID 체계가 없습니다. 블록을 다시 확정하는 데는 findProfileForLink(링크의 호스트를 프로필의 서버 URL과 매칭)와 링크에 이미 담겨 있는 파일 id만 있으면 되므로, 서버에서 파일 이름이 바뀌거나 이동해도 블록은 계속 올바르게 렌더링되며, 프로필이 삭제됐다가 같은 서버 URL로 다시 추가되어도 계속 동작합니다.

경로가 아니라 숫자 Nextcloud 파일 id만 저장됩니다 — 그래서 메타데이터 조회에 경로 기반의 단순한 PROPFIND 대신 WebDAV SEARCH``(``fetchFileMeta)를 사용합니다: 플러그인은 “이 경로에 뭐가 있는지”가 아니라 “이 id를 가진 파일이 무엇인지”를 서버에 물어야 하는데, 경로는 서버가 응답하기 전까지는 알 수 없기 때문입니다.

날짜 필드: “생성일” vs. “수정일”#

렌더링된 카드는 날짜를 생성일 또는 수정일로 표시합니다. fetchFileMeta/fetchFileMetaByPath는 WebDAV의 creationdate 속성을 우선하지만, 일부 Nextcloud/스토리지 백엔드는 이 값을 비워두는 대신 (예: 유닉스 에폭 같은) 자리표시자 값을 반환하기도 합니다. Nextcloud가 존재하기 훨씬 전인 2000년 이전(포함)의 creationdate는 “실제로는 알 수 없음”으로 간주되어, 카드는 대신 getlastmodified수정일로 표시합니다.

런타임 의존성 없음#

Obsidian 자체가 제공하는 것(obsidian 패키지) 외에 플러그인에는 런타임 의존성이 없습니다 — WebDAV 클라이언트, 로그인 흐름, 내부 링크 파싱 모두 npm에서 가져오지 않고 직접 작성되어, 번들을 작게 유지하고 서드파티 HTTP 라이브러리 자체의 취약점에 노출되는 것을 피합니다.