본문으로 건너뛰기
5분 AI.
GITHUB TRENDING TYPESCRIPT WEEKLY #12 딥다이브 · 2026-10-23 분석

dsh-pet 딥다이브
— 작은 투명 창으로 배우는 데스크톱 앱 설계

플러그인·데스크탑 앱

빠른 판단

무엇
투명 캐릭터 영상에 클릭·드래그·대화·상태 동기화를 붙인 DeepSeek Harness(DSH) 플러그인과 Electron 데스크탑 펫
이럴 때
DeepSeek Harness(DSH) 작업 상태를 캐릭터로 표시하거나 웹·데스크탑에서 같은 로직의 다중 캐릭터를 운영할 때
화면 위를 돌아다니는 작은 캐릭터 안에 설정 병합, 상태 동기화, 물리 계산, 프로세스 통신이 들어 있습니다. dsh-pet은 DeepSeek Harness 플러그인으로도, 독립 실행 프로그램으로도 동작하는 TypeScript 데스크톱 펫입니다. 이 글은 커밋 48007b6의 실제 파일과 함수를 따라가며, 초보자가 자신의 작은 앱에 옮겨 쓸 수 있는 설계를 설명합니다. (PC2005-cloud/dsh-pet · 스타 3,663 · 포크 266 · MIT · 2026-10-23 GitHub 기준)
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

투명한 애니메이션에 앱의 상태와 입력을 연결하는 프로그램

dsh-pet은 투명 배경 영상을 재생하는 캐릭터에 클릭·드래그·이동·대화 기능을 붙인 데스크톱 유틸리티입니다. 브라우저에서는 DeepSeek Harness(이하 DSH) 화면 위의 떠 있는 요소로, 데스크톱에서는 테두리 없는 Electron 창으로 나타납니다. 여러 캐릭터를 동시에 띄우고 각각 크기와 표시 위치를 정할 수 있습니다.

같은 캐릭터 로직을 브라우저와 데스크톱에 연결하는 방법이 이 저장소의 가장 큰 학습 자산입니다. 캐릭터 그림을 그리는 법만 배우는 것이 아닙니다. 설정을 읽고, 행동을 선택하고, 입력을 계산하고, 두 환경에 결과를 전달하는 전체 흐름을 배웁니다.

전체 구조를 이해하는 비유

같은 배우와 대본을 두 개의 무대에 올리는 극장

애니메이션 파일은 배우의 동작이고, 설정은 대본입니다. 호스트는 공연 순서를 정하는 진행자이며, 브라우저와 Electron은 서로 다른 무대입니다. 무대의 장치는 달라도, 다음 동작을 고르는 규칙과 움직임 계산은 함께 씁니다.

여기에 AI를 연결하면 배우가 말을 만들 수 있습니다. 그러나 몸짓 자체는 미리 만들어 둔 영상입니다. 모델이 매 프레임 캐릭터 영상을 생성하는 구조는 아닙니다.

1-1. 기능을 세 층으로 나누면 읽기 쉽다

층하는 일처음 읽을 파일
보이는 몸영상 교체, 클릭 반응, 이동, 말풍선 표시dsh-pet/src/client/pet.ts
dsh-pet/runtime/electron-helper/sprite.js
행동과 상태설정 병합, 행동 요청 처리, 상태 저장, 모델 호출dsh-pet/src/host/index.ts
dsh-pet/src/host/config.ts
소재 제작배경 제거, 크기 정규화, 투명 영상 인코딩scripts/chroma_step02.py
scripts/normalize_step03.py
용어 풀이
호스트와 렌더러
호스트는 설정·파일·모델처럼 프로그램의 능력을 관리하는 쪽입니다. 렌더러는 화면을 그리고 사용자 입력을 받는 쪽입니다. 이 글의 dsh-pet/src/host/는 플러그인의 핵심 로직을 뜻하며, Electron의 메인 프로세스와 항상 별도 프로세스인 것은 아닙니다.

DSH 안에서는 세션의 작업 상태와 잔액도 캐릭터에 연결합니다. DSH 없이 실행할 때는 작은 대체 컨텍스트가 필요한 서비스를 제공합니다. 다만 세션 연동과 AI 대화는 다른 조건을 갖습니다. 독립 AI 대화는 별도 설정으로 켤 수 있고, DSH 세션 이벤트는 DSH 호스트가 있어야 합니다.

2왜 주목받는가

보이는 재미에서 출발해 확장 가능한 작은 제품까지 이어지는 구성

2026-10-23 기준 GitHub Trending TypeScript 주간 12위에 선정됐고, 스타는 3,663개, 포크는 266개입니다. 생성일은 2026-08-14입니다. 이 수치가 특정 기능 때문에 올랐다는 인과관계는 확인할 수 없지만, 코드와 문서에서는 다음 세 가지 매력을 읽을 수 있습니다.

2-1. 결과가 눈에 보이고, 시작점이 다양하다

설정 파일의 pets[].display에는 web, desktop, both, none이 있습니다. 기본값은 both입니다. DSH 화면 안에서 쓰다가 데스크톱으로 옮길 수 있으며, 독립 CLI와 Windows 앱을 위한 진입점도 있습니다. 설치 방법이 한 가지에 묶이지 않는다는 점이 입문자에게 매력적입니다.

2-2. 캐릭터를 바꾸는 길이 소스 바깥에도 있다

내장 재생용 WebM은 106개, 약 51.83 MiB입니다. 별도 펫 팩은 $DSH_HOME/dsh-pet/pet/종류-config.json과 $DSH_HOME/dsh-pet/pet/종류-animation/으로 구성됩니다. 설정의 인스턴스 id와 소재 위치인 assetRoot를 구분해, 여러 인스턴스가 같은 소재를 재사용할 수 있습니다.

작은 결과물을 빨리 확인하면서도, 그 뒤의 설정·통신·배포 설계까지 단계적으로 공부할 수 있습니다. AI 영상 제작용 프롬프트와 Python 처리 스크립트도 있어, 앱 개발과 미디어 파이프라인이 어떻게 만나는지 살펴보기 좋습니다.

2-3. 다른 도구의 상태를 보여 주는 출력 장치가 된다

/dsh-pet-7340 아래의 API로 말풍선·애니메이션·위치를 제어할 수 있습니다. 커밋 48007b6은 제3자 메뉴 등록 시 표시할 쪽을 지정하는 target을 다룹니다. dsh-pet/src/host/menu.ts의 MenuRegistry.register()는 web / desktop / both를 검사하며, 생략하거나 null이면 기존 호출과 같은 both로 처리합니다.

2-4. 비슷한 목적의 구현과 비교하기

아래 표는 특정 경쟁 제품의 성능 순위가 아니라 구현 방식별 선택 기준입니다. dsh-pet의 장점은 영상 자산·호스트 연동·여러 실행 형태가 함께 있다는 데 있습니다.

구현 방식주로 얻는 것dsh-pet이 더 제공하는 것 / 부담
단순 GIF 위젯한 동작을 표시하는 간단한 구성행동 선택·물리·대화 연동을 제공하지만 코드와 설정이 더 많음
웹 전용 펫페이지의 DOM과 입력 API 사용Electron 창으로 데스크톱에도 표시함. 별도 런타임과 창 관리가 필요
독립 데스크톱 펫브라우저 밖에 캐릭터 표시DSH 세션·잔액·플러그인 API까지 연결하는 경로를 제공
직접 만든 Electron 앱화면과 동작을 자유롭게 설계투명 창·소재 체계·설정 병합을 참고할 수 있음. 기존 규약 이해는 필요
선택 기준

Electron을 쓰므로 파일 크기만 보고 가벼운 네이티브 위젯과 같은 자원 사용을 기대하면 안 됩니다. 저장소에 최소 RAM, 평균 CPU 사용률, 배터리 소모를 입증하는 벤치마크는 없습니다. 배우기 좋은 구조라는 판단과 성능이 우수하다는 판단은 별도로 해야 합니다.

3기술 스택 전체 지도

패키지 선언, 잠금 파일, 런타임 기본값을 구분해서 읽기

주 패키지는 저장소 루트 바로 아래의 dsh-pet/package.json에 있으며 버전은 0.3.8입니다. 의존성에 적힌 허용 범위와 잠금 파일에 고정된 버전은 서로 다른 정보입니다. 예를 들어 TypeScript는 ^5.5.0으로 선언돼 있지만 dsh-pet/pnpm-lock.yaml에는 5.5.2로 고정돼 있습니다.

용어 풀이
dependencies · peerDependencies · devDependencies
각각 실행에 필요한 패키지, 호스트가 제공해야 하는 패키지, 개발·검사·빌드에 쓰는 패키지입니다. 플러그인은 호스트의 React와 서비스를 재사용하므로 peerDependencies가 설계의 일부입니다. React는 개발용 목록에도 있어 두 역할이 겹칩니다.

3-1. 백엔드에 해당하는 호스트와 로컬 서버

기술실제 버전·선언역할과 근거
Node.js>=22.12.0engines.node. 파일·HTTP·자식 프로세스 관리
Cordis^4.0.4 → 4.0.4@deepseek-ai/cordis. 의존성 주입과 효과 해제
DSH 서비스주로 ^0.2.0-rc.1dsh-host-webserver, dsh-credentials, dsh-llm 등. 잠금 파일의 dsh-llm은 0.2.0-rc.2
AI 전송 SDKopenai 7.39.0
@anthropic-ai/sdk 0.135.0
dsh-pet/src/standalone/ai-transport.ts에서 독립 AI 요청을 전송
Zod4.6.7dsh-pet/src/standalone/ai-config.ts의 AI 설정과 요청 경계 검증
내장 HTTPnode:httpdsh-pet/src/standalone/server.ts. Express 서버가 아닌 Node 기본 서버

일반적인 클라우드 백엔드나 데이터베이스는 필수 구성 요소가 아닙니다. 설정과 대화 기록은 로컬 파일에, 화면 동기화 상태는 메모리에 둡니다. DSH 호스트의 모델·잔액 서비스를 쓰거나 독립 AI 설정을 켜면 외부 요청이 발생할 수 있습니다.

3-2. 프론트엔드와 데스크톱 껍데기

기술실제 버전·설정역할과 근거
React / React DOM^18.2.0 → 18.2.0브라우저 펫과 설정 화면. dsh-pet/src/client/app.ts가 호스트의 require로 주입
Electron다운로드 기본값 43.3.0dsh-pet/src/host/helper-process.ts와 dsh-pet/electron-builder.yml. 일반 npm 실행 의존성으로 고정한 방식과 다름
DOM / 영상 APIHTMLVideoElement, fetch, requestAnimationFrame데스크톱의 dsh-pet/runtime/electron-helper/sprite.js는 직접 DOM을 조작. 모든 화면이 React인 것은 아님
투명 영상VP9-alpha WebM
640×360 / 24fps
scripts/encode_thumbs.py의 출력 설정. HEVC-alpha MOV는 별도 경로

dsh-pet/tsconfig.json은 strict: true, moduleResolution: Bundler, jsx: react-jsx, noUnusedLocals: true를 사용합니다. 다만 호스트 접점에는 ctx: any도 남아 있습니다. 엄격한 컴파일 옵션이 외부 서비스 전체의 타입 안전성을 자동으로 보장하지는 않습니다.

3-3. 인프라·빌드·검사

도구선언 → 잠금 버전무엇을 담당하나
TypeScript^5.5.0 → 5.5.2타입 검사와 타입 선언 생성
tsdown / Rolldown^0.8.0 → 0.8.0
1.0.0-beta.7
네 진입점과 데스크톱 공유 코어 묶기
electron-builder^26.15.3 → 26.15.3Windows 배포 폴더 생성, ASAR 사용
Electron 다운로드@electron/get ^5.1.0 → 5.1.0
@electron-internal/extract-zip ^1.0.5 → 1.0.5
Electron이 없을 때 받기·압축 해제
ESLint / Prettier^10.9.0 → 10.9.0
^3.9.6 → 3.9.6
정적 검사와 서식 확인
테스트node:test
playwright 1.63.0
TypeScript 단위 테스트와 별도 UI 테스트
영상 처리Python, NumPy, SciPy, FFmpeg버전 잠금용 Python 의존성 파일은 없음. 일부 스크립트는 ffmpeg-9.0.1-essentials_build 경로를 지정

Cargo.toml, go.mod, pyproject.toml, Dockerfile은 없습니다. 핵심 런타임은 Node·Electron이며, Python·Swift는 주로 소재 제작을 돕습니다. Swift 인코더는 Apple의 AVFoundation·VideoToolbox를 사용합니다.

빌드에서 배울 점

dsh-pet/tsdown.config.mjs에는 브라우저 client, 호스트 index, 독립 CLI standalone, 앱 app의 네 진입점이 있습니다. 파일 머리 주석에는 세 구성이라고 남아 있지만 실제 배열은 네 개입니다. 브라우저·호스트는 es2020, 독립 CLI·앱은 es2022를 목표로 합니다.

4아키텍처 심화 분석

입력에서 화면까지 한 번 따라간 뒤, 네 가지 설계를 읽는다
[실행 환경] DSH 서비스 ────────────────┐ 독립 CLI의 대체 ctx ──────┼──> 호스트 : apply(ctx) Electron 앱의 대체 ctx ───┘ │ ┌─────────────┴──────────────┐ │ 설정 / 상태 / 행동 처리 │ │ readAllConfig │ │ PollStateStore │ │ handlePetRoute │ └─────────────┬──────────────┘ ┌────────────────────────┼────────────────────┐ │ │ │ DSH HTTP Helper 브리지 앱의 로컬 HTTP │ 사용자 정의 scheme │ │ + JSON 행 / 콜백 │ v v v React 브라우저 화면 Electron 메인 프로세스 <──── 앱 내 재사용 브라우저 펫 │ preload 브리지 / 제한된 IPC │ v 데스크톱 펫 / 영상 DOM dsh-pet/src/shared/{physics,pickers,motion,state}.ts └── 브라우저 번들 + 데스크톱 공유 코어가 함께 사용 prompts/ → 외부에서 만든 원본 영상 → scripts/ 처리 └──> dsh-pet/assets/webm/

화살표는 데이터와 호출의 방향을 요약합니다. DSH 플러그인과 독립 CLI는 호스트와 Helper가 나뉘지만, 독립 앱은 호스트 로직과 창 관리 코드를 같은 Electron 메인 프로세스에 둡니다. 앱의 메인 프로세스를 합쳤다는 뜻이지 Electron의 렌더러 프로세스까지 하나라는 뜻은 아닙니다. 일반적인 메인·렌더러 구분은 Electron 공식 프로세스 모델에서도 확인할 수 있습니다.

4-1. 패턴 ① 환경을 바꾸는 어댑터

dsh-pet/src/host/index.ts의 apply(ctx)는 호스트 서비스를 받아 라우트와 부수 효과를 등록합니다. 내부 handlePetRoute()가 기능별 요청을 처리합니다. 독립 실행은 이 로직을 복제하는 대신 dsh-pet/src/standalone/context.ts의 createStandaloneContext()로 필요한 모양의 ctx를 만듭니다.

대체 webServer.register()는 같은 핸들러를 배열에 모읍니다. dsh-pet/src/standalone/server.ts가 그 배열을 Node HTTP 서버에 연결합니다. effect()는 등록 함수가 돌려준 해제 함수를 모으고, dispose()는 역순으로 호출합니다. 창·타이머·라우트를 만드는 일에 종료 경로까지 짝지어 둔 것입니다.

비유

가전제품의 내부 회로를 나라별로 다시 만드는 대신 플러그 어댑터를 바꾸는 방식입니다. 여기서 바뀌는 것은 서비스 공급자이며, 라우트와 캐릭터 동작 규칙은 같은 것을 씁니다.

진입점환경이 제공하는 것차이
dsh-pet/src/host/index.tsDSH의 모델·자격 증명·세션·웹 서버DSH 플러그인으로 등록
dsh-pet/src/standalone/cli.ts대체 컨텍스트·로컬 서버·선택적 AI 서비스기존 Helper를 별도 실행
dsh-pet/src/app/main.ts대체 컨텍스트·앱 라우터·선택적 AI 서비스DSH_PET_IN_PROCESS=1로 Helper 중복 실행을 건너뛰고 창 코드를 재사용

빌드도 이를 지원합니다. dsh-pet/tsdown.config.mjs는 독립 실행에서만 DSH 전용 패키지 세 개를 dsh-pet/src/standalone/shims/로 바꿉니다. noExternal로 먼저 번들 대상에 포함한 뒤 alias를 적용합니다. 외부 패키지로 그대로 남으면 대체 구현을 고를 기회조차 없기 때문입니다.

4-2. 패턴 ② 설정을 한곳에서 완성하는 경계

dsh-pet/src/host/config.ts의 readAllConfig()는 내장 기본값, 사용자 주 설정, 종류별 펫 파일을 읽습니다. mergeEntry()는 최상위 필드를 검사해 유효한 값을 고르고, mergePet()는 인스턴스의 숫자·불리언·표시 위치를 정규화합니다.

dsh-pet/assets/config.jsonc 내장 기본값 │ ├── $DSH_HOME/dsh-pet/main-config.jsonc ──> main 항목 │ └── 사용자 공통 설정 중 허용된 항목 + $DSH_HOME/dsh-pet/pet/종류-config.json │ 종류 항목 │ { main: {...}, 종류: {...} } <─────────┘ │ flattenConfigPets() / flattenPetList() │ 화면·호스트가 소비할 인스턴스 목록

physics·font 같은 객체는 임의의 깊은 병합이 아니라 필드 단위의 전체 교체입니다. 잘못된 객체를 명시하면 해당 필드가 기본값으로 돌아갑니다. 반면 position은 하위 필드별로 병합합니다. 모든 중첩 객체가 같은 병합 규칙을 따를 것이라고 생각하면 설정이 예상과 달라집니다.

종류별 설정에는 packBase()가 허용한 공통 설정만 사용자층에서 전파합니다. 인스턴스의 whisperPrompt는 의도적으로 유효한 기본 문구를 미리 채우지 않습니다. 값을 비워 두는 것이 “종류의 설정을 계속 따라간다”는 뜻이므로, 저장 과정에서 상속이 고정값으로 변하지 않게 합니다.

용어 풀이
검증 경계와 정규화
파일이나 네트워크에서 받은 값을 바로 믿지 않고, 사용할 수 있는 모양인지 검사하고 일정한 형태로 만드는 지점입니다. 캐릭터 화면마다 누락값을 제각각 채우는 대신, 입력이 들어오는 입구에서 규칙을 한 번 적용합니다.

4-3. 패턴 ③ 최신 상태와 행동 결과를 구분하는 동기화

dsh-pet/src/host/state.ts의 PollStateStore는 공통 sections와 펫별 pets를 관리합니다. 각 말단 항목에는 { counter, data }가 있고, 쓰기는 writeSection() 또는 writePet()를 거칩니다. 내부 next()는 다음 식으로 같은 밀리초의 연속 쓰기도 구별합니다.

// dsh-pet/src/host/state.ts의 next() 핵심
this.last = Math.max(Date.now(), this.last + 1);
return this.last;

// 응답 형태를 줄인 학습용 예시
{
  counter: 1002,
  sections: { workStatus: { counter: 1001, data: {} } },
  pets: { main: { say: { counter: 1002, data: { text: "안녕" } } } }
}

클라이언트의 dsh-pet/src/shared/state.ts는 첫 응답의 버전을 flattenCounters()로 기록하고, 이후 takeChanged()로 달라진 말단 항목만 전달합니다. dsh-pet/src/client/pet.ts의 PetMulti는 since와 timeout을 보내 장기 폴링합니다. 현재 클라이언트 대기는 1,000ms, 호스트가 허용하는 상한은 10,000ms입니다.

요청을 받아들였다는 사실과 화면에서 실제 동작이 끝났다는 사실은 별도로 기록해야 합니다. dsh-pet/src/host/playback.ts의 PlaybackStore가 이 차이를 담당합니다. POST /anim을 수락하면 호스트가 accepted를 기록하고, 렌더러는 실제 재생에 따라 playing, ended / failed / cancelled를 보고합니다. 재생 시작 전에도 로딩 실패로 failed가 될 수 있고, 새 재생 요청이 기존 요청을 대체하면 호스트가 기존 요청을 cancelled로 바꿉니다. 끝난 상태는 늦게 도착한 보고로 다시 바뀌지 않습니다.

일반 재생: 행동 요청 ──> accepted ──> playing ──> ended 실패·취소: accepted 또는 playing ──> failed / cancelled 최신 상태: GET /state 여러 화면이 공유 재생 결과: GET /playback/재생ID 해당 요청을 추적 제어 권한: LockStore + expiresAt 일시적인 독점 제어

이 상태 저장소는 이벤트 전체를 보존하는 큐가 아닙니다. 같은 말단 항목을 빠르게 두 번 쓰면 중간 값은 조회 전에 덮일 수 있습니다. 첫 응답에서는 과거 말풍선을 재생하지 않지만, 메뉴처럼 현재 있어야 할 데이터는 별도로 읽어 넣습니다. counter는 프로세스 안에서 증가를 보장할 뿐, 영구 저장 로그나 시스템 시계 변경까지 해결하는 분산 시계는 아닙니다.

4-4. 패턴 ④ 계산은 공유하고, 화면 장치는 나눈다

dsh-pet/src/shared/physics.ts의 springStep()·throwStep(), dsh-pet/src/shared/pickers.ts의 rollKind(), dsh-pet/src/shared/motion.ts의 planMove()는 DOM 없이 계산할 수 있습니다. 브라우저는 이를 TypeScript 번들로 쓰고, dsh-pet/scripts/build-desktop-core.mjs는 같은 진입점 dsh-pet/src/shared/index.ts를 IIFE로 묶어 window.PetShared로 제공합니다.

반면 화면 그리기는 다릅니다. 브라우저의 makePetUI()는 React 훅을 받고, 데스크톱의 dsh-pet/runtime/electron-helper/sprite.js는 영상 DOM과 창 이동을 다룹니다. 공유 폴더 전체가 순수 함수인 것은 아닙니다. dsh-pet/src/shared/menu.ts에는 두 환경이 함께 쓰는 DOM 메뉴 구현도 있습니다.

이 분리는 테스트에도 유리합니다. 중력과 반사 계산을 확인하려고 Electron 창을 띄울 필요가 없습니다. 계산 결과를 검증하는 테스트와 실제 창이 올바르게 움직이는 UI 검사는 서로 다른 문제로 남깁니다.

5디렉토리 구조 해부

저장소 루트와 npm 패키지 루트가 한 단계 다르다

저장소 루트의 README.md·scripts/는 전체 프로젝트를 설명하고, 실제 npm 패키지와 앱 소스는 dsh-pet/ 아래에 모여 있습니다. 파일을 찾을 때는 실행 환경을 먼저 고르고, 그다음 공유 로직으로 내려가면 길을 잃기 어렵습니다.

./ 저장소 루트 ├── README.md · LICENSE 전체 안내와 MIT 라이선스 ├── API.md · CORDIS.md HTTP / 페이지 내부 서비스 계약 ├── openapi.yaml HTTP API의 구조화된 명세 ├── prompts/ AI 영상·알림 아이콘 제작용 프롬프트 ├── scripts/ Python·Swift·셸 소재 처리 ├── tools/ 외부 API 소비자·화면 점검 도구 │ └── cordis-probe/ 제3자 클라이언트 플러그인 모의 도구 ├── .github/workflows/ Windows 앱 배포·HEVC-alpha 처리 └── dsh-pet/ npm 패키지 루트 ├── package.json · pnpm-lock.yaml ├── tsdown.config.mjs · electron-builder.yml ├── assets/ 기본 설정·영상·폰트·이미지 ├── scripts/ 빌드·배포·개발·UI 검사 도구 ├── runtime/electron-helper/ Electron 창·preload·영상 렌더러 └── src/ ├── host/ 설정·라우트·모델·상태·Helper 관리 │ └── balance/ 잔액 공급자 등록과 개별 구현 ├── client/ DSH 브라우저 펫·설정 화면 ├── shared/ 공통 계산·상태 해석·메뉴 ├── standalone/ CLI·로컬 서버·독립 AI·대체 서비스 │ └── shims/ DSH 전용 모듈의 대체 구현 └── app/ 독립 Electron 앱 진입점·인증·라우터

5-1. 처음부터 끝까지 전부 읽지 않아도 된다

알고 싶은 것읽는 순서
다음 동작 고르기dsh-pet/assets/config.jsonc → dsh-pet/src/shared/pickers.ts → dsh-pet/src/client/pet.ts
캐릭터 던지기dsh-pet/src/shared/physics.ts → dsh-pet/src/shared/physics.test.ts → dsh-pet/runtime/electron-helper/sprite.js
독립 실행 재사용dsh-pet/src/standalone/cli.ts → dsh-pet/src/standalone/context.ts → dsh-pet/src/host/index.ts
모델 요청의 경계dsh-pet/src/standalone/ai-config.ts → dsh-pet/src/standalone/ai-provider.ts → dsh-pet/src/standalone/ai-transport.ts
제3자 메뉴 확장dsh-pet/src/host/menu.ts → dsh-pet/src/shared/menu.ts → dsh-pet/src/client/pet.ts

5-2. 코드 규모를 읽는 기준

다음은 커밋 48007b6에서 파일 확장자별로 센 물리적 줄 수입니다. 빈 줄과 주석, 테스트를 포함하고 .git은 제외했습니다. 코드의 복잡도나 테스트 커버리지를 뜻하는 수치는 아닙니다.

언어·범위파일 수줄 수
TypeScript .ts14934,418줄. 그중 .test.ts 55개, 13,658줄
JavaScript 계열357,284줄. .js / .mjs / .cjs 합계
Python91,219줄
Swift / 셸각 1161줄 / 116줄
HTML34,907줄. 도구 페이지의 내장 코드도 포함

TypeScript는 dsh-pet/src/host/ 17,431줄, dsh-pet/src/client/ 6,646줄, dsh-pet/src/shared/ 5,745줄, dsh-pet/src/standalone/ 3,970줄, dsh-pet/src/app/ 626줄입니다. 특히 호스트 쪽 테스트가 8,464줄이라, 실제 사용 조건과 실패 사례를 찾는 데 도움이 됩니다.

읽기 요령

dsh-pet/src/**/*.test.ts를 설명서처럼 읽어 보세요. 예를 들어 dsh-pet/src/host/config.test.ts는 유효한 설정과 잘못된 설정을 만들고 실제 readAllConfig()의 결과를 비교합니다. 비공개 검증 함수를 그대로 시험하는 대신 외부에서 관찰할 결과를 고정합니다.

6학습 포인트 (기술별)

한 기능을 고르고, 성공·실패·종료 조건까지 읽는다

6-1. TypeScript: 값의 종류를 타입으로 표현하기

dsh-pet/src/shared/state.ts의 SayCue는 말하기와 지우기를 서로 다른 형태로 표현합니다. 빈 문자열을 특별한 명령으로 쓰면 일반 입력 검증과 충돌할 수 있습니다. kind 같은 구분 필드를 두면 가능한 행동을 코드에서 드러낼 수 있습니다.

// 실제 SayCue에서 선택 필드를 생략한 학습용 축약
type SayCue =
  | { kind: 'say'; text: string; duration?: number }
  | { kind: 'clear' };

function describe(cue: SayCue): string {
  if (cue.kind === 'clear') return '말풍선 지우기';
  return cue.text; // 이 분기에서는 text가 있는 형태로 좁혀짐
}

배울 것: 선택 필드가 많은 객체 하나와, 구분 가능한 여러 형태의 합집합은 다릅니다. 작은 실습: 말풍선의 “숨김”과 “삭제”를 구별해야 한다면 필요한 형태를 먼저 종이에 써 보세요. 개념은 TypeScript 공식 타입 좁히기 문서와 연결됩니다.

6-2. React와 영상 API: 늦게 도착한 응답을 버리기

dsh-pet/src/client/pet.ts의 switchTo()는 두 영상 요소를 번갈아 사용합니다. 뒤쪽 영상에 새 파일을 준비하고, loadeddata를 받은 뒤 앞쪽으로 바꿉니다. 이전 영상의 onended를 지우고 멈춰, 이미 교체된 영상의 종료 이벤트가 다음 행동을 건드리지 못하게 합니다.

영상 A가 앞에서 재생 중 │ ├── 영상 B용 fetch → Blob → 객체 URL → loadeddata │ │ └── A의 이벤트 해제·정지 <──── B를 앞쪽으로 교체 │ play() 성공 │ playing 회신

여기에는 네 가지 수명 관리가 있습니다. AbortController로 파일 요청에 10초 제한을 걸고, pendingRef.current?.gen으로 뒤늦은 결과를 버리고, mountedRef로 이미 사라진 화면을 건드리지 않으며, 교체한 객체 URL은 URL.revokeObjectURL()로 해제합니다.

배울 것: “불러왔다”·“디코딩할 수 있다”·“재생이 시작됐다”는 서로 다른 시점입니다. 작은 실습: A 요청 뒤 B 요청을 보냈는데 B가 먼저 도착하는 시간표를 그리세요. 세대 번호가 없을 때 어떤 영상이 마지막에 보이는지 설명하면 비동기 경합을 이해한 것입니다.

6-3. 물리 계산: 초 단위 시간과 화면 좌표 연결하기

dsh-pet/src/shared/physics.ts에서 기본 중력은 1400 px/s², 반발 계수는 0.78, 바닥 마찰은 2.5 /s입니다. throwStep()는 입력 시간 간격을 최대 0.05초로 제한합니다. 탭이 멈췄다가 돌아왔을 때 한 번에 멀리 순간 이동하는 상황을 줄이는 장치입니다.

// throwStep()의 계산 순서에서 경계 처리를 제외한 부분
const dt = Math.min(Math.max(dtRaw, 0), MAX_STEP_DT);
vy += physics.gravity * dt;
x += vx * dt;
y += vy * dt;

// 오른쪽 벽 충돌: 왼쪽으로 돌아서며 속력 감소
vx = -Math.abs(vx) * physics.restitution;

화면에서는 아래쪽으로 갈수록 y가 커집니다. 정지 상태에서 1/60초를 진행하면 중력으로 vy가 약 23.33 px/s 증가합니다. 벽에 1000 px/s로 부딪히면 반대 방향 속력은 780 px/s가 됩니다.

드래그의 springStep()는 SPRING_K=200, SPRING_C=30으로 목표 위치와 현재 위치의 차이, 현재 속도를 함께 사용합니다. 작은 실습: 기본 강도에서 속도 0, 위치 0, 목표 100, 시간 1/60초라면 새 속도는 약 333.33이고, 호출자가 위치를 갱신한 뒤에는 약 5.56이 됩니다. 단위를 붙여 손으로 계산해 보세요.

용어 풀이
감쇠와 반발 계수
감쇠는 움직임이 점점 줄어드는 효과입니다. 반발 계수는 충돌 뒤 속력이 얼마나 남는지를 정합니다. 0.78은 속력의 78%를 뜻하며 에너지의 78%를 뜻하지는 않습니다. 물리 공식은 화면의 시간 단위가 맞아야 같은 느낌으로 동작합니다.

6-4. Electron: 투명함과 클릭 통과는 별개다

dsh-pet/runtime/electron-helper/main.js는 펫마다 작은 BrowserWindow를 만들고, transparent: true, frame: false, alwaysOnTop: true를 지정합니다. 화면 전체를 덮는 투명 창 하나를 만드는 방식이 아닙니다. 창은 캐릭터와 말풍선을 담도록 이동합니다.

픽셀이 투명하다고 클릭까지 자동으로 뒤에 전달되지는 않습니다. 메인 프로세스의 setIgnoreMouseEvents()와 preload의 setInteractive()·setInputBusy()가 이를 조정합니다. 드래그 중에 갑자기 클릭 통과로 바뀌면 pointerup을 놓칠 수 있으므로 입력 중 상태를 따로 전달합니다.

창 설정에는 contextIsolation: true, nodeIntegration: false가 있지만 sandbox: false도 있습니다. 이 세 설정은 같은 뜻이 아닙니다. dsh-pet/runtime/electron-helper/preload.js는 contextBridge.exposeInMainWorld()로 정해진 동작을 노출합니다. 옵션의 의미는 공식 BrowserWindow 문서를 함께 보세요.

작은 실습: 창 제어, 영상 재생, 파일 읽기 각각이 어느 프로세스의 책임인지 표를 만들어 보세요. dsh-pet/src/app/desktop-auth.ts의 installDesktopAuth()가 대상 origin과 로컬 최상위 렌더러를 확인한 뒤에만 헤더를 붙이는 이유도 설명해 보세요.

6-5. AI 연결: 설정을 켜는 일과 주기 호출을 허용하는 일

독립 AI의 DEFAULT_AI_SETTINGS는 enabled: false, automaticWhispers: false입니다. 최대 출력은 기본 1024 토큰, 요청 제한 시간은 30,000ms입니다. createStandaloneAi()는 설정·모델·주소·키가 갖춰졌을 때만 준비 상태가 됩니다.

dsh-pet/src/host/whisper-policy.ts의 automaticWhisperSchedule()는 독립 실행에서 펫별 허용, AI 준비 상태, 자동 발화 허용을 모두 확인합니다. dsh-pet/src/standalone/ai-provider.ts에는 동시 요청 제한, 분당 6회 제한, 취소 신호, 입력·출력 길이 제한도 있습니다. 작은 AI 기능에서도 사용자가 켠 기능과 실제 외부 요청이 시작되는 조건을 명확히 연결해야 합니다.

dsh-pet/src/standalone/ai-storage.ts의 writePrivateJson()은 임시 파일에 제한된 권한을 먼저 적용한 뒤 내용을 쓰고, fsyncSync()와 renameSync()로 교체합니다. POSIX에서는 0600, Windows에서는 사용자 SID 기반 권한을 적용합니다. 키는 로컬 JSON 파일에 저장되며, 운영체제 비밀 저장소로 암호화하는 구현은 아닙니다.

작은 실습: dsh-pet/src/standalone/ai-config.ts의 설정 반환에서 키 대신 hasKey만 내보내는 경로를 추적하세요. 대화 기록은 $DSH_HOME/dsh-pet/memory.json에 보존하면서, 모델에 보내는 문맥은 sliceMemoryRounds()로 최근 몇 턴만 고르는 차이도 확인하세요. 기본 chatMemoryRounds는 5이며, 문맥을 줄인다고 저장된 과거 기록이 삭제되는 것은 아닙니다.

6-6. 영상 처리: 투명도와 기준 좌표를 함께 보존하기

외부에서 생성한 녹색 배경 원본 │ scripts/chroma_step02.py HSV로 배경 판정, alpha 생성 │ step02/ 투명 영상 │ scripts/normalize_step03.py 캐릭터 크기·중심·발 위치 정렬 │ step03/ 2160×1215 기준 영상 │ scripts/encode_thumbs.py 640×360, 24fps, CRF 40 │ step04/ 재생용 WebM → dsh-pet/assets/webm/에 반영

scripts/chroma_step02.py의 실제 변환은 NumPy 배열로 HSV 색상을 판정합니다. scripts/normalize_step03.py는 처음과 끝의 서 있는 프레임을 읽어 중심·높이를 측정하고, 전체 동작의 범위도 확인합니다. 소재마다 크기가 달라 보이거나 발 위치가 튀는 문제를 제작 단계에서 줄이는 접근입니다.

scripts/encode_thumbs.py는 디코딩에 libvpx-vp9를 명시하고 format=yuva420p로 투명 채널을 유지합니다. 실행 화면의 dsh-pet/src/shared/constants.ts에는 CANVAS_H=360, FEET_Y=330, 고정된 HIT_BOX가 있습니다. 소재 형식을 바꿀 때는 재생 파일만 아니라 이 좌표 계약도 맞춰야 합니다.

설명보다 실행문이 우선하는 사례

scripts/encode_thumbs.py의 머리 설명은 예전의 360×360·1200×1200·dsh-pet/assets/thumb를 말하지만, 실제 상수는 640×360이고 현재 패키지는 dsh-pet/assets/webm을 배포합니다. 일부 스크립트의 “추가 의존성 없음”이라는 취지의 주석도 NumPy·SciPy import와 맞지 않습니다. 오래된 주석을 그대로 설치 조건으로 받아들이지 마세요.

6-7. 테스트와 CI: 빌드 성공의 범위 읽기

dsh-pet/package.json의 test는 Node 내장 테스트 러너와 타입 제거 옵션을 사용하고, test:ui는 별도의 dsh-pet/scripts/tests/*.test.mjs를 실행하도록 정의합니다. 테스트 API와 타이머 모의 기능의 개념은 Node 공식 테스트 문서에서 이어서 읽을 수 있습니다.

.github/workflows/release-app.yml은 Windows runner에서 Node 24·pnpm 12를 쓰고, --frozen-lockfile 설치, 버전 일치 검사, 번들, 타입 검사, 패키징을 진행합니다. 기존 릴리스에 ZIP을 올리는 구성입니다. 이 workflow에는 단위 테스트·UI 테스트 실행 단계가 보이지 않습니다.

작은 실습: dsh-pet/scripts/prepare.js의 전체 빌드와 릴리스 workflow를 대조해 보세요. 전체 빌드에는 build:desktop-core가 있지만, 이 커밋의 앱 workflow는 bundle을 호출합니다. dsh-pet/runtime/electron-helper/shared-core.js는 Git에서 제외되고 dsh-pet/scripts/dist.mjs도 이를 만들지 않으므로, 깨끗한 체크아웃에서 배포 자산의 생성 경로가 완결되는지 검토할 지점입니다. 실제 릴리스 ZIP의 포함 여부는 별도 확인이 필요합니다.

7하드웨어 / 시스템 요구사항

실행 모드와 소재 제작 모드를 나누어 준비한다
목적필요한 환경근거·주의할 범위
DSH 플러그인DSH와 호환되는 Node 환경·웹 화면README의 개발·시험 버전은 DSH 0.2.1-rc.2. 호환 선언에는 0.2.0-rc.1 / rc.2도 포함
독립 CLINode >=22.12.0, Electron, 그래픽 세션engines.node와 dsh-pet/src/host/helper-process.ts. 기본 Electron 43.3.0
독립 앱Windows x64용 ZIP 배포 경로.github/workflows/release-app.yml이 Windows x64 산출물을 생성. 세 OS용 앱 배포를 모두 제공하는 CI는 아님
브라우저 영상VP9-alpha를 표시할 수 있는 브라우저README는 Chrome·Edge·Firefox에서 투명 재생을 확인했다고 설명
Safari 대응HEVC-alpha MOV 소재와 별도 배포기본 npm 자산은 WebM. .github/workflows/hevc-alpha.yml은 macOS 14 runner 사용
AI 대화지원 프로토콜의 API 주소·모델·키독립 설정에서 명시적으로 활성화. 단순 애니메이션에는 모델이나 GPU가 필수 아님
소재 제작Python·NumPy·필요 시 SciPy·FFmpeg일부 스크립트는 Windows .tools/ 경로를 고정. HEVC 인코더는 macOS의 Swift 환경 사용

README는 Windows·Linux·macOS에서의 런타임 지원을 설명합니다. 코드도 운영체제별 Electron 경로를 고르지만, 이것만으로 모든 창 관리자와 하드웨어에서 같은 동작을 보장할 수는 없습니다. 특히 Electron의 플랫폼 안내는 Wayland에서 창 위치·크기 변경 등에 제약이 있다고 설명합니다.

hasGraphicalDisplay()는 Linux에서 DISPLAY 또는 WAYLAND_DISPLAY를 검사합니다. 둘 다 없으면 데스크톱 실행을 건너뛰며, DSH_PET_DESKTOP_FORCE=1은 예외 처리용입니다. 그래픽 세션 없는 서버가 캐릭터를 직접 그려 주는 것은 아닙니다.

7-1. 메모리와 저장 공간은 얼마나 필요한가

공식 최소 CPU·RAM·GPU 사양은 명시돼 있지 않습니다. 재생용 WebM의 약 51.83 MiB는 소재 크기이며, Electron 런타임과 실행 중 메모리까지 포함한 크기가 아닙니다. 데스크톱에서 여러 펫을 띄우면 창·영상 디코더·상태 조회도 늘어납니다. 최소 사양을 임의의 숫자로 단정하기보다 인스턴스 수별로 측정해야 합니다.

처음 살펴볼 때의 기준

캐릭터 한 개, 기본 크기, 자동 AI 호출을 끈 조건부터 비교하면 원인을 구분하기 쉽습니다. 이후 펫 수·영상 크기·자동 발화 주기를 하나씩 바꾸며 CPU, 메모리, 프레임 끊김을 기록하세요. 이는 측정 설계 제안이며 이 저장소의 성능 측정 결과는 아닙니다.

8직접 해볼 수 있는 실습 과제

작은 계산에서 시작해 상태와 배포의 계약까지 확장한다

아래 과제는 종이 추적이나 별도 연습 파일로 시작할 수 있습니다. 외부 모델·잔액 서비스에 연결하지 않아도 핵심 원리를 확인할 수 있습니다. 과제의 완료 기준을 눈에 보이는 결과와 실패 사례로 정하면, 단순히 코드를 따라 쓰는 데서 벗어날 수 있습니다.

과제 1

행동 선택기를 손으로 실행하기 입문 · 30분

읽을 곳: dsh-pet/src/shared/pickers.ts의 rollKind()와 dsh-pet/assets/config.jsonc의 animationWeights.

할 일: 기본 가중치 { idle: 10, turn: 5, move: 5 }에 0.05 / 0.12 / 0.18 / 0.90을 넣은 결과를 적으세요. 같은 값을 fixed: true에서도 비교하세요.

제출물: 입력·일반 모드·고정 모드의 세 열로 된 표. 완료 기준: 일반 모드에서는 차례로 idle / turn / move / action, 고정 모드에서는 idle / action / action / action이 나와야 합니다. 고정 모드에서 빠진 확률이 어디로 가는지도 설명하세요.

// 경계값까지 추가하면 더 좋은 연습이 된다
const weights = { idle: 10, turn: 5, move: 5 };
const rolls = [0.0999, 0.10, 0.1499, 0.15, 0.1999, 0.20];
// 비교 연산이 미만인지 이하인지 원문에서 확인한다.
과제 2

설정 병합의 예상 결과 쓰기 중급 · 1시간

읽을 곳: dsh-pet/src/host/config.ts의 mergeEntry()·mergePet()와 dsh-pet/src/host/config.test.ts.

할 일: 기본 설정에 사용자층이 position.marginX만 바꾼 경우, physics에 gravity만 쓴 경우, 두 인스턴스의 id가 같은 경우를 비교하세요. 검증 결과와 기본값으로 돌아가는 범위를 예상한 뒤 실제 분기와 대조합니다.

제출물: 세 입력의 최종 객체와 경고 이유. 완료 기준: 위치의 하위 필드 병합과 물리 객체의 전체 검증을 구별하고, 중복 id가 캐릭터 수에 미치는 영향을 설명해야 합니다.

과제 3

영상 요청의 순서 뒤집기 중급 · 1~2시간

읽을 곳: dsh-pet/src/client/pet.ts의 switchTo().

할 일: 파일 요청 대신 직접 완료시킬 수 있는 가짜 비동기 함수를 사용합니다. A를 먼저 요청하고 B를 나중에 요청한 뒤, B→A 순서로 완료하세요. 요청 번호를 비교하는 구현과 비교하지 않는 구현의 마지막 화면 상태를 적습니다.

제출물: 요청·완료·채택·폐기의 시간표. 완료 기준: 마지막 의도인 B를 유지해야 하며, 화면이 사라진 뒤 완료된 요청도 채택하지 않아야 합니다. 새 영상의 play()가 실패했을 때 회신할 상태까지 정해 보세요.

// 저장소 구현을 읽기 위한 독립적인 학습용 의사 코드
let generation = 0;
async function change(load: () => Promise<string>) {
  const mine = ++generation;
  const result = await load();
  if (mine !== generation) return;
  show(result);
}
// show와 load는 연습용 가짜 함수로 정의한다.
과제 4

현재 상태와 재생 회신을 따로 추적하기 고급 · 2시간

읽을 곳: dsh-pet/src/host/state.ts, dsh-pet/src/shared/state.ts, dsh-pet/src/host/playback.ts와 각 테스트.

할 일: 같은 밀리초에 말풍선을 두 번 쓰는 경우, 재생이 cancelled된 뒤 늦게 ended가 보고되는 경우, 대기 중인 연결이 취소되는 경우의 순서를 각각 그리세요.

제출물: 버전·현재 값·재생 상태·남은 대기자 수의 변화 표. 완료 기준: 같은 말단 항목의 중간 값이 보존되지 않을 수 있음을 설명하고, 종료된 재생 기록이 바뀌지 않으며 대기자와 타이머가 남지 않는 조건을 제시해야 합니다.

과제 5

새 컴퓨터에서도 완성되는 배포 지도 만들기 고급 · 2~3시간

읽을 곳: dsh-pet/tsdown.config.mjs, dsh-pet/scripts/prepare.js, dsh-pet/scripts/dist.mjs, dsh-pet/electron-builder.yml, .github/workflows/release-app.yml.

할 일: dsh-pet/lib/app.js, dsh-pet/runtime/electron-helper/shared-core.js, 타입 선언, WebM마다 생성 명령과 포함 규칙을 연결하세요. 생성 단계가 보이지 않는 자산에는 필요한 단계를 제안합니다.

제출물: “원본 → 생성 단계 → 파일 → 패키징 규칙” 표와 누락 검증 목록. 완료 기준: 미리 만들어진 로컬 파일이 없어도 모든 필수 자산에 생성 경로가 있어야 하며, 타입 검사 통과와 실행 자산 완전성을 별도로 확인해야 합니다.

9관련 기술 심화 학습 로드맵

주당 3~5시간을 가정한 여섯 주 순서

함수와 객체를 읽을 수 있다면 시작할 수 있습니다. async/await가 낯설다면 2주 차에 시간을 더 쓰세요. 화면을 먼저 꾸미기보다 계산·상태·입력 경계를 차례로 익히면, 환경이 달라져도 배운 설계를 재사용할 수 있습니다.

주차학습 주제·읽을 곳남길 결과물
1주TypeScript 객체·합집합·타입 좁히기
dsh-pet/src/shared/types.ts, dsh-pet/src/shared/pickers.ts
행동 선택 표와 잘못된 입력 예시 5개. 타입과 실행 시 검사의 차이 설명
2주Promise·비동기 반복·취소·React 수명
dsh-pet/src/client/pet.ts, dsh-pet/src/standalone/context.ts
응답 순서가 뒤집힌 시간표와 정리해야 할 자원 목록
3주입력 검증·설정 계층·파일 저장
dsh-pet/src/host/config.ts, dsh-pet/src/standalone/ai-storage.ts
부분 병합과 전체 교체 비교표, 저장 도중 실패할 때의 결과
4주상태 버전·장기 폴링·상태 기계
dsh-pet/src/host/state.ts, dsh-pet/src/host/playback.ts, dsh-pet/src/host/lock.ts
성공·실패·취소·만료를 포함한 순서도와 검증 사례
5주Electron 메인·렌더러·preload·좌표계
dsh-pet/runtime/electron-helper/, dsh-pet/src/shared/physics.ts
프로세스 책임 표, 화면 좌표와 속도 단위가 적힌 계산 노트
6주번들·자산·재현 가능한 배포
dsh-pet/tsdown.config.mjs, dsh-pet/scripts/prepare.js, 두 workflow
생성물의 의존 관계 지도와 앱·소재 배포의 차이 설명

9-1. 그다음에는 관심사별로 갈라진다

학습 순서를 정하는 비유

자동차를 배우며 처음부터 도색과 엔진과 도로망을 한꺼번에 보지 않는 것과 같습니다. 작은 함수로 “바퀴가 어떻게 도는지”를 알고, 상태로 “언제 움직이는지”를 배우고, 마지막에 실행 환경과 배포로 “다른 사람에게 어떻게 전달하는지”를 연결하세요.

10핵심 키워드 사전

처음 보는 용어를 이 저장소의 구체적인 장면에 붙여 기억한다
용어쉬운 설명
DSHDeepSeek Harness. 이 펫이 모델·세션·설정 화면 서비스를 받아 쓰는 호스트
Cordis서비스를 등록하고 필요한 서비스가 준비되면 플러그인을 연결하는 기반
의존성 주입함수가 서비스를 직접 만들지 않고 바깥에서 받는 것. apply(ctx)가 예
어댑터다른 환경을 같은 인터페이스로 맞추는 연결부. 독립 실행의 대체 컨텍스트
렌더러영상·말풍선·메뉴를 실제 화면에 그리는 부분
preloadElectron 화면 코드가 시작되기 전에 필요한 연결 기능을 준비하는 코드
IPC프로세스 간 통신. 창 이동 요청 등을 렌더러에서 메인으로 전달
클릭 통과위에 있는 투명 창이 입력을 받지 않고 아래 앱으로 보내는 동작
DIP / DPI화면 배율과 관련된 논리 좌표·픽셀 밀도. 다중 모니터에서는 좌표 단위를 맞춰야 함
순수 함수같은 입력에 같은 결과를 주며 외부 상태를 바꾸지 않는 계산. rollKind()가 예
타입 좁히기조건을 확인한 뒤 가능한 타입을 줄여 안전하게 필드를 읽는 것
검증 경계외부 입력이 내부 로직에 들어가기 전에 형태와 허용값을 확인하는 입구
JSONC주석을 허용하는 JSON 계열 설정 형식. 이 저장소는 전용 제거 로직으로 주석을 처리
장기 폴링변화가 없으면 응답을 잠시 기다리고, 변화나 제한 시간이 오면 돌려주는 조회
counter상태가 바뀌었는지 비교하는 번호. 현재 값 자체와 분리돼 있음
상태 기계허용된 상태와 이동 규칙의 집합. 재생의 수락·시작·종료·실패·취소
멱등성같은 요청이나 보고를 반복해도 결과가 불필요하게 누적되지 않는 성질
TTL정해진 유효 시간. 펫 제어 잠금은 expiresAt 이후 영원히 남지 않게 설계
더블 버퍼현재 영상을 보여 주는 동안 다른 영상 요소에 다음 장면을 준비하는 방식
alpha 채널픽셀의 투명도 정보. 영상 재생 가능 여부와 투명도 보존 여부는 별개
VP9-alpha투명도를 담은 VP9 영상. 기본 배포의 WebM 자산에 사용
HEVC-alphaApple 계열 재생 경로를 위해 별도로 만드는 투명 HEVC 영상
크로마키특정 배경색을 찾아 투명하게 만드는 처리. 여기서는 HSV 기반 녹색 판정
락 파일패키지의 실제 해결 버전을 기록하는 파일. 제어 권한을 뜻하는 잠금과는 다른 용어
IIFE정의하자마자 실행하는 함수. 브라우저 번들의 전역 이름 충돌을 줄이는 껍데기
shim없는 모듈의 필요한 인터페이스를 대신 제공하는 작은 구현
ASARElectron 앱의 파일을 묶는 보관 형식. 코드 보안을 보장하는 암호화는 아님
지수 백오프반복 실패할수록 재시도 간격을 늘려 실패가 연속 폭주하지 않게 하는 방식

이 용어들은 펫 앱에만 쓰이지 않습니다. 업로드 UI, 채팅 앱, 작업 상태 표시기, 작은 데스크톱 도구에서도 비동기 결과·설정·렌더링·종료 조건을 연결할 때 같은 개념을 사용합니다.

11참고 링크

계속 갱신되는 공식 문서와 특정 커밋의 소스를 구별해 읽는다

11-1. 공식 저장소와 코드의 출발점

11-2. 개념을 더 배우기 위한 공식 문서

공식 문서는 최신 버전 설명으로 바뀔 수 있습니다. 소스의 동작은 커밋 48007b6, 패키지 버전은 이 커밋의 선언과 잠금 파일을 기준으로 읽으세요.

현재 상태 (2026-10-23 GitHub 기준)

항목값과 해석
선정GitHub Trending TypeScript Weekly #12
스타 / 포크3,663 / 266 · 2026-10-23 GitHub 기준
생성일2026-08-14
주 언어TypeScript
최신 릴리스v0.3.8 · 2026-10-22
최근 push2026-10-23
최근 커밋48007b6 · 2026-10-23
제3자 메뉴의 표시 대상 지정 추가, 생략하면 양쪽 표시
코드 규모TypeScript 34,418줄 · JavaScript 계열 7,284줄 · Python 1,219줄
주석·빈 줄·테스트 포함, 커밋 기준 파일별 집계
내장 재생 소재WebM 106개 · 약 51.83 MiB. 소개용 GIF 106개는 약 64.28 MiB이며 npm 배포 목록에서 제외
라이선스MIT · LICENSE에 2026 PC2005-cloud 표기
호환성 선언DSH 0.2.0-rc.1, 0.2.0-rc.2, 0.2.1-rc.2는 compatible
0.1.7-rc.2는 incompatible
쓰기 전에 알아둘 한계
문서의 약속, 코드의 경로, 실행 결과는 구별해야 합니다

독립 AI: README와 일부 머리 주석은 독립 실행에서 대화가 불가능하다고 설명하지만, 이 커밋의 dsh-pet/src/standalone/cli.ts는 createStandaloneAi()를 주입합니다. dsh-pet/src/standalone/options.ts의 도움말도 API 설정 후 활성화를 안내합니다. 미설정 상태의 실패와 기능 자체의 부재를 구별하세요.

API 범위: README의 “페이지 내부 서비스로 제어”라는 취지의 개요보다 현재 코드의 범위가 좁습니다. dsh-pet/src/client/app.ts가 등록하는 petApi7340는 pose·progress 읽기 서비스입니다. 제어 요청은 HTTP API로 나뉘며, 독립 실행 HTTP 경계는 Host·Origin·토큰을 검사합니다. README의 “인증 없음”을 모든 실행 형태에 적용하지 마세요.

재생·배포: Safari용 MOV는 기본 npm 파일 목록에 없습니다. 소스에는 WebKit에서 MOV를 고르는 경로와 누락 시 WebM으로 되돌리는 경로가 있지만, 되돌리기가 투명도까지 보장하지는 않습니다. 앱 릴리스 workflow의 공유 코어 생성 단계도 실제 배포 자산과 함께 점검해야 합니다.

자료의 범위: 이 글은 해당 커밋의 정적 코드에 근거합니다. OS별 실제 동작, 성능, 릴리스 ZIP의 완전성을 보장하는 실행 검증 자료는 아닙니다. 자동 대화는 외부 API 비용과 문맥 전송을 수반할 수 있고, 대화 기록·독립 AI 키는 로컬 파일로 남습니다. 녹색 배경 영상과 수동 보정까지 필요한 소재 제작을 완전 자동 생성으로 이해하지 마세요.