직접 다운로드 링크를 꺼내는 유저스크립트.user.js 9,671줄 · 정식 v1.1.3 / 카나리아 v1.1.3.1-Preview · TrendShift 오늘 Daily #17 · repo id 13630)
LinkSwift는 "클라우드 저장소 웹페이지에 다운로드 버튼을 몰래 붙여 주는 브라우저 부가 스크립트"다. 중국의 대표 클라우드(网盘, "왕판")들은 웹에서 파일을 받으려 하면 느린 전용 클라이언트를 설치하라고 강요하거나 속도를 조인다. LinkSwift를 깔아 두면, 네트디스크 페이지에 "下载助手(다운로드 도우미)" 버튼이 생기고, 파일을 고른 뒤 그 버튼을 누르면 브라우저가 대신 파일의 실제 다운로드 URL을 알아내 준다.
이 실제 URL(직링크)을 손에 쥐면, 그걸 내가 원하는 다운로더에 붙여 넣어 받을 수 있다 — 예컨대 IDM으로 다중 연결 다운로드를 하거나, 리눅스 서버에서 aria2c 명령으로 받거나, NAS의 Motrix로 원격 다운로드를 걸거나. LinkSwift는 그 URL을 5가지 형식(API 링크 / aria2 명령 / cURL 명령 / BitComet 푸시 / 브라우저 내 증강 다운로드)으로 예쁘게 포장해 준다.
.user.js 파일을 얹는다. LinkSwift 전체가 바로 이 .user.js 파일 하나다 — 서버도, 앱 설치도 없이 "내 브라우저 안에서만" 도는 프로그램이다.filemetas?dlink=1, 아리 get_download_url). LinkSwift는 당신의 로그인 세션으로 그 공식 API를 호출해 dlink를 받아 온다 — 없던 문을 뚫는 게 아니라, 이미 열려 있는 API 문을 대신 눌러 주는 것.네트디스크 웹사이트는 "파일을 받으려면 우리 전용 앱을 깔라"고 안내한다. 하지만 그 사이트의 서버에는 웹페이지 스스로가 쓰는 '파일 주소 조회 API'가 이미 열려 있다(로그인한 본인만 접근 가능).
LinkSwift는 그 API를 당신의 로그인 상태로 대신 호출해, 돌아온 실제 다운로드 주소를 화면에 꺼내 준다. 자물쇠를 부수는 해킹이 아니라, "뒷문이 여기 있고 당신은 이미 열쇠(로그인 쿠키/토큰)를 갖고 있어요"라고 알려주는 안내판에 가깝다. 그래서 README도 "우리는 속도 제한을 뚫지 않는다, 공식 공개 API만 쓴다"고 못 박는다(§2 참고).
LinkSwift는 갑자기 튀어나온 신생 프로젝트가 아니라, 중국에서 어마어마한 사용자층을 가진 유틸리티 계보 위에 있다. 원조 【网盘直链下载助手】(油小猴/syhyz1990)는 오래된 인기 스크립트지만 활성화 "暗号(암호)"를 위챗으로 받아야 하고 광고가 붙는 제약이 있었다. LinkSwift는 이걸 포크해 그 제약을 걷어낸 판이라, "원조보다 낫다"는 입소문으로 트렌딩에 올랐다. (TrendShift의 굵은 숫자는 별 수가 아니라 사회적 언급 모멘텀이다.) 인기 이유를 네 갈래로 본다.
LinkSwift의 슬로건이 곧 차별점이다. 오픈소스(开源)라 코드가 전부 공개돼 있고, 원조가 요구하던 활성화 암호·유료 요소·광고를 제거(去广)했다. 원조는 무료 사용을 위해 위챗 스캔으로 "暗号"를 받아 버튼을 "점등"시켜야 했는데, LinkSwift는 그 관문 없이 바로 쓴다. "그냥 깔면 되고, 코드도 볼 수 있다"는 투명함이 가장 큰 매력.
바이두 하나만 지원하는 도구가 많은데, LinkSwift는 百度·阿里·中国移动·天翼·迅雷·光鸭·夸克·UC·123云盘 아홉 곳을 한 스크립트로 커버한다. 중국 사용자는 파일마다 다른 네트디스크를 쓰기 때문에, "하나로 다 되는" 통합이 실질적 편의를 준다. 각 사이트의 페이지 구조·API가 자주 바뀌는데도 업데이트 로그를 보면 거의 매달 적응(适配) 패치가 돈다.
꺼낸 직링크를 IDM·NDM·AB Download Manager(API 링크), aria2/XDown/Motrix(Aria·RPC), cURL 터미널, BitComet(BC)에 각각 맞는 형식으로 만들어 준다. 게다가 다운로더를 아예 못 까는 환경(회사·학교 PC 등)을 위해, 브라우저 안에서 도는 멀티스레드 "증강 다운로드(Beta)"까지 넣었다 — 이게 기술적으로 가장 흥미로운 부분이다(§4-4).
원조 대비 SweetAlert2 팝업 + Font Awesome 아이콘으로 UI를 다시 그렸고, 다크 모드, 네트디스크 페이지 자체를 깔끔하게 정리하는 "绿化/美化(녹화·미화)", 다운로드 속도·남은 시간 표시, 링크 캐시 등을 붙였다. "기능은 원조를 넘고, 보기도 예쁘다"는 완성도가 재방문을 부른다.
README는 반복해서 강조한다 — 이 프로젝트가 얻는 링크는 전부 각 네트디스크가 공개한 공식 API로 취득하며, 속도 제한 메커니즘의 "크랙(파해)"은 약속·구현하지 않는다. 빠른 속도가 나오더라도 그건 서버 정책·본인 네트워크 환경 때문이지, 이 도구가 제한을 뚫어서가 아니라고 명시한다.
즉 "가속·비속돌파" 같은 제3자의 과장 홍보는 프로젝트 입장이 아니라는 것. 다만 실제 사용은 각 네트디스크의 이용약관(ToS)과 회색지대에 걸릴 수 있으니, 학습용으로 구조를 이해하되 사용은 각자 책임이다. 라이선스는 AGPL-3.0이라 파생·재배포 시 소스 공개 의무가 따른다.
| 도구 | 성격 · LinkSwift와의 차이 |
|---|---|
| 网盘直链下载助手 (원조 · 油小猴) | LinkSwift의 모태. 성숙하고 안정적이지만 활성화 暗号·광고·부분 유료 요소가 있고 클로즈드. LinkSwift는 이를 포크해 오픈소스·무료·거광으로 재배포. |
| 各종 "속도 해제" 클라이언트 (PanDownload류) | 별도 EXE 앱으로 프로토콜을 파고들어 실제로 제한을 우회하려는 부류 — 법적 리스크가 크고 자주 폐쇄된다. LinkSwift는 앱이 아니라 브라우저 스크립트이며 공식 API만 호출한다는 점에서 결이 다르다. |
| 공식 클라이언트 | 각 네트디스크 전용 앱. 안전하지만 설치 강제·광고·속도 정책이 불편. LinkSwift는 "웹에서 링크만 꺼내 내 다운로더로"라는 대안 워크플로. |
LinkSwift는 백엔드가 없다. 서버·데이터베이스·빌드 파이프라인이 전부 없고, 모든 로직이 사용자의 브라우저 안 스크립트 매니저 샌드박스에서 돈다. 스택을 세 계층으로 나눠 본다.
LinkSwift는 브라우저가 아니라 스크립트 매니저 확장 위에서 실행된다. 이 확장이 제공하는 특수 함수들(GM_*)이 일반 웹페이지 JS로는 불가능한 일을 가능케 한다.
| 항목 | 내용 |
|---|---|
| 스크립트 매니저 | Tampermonkey / Violentmonkey / ScriptCat. 헤더의 @compatible에 Chrome·Edge·Firefox·Safari·Opera 명시. Chrome ≥76 / Edge ≥88, Windows·Mac·Linux·Android 지원. |
| 실행 시점 | @run-at document-start + @early-start — 페이지 스크립트보다 먼저 끼어들어 네트워크 요청을 가로채거나 UI를 일찍 심는다. |
| 핵심 GM 권한 | GM_xmlhttpRequest(교차 출처 요청 — 이게 전부의 심장), GM_setValue/getValue/deleteValue(설정 저장), GM_setClipboard(명령 복사), GM_registerMenuCommand(⚙️설정·🍃美化·📃업데이트·🛠️디버그 메뉴), GM_openInTab, GM_getResourceText, unsafeWindow. |
| @match / @connect | 9개 네트디스크의 URL 패턴 수십 개를 @match로 지정해 그 페이지에서만 발동. @connect로 baidu·aliyundrive·139·189·xunlei·quark·uc·123pan 등 허용 도메인 화이트리스트를 선언(교차 출처 보안). |
GM_xmlhttpRequest는 확장 프로그램 권한으로 요청을 대신 보내 이 벽을 넘는다. LinkSwift가 바이두 페이지에 있으면서 아리·夸克의 API까지 부르고, dlink CDN에 Range 요청을 날려 파일 조각을 받는 것 모두 이 함수 덕분이다. @connect에 적힌 도메인으로만 허용된다.빌드 도구가 없으므로, 의존 라이브러리는 헤더의 @require로 CDN에서 직접 로드한다. 딱 3개다.
| jQuery 3.6.0 | DOM 조작·이벤트 바인딩·AJAX의 뼈대. 스크립트 전체가 $(...) 기반. 진입 함수도 linkSwift($)로 jQuery를 주입받는다. |
| SweetAlert2 11.4.8 | 모든 팝업 UI(다운로드 창·설정·美化·업데이트·디버그)의 토대. 다크 테마(@sweetalert2/theme-dark)도 @resource로 함께 로드해 다크 모드 핫스왑 구현. |
| js-md5 0.7.3 | 일부 네트디스크 API 서명(signature)·해시 계산용. 요청 파라미터를 서명해야 하는 곳(예: 특정 토큰/타임스탬프 서명)에 사용. |
config/ 폴더의 JSON들은 원조 油小猴 서버 설정의 "백업본"이다 — 스크립트가 그 서버에 못 붙을 때 대신 읽는 폴백. 네트디스크별로 API 엔드포인트·DOM 선택자·다국어 문구가 들어 있다.
| config.json | 공통·바이두 계열. pcs에 바이두 filemetas?dlink=1·공유 다운로드·OAuth authorize 엔드포인트, 다운로드 모드별(api/aria/rpc/curl/bc) 안내 HTML. |
| ali.json / quark.json / tianyi.json / yidong.json / xunlei.json | 각 네트디스크의 API URL(pcs), 버튼을 꽂을 DOM 선택자(btn·dom), 리스트/그리드 뷰 셀렉터, 다국어 문구. 사이트가 개편되면 이 선택자만 갈아끼우면 되는 설정 주도 설계. |
LinkSwift 본체(.user.js)가 만능 리모컨이라면, config/*.json은 "TV 제조사별 적외선 코드표"다. 바이두 채널에선 바이두 코드(API·버튼 위치)를, 아리 채널에선 아리 코드를 꺼내 쓴다. 새 네트디스크가 UI를 바꿔도 리모컨 회로(코드 로직)는 그대로 두고 코드표(선택자·엔드포인트)만 갱신하면 되니, 9개나 되는 사이트를 한 스크립트로 유지보수할 수 있다.
파일 하나지만 내부는 객체 단위로 잘 나뉜 미니 프레임워크다. info(스크립트 정보) → temp(런타임 상태) → message(다국어) → config(엔드포인트·선택자) → base(거대한 코어 엔진) → 네트디스크별 핸들러($baidu·$aliyun…) → main(초기화·라우팅) 순서로 구성된다. 하나씩 해부한다.
main.init()이 진입점이다. 가장 먼저 현재 페이지 주소(hostname)를 보고 어느 네트디스크인지 판별해, 그에 맞는 핸들러 객체를 temp.main에 꽂는다.
// main.init() 내부 — 호스트명으로 핸들러 라우팅
if (/(pan|yun).baidu.com/.test(location.host)) temp.main = $baidu;
else if (/www.(aliyundrive|alipan).com/.test(host)) temp.main = $aliyun;
else if (/(yun|caiyun).139.com/.test(host)) temp.main = $mcloud; // 中国移动
else if (/cloud.189.cn/.test(host)) temp.main = $tcloud; // 天翼
else if (/pan.xunlei.com/.test(host)) temp.main = $xunlei;
else if (/pan.quark.cn/.test(host)) temp.main = $quark;
else if (/drive.uc.cn/.test(host)) temp.main = $uc;
else if (/123(pan|684|865|952|912)/.test(host)) temp.main = $123pan;
// … 이어서 공통 초기화 …
base.initDefaultConfig(); // 스마트 기본 설정
base.addPanLinkerStyle(); // 美化 CSS 주입
base.addPageListener(); // 버튼 클릭 이벤트 위임 바인딩
temp.main.initPanLinker(); // 이 사이트에 다운로드 버튼 심기
temp.main.greenerPage(); // 광고 제거·페이지 绿化
즉 "어느 집에 왔는지 확인 → 그 집 열쇠(핸들러) 꺼내기 → 공통 인테리어(스타일·이벤트) 깔기 → 그 집 전용 버튼 심기" 순서다. 전체 그림:
base는 사이트에 무관한 공용 도구 상자다. 네트디스크 핸들러들은 자기 API만 알고, 나머지 궂은일(HTTP·다운로드·UI·설정)은 전부 base에 위임한다.
| base의 주요 책무 | 설명 |
|---|---|
| 메뉴 등록 | registerMenuCommand() — 스크립트 매니저 메뉴에 ⚙️설정·🍃美化·📃업데이트·🛠️디버그 4개 입구. |
| HTTP 래퍼 | GM_xmlhttpRequest를 Promise로 감싼 요청 함수 + 스마트 head(): 응답 헤더가 필요할 때 무거운 GET 대신 Range: bytes=0-0로 1바이트만 받아 헤더를 확보(파일 크기·최종 URL·파일명)하는 경량화. |
| 다운로드 엔진 | download()(증강 멀티스레드), blobDownload()(Blob→브라우저 저장), aria2/cURL 명령 생성, RPC/BC 푸시(§4-4·4-5). |
| UI | SweetAlert2 기반 다운로드 창·설정·美化·업데이트·디버그 다이얼로그, 드래그 이동, 툴팁, 프로그레스 바. |
| 설정·유틸 | getValue/setValue(GM 저장), 기본값 병합(mergeDefaults), 타입 판별(isType), 크기·시간 포맷, 버전 비교. |
$baidu·$aliyun 등 9개 핸들러는 각자 "파일 선택 감지 → 자기 API 호출 → dlink 추출"만 담당한다. 예를 들어 바이두는 공식 xpan/multimedia?method=filemetas&dlink=1로 메타데이터와 dlink를, 아리는 get_download_url/get_share_link_download_url로 링크를 받는다.
// 핸들러가 만드는 표준 file 객체 (모든 네트디스크 공통 형태)
const file = {
index: status.item.data("index"), // 몇 번째 파일
link: status.item.data("link"), // ★ 추출한 실제 dlink
name: status.item.data("name"), // 파일명
size: status.item.data("size") || 0 // 바이트
};
// 이 file.link 를 base 의 다운로드/명령 생성기에 넘기면 끝
base.download(file.link, { "User-Agent": ua.downloadLink, "Referer": "" }, {
...file,
onProgress: (d) => updateBar(d.progress, d.speedText, d.etaText)
});
핵심 설계 관찰: 모든 핸들러가 동일한 file 객체 형태로 수렴하기 때문에, 그 뒤 단계(다운로드·명령 생성·UI)는 어느 네트디스크든 완전히 공유된다. "사이트별 차이는 핸들러에 가두고, 그 아래는 통일"이 9개 지원의 비결이다. 참고로 바이두는 보안을 위해 예전 BDUSS 쿠키 방식을 폐기하고 AccessToken(OAuth)로 옮겼다(v1.1.1.6).
이 스크립트에서 가장 공학적으로 재미있는 부분. 외부 다운로더를 못 쓰는 환경을 위해, GM_xmlhttpRequest로 파일을 여러 조각(Range)으로 병렬 요청해 브라우저 안에서 직접 받고, 조각들을 Blob으로 합쳐 저장한다. 단순 병렬이 아니라 네트워크 상태에 맞춰 조각 크기를 실시간으로 조절하는 적응형 알고리즘이 들어 있다.
// 증강 다운로드의 핵심 로직 (요지)
const maxThreads = Math.min(extra.thread || 3, global_maxThreads); // 동시 스레드
let chunk = Math.min(256 * 1024, absoluteMaxChunk); // 시작 조각 = 256KB
// 각 워커: [start, end] 범위를 Range 헤더로 요청
const end = Math.min(start + chunk - 1, size - 1);
base.head(url, { ...headers, Range: `bytes=${start}-${end}` });
// ── 적응형 조각 크기 (AIMD 비슷한 혼잡 제어) ──
if (오류_또는_끊김) chunk = Math.max(minChunk, Math.floor(chunk * 0.5)); // 반토막
else {
nextOptimalChunk = 측정속도 * 목표시간;
nextOptimalChunk = Math.min(nextOptimalChunk, chunk * 2.0); // 급등 방지(최대 2배)
nextOptimalChunk = Math.max(nextOptimalChunk, chunk * 0.33); // 급락 방지(최소 1/3)
chunk = Math.floor(chunk * 0.4 + nextOptimalChunk * 0.6); // 평활화(EMA)
}
Range: bytes=start-end 헤더는 파일의 특정 구간만 달라고 요청하는 표준 기능(이어받기·스트리밍의 토대). LinkSwift는 이걸로 파일을 조각내 여러 스레드가 동시에 받는다. 그리고 조각 크기를 고정하지 않고, 끊기면 절반으로 줄이고(곱셈 감소) 잘 되면 키우되(증가) 급등·급락에 상한을 두는 혼잡 제어를 쓴다 — TCP 혼잡 제어(AIMD)의 축소판이다. 다운로더를 못 까는 환경에서도 나름 빠른 속도를 내려는 장치.버튼 툴팁에 직접 적혀 있다 — 이 기능은 브라우저의 동시 연결 제한에 종속되고 메모리에 통째로 Blob을 쌓으므로, 시스템 환경상 프로그램을 설치할 수 없는 경우가 아니면 권장하지 않는다. 정석은 어디까지나 API 링크를 IDM·aria2 같은 전용 다운로더에 넘기는 것.
추출한 dlink를 사용자의 다운로드 도구별로 변환한다. 명령 문자열 생성은 순수 함수, 원격 푸시는 JSON-RPC 호출이다.
// cURL 명령 (터미널용, 이어받기 -C - 포함)
return `curl -L -C - "${link}" -o "${filename}"${headers ? " "+headers : ""}`;
// aria2c 명령 (XDown·리눅스 셸용)
return `aria2c "${link}" --out "${filename}"${headers ? " "+headers : ""}`;
// aria2 RPC 푸시 (Motrix 등 원격 다운로더로 전송)
{ jsonrpc: "2.0", method: "aria2.addUri", params: [ `token:${token}`, [link], { out: filename, dir } ] }
| 모드 | 대상 · 방식 |
|---|---|
| API 링크 | 클릭 시 직접 다운로드. IDM·NDM·AB Download Manager·브라우저 기본 다운로드가 낚아챔. 여러 파일 링크 일괄 복사 가능. |
| Aria 다운로드 | aria2c "..." --out "..." 명령 복사(클립보드) 또는 aria2 RPC로 푸시(Motrix·AriaNg). 구 RPC 모드가 여기로 통합됨(v1.1.1). |
| cURL 다운로드 | Windows·Linux·macOS 터미널용 curl 명령. -C -로 이어받기 지원. |
| BC 다운로드 | BitComet(比特彗星) RPC로 푸시(v1.1.1 추가). |
| 증강 다운로드(Beta) | §4-4 — 브라우저 내부 멀티스레드 다운로드(설치 불가 환경용). |
네트디스크들은 대부분 SPA(단일 페이지 앱)라, 요소가 비동기로 나타났다 사라진다. LinkSwift는 waitForKeyElements(내부적으로 MutationObserver 기반)로 목표 요소가 DOM에 등장할 때까지 감시하다가 버튼을 심는다. 마운트 지점은 idontknow("LinkSwift")로 만든 커스텀 태그를 <head> 뒤에 붙여, 페이지 재렌더에도 자기 UI가 날아가지 않게 격리한다.
绿化(녹화)·美化(미화)는 config의 선택자로 광고·프로모션 요소를 숨기고, 네트디스크 UI에 테마색을 입히는 기능. 이것도 config JSON의 선택자에 의존하므로, 사이트 개편 때 코드가 아니라 선택자만 고치면 된다.
MutationObserver는 DOM이 바뀔 때마다 콜백을 부르는 브라우저 표준 API. SPA는 사용자가 폴더를 열 때마다 화면을 새로 그리므로, "툴바가 생기면 그때 버튼을 꽂아라"를 이걸로 구현한다. waitForKeyElements는 이 관용구를 "이 선택자에 맞는 요소가 나타나면 이 함수 실행"으로 감싼 유틸(그리스몽키 생태계의 고전). 동적 웹에 무언가를 주입할 때 반드시 만나는 패턴이다.대형 프레임워크와 달리 LinkSwift는 파일 수가 아주 적다. 거의 모든 로직이 단일 스크립트에 있고, 나머지는 설정·문서·부속이다.
| 파일 / 폴더 | 역할 |
|---|---|
| (改)网盘直链下载助手.user.js | 본체. 헤더의 UserScript 메타블록(@match·@grant·@require 등) + IIFE linkSwift($) 본문. 이 안에 §4의 전 구조가 들어 있다. |
| config/*.json | 네트디스크별 API URL·DOM 선택자·다국어 문구. 사이트 개편 시 갱신 포인트(설정 주도 설계의 실체). |
| package.json | 빌드는 없고 ESLint 검사(npm run check → npx eslint . --fix)만. 의존성도 eslint류뿐 — "빌드리스" 성격을 보여줌. |
| README(-ScriptCat).md | 설치 소스(GitHub/OpenUserJS/脚本猫), 법적 고지, 방대한 업데이트 로그(2022~2026, 프로젝트 진화사의 보고). |
| .github/ | 이슈 템플릿(bug/feature)·기여 가이드·CoC. "피드백은 GitHub Issues로만"이라는 유지보수 정책이 여기 반영. |
.user.js URL 하나로 끝난다. 대가: 9천 줄이 한 파일이라 탐색이 어렵고, 모듈 경계가 코드 규약(객체 분리)으로만 유지된다. "빌드리스 단순함 ↔ 유지보수 복잡함"의 교환을 보여주는 실물 사례.LinkSwift는 "네트디스크 다운로드"라는 목적을 넘어, 브라우저 확장·유저스크립트로 남의 웹사이트를 증강하는 기술의 종합 교재다. 기술별로 배울 거리를 정리한다.
UserScript 메타블록(@match·@grant·@require·@connect·@run-at)을 어떻게 설계하는지, GM_xmlhttpRequest로 CORS를 넘고, GM_setValue로 설정을 저장하고, GM_registerMenuCommand로 메뉴를 만드는 법을 통째로 볼 수 있다. Tampermonkey로 무언가를 자동화하려는 사람에게 살아있는 레퍼런스다.
각 네트디스크가 자기 웹페이지에서 쓰는 내부/공식 API를 관찰(네트워크 탭)하고 그 요청을 재현하는 방법론을 배운다. 파라미터 서명(js-md5), 토큰·타임스탬프, Range 헤더 활용까지 — "웹은 결국 HTTP 요청의 집합"이라는 감각을 길러 준다. (단, 실제 적용은 대상 사이트 ToS를 반드시 확인.)
§4-4의 적응형 청킹은 네트워크 프로그래밍의 좋은 축소 실습이다. Range로 병렬 다운로드를 구현하고, "끊기면 줄이고 잘 되면 늘리되 상·하한을 두는" AIMD식 제어와 EMA 평활화를 코드로 익힐 수 있다. TCP 혼잡 제어의 직관을 애플리케이션 레벨에서 체득.
동적으로 바뀌는 남의 SPA에 버튼·패널을 안정적으로 심는 기술(waitForKeyElements/MutationObserver, 격리된 마운트 태그)은 브라우저 확장·자동화·크롤링에 두루 쓰인다. "요소가 언제 나타날지 모르는 페이지"를 다루는 정석을 배운다.
9개 사이트를 한 코드로 지원하는 비결은 사이트별 차이(API·선택자)를 JSON으로 외부화하고 핸들러 인터페이스를 통일한 것이다. "변하는 것(설정)과 안 변하는 것(엔진)을 분리"하는 설계 원칙의 실전 사례 — 멀티 테넌트·멀티 플랫폼 도구를 만들 때 그대로 응용된다.
Tampermonkey를 깔고, 아무 사이트에서 GM_registerMenuCommand("인사", ()=>alert("hi"))와 GM_xmlhttpRequest로 공개 API(예: https://api.github.com) 한 번 부르는 20줄 스크립트를 써 보면, LinkSwift가 쓰는 GM API·CORS 우회·메뉴의 핵심이 손에 잡힌다. 그다음 LinkSwift 본체에서 같은 API가 어떻게 쓰였는지 대조해 보라.
실행 요구가 매우 가볍다. 스크립트 매니저 확장 하나만 있으면 되고, 외부 다운로더(IDM·aria2 등)는 선택이다.
| 항목 | 요구 |
|---|---|
| 브라우저 | Chrome ≥76 · Edge ≥88 · Firefox · Safari · Opera. Windows·macOS·Linux·Android 지원. |
| 스크립트 매니저 | Tampermonkey(篡改猴) / Violentmonkey / ScriptCat(脚本猫) 중 하나. LinkSwift는 이 위에서만 동작. |
| 외부 다운로더(선택) | IDM · NDM · AB Download Manager · aria2/Motrix/AriaNg · XDown · BitComet · cURL. 없으면 증강 다운로드(Beta)로 대체. |
| 네트워크 | @require 라이브러리(jQuery·SweetAlert2·js-md5)를 CDN(unpkg)에서 로드하므로 최초 로드 시 인터넷 필요. |
README가 명시한다 — 이 스크립트는 GitHub·Gitee·脚本猫·OpenUserJS에서만 배포한다. 다른 경로에서 받은 사본은 변조 위험이 있어 개발자가 책임지지 않는다. 유저스크립트는 페이지에 코드를 주입하고 광범위한 @connect 권한을 갖는 만큼, 출처 신뢰가 매우 중요하다.
Tampermonkey를 깔고, 아무 페이지에서 GM_registerMenuCommand로 메뉴를 만들고 GM_xmlhttpRequest로 https://api.github.com/repos/hmjz100/LinkSwift를 호출해 별 수를 alert로 띄워 본다. CORS를 넘는 GM 요청과 메뉴 등록을 체감 — LinkSwift base의 두 기둥.
임의의 SPA(예: 유튜브)에서 특정 요소가 나타날 때 옆에 내 버튼을 삽입하는 스크립트를 MutationObserver로 짜 본다. 페이지를 이동해도 버튼이 다시 붙는지 확인해, §4-6의 waitForKeyElements 원리를 이해한다.
공개된 큰 파일 URL에 fetch(url, {headers:{Range:"bytes=0-1023"}})로 첫 1KB만 받아 보고, 이어서 여러 구간을 병렬로 받아 Blob으로 합쳐 저장(URL.createObjectURL)해 본다. 증강 다운로드(§4-4)의 뼈대를 직접 구현.
로컬에 aria2를 --enable-rpc로 띄우고, aria2.addUri JSON-RPC를 fetch로 보내 다운로드를 원격 트리거해 본다. LinkSwift가 Motrix에 링크를 밀어 넣는 §4-5 RPC 푸시의 실체를 손으로 만든다.
과제 3을 확장해, 조각 크기를 "끊기면 ÷2, 잘 되면 속도×목표시간(단 ×2·⅓ 상하한), EMA로 평활화"하는 혼잡 제어를 넣는다. 다양한 네트워크에서 총 소요 시간을 로그로 비교해, 왜 고정 크기보다 나은지(혹은 아닌지) 실측한다. §4-4의 알고리즘을 재현·검증.
| 주차 | 목표 · 학습 내용 |
|---|---|
| 1주차 유저스크립트 기본 | UserScript 메타블록 전체(@match·@grant·@require·@connect·@run-at), Tampermonkey 개발 흐름, GM API 훑기. → 과제 1로 메뉴·GM 요청 체득. |
| 2주차 DOM 주입·관찰 | MutationObserver, 이벤트 위임($(document).on), SPA 라우팅 감지, 격리된 마운트. → 과제 2로 "안정적으로 버튼 심기". |
| 3주차 HTTP·다운로드 | Range 요청·이어받기, Blob·createObjectURL, 멀티스레드 다운로드, aria2/cURL 명령·JSON-RPC. → 과제 3·4. |
| 4주차 네트워크 최적화·기여 | 혼잡 제어(AIMD)·EMA 평활화, 요청 서명(md5)·토큰, config 주도 다중 대상 설계. LinkSwift 본체 정독 후 이슈/PR 도전. → 과제 5. |
LinkSwift는 "쓰기는 클릭 한 번, 이해는 여러 겹"인 도구다. 먼저 유저스크립트 = 남의 웹페이지에 내 JS를 끼워 넣는 것이라는 큰 그림을 잡고, GM API → DOM 주입 → HTTP/다운로드 순으로 층층이 파는 게 정석이다. 9천 줄 본체는 통독하려 하지 말고, main.init()(라우팅)부터 시작해 관심 있는 핸들러 하나($baidu)와 base.download만 좇아가면 전체 흐름이 잡힌다.
| 용어 | 뜻 |
|---|---|
| 유저스크립트 (.user.js) | 특정 사이트에 브라우저가 끼워 실행하는 사용자 JS. 스크립트 매니저 위에서 동작. |
| 스크립트 매니저 | Tampermonkey·Violentmonkey·ScriptCat 등 유저스크립트를 관리·실행하는 브라우저 확장. |
| GM_* API | 스크립트 매니저가 주는 특수 함수. GM_xmlhttpRequest(교차출처 요청)·GM_setValue(저장)·GM_setClipboard·GM_registerMenuCommand 등. |
| @match / @connect | 메타블록 지시어. @match=발동할 URL, @connect=요청 허용 도메인 화이트리스트. |
| 网盘 (왕판) | 중국의 클라우드 스토리지 서비스. 百度·阿里·夸克·123 등. 이 문서에선 "네트디스크". |
| dlink / 직링크 | 파일의 실제 CDN 다운로드 주소. 각 네트디스크의 공식 API로 조회. |
| Range 요청 | Range: bytes=start-end로 파일의 특정 구간만 받는 HTTP 기능. 이어받기·멀티스레드의 토대. |
| 적응형 청킹 | 네트워크 상태에 따라 다운로드 조각 크기를 실시간 조절하는 기법(AIMD+EMA). |
| MutationObserver | DOM 변경을 감지해 콜백을 부르는 표준 API. SPA에 UI를 주입할 때 필수. |
| JSON-RPC / aria2.addUri | 원격 다운로더(aria2/Motrix)에 링크를 밀어 넣는 원격 호출 규약·메서드. |
| 绿化 / 美化 | 네트디스크 페이지의 광고·잡요소를 제거하고 테마색을 입히는 기능(녹화·미화). |
| AGPL-3.0 | 강한 카피레프트 라이선스. 파생·네트워크 서비스로 제공 시에도 소스 공개 의무. |
| BDUSS / AccessToken | 바이두 인증 수단. 구 쿠키(BDUSS) 방식을 폐기하고 OAuth AccessToken으로 전환(더 안전). |
| 구분 | 링크 |
|---|---|
| GitHub | github.com/hmjz100/LinkSwift — 이 저장소(본체·설정·문서) |
| 원조 스크립트 | github.com/syhyz1990/baiduyun — 【网盘直链下载助手】(油小猴), LinkSwift의 모태 |
| 脚本猫(국내) | scriptcat.org/script-show-page/1604 — 설치 및 소개 페이지 |
| 이슈·피드백 | github.com/hmjz100/LinkSwift/issues — 버그·기능 요청은 여기서만 접수 |
| Tampermonkey | tampermonkey.net — 대표 스크립트 매니저(먼저 설치) |
| GM API 문서 | Tampermonkey / Violentmonkey 공식 문서의 GM_* 레퍼런스(유저스크립트 개발 필독) |
| MDN — Range 요청 | HTTP Range requests · Fetch API · Blob — 다운로드 로직의 표준 근거 |