탭이 안 되는 링크 — 텔레그램에서 옵시디언 노트 열기
셀프호스팅 구축기 15편. 구축기를 마쳤어도 막상 쓰다 보면 후속 문제가 나온다 — 13편에서 동기화한 노트, 그 링크가 폰에서 탭이 안 됐다.
요약
- 옵시디언 노트는
obsidian://open?vault=...&file=...형태의 고유 링크를 가진다 - 이 링크를 텔레그램으로 보내면 파란 링크가 아니라 회색 죽은 글씨 — 탭이 안 된다
- 원인: 채팅앱은
http/https만 자동으로 링크화한다.obsidian://같은 커스텀 스킴은 무시 - 해결: https 주소 한 단을 끼운다 — https로 받아서 브라우저가
obsidian://로 튕기는 “셔틀” 페이지 - 그 셔틀을 집 서버 대신 Cloudflare Worker(서버리스)에 올렸다 — 집 컴퓨터가 꺼져도 동작, 비용 0원
1. 증상 — 회색으로 죽은 링크
옵시디언은 노트마다 고유 링크를 준다. 노트를 우클릭해 “obsidian URL 복사”를 누르면 obsidian://open?vault=내볼트&file=폴더/노트 같은 주소가 잡힌다. 이걸 텔레그램으로 보내면 파란 글씨가 될 줄 알았는데, 회색 평문으로 떴다. 눌러도 아무 일도 안 일어난다. 링크가 아니라 그냥 글자였던 것이다.
2. 원인 — 채팅앱은 http/https만 링크로 만든다
채팅앱은 메시지 글자를 훑어 URL을 찾으면 자동으로 탭 가능한 링크로 바꾼다. 단 이 자동 변환은 http://·https://(그리고 텔레그램 자체의 tg://)에만 적용된다. obsidian://·notion://·slack:// 같은 앱 전용 커스텀 스킴은 변환 대상이 아니라 평문으로 남는다. 텔레그램 iOS 저장소에도 “커스텀 URI 스킴 링크는 클릭이 안 된다”는 같은 이슈가 올라와 있다.
버튼이나 마크다운 링크로 우회하면 되지 않냐 — 그것도 안 된다. 봇이 메시지에 붙이는 인라인 버튼·링크 역시 http/https/tg만 허용한다. 커스텀 스킴은 거부된다.
3. 정석 패턴 — https “셔틀” 한 단 끼우기
커스텀 스킴이 직접 못 가니, 중간에 https를 한 번 거치게 한다.
- 텔레그램에는 https 링크를 보낸다 → 파란 링크가 된다
- 탭하면 브라우저가 그 https 페이지를 연다
- 그 페이지의 자바스크립트가
obsidian://로 튕긴다 → 옵시디언이 열린다
이 패턴은 새로운 게 아니다. obsid.net이라는 공개 무료 서비스가 정확히 이 일만 한다 — vault와 file을 받아 브라우저에서 obsidian://로 넘겨주는 정적 페이지다. 그대로 써도 된다.
4. 그런데 왜 직접 만들었나
obsid.net이 있는데 굳이 직접 만든 이유는 이 시리즈의 일관된 이유와 같다.
- 제3자를 안 거친다 — 내 볼트 이름과 노트 경로가 남의 서버(로깅을 안 한다 해도)를 지나가지 않는다
- 내 도메인 —
o.내도메인한 줄, 링크가 내 것 - 내가 페이지를 통제 — 자동 이동이 막힐 때 누를 버튼, 어떤 노트인지 보여주는 파일명을 직접 넣을 수 있다 (7절 함정 때문에 중요하다)
처음엔 이미 띄워 둔 다른 앱의 정적 폴더에 페이지를 하나 얹었다. 작동은 했지만, 알림용 리다이렉트를 상관없는 앱 안에 끼워 넣은 꼴이라 경계가 더러워졌다. 리다이렉트는 어느 앱에도 속하지 않는 범용 유틸이다. 그래서 떼어내 독립시켰다.
5. 어디에 둘까 — 집 서버 vs 엣지
독립시킨다면 둘 중 하나다.
| 집 서버에 작은 웹서버 | Cloudflare Worker(엣지) | |
|---|---|---|
| 동작 조건 | 집 컴퓨터가 켜져 있어야 | 집 컴퓨터 꺼져도 동작 |
| 인증서 | 직접 발급·갱신 | 자동 |
| 비용 | 전기 | 무료(하루 10만 요청) |
| 종속 | 없음 | Cloudflare |
리다이렉트는 1초짜리 정적 응답이라 집 서버를 굳이 깨워 둘 이유가 없다. Cloudflare Worker를 골랐다 — 코드 한 조각을 클라우드 엣지에 올리면 서버를 직접 굴리지 않고도 응답한다(이걸 서버리스라 한다). 6편에서 산 도메인이 이미 Cloudflare에 있으니 서브도메인 하나 붙이면 끝이다.
6. 작동 — https가 받아서 obsidian://로 튕긴다
Worker가 하는 일은 짧다. ?f=노트경로를 받아, 그걸 obsidian:// 링크로 바꾼 HTML 한 장을 돌려준다.
export default {
async fetch(request) {
const url = new URL(request.url);
const file = url.searchParams.get('f') || '';
const vault = url.searchParams.get('v') || 'MyVault';
const uri = 'obsidian://open?vault=' +
encodeURIComponent(vault) + '&file=' + encodeURIComponent(file);
const html = '<!doctype html><meta charset=utf-8>'
+ '<p>' + file + '</p>'
+ '<a href="' + uri + '">노트 열기</a>'
+ '<script>setTimeout(function(){location.href=' + JSON.stringify(uri) + '},350)</' + 'script>';
return new Response(html, {
headers: { 'content-type': 'text/html; charset=utf-8' }
});
}
}
텔레그램에는 https://o.내도메인/?f=노트경로만 보낸다. 이건 https라 파란 링크가 되고, 탭하면 위 페이지가 떠서 옵시디언으로 넘긴다.
7. 함정 셋
① 자동 이동이 막힌다. location.href = obsidian://...로 자동 전환을 걸어도, 모바일 브라우저(특히 안드로이드 크롬·채팅앱 내장 브라우저)는 사용자가 직접 누르지 않은 커스텀 스킴 이동을 보안상 막는 경우가 있다(“Navigation is blocked”). 그래서 자동 이동에만 기대면 안 되고, 사용자가 탭할 “노트 열기” 버튼을 반드시 같이 둔다. 자동 전용 페이지가 가끔 안 먹는 이유가 이거다.
② 볼트 이름이 기기마다 다르다. obsidian://open?vault=X의 X는 그 기기에 등록된 볼트 표시 이름과 정확히 같아야 한다. PC에선 열리는데 폰에선 “노트 없음”이 뜨면 십중팔구 볼트 이름이 다른 것이다. 옵시디언 포럼에도 “안드로이드에서 앱은 열리는데 노트는 안 열린다”는 같은 사례가 있다. 링크에 &v=폰볼트이름을 맞춰 주면 된다.
③ 동기화가 먼저다. 링크는 노트를 여는 거지 만들어 주는 게 아니다. 그 노트가 폰에 동기화돼 있어야(13편 LiveSync) 열린다.
8. 비용
- Cloudflare Worker: 0원 (무료 플랜 하루 10만 요청)
- 도메인: 이미 보유(6편), 서브도메인 추가비 없음
- 집 서버 부담: 없음(엣지가 응답)
한 줄 정리
채팅앱은 http/https만 링크로 만든다. obsidian:// 같은 앱 링크는 https 셔틀 한 단을 끼워야 탭이 된다. 그 셔틀을 Cloudflare Worker에 올리면 집 서버와 무관하게 공짜로 돈다. 단 자동 이동은 막힐 수 있으니 버튼을 같이 두고, 볼트 이름을 기기에 맞춰라.
답글 남기기