Local Development

이 문서는 local machine에서 source-repo 예제를 실행하기 위한 개발 환경 기준입니다. 목표는 CI와 최대한 같은 도구와 명령을 local에서도 사용하게 만드는 것입니다.

처음 설정하는 경우에는 ## 4. 처음 설치## 5. 환경 점검을 먼저 따라가고, 이미 환경이 있는 경우에는 make doctor/demo로 현재 상태를 확인한 뒤 필요한 부분만 보완합니다.

1. 문서의 목적

1-1. Local 개발에서 맞춰야 하는 것

이 repo는 TypeScript, Python, Rust, Go, C/Firmware, Docker Compose가 함께 있는 polyglot monorepo입니다. 모든 언어를 하나의 공통 manager로 억지로 감싸지 않고, 각 생태계에서 자연스럽게 쓰이는 도구를 우선 사용합니다.

Local 개발에서 맞춰야 하는 것은 세 가지입니다.

  1. Runtime version이 repo 기준과 맞아야 합니다.
  2. Dependency install이 lockfile 기준으로 재현 가능해야 합니다.
  3. Demo와 test가 CI와 같은 entrypoint로 실행되어야 합니다.

Makefile은 이 세 가지를 하나의 command surface로 모아 줍니다. 직접 언어별 command를 실행해도 되지만, repo 전체 흐름을 확인할 때는 make doctor/*, make demo/*, make package/*를 먼저 사용합니다.

1-2. 표준 선택 원칙

이 문서에서 말하는 표준은 한 종류가 아닙니다.

Category Meaning Local example
Industry de facto 해당 생태계에서 널리 쓰이는 기본 선택 nvm, rustup, cargo, go, cmake, Docker Compose
Team standard Tirosh repo 간 반복성과 CI parity를 위해 고정한 선택 uv, Corepack-managed pnpm, root Makefile target
Product-specific 이 SaMD demo의 실행 topology 때문에 필요한 선택 PostgreSQL/Redis Compose demo, Arm firmware toolchain, Zephyr west

따라서 새 도구를 추가할 때는 먼저 생태계 기본값으로 해결할 수 있는지 보고, 반복성과 CI parity가 필요한 부분만 team standard로 고정합니다.

1-3. 실행 위치

아래 명령은 별도 표기가 없으면 source-repo root에서 실행합니다.

cd source-repo

상위 repo root에서도 일부 Make target은 passthrough로 동작하지만, language workspace command는 source-repo root에서 실행하는 편이 가장 명확합니다. 예를 들어 .nvmrcsource-repo/.nvmrc에 있으므로, 상위 tirosh-dev-guide root에서 nvm install을 실행하면 No .nvmrc file found가 나옵니다.

2. Foundation toolchain

2-1. Foundation toolchain이란

Foundation toolchain은 언어별 package나 app을 다루기 전에 shell에서 먼저 준비되어야 하는 도구입니다. 이 도구들이 맞아야 pnpm install, uv sync, cargo test, go test, Docker Compose demo가 같은 기준으로 실행됩니다.

Foundation toolchain은 설치 방법 자체보다 “repo가 기대하는 version과 command가 현재 shell에서 실행되는가”를 기준으로 봅니다. 최종 판정은 make doctor/*가 담당합니다.

2-2. Version manager와 runtime

이 묶음은 각 언어의 runtime version을 repo 기준으로 맞추는 역할을 합니다. Runtime version이 맞지 않으면 lockfile install, native extension build, test 결과가 CI와 달라질 수 있습니다.

Tool Role Repo standard Verify
nvm Node.js version manager .nvmrc를 읽어 Node 24를 활성화합니다. nvm current
Node.js TypeScript/React runtime .nvmrc의 major version과 맞아야 합니다. node --version
uv-managed Python Repo 기준 Python interpreter uv python install 3.12로 설치한 Python을 사용합니다. uv python find --managed-python 3.12
rustup Rust toolchain manager rust-toolchain.toml의 toolchain을 활성화합니다. rustup show
Go Go runtime/toolchain go.work와 module go.mod 기준 version을 사용합니다. go version

nvm은 Homebrew로 설치할 수는 있지만, upstream nvm project가 Homebrew 설치를 support path로 보지 않습니다. 그래서 guide의 기본 경로는 install script 또는 git install로 두고, 실제 검증은 nvm currentnode --version으로 합니다.

Python은 uv가 설치하고 관리하는 interpreter를 기준으로 둡니다. conda, OS 기본 Python, Homebrew Python이 PATH에 있어도 repo 표준 Python으로 보지 않습니다.

nvm install
nvm use
uv python install 3.12
rustup show
go version

2-3. Package manager와 workspace runner

이 묶음은 dependency install, workspace command, build/test command를 실행합니다. Runtime을 고정한 뒤 lockfile과 workspace metadata를 기준으로 사용합니다.

Tool Role Repo standard Verify
Corepack pnpm version activation Node.js에 포함된 Corepack을 사용합니다. corepack --version
pnpm TypeScript workspace package manager root package.jsonpackageManager field와 pnpm-lock.yaml을 따릅니다. pnpm --version
uv Python dependency/workspace runner uv.lock, pyproject.toml, uv-managed Python을 함께 사용합니다. uv --version
Cargo Rust package/build/test command rustup-managed Rust toolchain에 포함된 Cargo를 사용합니다. cargo --version
go command Go module/workspace command go.work와 module go.mod를 기준으로 실행합니다. go env GOWORK

Node.js와 pnpm은 역할이 다릅니다. Node.js는 JavaScript/TypeScript 코드를 실행하는 runtime이고, pnpm은 package.jsonpnpm-lock.yaml을 기준으로 dependency를 설치하고 workspace script를 실행하는 package manager입니다.

pnpm은 Node.js에 포함된 Corepack으로 활성화합니다. 실제 pnpm version은 root package.jsonpackageManager field와 pnpm-lock.yaml로 고정합니다. 따라서 문서 명령에 pnpm version을 반복해서 적지 않고, source-repo root에서 Corepack이 packageManager 값을 읽게 둡니다.

이 repo의 Python command는 가능한 한 아래 형태를 사용합니다.

uv run --managed-python ...
uv sync --managed-python --locked

--managed-python은 uv-managed Python만 사용하겠다는 선언입니다. uv는 기본적으로 uv-managed Python을 선호하지만, 필요한 managed Python이 없으면 system Python을 사용할 수 있습니다. 이 repo는 conda, OS Python, Homebrew Python으로 우연히 실행되는 상황을 피하려고 문서와 Make target에서 --managed-python을 명시합니다.

이미 uv python install 3.12make doctor/python을 통과한 local shell에서는 interactive command에서 생략해도 대개 같은 Python을 사용합니다. 다만 guide, Makefile, CI command에는 repo 표준을 강제하기 위해 계속 명시합니다.

corepack enable
pnpm install --frozen-lockfile
uv sync --managed-python --all-packages --locked
cargo --version
go work sync

2-4. Container runtime

Local storage-backed demo는 PostgreSQL과 Redis를 Docker container로 띄웁니다. doctor/container는 Docker CLI, Compose plugin 또는 docker-compose, 그리고 daemon 연결 상태를 모두 확인합니다.

Docker Desktop, OrbStack, Colima처럼 Docker-compatible CLI와 daemon을 제공하는 runtime이면 됩니다. 중요한 것은 docker infodocker compose version이 local shell에서 통과하는 것입니다.

docker info
docker compose version

2-5. 설치 경로 선택 기준

Homebrew, MacPorts, apt, winget 같은 package manager로 설치 가능한 도구가 많습니다. 이 문서는 package manager 사용을 금지하지 않습니다. 다만 사내 guide의 기본 경로는 upstream이 직접 안내하거나 CI와 같은 version pinning을 만들기 쉬운 방법을 우선합니다.

Tool Primary path Package manager note
nvm https://github.com/nvm-sh/nvm install script 또는 git install Homebrew formula는 있지만 upstream support path가 아니므로 기본 안내로 두지 않습니다.
Node.js .nvmrc의 Node 24를 nvm으로 활성화 직접 installer나 package manager로 설치해도 되지만 .nvmrc와 맞아야 합니다.
pnpm Corepack이 root package.jsonpackageManager 값을 읽어 활성화 Homebrew/npm 설치도 가능하지만 repo는 Corepack과 packageManager pin을 기준으로 둡니다.
uv https://docs.astral.sh/uv/getting-started/installation/ standalone installer Homebrew도 공식 설치 방법 중 하나입니다. 사내 guide는 standalone installer를 기본 예시로 둡니다.
rustup https://rustup.rs install script package manager의 Rust보다 rustup-managed toolchain을 우선합니다.
Go https://go.dev/dl/ official installer 또는 runner provisioning package manager 설치도 가능하지만 go.work 기준 version과 맞아야 합니다.
Docker-compatible runtime Docker Desktop, OrbStack, Colima 중 하나 어떤 runtime이든 docker info와 Compose가 통과하면 됩니다.

3. 생태계별 도구 지도

3-1. Toolchain map

Foundation toolchain을 준비한 뒤에는 각 생태계별 command를 사용합니다. Makefile은 이 command들을 CI와 비슷한 순서로 묶어 주는 entrypoint입니다.

Ecosystem Tooling Standard category Why
TypeScript / React Node.js, nvm, Corepack, pnpm, Vite, Vitest, Storybook Industry de facto/team standard frontend workspace, package build, UI test, Storybook을 다룹니다.
Python uv, uv-managed Python, pytest, MkDocs, mkdocstrings Team standard on top of Python practice Python version, dependency sync, test, docs command를 일관되게 다룹니다.
Rust rustup, Cargo, rustfmt, clippy, rustdoc Industry de facto signal preprocessing core와 PyO3 package를 Rust 생태계 방식으로 검증합니다.
Go Go toolchain, go work, controller-runtime style Industry de facto operator/controller와 platform automation 예제를 Go 방식으로 표현합니다.
C package CMake, Ninja, CTest, Conan 2 Industry de facto/team standard board-independent C core를 testable library와 package artifact로 관리합니다.
Firmware Arm GNU Toolchain, objcopy, CMake presets Industry de facto/product-specific .elf, .hex, .bin, .map 산출물을 만들고 firmware release evidence를 남깁니다.
Zephyr west, CMake, Ninja, host/native_sim toolchain Industry de facto/product-specific RTOS app의 board/RTOS boundary를 local simulation으로 확인합니다.
Container Docker-compatible CLI, Docker BuildKit, Docker Compose Industry de facto/team standard app image build와 local storage-backed demo topology를 검증합니다.

3-2. 확장 toolchain

아래 도구들은 전체 demo의 첫 실행에는 필요하지 않지만, 해당 영역을 개발할 때는 필요합니다.

Area Tool How to install or activate Verify
C package CMake https://cmake.org/download/ official installer 또는 runner provisioning cmake --version
C package Ninja https://ninja-build.org/ official binary 또는 runner provisioning ninja --version
C package Conan 2 uv tool install conan 또는 pipx install conan conan --version
Firmware Arm GNU Toolchain https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads arm-none-eabi-gcc --version
Zephyr west uv tool install west 또는 pipx install west west --version

4. 처음 설치

4-1. 필수 runtime 설치

처음 clone한 뒤에는 Node, Python, Rust runtime을 먼저 맞춥니다. Go app이나 Go package를 다룰 예정이면 go version까지 확인합니다.

nvm install
nvm use
uv python install 3.12
rustup show
go version

rustup show는 설치와 active toolchain 확인을 겸합니다. rust-toolchain.toml이 있으므로 Cargo command를 실행하면 필요한 toolchain을 rustup이 확인합니다.

4-2. Workspace dependency 설치

Runtime을 맞춘 뒤에는 lockfile 기준으로 dependency를 설치합니다.

corepack enable
pnpm install --frozen-lockfile
uv sync --managed-python --all-packages --locked
go work sync

Go를 아직 다루지 않는다면 go work sync는 나중에 실행해도 됩니다. Demo smoke와 docs만 볼 때는 Node, pnpm, uv, Rust, Docker가 먼저 필요합니다.

4-3. Nexus 인증

Nexus 인증이 필요한 환경에서는 dependency install 전에 Nexus 문서의 local 설정을 적용합니다.

CI는 local helper를 사용하지 않습니다. GitHub Actions workflow와 composite action이 npm, uv, Cargo, Conan credential을 job 안에서 주입합니다.

5. 환경 점검

5-1. Doctor 먼저 실행하기

설치가 끝났거나, 이미 설치된 도구가 표준에 맞는지 확인하려면 doctor target을 실행합니다.

make doctor/demo

doctor/demo는 local demo에 필요한 최소 묶음을 확인합니다.

  • TypeScript runtime: node, corepack, pnpm
  • Python runtime: uv, uv-managed Python
  • Rust runtime: rustup, cargo
  • Container runtime: Docker CLI, Compose, daemon

5-2. Doctor target 목록

전체 repo toolchain을 확인하려면 make doctor를 사용합니다.

make doctor
make doctor/typescript
make doctor/python
make doctor/rust
make doctor/go
make doctor/c
make doctor/firmware
make doctor/zephyr
make doctor/container
Target Required tools Purpose
doctor/demo node, corepack, pnpm, uv, uv-managed Python, rustup, cargo, docker, Docker Compose, Docker daemon local demo 실행 준비 확인
doctor/typescript node, corepack, pnpm React/TypeScript workspace install, test, build, package
doctor/python uv, uv-managed Python Python version, workspace sync, test, build, docs
doctor/rust rustup, cargo Rust toolchain pin, test, package, publish
doctor/go go Go workspace, module test, module publish validation
doctor/c cmake, ninja, ctest, cc, conan host C package build, test, package
doctor/firmware cmake, ninja, arm-none-eabi-gcc, arm-none-eabi-objcopy bare-metal firmware image build
doctor/zephyr west, cmake, ninja Zephyr RTOS workspace and build
doctor/container docker, Docker Compose, Docker daemon container image build/push and local Compose topology

5-3. Doctor 결과 읽기

Doctor는 설치를 자동으로 수행하지 않습니다. 개발자 workstation, self-hosted runner, firmware build machine의 OS와 권한이 다르기 때문입니다.

출력은 세 가지로 읽습니다.

Output Meaning Next action
ok 현재 shell에서 도구를 찾았고 version 확인을 통과했습니다. 다음 target 또는 demo command로 진행합니다.
missing 현재 shell에서 도구를 찾지 못했습니다. 출력된 install hint에 따라 설치합니다.
version 도구는 있지만 repo 기준 version과 다릅니다. .nvmrc, .python-version, rust-toolchain.toml, go.work 기준으로 맞춥니다.

예를 들어 Node가 v26.x인데 .nvmrc24라면 doctor/typescript는 실패합니다. 이 경우 nvm install && nvm use로 repo 기준 runtime을 활성화합니다.

6. Demo 실행

6-1. 빠른 smoke

가장 작은 end-to-end 확인은 아래 명령입니다.

make demo/check

이 명령은 local 임시 포트를 사용합니다. 별도 서버를 미리 띄울 필요는 없습니다.

흐름은 아래와 같습니다.

signal fixture
  -> signal-device-simulator
  -> patient-risk-python-api
  -> monitoring snapshot and review queue
  -> patient-monitor-ui-core view model
  -> patient-monitor-react-components render
  -> alarm acknowledge/mute workflow

6-2. Storage-backed smoke

PostgreSQL/Redis adapter까지 확인하려면 storage-backed smoke를 사용합니다.

make demo/check/storage

이 target은 PostgreSQL과 Redis container를 띄우고, migration을 적용한 뒤, 같은 demo smoke를 PATIENT_RISK_STORAGE_BACKEND=postgres-redis로 실행합니다. 완료되면 container와 demo volume을 정리합니다.

기본 local URL은 아래와 같습니다.

Store URL
PostgreSQL postgresql+psycopg://patient_risk:patient_risk@127.0.0.1:55432/patient_risk
Redis redis://127.0.0.1:56379/0

저장소를 계속 띄워 둔 채 API나 adapter를 직접 확인하려면 아래 target을 사용합니다.

make storage/up
make storage/down

6-3. Compose demo

브라우저에서 직접 확인하려면 Compose demo를 띄웁니다.

make demo/run

make demo/run은 API, Web, PostgreSQL, Redis를 Docker Compose로 띄우고, API container 안에서 Alembic migration을 적용한 뒤 signal-device-simulatorlow-spo2-trend signal packet을 한 번 전송합니다.

Compose demo는 developer workstation의 OS가 아니라 Docker daemon의 Linux platform을 기준으로 API image를 만듭니다. Rust Python package처럼 native wheel이 필요한 경우 local macOS wheel을 Linux container에 복사하지 않고, Docker build stage 안에서 현재 Docker platform에 맞는 Linux wheel을 만듭니다. Release용 multi-platform wheel과 image 기준은 CI/CDContainers 문서에서 다룹니다.

Service URL
Python API http://127.0.0.1:39080
Web UI http://127.0.0.1:39081

종료와 로그 확인은 아래 명령을 사용합니다.

make demo/logs
make demo/stop

make demo/stop은 Compose container를 내리지만 demo volume은 보존합니다. Migration을 적용하기 전 실패한 실행처럼 local database schema가 중간 상태로 남았다면 volume까지 지우는 reset target을 사용합니다.

make demo/reset

6-4. Port와 uv 인자 조정

로컬에서 39080 또는 39081이 이미 사용 중이면 API/Web port를 함께 바꿉니다.

make demo/run \
  DEMO_API_PORT=39082 \
  DEMO_WEB_PORT=39083 \
  DEMO_API_URL=http://127.0.0.1:39082 \
  DEMO_WEB_URL=http://127.0.0.1:39083

make demo/checkmake demo/check/storagesource-repo/pyproject.toml의 Nexus PyPI index를 사용합니다. 로컬에서는 .env.example.env로 복사한 뒤 Nexus credential을 채웁니다. make target은 .env가 있으면 자동으로 읽고, NEXUS_USERNAME/NEXUS_PASSWORD를 uv가 요구하는 UV_INDEX_TIROSH_PYPI_USERNAME/UV_INDEX_TIROSH_PYPI_PASSWORD로 넘깁니다.

cp .env.example .env
# edit .env
make demo/check

.env를 쓰지 않는 임시 shell에서는 아래처럼 export해도 됩니다.

export NEXUS_USERNAME=github-actions
export NEXUS_PASSWORD=...
make demo/check

공개 PyPI override는 lockfile 기준과 달라 uv.lock을 PyPI URL로 다시 쓰게 만들 수 있습니다. 임시 local 확인에만 사용하고, uv.lock 변경은 커밋하지 않습니다.

7. 언어별 개발 루프

7-1. TypeScript / React

corepack enable
pnpm install --frozen-lockfile
pnpm test
pnpm build
pnpm dev:web
pnpm docs:ts

pnpm dev:web은 web app 개발 서버를 띄우기 전에 필요한 workspace package build를 수행합니다. UI package가 patient-monitor-ui-corepatient-monitor-react-components로 나뉘어 있으므로, web app만 바로 실행하는 것보다 root script를 쓰는 편이 안전합니다.

7-2. Python

uv sync --managed-python --all-packages --locked
uv run --managed-python --package patient-risk-python-api pytest
uv run --managed-python --package patient-risk-python-api python -m patient_risk_python_api

Python package나 API command는 shell의 python에 의존하지 않습니다. uv run --managed-python을 통해 repo 기준 Python으로 실행합니다.

7-3. Rust

rustup show
cargo test --locked
cargo doc --workspace --open

Rust/PyO3 package를 Python API test와 함께 확인하려면 Make target을 사용합니다.

make package/test/python-rust

7-4. Go

go work sync
go test ./...
go run ./apps/risk-monitor-operator manifests/risk-monitor.demo.json
pkgsite

Go module download가 사내 proxy를 사용해야 하는 환경에서는 GOPROXYGONOSUMDB 설정을 Nexus 문서와 CI workflow 기준으로 맞춥니다.

7-5. C package

cmake --preset host-debug -S packages/patient-signal-c-core
cmake --build packages/patient-signal-c-core/build/host-debug
ctest --test-dir packages/patient-signal-c-core/build/host-debug --output-on-failure

C package는 board-independent core를 먼저 host에서 검증합니다. Firmware app은 이 package를 Conan package boundary로 consume하는 흐름을 보여줍니다.

7-6. Firmware와 Zephyr

Bare-metal firmware build는 Arm GNU Toolchain이 필요합니다.

cmake --preset arm-none-eabi-debug -S apps/wearable-firmware
cmake --build apps/wearable-firmware/build/arm-none-eabi-debug

Zephyr native_sim은 host gcc로 simulation binary를 만들지만, toolchain variant를 명시하지 않으면 Zephyr SDK를 찾습니다.

cd apps
west init -l wearable-zephyr-firmware
west update
west zephyr-export
west build --pristine=always -b native_sim wearable-zephyr-firmware -- -DZEPHYR_TOOLCHAIN_VARIANT=host

이 repo는 self-hosted runner와 local host simulation 모두에서 -DZEPHYR_TOOLCHAIN_VARIANT=host를 명시하고, stale CMake cache를 피하기 위해 --pristine=always를 사용합니다.

8. 문서 실행

8-1. MkDocs serve와 strict build

MkDocs는 docs/ 아래의 팀 가이드를 렌더링합니다. 각 app/package의 상세 사용법은 해당 디렉터리의 README.md를 봅니다.

make docs/serve
make docs/check

make docs/check는 contract validation과 MkDocs strict build를 함께 실행합니다. 문서 링크, nav, code reference가 깨졌는지 확인할 때 사용합니다.

8-2. 문서 command의 Python 기준

문서 command도 uv-managed Python으로 실행됩니다.

uv run --managed-python mkdocs serve
uv run --managed-python mkdocs build --strict

Nexus PyPI credential/proxy 경로를 확인해야 할 때는 DOCS_UV_RUN_ARGS=처럼 Make variable을 비워 실행합니다.

make docs/check DOCS_UV_RUN_ARGS=