본문 바로가기
C.W.K.
← C.W.K. Quests
🔺

Delta Quest

신규: 2026-11-13최근 수정: 2026-11-12

Mac 가족이 SSH로 파일을 밀어넣던 걸 그만두고, 직접 읽고 고치고 잴 수 있는 우리만의 데이터 채널을 지은 이야기

그림 앱 하나가 다른 Mac에 썸네일 384장을 달라고 했어. 한 장에 SSH 명령 하나씩. 그중 152장이 끝내 안 왔어. 원인은 새 연결이 몰리면 잘라버리는 sshd 설정이었고, 그 데몬은 우리가 짠 것도 아니고 우리가 고칠 수 있는 것도 아니었어. 그래서 나온 게 Delta야. Mac마다 작은 서비스 하나. 파이썬 표준 라이브러리로만 짰어. 자기 Mac 파일을 다른 Mac에 내주고, 남의 파일은 원본을 따라가는 캐시로 읽어. 파일이랑 폴더는 끊겨도 이어받고, 검증까지 하면서 옮겨. 이 퀘스트는 그걸 코드로 따라가. conda 파이썬은 집 네트워크에 못 닿는데 왜 Apple 파이썬은 닿는지. 공유는 왜 루트 클래스고 앱 데이터는 왜 자식 클래스인지. 상대 Mac으로 가는 경로를 왜 머신 이름이 아니라 검증된 사실로 고르는지. 비밀번호 하나 안 옮기고 호출자를 어떻게 알아보는지. 낡은 사본은 왜 낡았다고 말해야 하는지. 전송은 왜 조용하다고 끝난 걸로 치면 안 되는지. 다른 Mac 앱에 보내는 쓰기는 왜 최대 한 번만 보내는지까지.

8 tracks · 43 lessons · ~10h · difficulty: intermediate-to-advanced

Level 0흩어진 파일
0 XP0/43 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
Delta는 CWK 가족 Mac들 사이의 우리 데이터 채널이야. 2026-11-09, SSH가 몰리는 요청에 또 한 번 무너진 날 태어났어. household Mac만 빼고 가족 Mac은 전부 Delta를 하나씩 돌려. launchd 아래에서 Apple의 /usr/bin/python3로, 표준 라이브러리만 써서 돌아. 자기 Mac의 공유를 다른 Mac에 내주고, 자기 Mac에 있는 앱 대신 남의 공유를 읽어줘. Mac 사이로 파일이랑 폴더를 옮길 땐 진행 상황을 실시간으로 보여줘. 출발점은 그 사고랑 거기서 나온 원칙이야. SSH는 명령만 나르고, 데이터는 우리가 가진 서비스로 다녀. 집 네트워크에 다이얼하는 프로세스는 딱 하나고, 그게 macOS 로컬 네트워크 게이트가 통과시켜주는 플랫폼 바이너리야. 다음은 무엇을 내주느냐야. 어휘가 작은 DataShare 루트 클래스가 있고, 그 자식으로 평범한 폴더 트리랑 홈 폴더가 있어. 거절은 장소를 기준으로 정해(자격 증명 저장소, 그리고 macOS가 프롬프트로 지키는 폴더). 앱 자식 클래스는 앱 레포에 들어 있다가, 실패해도 혼자서만 실패해. 다음은 선 위의 이야기야. 같은 집이란 상대 주소가 이 Mac의 살아 있는 인터페이스와 on-link라는 뜻이야. 경로는 빠른 선부터 골라. Thunderbolt 케이블, Ethernet, Wi-Fi, 그다음이 tailnet이야. 신원은 tailnet 자체 명단이 정하고, 티켓은 신원이 증명된 자리에서만 발급돼. 공유 LAN에선 Mac마다 인증서를 핀으로 고정하고, 루프백은 신원이 아니니까 로컬 토큰을 들고 다녀. 다음은 받는 쪽이야. 콘텐츠 주소로 저장하는 캐시가 있고, 항목마다 주인의 검증값으로 다시 확인해. 낡은 답엔 낡았다는 라벨을 붙이고, 커밋은 마지막 바이트가 나가기 전에 끝내. 다음은 바이트 그 자체야. 터미널을 닫아도 살아남는 작업, 병렬 워커에 실어 보내는 16 MiB 청크와 작은 파일 번들, 마감 시간 대신 멈춤 타임아웃을 봐. 청크 단위로 이어받는 partial, 링크와 패턴과 안 열리는 폴더도 다루고, rsync·scp와 붙인 솔직한 측정표도 있어. rsync가 아직 이기는 자리까지 다 적었어. 그다음은 양방향이야. 이름 붙은 pair는 두 쪽이 합의한 아카이브로 삭제와 새 파일을 구분하고, 쓰기 호출은 앱 주인이 직접 처리해. 쓰기 호출은 최대 한 번만 보내고, 결과 셋은 요청이 떠났는지로 갈라. 마지막은 운영(Mac마다 서비스 하나, 쌓이는 건 전부 상한, 실패 모드마다 테스트 하나)이랑 네 손으로 짓는 작은 채널이야. 개념 오픈소싱이야. 결과물은 아키텍처고, 클론할 레포가 아냐.

Tracks

  1. 01🔧왜 우리 채널이어야 해

    0/5 lessons

    빈 썸네일 152장, 우리 것이 아니던 데몬, 그리고 바이너리를 보는 게이트

    Delta는 썸네일 384장짜리 그리드에서 시작했어. 그중 152장이 비어 있었지. 타일마다 SSH 세션을 새로 열었고, 그 몰림이 가족 누구도 주인이 아닌 데몬의 한도를 넘었거든. 이 트랙은 그다음에 이어진 생각을 따라가. 왜 답이 sshd를 더 잘 조이는 게 아니라 우리 서비스였는지, 그 서비스가 뭘 하고 뭐가 되면 절대 안 되는지 봐. 왜 Apple의 Python 3.9에서 표준 라이브러리만 쓰는지도 봐. macOS 로컬 네트워크 게이트는 플랫폼 바이너리를 통과시키고, launchd 에이전트는 권한 창에 답을 못 하거든. 마지막으로 Mac마다 프로세스 하나가 문 두 개로 갈라지는 걸 봐. 하나는 이 Mac의 앱들을 위한 루프백 엔진이고, 하나는 들어오는 연결을 들어온 길로 분류하는 게이트웨이야.

    Lesson list (5)퀴즈 · 5 문제→
  2. 02🌳루트 클래스, 자식 공유들

    0/6 lessons

    서버가 말 거는 클래스는 하나, 자식은 저마다 한 종류의 데이터, 거절은 경로를 어떻게 쓰든 버텨

    Delta는 홈 폴더도, Eagle 이미지 라이브러리도, soul vault 저장소도 같은 서버 코드로 내줘. 서버가 말 거는 클래스가 언제나 하나뿐이라서야. 이 트랙은 그 루트 클래스를 뜯어 봐. 모든 공유가 쓰는 어휘가 있고, 빼먹은 메서드를 닫힌 문으로 만드는 거절 기본값이 있어. 평범한 트리는 scp가 보는 그대로 홈을 내주되, 디스크를 건드리기 전에 경로부터 씻고 링크가 절대 밖으로 안 새게 해. 자격 증명 저장소와 macOS 보호 폴더는 stat 한 번 하기 전에 장소를 보고 거절돼. 앱은 delta.json 선언으로 자기 자식 클래스를 보내고, 로더는 그걸 파일 경로로 올리되 고장 난 자식 하나가 자기 공유만 끄도록 격리해. 정책은 기계 이름 대신 역할, 그룹, 앱으로 Mac을 불러. 마지막으로 데이터가 허락하는 만큼 싸게 "안 바뀌었어"라고 답하는 읽기 API와 파생 출력까지 봐.

    Lesson list (6)퀴즈 · 5 문제→
  3. 03🏠같은 집, 아니면 밖

    0/5 lessons

    Delta가 요청마다, 검증된 사실로 바이트를 어느 선에 실을지 정하는 법

    한 책상 위 Mac 두 대는 Thunderbolt 케이블로 28 Gbit/s, 집 10GbE로 9, tailnet으로는 3분의 1로 얘기할 수 있어. 그중 하나를 고르는 게 Delta가 내리는 제일 무거운 결정이야. 요청마다 사실을 보고 내리고, 머신 이름으로는 절대 안 정해. 이 트랙은 그 결정을 따라가. 상대가 알린 자기 주소, 이 Mac의 켜진 인터페이스, 라우팅 테이블로 하는 on-link 검사. Thunderbolt, Ethernet, Wi-Fi, tailnet의 정해진 순서, 그리고 길마다 지키는 법이 다른 이유. LAN 경로가 10초마다 자기를 다시 증명하는 동안 선택을 1분 쥐고 있는 경로 장부. 빠른 길이 죽으면 새 길로 딱 한 번 하는 재시도, 그리고 잠든 Mac이 호출자 하나한테 타임아웃을 물리고 나면 그 뒤에 오는 호출자는 바로 듣게 하는 hold-down. 마지막으로 실패마다 단계를 말하는 풀 연결, Python 표준 라이브러리가 말없이 돌려주는 짧은 읽기까지.

    Lesson list (5)퀴즈 · 5 문제→
  4. 04🪪복사한 비밀 없는 신원

    0/5 lessons

    호출자는 전부 이름으로 불리고, 자격 증명은 신원이 이미 증명된 곳에서 발급되고, Mac에서 Mac으로 복사되는 건 하나도 없어

    Delta는 바이트 하나 내주기 전에 어느 Mac이 묻는지 알아야 해. 그리고 그걸 누가 복사한 비밀로 알아내는 건 거절해. 이 트랙은 Delta가 경로마다 하나씩 내놓는 네 가지 답을 따라가. tailnet에선 WireGuard가 출발지 주소를 이미 키에 묶어 놨으니까 Delta는 번역만 해. Tailscale 명단으로 주소를 노드로, 레지스트리로 노드를 fleet 라벨로. "모르겠다"와 "넌 남이야"도 갈라. Thunderbolt와 공유 LAN에선 호출자가 티켓을 내밀어. 서빙하는 Mac이 tailnet에서 그 호출자한테 발급하고 해시로만 들고 있는 티켓이야. LAN에선 그 티켓이 TLS 안에 타고, TLS는 같은 tailnet에서 공개된 인증서 지문에 핀으로 고정돼. 루프백에선 0600 토큰 파일이, 호출자가 Delta와 같은 계정에서 돈다는 걸 증명해. 그리고 fleet 밖 가족 Mac은 다른 Mac처럼 이름으로 불린 다음, 자기 그룹을 정책에 적은 공유 하나에만 들어가.

    Lesson list (5)퀴즈 · 5 문제→
  5. 05🗄️원본을 따라가는 캐시

    0/5 lessons

    다른 Mac 파일을 가까이 두면서, 바뀌었는지는 한 번도 짐작하지 않기

    앱이 Delta로 하는 읽기는 전부 캐시를 지나가. 이 트랙은 그 캐시가 어떻게 거짓말을 안 하는지 다뤄. 한 번 건너온 바이트는 SHA-256이 이름인 블롭으로 남아. 자리는 데이터 안이 아니라 Delta 자기 상태 폴더고, 인덱스 행이 주인의 검증값을 기억해. 그러면 읽기 하나에 답이 넷 나올 수 있고, 응답은 어느 쪽인지 꼭 말해. 호출자가 원하는 버전을 짚으면 hit. 네트워크도 경로도 안 써. 주인이 304로 답하면 재확인. 새 바이트가 오면 miss. 주인한테 닿을 수 없으면 이유를 라벨로 붙인 stale 사본. 검증값이 어디서 오는지도 볼 거야. 파일이면 크기, 나노초 시각, inode고, 앱이면 자기만의 싼 태그야. stat으로는 못 보는 편집 하나, 디스크 하한선을 왜 System Settings 방식으로 읽는지, 작은 본문은 왜 마지막 바이트 전에 커밋하는지도 다뤄.

    Lesson list (5)퀴즈 · 5 문제→
  6. 06📦움직이는 바이트

    0/7 lessons

    터미널보다 오래 사는 작업, 청크와 번들, 마감 대신 멈춤 상한, 그리고 조용하다고 끝났다고 하지 않는 전송

    트랙 3부터 5까지 경로를 고르고, 호출자를 증명하고, 읽기를 캐시했어. 이 트랙은 파일을 옮겨. 복사가 왜 터미널의 명령이 아니라 데몬 안의 작업인지부터 봐. 큰 파일은 16 MiB 청크로 쪼개져 워커 전부에 실리고, 작은 파일은 번들로 묶여서 가. 요청 하나에 최대 400개, 아니면 16 MiB까지. 중요한 타임아웃이 마감이 아니라 30초 침묵인 이유도 봐. sendall 안에 숨은 버그 하나 때문에 느린 업링크에선 push가 매번 실패했거든. 그다음은 디스크에 닿은 뒤에야 청크를 기록하는 partial과 그 기록에 있던 버그 둘, 파일이 빠졌는데 끝났다고 말하지 못하게 막은 변경 넷이야. 링크와 패턴과 안 열리는 폴더를 다루는 rsync의 규칙, 그리고 rsync·scp와 붙인 정직한 표까지 봐. Delta가 지는 줄도 빼지 않았어.

    Lesson list (7)퀴즈 · 5 문제→
  7. 07🔁양방향 쓰기

    0/5 lessons

    두 Mac이 다 쓸 때: 삭제와 새 파일을 가르는 pair, 그리고 절대 두 번 안 가는 호출

    지금까지는 데이터가 한 방향으로만 갔고, 한쪽이 늘 정답이었어. 이 트랙은 그 규칙이 깨지는 두 경우를 다뤄. 첫째는 Mac 두 대가 한 폴더를 같이 고칠 때야. 이름 붙은 pair는 두 쪽이 마지막으로 합의한 걸 아카이브로 들고 있고, 삭제와 새 파일을 가를 수 있는 건 그것뿐이야. 결정표도 따라가. 수정이 삭제를 이기고, 같은 변경은 합의고, 새 쪽이 이기되 진 쪽은 옆에 남아. 덮어쓰기 전에 Mac마다 챙겨두는 백업, 그리고 뭐든 바꾸기 전에 실행 전체를 거절하는 멈춤도 봐. 둘째는 주인이 있는 데이터야. 쓰기 호출은 공유를 가진 앱한테 자기 정식 writer로 써달라고 부탁해. 최대 한 번만 보내고, 결과 셋은 요청이 이 Mac을 떠났는지로 갈라.

    Lesson list (5)퀴즈 · 4 문제→
  8. 08🛠️운영하고, 짓기

    0/5 lessons

    Mac마다 서비스 하나, 더미마다 상한, 실패마다 테스트, 그리고 네 손으로 짓는 작은 채널

    일곱 트랙 분량의 메커니즘이 Mac마다 프로세스 하나 안에 살아. 그래서 이 트랙은 그 프로세스를 정직하게 지키는 얘기야. 먼저 설치기를 읽어. 행동 전에 거절부터 하고, health가 대답해야만 성공을 보고하는 설치기야. 그게 렌더하는 LaunchAgent, 그리고 Delta에 자기 스케줄러가 왜 없는지도 봐. 그다음은 불변식 10번 뒤의 목록이야. 더미마다 숫자가 있고, 리뷰가 찾은 누수 둘, launchd가 아직 잡고 있는데도 제자리에서 잘리는 로그까지 있어. 그다음은 테스트 스위트야. Mac 한 대에 진짜 데몬 둘을 띄우고, 건강한 데몬은 못 만드는 실패는 대본 피어로 만들고, 결함은 엄격한 예상 실패로 먼저 커밋해. 초록 테스트가 못 잡는 건 측정 스크립트가 잡아. 그다음엔 모든 앱이 쓰는 클라이언트로 앱을 SSH에서 떼어내. 마지막은 직접 돌려 볼 작은 채널, 그리고 들고 갈 체크리스트야.

    Lesson list (5)퀴즈 · 4 문제→
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.