업데이트로 돌아가기
New releaseSep 4, 2026

sonar v0.4.1

localhost 포트에서 수신 중인 서비스를 검사하고 관리하기 위한 CLI 도구

공유
██████    ███    ██████    ██    ██████
██           ██      ██  ██  ██      ██
██      ██   ██  ██  ██  ██  ██      ██
  ██  ██   ██  ██  ██  ██  ██  ████
    ██  ██       ██  ██  ██  ██  ██  ██
██████    ███    ██  ██  ██  ██  ██  ██

여러분의 머신에서 무엇이 실행 중인지 파악하세요.

Sonar는 localhost에서 수신 대기 중인 모든 것을 보여주고 정리합니다: 모든 포트는 그룹에 속하며 — 일반적으로 해당 포트가 시작된 저장소 — 그 그룹 내에서는 이름이 지정된 서비스에 속합니다. sonar start로 개발 서버를 시작하면 프로젝트 전체가 하나의 트리로 나열하고, 대기하고, tail하고, 단일 명령으로 중지할 수 있는 하나의 대상이 됩니다. Docker 컨테이너, Compose 프로젝트, 직접 시작한 프로세스도 별도 설정 없이 함께 감지됩니다.

$ sonar list --tree
my-app  (3 ports, running)                        ~/code/my-app
├─ 5432  db          postgres:17                  http://localhost:5432
├─ 5173  frontend    vite (v5.4)                  http://localhost:5173
└─ 8000  api         uvicorn app:app              http://localhost:8000
ungrouped (1 port)
└─ 3000  next-server (v16.1.6)                    http://localhost:3000

설치

Homebrew (macOS / Linux)

brew install raskrebs/sonar/sonar

Homebrew 6은 신뢰할 수 있는 탭으로 한 번 신뢰할 때까지 서드파티 탭의 포뮬러를 거부합니다 (Error: Refusing to load formula raskrebs/sonar/sonar from untrusted tap):

brew trust raskrebs/sonar

설치 스크립트

curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | bash

최신 바이너리를 ~/.local/bin에 다운로드하고 필요하면 PATH에 추가합니다. 터미널을 재시작하거나 source ~/.zshrc를 실행하세요.

Windows(PowerShell)에서는:

irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex

사용자 지정 설치 위치:

curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_INSTALL_DIR=/usr/local/bin bash

특정 버전 설치:

curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_VERSION=vX.Y.Z bash
$env:SONAR_VERSION="vX.Y.Z"; irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex

Go 사용하기

go install github.com/raskrebs/sonar@latest

셸 자동 완성(탭으로 포트 번호 자동 완성):

sonar completion zsh > "${fpath[1]}/_sonar"   # zsh
sonar completion bash > /etc/bash_completion.d/sonar  # bash
sonar completion fish | source                 # fish

60초

저장소 루트의 dev.sh 대신 sonar.yaml에 프로젝트가 어떻게 실행되는지 설명하세요:

name: my-app
services:
  - name: db
    cmd: docker compose up db
    port: 5432
  - name: api
    cmd: uv run uvicorn app:app --port ${port}
    port: auto
    depends_on: [db]
  - name: frontend
    cmd: npm run dev -- --port ${port} --strictPort
    port: auto
    depends_on: [api]
    env:
      VITE_API_URL: ${api.url}

그런 다음 시작하세요:

sonar start
  ✓ db        http://localhost:5432   pid 41022  ~/.config/sonar/logs/my-app/db.log
  ✓ api       http://localhost:21408  pid 41040  ~/.config/sonar/logs/my-app/api.log
  ✓ frontend  http://localhost:21409  pid 41077  ~/.config/sonar/logs/my-app/frontend.log

3 started
following the logs; Ctrl+C stops the 3 services started here
api      | INFO:     Uvicorn running on http://127.0.0.1:21408
frontend | VITE v5.4  ready in 312 ms

Sonar는 모든 port: auto 서비스에 대해 사용 가능한 포트를 선택하고 각 서비스에 다른 서비스의 위치를 알려주므로, 두 번째 worktree가 첫 번째 옆에서 포트 충돌 없이 실행됩니다. sonar start -d는 백그라운드에서 동일한 작업을 수행하며, sonar down은 이를 중지하고 포트를 반환합니다. 다른 터미널에서:

sonar list --tree
my-app  (3 ports, running)                        ~/code/my-app
├─ 5432   db          postgres:17                 http://localhost:5432
├─ 21408  api         uvicorn app:app             http://localhost:21408
└─ 21409  frontend    vite (v5.4)                 http://localhost:21409

sonar.yaml이 없나요? sonar start -- npm run dev는 하나의 명령을 이름이 지정된 서비스로 실행하며, sonar list와 sonar kill은 수신 대기 중인 모든 항목에 대해 작동합니다.

아래에서 # check로 표시된 예제는 매 CI 실행 시 scripts/readme-check.sh에 의해 새 빌드에 대해 실행됩니다.

명령어

sonar list

sonar list
sonar list --tree
sonar list --group my-app
sonar list --json
# check
sonar list --stats             # CPU, memory, threads, uptime, state
sonar list --health            # HTTP health checks
sonar list --filter docker     # only Docker ports
sonar list --sort name         # port | pid | name | type
sonar list -a                  # include desktop apps
sonar list -c port,process,group,cpu,mem
sonar list --host user@server  # scan a remote machine over SSH

기본 열은 port, process, group, container, image, containerport, url이며, 여기서 process는 포트에 지정한 이름 (sonar rename)을, 그다음 서비스 이름을, 그다음 감지된 항목을 표시합니다.

사용 가능한 열: port, process, pid, type, url, group, cpu, mem, threads, uptime, state, connections, health, latency, container, image, containerport, compose, project, user, bind, ip.

우연히 리스닝 중인 데스크톱 앱과 시스템 서비스 — Figma, Discord, Spotify, ControlCenter, macOS .app 번들, /System/Library/ 데몬 — 는 -a를 전달하지 않는 한 숨겨집니다.

sonar start

sonar.yaml에서 프로젝트를 시작합니다:

sonar start                    # every service in the nearest sonar.yaml
sonar start api frontend       # only these (they still wait for their dependencies)
sonar start ../other-project   # the sonar.yaml in or above another directory
sonar start -d                 # in the background, like `sonar up`

포그라운드에서 sonar는 데몬을 통해 서비스를 시작하고, 모든 줄 앞에 서비스 이름을 붙여 로그를 따라가며, Ctrl+C를 누르면 자신이 시작한 서비스를 중지합니다. 이미 실행 중이던 서비스는 그대로 두고, sonar가 시작한 모든 서비스가 종료되면 sonar도 스스로 종료됩니다.

-- 앞의 인수는 디렉터리나 서비스를 지정하며, -- 뒤의 모든 것은 명령입니다. 또는 하나의 명령을 그룹 내 이름 있는 서비스로 실행할 수도 있습니다:

sonar start -- npm run dev
sonar start --group my-app --name frontend -- npm run dev
sonar start --port 5173 -- npm run dev        # expected port, before it binds
sonar start --detach --name api -- uv run uvicorn app:app
sonar start --list

아무것도 전달할 필요가 없습니다:

  • Group — --group, 그렇지 않으면 가장 가까운 sonar.yaml의 name, 그렇지 않으면 git 루트의 디렉터리 이름(워크트리는 repo@worktree가 됨), 그렇지 않으면 현재 디렉터리의 이름.
  • Name — --name, 그렇지 않으면 cmd가 일치하는 sonar.yaml 서비스, 그렇지 않으면 명령에서 추론됨(npm run dev → dev, uv run api → api, python -m uvicorn → uvicorn, ./dev.sh → dev.sh).
  • Port — --port는 힌트일 뿐 바인딩이 아닙니다: 포트가 실제로 리스닝될 때까지 실행은 starting으로 표시되며, 데몬은 이를 사용해 프로세스를 포트에 매칭합니다.

자식 프로세스는 stdin, stdout, stderr, cwd 및 환경을 상속받고, 추가로 SONAR_GROUP, SONAR_NAME, SONAR_RUN_ID를 받습니다. 자체 프로세스 그룹을 가지므로 sonar kill이 전체 트리 — 워처와 워커를 포함한 개발 서버 — 를 종료시킵니다. Ctrl+C는 전달되며, sonar는 자식의 종료 코드로 종료됩니다.

--detach는 즉시 반환하고 출력을 ~/.config/sonar/logs/<group>/<name>.log에 기록합니다. --list는 sonar가 시작한 것과 그 아래에 이후 종료된 것 — 종료 코드, 그리고 크래시했는지 중지되었는지와 함께 — 을 보여줍니다(기계 판독 가능한 형식은 --json):

sonar start --list
sonar start --detach --name demo --port 8123 -- sleep 5
sonar start --list --json
# check

sonar.yaml

프로젝트는 저장소 루트의 sonar.yaml에서 자신과 서비스를 명명합니다. 이 파일은 선택 사항이며 — sonar는 없어도 git 루트를 기준으로 그룹화합니다 — 커밋하는 것을 의도한 것입니다:

name: my-app
services:
  - name: db
    cmd: docker compose up db
    port: 5432
    health: /
    description: Postgres 17
    icon: database
    color: "#4f8cc9"
  - name: api
    cmd: uv run uvicorn app:app --port 8000
    cwd: backend
    port: 8000
    health: /healthz
    depends_on: [db]
  - name: frontend
    cmd: npm run dev
    port: 5173
    depends_on: [api]
ports: [9229]        # ports that belong to this project without a service
  • name — 그룹 이름. 슬래시 없음, 공백 없음.
  • cmd, cwd, port — sonar start가 서비스를 시작하는 방식. cwd는 파일 기준 상대 경로이며 해당 디렉터리를 벗어날 수 없다.
  • health — 서비스가 실행 중일 때 데몬이 폴링하는 HTTP 경로로, 서비스가 실행 중이지만 아직 정상이 아닐 수 있다. ok, fail 또는 unknown을 보고하며, 실패 시 그 이유를 함께 보고한다.
  • description, icon, color — 데스크톱 앱을 위한 자유 형식 메타데이터로, sonar는 이를 절대 추론하지 않는다.
  • depends_on — 시작 순서. 파일에 없는 서비스를 지정하거나 순환이 있으면 오류이다. 유효하지 않은 파일은 한 번 보고되며 스캔을 중단시키지 않는다.
  • env — sonar가 서비스를 시작할 때 해당 서비스에 설정되는 변수.

port: auto와 참조

서비스는 포트를 sonar에 맡길 수 있으며, 하드코딩된 localhost:8080 대신 환경 변수를 통해 다른 서비스를 찾을 수 있다:

카테고리