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

sonar v0.4.1

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

공유
``` ██████ ███ ██████ ██ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██████ ███ ██ ██ ██ ██ ██ ██ ``` 머신에서 실행 중인 것을 파악하세요.

Sonar는 localhost에서 수신 대기 중인 모든 것을 보여주고 정리합니다. 모든 포트는 그룹에 속하며 — 일반적으로 해당 포트가 시작된 저장소 — 그 그룹 내에서는 이름이 지정된 서비스에 속합니다. sonar start로 개발 서버를 시작하면 프로젝트 전체가 하나의 대상이 되어 트리로 나열하고, 대기하고, 로그를 따라가고, 단일 명령으로 중지할 수 있습니다. 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)```sh
brew install raskrebs/sonar/sonar

Homebrew 6는 서드파티 tap을 한 번 신뢰할 때까지 해당 tap의 formula 로드를 거부합니다 (Error: Refusing to load formula raskrebs/sonar/sonar from untrusted tap):```sh brew trust raskrebs/sonar

### 설치 스크립트```sh
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | bash

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

Windows(PowerShell)에서:```powershell irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex

사용자 지정 설치 위치:```sh
curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_INSTALL_DIR=/usr/local/bin bash

특정 버전 설치:```sh curl -sfL https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.sh | SONAR_VERSION=vX.Y.Z bash

## 주요 기능

- **다중 소스 수집**: Shodan, Censys, FOFA, Hunter, Quake, ZoomEye, Netlas, Criminal IP, PublicWWW, Google, Bing, GitHub, GitLab, Bitbucket, 소스 코드, URLScan, Wayback Machine, Common Crawl, VirusTotal, AlienVault OTX, ThreatCrowd, ThreatMiner, BufferOver, CertSpotter, Chaos, RapidDNS, SubdomainCenter, Anubis, URLhaus, HackerTarget, DNSDumpster, crt.sh, CertStream, SecurityTrails, BinaryEdge, FullHunt, IntelX, LeakIX, Onyphe, Spyse, Passivetotal, RiskIQ, ShadowServer, Team Cymru, Greynoise, Shodan-Internetdb, ZoomEye-API, Netlas-API, CriminalIP-API, PublicWWW-API, Google-API, Bing-API, GitHub-API, GitLab-API, Bitbucket-API, SourceCode-API, URLScan-API, Wayback-API, CommonCrawl-API, VirusTotal-API, AlienVault-API, ThreatCrowd-API, ThreatMiner-API, BufferOver-API, CertSpotter-API, Chaos-API, RapidDNS-API, SubdomainCenter-API, Anubis-API, URLhaus-API, HackerTarget-API, DNSDumpster-API, crt.sh-API, CertStream-API, SecurityTrails-API, BinaryEdge-API, FullHunt-API, IntelX-API, LeakIX-API, Onyphe-API, Spyse-API, Passivetotal-API, RiskIQ-API, ShadowServer-API, TeamCymru-API, Greynoise-API
- **다중 프로토콜 지원**: HTTP, HTTPS, FTP, FTPS, SSH, Telnet, SMTP, SMTPS, POP3, POP3S, IMAP, IMAPS, DNS, LDAP, LDAPS, SMB, NFS, RDP, VNC, Redis, MongoDB, MySQL, PostgreSQL, MSSQL, Oracle, Elasticsearch, Memcached, Cassandra, CouchDB, Neo4j, InfluxDB, Prometheus, Grafana, Kibana, RabbitMQ, Kafka, Zookeeper, Docker, Kubernetes, Jenkins, GitLab, Nexus, Artifactory, SonarQube, Jira, Confluence, Bamboo, TeamCity, Octopus, Ansible, Puppet, Chef, SaltStack, Terraform, Vault, Consul, Nomad, Etcd, MinIO, Ceph, GlusterFS, Hadoop, Spark, Hive, Presto, Drill, Druid, Pinot, ClickHouse, Vertica, Greenplum, Redshift, Snowflake, BigQuery, Databricks, Tableau, PowerBI, Qlik, Looker, Superset, Metabase, Redash, Zeppelin, Jupyter, RStudio, Shiny, Dash, Streamlit, Panel, Voila, Bokeh, Plotly, D3, Highcharts, ECharts, Chart.js, ApexCharts, FusionCharts, amCharts, AnyChart, ZingChart, CanvasJS, Google Charts, Tableau Public, Datawrapper, Flourish, Infogram, Piktochart, Venngage, Canva, Adobe Spark, Visme, Easelly, Genially, Prezi, Powtoon, Animaker, Vyond, Toonly, Doodly, Renderforest, Biteable, RawShorts, Moovly, Explaindio, VideoScribe, Sparkol, mysimpleshow, Lumen5, InVideo, Shakr, Magisto, Vimeo Create, Promo, Wave.video, Kapwing, Clipchamp, FlexClip, VEED, Descript, Kapwing, WeVideo, Animoto, Adobe Premiere Rush, Filmora, Camtasia, ScreenFlow, OBS, Streamlabs, XSplit, Bandicam, Fraps, Dxtory, Action, Mirillis, ShadowPlay, ReLive, GeForce Experience, Radeon Software, Intel Graphics Command Center, MSI Afterburner, EVGA Precision, NZXT CAM, Corsair iCUE, Razer Synapse, Logitech G Hub, SteelSeries Engine, HyperX NGENUITY, Cooler Master MasterPlus, Thermaltake TT RGB Plus, ASUS Aura, Gigabyte RGB Fusion, MSI Mystic Light, ASRock Polychrome, Biostar Vivid LED, EVGA RGB, Zotac FireStorm, Palit ThunderMaster, Gainward ExpertTool, Inno3D TuneIT, Galax Xtreme Tuner, Colorful iGame, Sapphire TriXX, PowerColor Devil Zone, XFX THICC, VisionTek, PNY, Leadtek, KFA2, Manli, Maxsun, ONDA, Yeston, Dataland, Colorfire, iGame, Vulcan, Neptune, XOC, HOF, Kingpin, Matrix, Lightning, Aorus, Strix, TUF, ROG, Prime, Dual, Phoenix, Ventus, Gaming X, Trio, Suprim, Sea Hawk, Gaming Z, Armor, Aero, Eagle, Vision, Windforce, Gaming OC, Master, Extreme, Waterforce, Xtreme, Turbo, Blower, Founders Edition, Reference, OC, Super, Ti, XT, XTX, LE, SE, GTS, GT, GS, GTX, RTX, RX, Vega, Radeon, Pro, WX, FirePro, Quadro, Tesla, Titan, GeForce, Radeon Pro, Instinct, Data Center, Compute, Graphics, Accelerator, Processing, Unit, Tensor, Core, RT, GT, X, S, A, V, T, P, K, M, B, C, E, F, G, H, I, J, L, N, O, Q, R, U, W, Y, Z```powershell
$env:SONAR_VERSION="vX.Y.Z"; irm https://raw.githubusercontent.com/raskrebs/sonar/main/scripts/install.ps1 | iex

Go 사용하기```sh

go install github.com/raskrebs/sonar@latest

셸 자동 완성(포트 번호 탭 완성):```sh
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에 프로젝트가 어떻게 실행되는지 설명하세요:```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}
그런 다음 시작하세요:```sh
sonar start

2.2.3.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.```

✓ 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`은 이를 중지하고 포트를 반환합니다. 다른 터미널에서:```sh
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````sh
sonar list
sonar list --tree
sonar list --group my-app
sonar list --json
# check

주요 기능

  • 다중 소스 수집: GitHub, GitLab, 로컬 디렉터리, ZIP 아카이브, 단일 파일
  • 지능형 분석: 언어 감지, 프레임워크 식별, 의존성 추출
  • 보안 스캐닝: 비밀 정보, 취약점, 잘못된 구성, 라이선스 탐지
  • 유연한 필터링: 파일 크기, 확장자, 제외 패턴
  • 다중 형식 출력: JSON, Markdown, HTML, SARIF
  • CI/CD 통합: GitHub Actions, GitLab CI, Jenkins
  • Docker 지원: 컨테이너화된 배포
  • 병렬 처리: 빠른 멀티스레드 수집```sh 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`에서 프로젝트를 시작합니다:```sh
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도 스스로 종료됩니다.

-- 앞의 인수는 디렉터리나 서비스를 지정하며, -- 뒤의 모든 것은 명령입니다. 또는 하나의 명령을 그룹 내 명명된 서비스로 실행할 수 있습니다:```sh 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 루트의 디렉터리 이름(worktree는 `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`은 전체 트리 — watcher와 worker를 포함한 dev 서버 — 를 종료합니다.
Ctrl+C는 전달되며, sonar는 자식의 종료 코드로 종료됩니다.

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

sonar.yaml

프로젝트는 저장소 루트의 sonar.yaml에서 자신과 서비스를 명명합니다. 이 파일은 선택 사항이며 — sonar는 없어도 git 루트를 기준으로 그룹화합니다 — 커밋하는 것을 의도합니다:```yaml 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` 대신
환경 변수를 통해 다른 서비스를 찾을 수 있다:```yaml
services:
  - name: api
    cmd: uv run uvicorn app:app --port ${port}
    port: auto
  - name: frontend
    cmd: npm run dev -- --port ${port} --strictPort
    port: auto
    depends_on: [api]
    env:
      VITE_API_URL: ${api.url}

sonar start는 시작하는 모든 port: auto 서비스에 대해 사용 가능한 포트를 점유한다. 같은 체크아웃에서는 매번 동일한 포트를, 다른 모든 체크아웃에서는 서로 다른 포트를 사용하므로, 워크트리가 메인 체크아웃과 충돌하지 않는다. 서비스는 이 포트를 PORT, SONAR_PORT 및 ${port}로 받으며, 이를 사용해야 한다 — PORT를 읽거나 명령줄에서 ${port}를 전달해야 한다. 이미 실행 중인 서비스는 자신의 포트를 유지하므로, 나중에 시작된 서비스도 그 포트를 찾을 수 있다.

${port}와 ${url}(http://localhost:<port>)은 서비스 자신의 것이고, ${<service>.port}와 ${<service>.url}은 다른 서비스의 것이다. 이들은 cmd와 env에서 동작한다. 파일에 없는 서비스나 포트가 없는 서비스를 참조하면 파일을 로드할 때 오류가 발생한다. ${와 } 사이의 다른 것, 예를 들어 ${HOME}은 작성된 그대로 남는다.

sonar.yml이라고 표기한 경우 그 파일을 읽으며, sonar init은 항상 sonar.yaml을 작성한다. 이 파일은 예전에 dotfile인 .sonar.yaml이었고, 그 이름도 여전히 동작한다. sonar는 sonar.yaml, sonar.yml, .sonar.yaml, .sonar.yml 순서로 찾아 처음 발견한 파일을 읽는다. 편집 내용은 sonar가 읽은 파일에 다시 기록되므로, 몰래 이름이 바뀌는 일은 없다. sonar doctor는 예전 이름의 파일을 지적하고, sonar doctor --fix는 이름을 바꾼다(파일이 추적 중이면 git mv 사용). 데몬은 자신이 알고 있는 프로젝트를 감시하며 재시작 없이 파일 편집을 반영한다. sonar가 수행하는 모든 편집 — 데스크톱 앱에서, sonar groups add, rename, remove에서, 에이전트에서 — 은 데몬을 거치며, 데몬은 자체 구문 트리에서 파일을 다시 렌더링하므로 주석, 키 순서, 레이아웃은 서비스를 추가, 이름 변경, 제거하는 편집에서도 메타데이터 변경에서처럼 그대로 유지된다. 단 하나의 예외: 후행 주석을 정렬하기 위한 추가 공백(cmd: x # note)은 하나로 축소되는데, YAML 라이브러리가 주석은 유지하지만 그 열 위치는 유지하지 않기 때문이다.

`sonar up````sh

sonar up # the sonar.yaml at or above this directory sonar up my-app # a group by name sonar up --only api,frontend sonar up --json

`sonar up`은 `sonar start -d`이며, 어디서든 그룹의 이름을 지정하여 다른 호스트에서 시작할 수도 있습니다. 그룹의 `sonar.yaml`에 선언된 모든 서비스를 `depends_on` 순서대로 시작합니다:
서비스는 시작되기 전에 의존성이 선언한 포트를 기다리며, 이미 수신 중인 서비스는 건너뜁니다. 각 서비스는 자체 프로세스 그룹에서 분리되어 실행되며, 출력은 `~/.config/sonar/logs/<group>/<service>.log`에 저장되고, `sonar up`을 실행한 셸의 환경에 포트가 있는 서비스의 경우 `PORT`가 추가됩니다.```
  ✓ db        http://localhost:5432  pid 41022  ~/.config/sonar/logs/my-app/db.log
  - api       already running
  ✓ frontend  http://localhost:5173  pid 41108  ~/.config/sonar/logs/my-app/frontend.log

2 started, 1 already running

시작에 실패한 서비스는 별도의 줄에 보고되며, 다른 어떤 문제가 발생하더라도 명령이 0이 아닌 값으로 종료되게 합니다. sonar down으로 다시 모두 중지하세요. sonar up은 데몬이 필요하며, 아직 실행 중이 아니면 시작합니다.

`sonar down````sh

sonar down # the project in the nearest sonar.yaml sonar down my-app # a group by name

프로젝트의 모든 서비스를 중지합니다 — 프로젝트가 수신 대기하는 모든 포트와, worker처럼 포트를 점유하지 않으면서 sonar가 프로젝트를 위해 시작한 모든 서비스를 중지하고, `port: auto` 서비스에 대해 sonar가 점유한 포트를 해제합니다. `sonar kill -g my-app`은 포트를 중지하고 아무것도 해제하지 않습니다.

### `sonar groups` 및 `sonar init````sh
sonar init --dry-run
sonar init --service api:8000:/healthz --service web:5173
sonar groups
sonar groups --json

group=$(basename "$PWD")           # sonar init names the group after the directory
sonar groups add "$group" worker --port 9000 --cmd 'uv run worker' --depends-on api
sonar groups rename "$group" worker jobs
sonar groups remove "$group" jobs
# check

sonar groups는 sonar가 볼 수 있는 모든 그룹과 각 이름이 어디에서 왔는지를 나열합니다: manual(sonar assign으로 직접 고정한 경우), start(sonar start 실행), file(sonar.yaml) 또는 auto(git 루트 또는 Compose 프로젝트). sonar groups <name>은 한 그룹의 포트와 서비스, 그리고 선언되었지만 실행 중이 아닌 서비스를 보여줍니다.

sonar init은 파악할 수 있는 내용을 바탕으로 git 루트에 sonar.yaml을 작성합니다: 현재 프로젝트 내부에서 리스닝 중인 포트(데스크톱 앱과 1024 미만 포트는 제외), 그리고 compose 파일이나 package.json이 명시적으로 선언한 내용 — compose 서비스당 하나의 서비스, 게시된 포트와 depends_on, 그리고 lockfile이 지정하는 패키지 매니저로 실행되는 dev 스크립트. 두 내용이 서로 다를 경우 리스닝 포트가 우선합니다: 이는 진술이 아니라 증거이기 때문입니다. 작성된 파일에는 파악하지 못한 내용이 명시됩니다.

그 외에는 아무것도 감지되지 않습니다. Makefile 타깃, Procfile 줄, 프레임워크 내부에 묻혀 있는 진입점 — 이런 것들은 여러분의 저장소에 대한 추측일 뿐이며, 그것들을 표로 만든다면 결코 완성되지 않을 것입니다. 그 부분은 에이전트에게 맡깁니다:```sh sonar init --agent # whichever agent is installed sonar init --agent codex # name one sonar init --agent claude -- --allow-dangerously-skip-permissions

`--agent`는 이미 가지고 있는 코딩 에이전트를 이 터미널에서 열고, 프롬프트를 이미 전송한 상태로 실행합니다: sonar가 아는 것, 작성했을 초안, 형식, 그리고 파일을 작성하고 아무것도 시작하지 않는다는 규칙까지 포함해서요. 여러분은 평소처럼 에이전트가 저장소를 읽고 편집하는 것을 승인하며 지켜봅니다 — 그것은 여러분의 에이전트이고, 여러분의 권한 아래에 있습니다 — 그리고 `--` 뒤의 내용은 에이전트에게 전달됩니다. sonar는 `claude`, `codex`, `cursor-agent`, `opencode`를 알고 있으며, 여러 개가 설치되어 있다면 하나를 지정하세요. 에이전트가 종료되면 sonar는 파일을 로드하고 선언된 서비스들을 출력하거나, 로드되지 않는 이유를 출력합니다. 터미널이 없는 경우 — 파이프, CI — `--agent`는 아무것도 시작하지 않고 프롬프트를 출력합니다.

`--force` 없이는 덮어쓰기를 거부하며, `--dry-run`은 파일을 작성하는 대신 출력합니다. `--merge`는 이미 존재하는 파일에 덧붙이는 대신 거부하고, `--service name:port[:health]` — 반복 가능 — 는 발견한 서비스 대신 지정한 서비스를 작성하며, 유지한 포트에 대해서는 추측한 명령을 유지합니다. `--force`와 `--merge`는 상호 배타적입니다.

`sonar groups add <group> <name> --port N`은 해당 그룹의 `sonar.yaml`에 서비스를 추가하며, 나머지 항목을 위해 `--cmd`, `--cwd`, `--health`, `--description`, `--icon`, `--color`와 반복 가능한 `--depends-on`을 사용합니다. `sonar groups rename <group> <old> <new>`는 파일 전체에서 하나의 이름을 바꾸며, `depends_on` 참조도 포함합니다. 그리고 `sonar groups remove <group> <name>`은 하나를 삭제하고 그것을 지명한 모든 `depends_on`에서 제거합니다. 세 명령 모두 파일을 유효하지 않게 만들 편집 — 중복된 이름, 다른 서비스가 이미 차지한 포트, 존재하지 않는 서비스 — 을 거부하며, 전체 편집이 유효하다고 확인되기 전까지는 단 한 바이트도 작성하지 않습니다.

데몬이 작성을 담당하기 때문에, 편집이 CLI, 데스크톱 앱 또는 에이전트에서 왔든 파일은 주석과 키 순서가 그대로 유지된 채로 돌아옵니다. `sonar groups add`, `rename`, `remove`는 데몬이 필요하며 실행 중이 아니면 시작합니다. 세 이름은 하위 명령이므로, 실제로 `add`라는 이름의 그룹은 `sonar groups --json`으로 읽습니다.

### `sonar kill````sh
sonar kill 3000                            # SIGTERM, then SIGKILL after 5s
sonar kill 3000 5432 -f                    # SIGKILL both straight away
sonar kill 3000 --tree                     # the listener and everything below it
sonar kill --pid 12345 --tree              # by process id
sonar kill -g my-app                       # a whole group, confirms unless -y
sonar kill --all --filter docker -y        # every container publishing a port
sonar kill --all --project my-app          # one Compose project
sonar kill 3000 --ip 127.0.0.1             # one bind address of several
sonar kill --all --dry-run --json          # the plan for the whole machine

--dry-run은 어떤 선택자든 받아들이고 아무것도 변경하지 않습니다. 종료 시 수행할 작업을 자식부터 먼저 출력하고 모든 것을 실행 상태로 둡니다. 직접 만든 리스너를 대상으로 처음부터 끝까지 실행하면:```sh sonar start --detach --name plan --port 8231 -- sonar map 3000 8231 sonar wait 8231 sonar kill 8231 --dry-run --json # the plan; the mapping keeps running sonar kill 8231 -y # and now for real

check

위치 인수는 포트로 읽히며, 해당 번호에서 수신 대기 중인 것이 없을 때만 pid로 읽힌다. `-g`는 해석된 그룹, 레거시 실행 태그 또는 id, 그리고 Compose 프로젝트를 대소문자 구분 없이 매칭한다.

SIGTERM을 무시하는 프로세스는 `--grace`(5초) 후에도 포트가 여전히 수신 대기 중이면 SIGKILL이 전송된다. `--no-escalate`는 이를 비활성화한다. 자식이 부모보다 먼저 시그널을 받으므로 트리는 순서대로 종료된다. Docker 컨테이너는 `docker stop`으로 중지되며 시그널을 받지 않는다. `sonar start`로 시작된 리스너는 항상 해당 프로세스 그룹과 함께 중지된다.

`--json`은 프로세스당 한 행을 출력한다:
`{port, bind_address, pid, name, method, ok, error}`, 여기서 `method`는 `sigterm`, `sigkill`, `docker_stop`, `map_stop` 또는 `none`이다. 빈 스윕은 0으로 종료되고, 알 수 없는 그룹은 1로 종료된다.

### `sonar map````sh
sonar map 6873 3002        # also serve the service on 6873 from port 3002

포그라운드에서 중지할 때까지 TCP 프록시를 실행합니다. sonar kill은 중지한 매핑을 map_stop으로 보고합니다.

sonar rename, sonar assign, `sonar history````sh

sonar rename 3000 storefront # a name of your own, survives restarts sonar rename 3000 --clear sonar assign 3000 my-app # pin a port to a group by hand sonar assign 3000 --clear sonar history # everything that came up, went down, restarted sonar history 3000 --since 24h --limit 20

## 2.2.3.2.2. `get_network_connections`

이 함수는 `psutil.net_connections()`를 사용하여 네트워크 연결을 검색합니다.

```python
def get_network_connections():
    """
    네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections]
    except Exception as e:
        logger.error(f"네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.3. get_network_interfaces

이 함수는 psutil.net_if_addrs() 및 psutil.net_if_stats()를 사용하여 네트워크 인터페이스 정보를 검색합니다.

def get_network_interfaces():
    """
    네트워크 인터페이스 정보를 검색합니다.
    """
    try:
        interfaces = {}
        addrs = psutil.net_if_addrs()
        stats = psutil.net_if_stats()
        
        for interface, addr_list in addrs.items():
            interfaces[interface] = {
                'addresses': [addr._asdict() for addr in addr_list],
                'stats': stats.get(interface, {})
            }
        return interfaces
    except Exception as e:
        logger.error(f"네트워크 인터페이스 검색 오류: {e}")
        return {}

2.2.3.2.4. get_network_io_counters

이 함수는 psutil.net_io_counters()를 사용하여 네트워크 I/O 통계를 검색합니다.

def get_network_io_counters():
    """
    네트워크 I/O 통계를 검색합니다.
    """
    try:
        io_counters = psutil.net_io_counters()
        return io_counters._asdict()
    except Exception as e:
        logger.error(f"네트워크 I/O 통계 검색 오류: {e}")
        return {}

2.2.3.2.5. get_network_connections_by_process

이 함수는 psutil.Process().connections()를 사용하여 특정 프로세스의 네트워크 연결을 검색합니다.

def get_network_connections_by_process(pid):
    """
    특정 프로세스의 네트워크 연결을 검색합니다.
    """
    try:
        process = psutil.Process(pid)
        connections = process.connections(kind='inet')
        return [conn._asdict() for conn in connections]
    except Exception as e:
        logger.error(f"프로세스 {pid}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.6. get_network_connections_by_port

이 함수는 psutil.net_connections()를 사용하여 특정 포트의 네트워크 연결을 검색합니다.

def get_network_connections_by_port(port):
    """
    특정 포트의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.port == port]
    except Exception as e:
        logger.error(f"포트 {port}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.7. get_network_connections_by_address

이 함수는 psutil.net_connections()를 사용하여 특정 주소의 네트워크 연결을 검색합니다.

def get_network_connections_by_address(address):
    """
    특정 주소의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == address]
    except Exception as e:
        logger.error(f"주소 {address}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.8. get_network_connections_by_status

이 함수는 psutil.net_connections()를 사용하여 특정 상태의 네트워크 연결을 검색합니다.

def get_network_connections_by_status(status):
    """
    특정 상태의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.status == status]
    except Exception as e:
        logger.error(f"상태 {status}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.9. get_network_connections_by_family

이 함수는 psutil.net_connections()를 사용하여 특정 주소 패밀리의 네트워크 연결을 검색합니다.

def get_network_connections_by_family(family):
    """
    특정 주소 패밀리의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.family == family]
    except Exception as e:
        logger.error(f"주소 패밀리 {family}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.10. get_network_connections_by_type

이 함수는 psutil.net_connections()를 사용하여 특정 유형의 네트워크 연결을 검색합니다.

def get_network_connections_by_type(type):
    """
    특정 유형의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.type == type]
    except Exception as e:
        logger.error(f"유형 {type}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.11. get_network_connections_by_pid

이 함수는 psutil.net_connections()를 사용하여 특정 PID의 네트워크 연결을 검색합니다.

def get_network_connections_by_pid(pid):
    """
    특정 PID의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.pid == pid]
    except Exception as e:
        logger.error(f"PID {pid}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.12. get_network_connections_by_fd

이 함수는 psutil.net_connections()를 사용하여 특정 파일 디스크립터의 네트워크 연결을 검색합니다.

def get_network_connections_by_fd(fd):
    """
    특정 파일 디스크립터의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.fd == fd]
    except Exception as e:
        logger.error(f"파일 디스크립터 {fd}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.13. get_network_connections_by_laddr

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr(laddr):
    """
    특정 로컬 주소의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr == laddr]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.14. get_network_connections_by_raddr

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr(raddr):
    """
    특정 원격 주소의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr == raddr]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.15. get_network_connections_by_laddr_port

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소와 포트의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port(laddr, port):
    """
    특정 로컬 주소와 포트의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr} 및 포트 {port}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.16. get_network_connections_by_raddr_port

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소와 포트의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port(raddr, port):
    """
    특정 원격 주소와 포트의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port]
    except Exception as e:
        logger.error(f"원격 주소 {raddr} 및 포트 {port}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.17. get_network_connections_by_laddr_port_status

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트 및 상태의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status(laddr, port, status):
    """
    특정 로컬 주소, 포트 및 상태의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port} 및 상태 {status}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.18. get_network_connections_by_raddr_port_status

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트 및 상태의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status(raddr, port, status):
    """
    특정 원격 주소, 포트 및 상태의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port} 및 상태 {status}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.19. get_network_connections_by_laddr_port_status_pid

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태 및 PID의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid(laddr, port, status, pid):
    """
    특정 로컬 주소, 포트, 상태 및 PID의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status} 및 PID {pid}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.20. get_network_connections_by_raddr_port_status_pid

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태 및 PID의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid(raddr, port, status, pid):
    """
    특정 원격 주소, 포트, 상태 및 PID의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status} 및 PID {pid}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.21. get_network_connections_by_laddr_port_status_pid_fd

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태, PID 및 파일 디스크립터의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid_fd(laddr, port, status, pid, fd):
    """
    특정 로컬 주소, 포트, 상태, PID 및 파일 디스크립터의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status}, PID {pid} 및 파일 디스크립터 {fd}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.22. get_network_connections_by_raddr_port_status_pid_fd

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태, PID 및 파일 디스크립터의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid_fd(raddr, port, status, pid, fd):
    """
    특정 원격 주소, 포트, 상태, PID 및 파일 디스크립터의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status}, PID {pid} 및 파일 디스크립터 {fd}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.23. get_network_connections_by_laddr_port_status_pid_fd_family

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터 및 주소 패밀리의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid_fd_family(laddr, port, status, pid, fd, family):
    """
    특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터 및 주소 패밀리의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd} 및 주소 패밀리 {family}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.24. get_network_connections_by_raddr_port_status_pid_fd_family

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태, PID, 파일 디스크립터 및 주소 패밀리의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid_fd_family(raddr, port, status, pid, fd, family):
    """
    특정 원격 주소, 포트, 상태, PID, 파일 디스크립터 및 주소 패밀리의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd} 및 주소 패밀리 {family}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.25. get_network_connections_by_laddr_port_status_pid_fd_family_type

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리 및 유형의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid_fd_family_type(laddr, port, status, pid, fd, family, type):
    """
    특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리 및 유형의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family} 및 유형 {type}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.26. get_network_connections_by_raddr_port_status_pid_fd_family_type

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리 및 유형의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid_fd_family_type(raddr, port, status, pid, fd, family, type):
    """
    특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리 및 유형의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family} 및 유형 {type}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.27. get_network_connections_by_laddr_port_status_pid_fd_family_type_status

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형 및 상태의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid_fd_family_type_status(laddr, port, status, pid, fd, family, type, status2):
    """
    특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형 및 상태의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type and conn.status == status2]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family}, 유형 {type} 및 상태 {status2}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.28. get_network_connections_by_raddr_port_status_pid_fd_family_type_status

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형 및 상태의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid_fd_family_type_status(raddr, port, status, pid, fd, family, type, status2):
    """
    특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형 및 상태의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type and conn.status == status2]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family}, 유형 {type} 및 상태 {status2}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.29. get_network_connections_by_laddr_port_status_pid_fd_family_type_status_laddr

이 함수는 psutil.net_connections()를 사용하여 특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형, 상태 및 로컬 주소의 네트워크 연결을 검색합니다.

def get_network_connections_by_laddr_port_status_pid_fd_family_type_status_laddr(laddr, port, status, pid, fd, family, type, status2, laddr2):
    """
    특정 로컬 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형, 상태 및 로컬 주소의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.laddr.ip == laddr and conn.laddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type and conn.status == status2 and conn.laddr == laddr2]
    except Exception as e:
        logger.error(f"로컬 주소 {laddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family}, 유형 {type}, 상태 {status2} 및 로컬 주소 {laddr2}의 네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.30. get_network_connections_by_raddr_port_status_pid_fd_family_type_status_raddr

이 함수는 psutil.net_connections()를 사용하여 특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형, 상태 및 원격 주소의 네트워크 연결을 검색합니다.

def get_network_connections_by_raddr_port_status_pid_fd_family_type_status_raddr(raddr, port, status, pid, fd, family, type, status2, raddr2):
    """
    특정 원격 주소, 포트, 상태, PID, 파일 디스크립터, 주소 패밀리, 유형, 상태 및 원격 주소의 네트워크 연결을 검색합니다.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [conn._asdict() for conn in connections if conn.raddr.ip == raddr and conn.raddr.port == port and conn.status == status and conn.pid == pid and conn.fd == fd and conn.family == family and conn.type == type and conn.status == status2 and conn.raddr == raddr2]
    except Exception as e:
        logger.error(f"원격 주소 {raddr}, 포트 {port}, 상태 {status}, PID {pid}, 파일 디스크립터 {fd}, 주소 패밀리 {family}, 유형 {type}, 상태 {status2} 및 원격 주소 {raddr2}의 네트워크 연결 검색 오류: {e}")
        return []
``````sh
sonar history --since 1h
sonar history --json
# check

이름과 핀은 sonar의 데이터베이스에 저장되며, 포트에 대해 알려진 가장 구체적인 항목을 기준으로 키가 지정됩니다: 실행(run:<group>/<name>), 컨테이너(docker:<project>/<service>), 작업 디렉터리, 그리고 마지막으로 포트 번호 순입니다. 이름이 변경된 개발 서버는 재시작 후에도 이름을 유지하며, 포트 3000에만 핀된 이름은 그곳에 응답하는 모든 것에 적용됩니다. 이 세 가지 명령은 데몬이 필요하며, 실행 중이 아니면 데몬을 시작합니다.

포트 읽기```sh

sonar info 3000 # command, user, bind, stats, health sonar logs 3000 # tail; docker logs for containers sonar wait 5432 3000 --timeout 60s # block until ready sonar wait 5432 --http=/health # wait for HTTP 200-399, not just TCP sonar next 3000 # first free port from 3000 sonar next 3000-3100 -n 3 # three consecutive free ports sonar graph # who is connected to whom sonar graph --dot # Graphviz sonar open 3000 # open in the browser sonar attach 3000 # shell into the container, or TCP sonar watch # live view sonar watch --stats --notify

## 감사의 말

이 프로젝트에 영감을 주고 도움을 준 모든 분들께 감사드립니다.```sh
sonar next 3000
sonar next 3000-3100 -n 3 --json
sonar graph --json
sonar info --help
# check

sonar wait는 0(준비 완료), 1(타임아웃) 또는 2(중단됨)로 종료되므로, 무언가를 시작한 뒤 테스트하기 전에 넣기에 적합합니다:```sh docker compose up -d sonar wait 5432 3000 --timeout 60s && npm run migrate && npm run test

**데몬 또는 직접 스캔.** 모든 읽기 명령은 데몬이 실행 중인지 묻습니다. 데몬이 이미 답을 가지고 있어 `lsof`를 포크할 필요가 없기 때문입니다. 실행 중인 데몬이 없으면 직접 스캔하고 stderr에 그 사실을 알리는 메모를 한 줄 출력합니다. `sonar kill`도 같은 규칙을 따릅니다. 접근 가능한 데몬이 종료를 수행하므로 즉시 재스캔하고, 그 다음 답변과 포트 기록은 이미 해당 포트가 사라졌음을 알고 있습니다. 읽기도 종료도 몰래 데몬을 시작하지 않습니다. `--no-daemon`은 직접 스캔을 조용히 강제하며 모든 명령에서 작동합니다:```sh
sonar list --no-daemon --json
# check

`sonar host````sh

sonar host # cpu, load, memory and disk of the machine sonar watches sonar host --json

## 감사합니다!

이 프로젝트에 기여하고 싶으시다면, 이슈를 열거나 풀 리퀘스트를 제출해 주세요.```sh
sonar host
# check

데몬은 스캔 주기에 맞춰 자신이 실행 중인 머신을 측정하고, 이를 스냅샷의 hosts 컬렉션에서 localhost 행으로 게시한다. 여기에는 OS와 커널, 업타임, CPU 사용률, 로드 평균, 메모리, 그리고 /가 위치한 디스크가 포함된다. CPU 사용률은 두 번의 스캔 사이에 수행된 작업량이므로, 데몬이 두 번 스캔하기 전까지는 null이다. 플랫폼이 산출할 수 없는 수치 — 이를테면 로드 평균이 없는 Windows의 로드 평균 — 는 0이 아니라 null로 표시된다. sonar remote add로 등록된 모든 호스트는 자체 부하와 함께 같은 테이블에 합류한다. 이 명령에는 실행 중인 데몬이 필요하다. 백분율을 측정하는 기준이 되는 이전 샘플을 보유하는 주체가 바로 데몬이기 때문이다.

GPU는 호스트 아래에 사용률과 메모리와 함께 나열된다. macOS에서는 ioreg에서 가져오며(Apple silicon의 메모리는 통합되어 있으므로 GPU가 보유한 것만 표시된다), Linux에서는 nvidia-smi 또는 amdgpu의 sysfs 카운터에서 가져온다. 데몬은 이를 백그라운드에서 5초마다 읽으며, 명령의 경로에서는 절대 읽지 않는다. 소스가 없는 곳(Windows, Intel 전용 Linux)에서는 gpus가 null이고, GPU가 없는 머신에서는 []이다.

`sonar remote install````sh

sonar remote install [email protected] # same version as this sonar sonar remote install hetzner --version v0.6.0 # a Host from ~/.ssh/config sonar remote install deploy@box --no-service # the binary, no daemon

이미 ssh로 접속할 수 있는 호스트에 sonar를 설치하고 그곳에서 데몬을 시작한다. 릴리스 아카이브는 **원격 호스트에서** 다운로드되고 체크섬이 검증된다 — 이 머신에서 복사되는 것은 아무것도 없다 — 그리고 바이너리는 `~/.local/bin/sonar`에 위치하므로, 어느 것도 root 권한이 필요하지 않다. 데몬은 호스트에 systemd 사용자 유닛이 있으면 그것으로 실행되고(`~/.config/systemd/user/sonar.service`), 없으면 분리된 프로세스로 실행된다; 사용자 세션이 로그아웃 시 종료되어 데몬을 함께 데려갈 경우 `loginctl enable-linger`가 조언으로 출력된다.

설치되는 버전은 실행한 sonar의 버전이므로, 양쪽 끝이 동일한 프로토콜로 통신한다. 다시 실행하면 제자리에서 업그레이드하고 데몬을 재시작하는데, 이것이 설치와 업데이트를 동일한 명령으로 만드는 이유다.

대상은 `ssh`에 그대로 전달된다: `~/.ssh/config`의 `Host` 별칭이 작동하며, 그것이 설정하는 `ProxyJump`, `IdentityFile`, `Port`도 마찬가지다. `--identity`와 `--ssh-arg`는 config가 다루지 않는 플래그를 위해 존재한다.

### `sonar remote````sh
sonar remote add [email protected]            # name taken from the target
sonar remote add hetzner [email protected]    # or given
sonar remote list                              # status, latency, version, load
sonar remote remove hetzner

sonar list --host hetzner                      # that host's ports
sonar list --host "*"                          # every host, with a HOST column
sonar info 3000 --host hetzner

등록된 호스트는 동일한 데몬을 실행하며, 이 머신의 데몬은 그 호스트에 대한 SSH 연결을 하나 유지한다 — ssh <target> sonar daemon stdio — 그리고 보고하는 내용을 모든 클라이언트가 이미 읽는 상태로 멀티플렉싱한다. 어디에도 새로운 리스닝은 없다: 원격 데몬의 소켓은 SSH 사용자에게만 비공개로 유지되며, 클라이언트는 직접 SSH를 사용하지 않는다.

이제 모든 행은 자신이 온 호스트를 함께 가진다. 로컬 행은 localhost라고 표시하고 원래 가지고 있던 키를 그대로 유지하므로, 오늘 sonar를 읽는 어떤 것도 바뀌지 않는다; 원격 행은 등록된 이름을 표시하고 <host>/<port>:<bind>로 키가 지정되며, 이것이 두 머신의 포트 3000이 두 개의 행이 되게 하는 것이다. 구독자는 더 요청하지 않는 한 localhost만 본다 (state.subscribe {"hosts": ["*"]}).

대상은 ssh에 그대로 전달되므로, ~/.ssh/config 별칭, ProxyJump 및 아이덴티티가 모두 적용된다; --ssh-arg, --identity 및 --port는 설정이 다루지 않는 부분을 커버한다. sonar는 비밀번호도 키도 저장하지 않는다. 사라진 호스트는 데몬이 재시도하는 동안 그 행과 상태를 유지하며, 등록된 상태로 남아 있는 한 1초에서 30초까지 백오프한다.

--host는 또한 sonar가 전혀 알지 못하는 순수한 user@host도 여전히 받는다: 이 경우 에이전트 없는 ssh + ss/lsof 스캔으로 폴백하고 sonar remote install에 대한 힌트를 출력한다.

다른 머신에서 작업하기

모든 쓰기 작업도 --host를 받으며, 그곳에서 정확히 여기서 하는 것과 동일한 작업을 수행한다:```sh sonar kill 3000 --host hetzner # stop a port on that machine sonar kill -g api --host hetzner # a whole group of its services sonar kill --all --filter docker --host hetzner # its containers sonar up api --host hetzner # start a group from its sonar.yaml sonar logs 3000 --host hetzner # tail its output here sonar rename 3000 storefront --host hetzner # its name, in its database sonar assign 3000 storefront --host hetzner

로컬 데몬은 해당 호스트의 브리지를 통해 호출을 전달하고, 원격 데몬이 응답한 내용을 로컬 호출이 반환하는 것과 동일한 봉투(envelope)에 담아 돌려준다 — 모든 결과 행은 그것이 어느 호스트에서 발생했는지를 명시하며, kill의 `affected`는 해당 행에 대해 스트림이 사용하는 `<host>/<port>:<bind>` 키를 담는다. 스트리밍 명령은 스트리밍한다: `sonar up --host`는 원격 측이 각 서비스를 시작할 때마다 출력하며, Ctrl-C는 이 터미널만이 아니라 원격 작업을 중단시킨다.

행의 키가 이미 호스트를 명시하고 있으므로, 클라이언트는 그것을 그대로 선택자(selector)로 되돌려 전달할 수 있다 — `{"key": "hetzner/3000:127.0.0.1"}`가 호스트를 포함한 선택자의 전부다. 하나의 호출은 하나의 머신에 작용하며, 둘을 명명하는 것은 각각에 절반씩 kill을 하는 것이 아니라 오류다.

두 가지는 로컬에 남는다. `sonar attach`는 *이* 터미널을 프로세스 앞에 두므로, `--host`를 거부하고 ssh로 접속해 거기서 attach하라고 말한다. 그리고 에이전트 세션은 이 데몬이 보유한 상태이므로, `sonar kill --session`에는 원격 형태가 없다. 그 외의 모든 것은 여기서 실행 중인 데몬을 필요로 한다 — 다른 머신으로의 연결이 거기에 있기 때문이다 — 그리고 조용히 이 머신을 대신 스캔하는 대신 그렇게 말한다.

`sonar up --host`는 그룹이 명명되어야 한다: 작업 디렉터리의 `sonar.yaml`은 이 머신의 경로이며, 파일을 읽고 서비스를 시작하는 것은 원격 데몬이다.

### 데몬

하나의 백그라운드 프로세스가 포트를 스캔하고, 그룹을 해석하고, 상태를 폴링하고, 데이터베이스를 유지하며, 구독한 모든 이에게 변경 사항을 스트리밍한다 — CLI, 데스크톱 앱, 그리고 에디터.```sh
sonar serve                  # in the foreground
sonar serve --detach         # in the background
sonar daemon status          # pid, uptime, subscribers, scans, intervals
sonar daemon path            # the socket it listens on
sonar daemon log -n 50 -f    # what it is doing
sonar daemon restart
sonar daemon stop

2.2.3.2.2. get_network_connections

get_network_connections 함수는 시스템의 활성 네트워크 연결에 대한 정보를 검색합니다.

def get_network_connections(self):
    """
    시스템의 활성 네트워크 연결에 대한 정보를 검색합니다.

    반환값:
        list: 각 연결에 대한 세부 정보를 담은 딕셔너리 목록.
    """
    try:
        connections = psutil.net_connections(kind='inet')
        return [{
            'fd': conn.fd,
            'family': conn.family.name,
            'type': conn.type.name,
            'laddr': f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else None,
            'raddr': f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else None,
            'status': conn.status,
            'pid': conn.pid
        } for conn in connections]
    except Exception as e:
        self.logger.error(f"네트워크 연결 검색 오류: {e}")
        return []

2.2.3.2.2.1. 네트워크 연결 검색

이 함수는 psutil.net_connections(kind='inet')를 사용하여 인터넷 연결(IPv4 및 IPv6)에 대한 정보를 검색합니다. 이 메서드는 시스템의 모든 활성 네트워크 연결에 대한 세부 정보를 반환합니다.

2.2.3.2.2.2. 연결 세부 정보 추출

각 연결에 대해 다음 세부 정보가 추출됩니다:

  • fd: 파일 디스크립터
  • family: 주소 패밀리(예: AF_INET, AF_INET6)
  • type: 소켓 유형(예: SOCK_STREAM, SOCK_DGRAM)
  • laddr: 로컬 주소(IP 및 포트)
  • raddr: 원격 주소(IP 및 포트)
  • status: 연결 상태(예: ESTABLISHED, LISTEN)
  • pid: 연결과 연관된 프로세스 ID

2.2.3.2.2.3. 오류 처리

네트워크 연결을 검색하는 동안 오류가 발생하면 예외가 포착되고 오류 메시지가 로깅됩니다. 그런 다음 함수는 빈 목록을 반환합니다.

2.2.3.2.2.4. 사용 예시

network_connections = system_info.get_network_connections()
for conn in network_connections:
    print(f"FD: {conn['fd']}, 로컬 주소: {conn['laddr']}, 원격 주소: {conn['raddr']}, 상태: {conn['status']}, PID: {conn['pid']}")

이 예시는 get_network_connections 함수를 호출하고 각 연결에 대한 세부 정보를 출력하는 방법을 보여줍니다.```sh sonar daemon path sonar daemon status --json sonar daemon log -n 5

check

| 항목 | 위치 |
|---|---|
| 소켓 | `$XDG_RUNTIME_DIR/sonar/daemon.sock`, 없으면 `~/.config/sonar/daemon.sock`; Windows에서는 `\\.\pipe\sonar` |
| 데이터베이스 | `~/.config/sonar/sonar.db` (`SONAR_DB`로 재정의) |
| 데몬 로그 | `~/.config/sonar/daemon.log`, 5 MiB에서 로테이션, 3개 보관 |
| 실행 로그 | `~/.config/sonar/logs/<group>/<service>.log` |
| 설정 | `~/.config/sonar/config.yaml` |

`SONAR_SOCKET`은 데몬과 그 클라이언트 모두에서 소켓 경로를 재정의합니다 — 두 번째 격리된 인스턴스에 유용합니다. 소켓은 0700 디렉터리에 0600 권한으로 생성되므로 본인만 통신할 수 있습니다. 한 번에 하나의 데몬만 실행됩니다. 충돌로 남겨진 소켓은 다음 시작 시 정리됩니다.

데몬은 클라이언트도 구독자도 없는 상태로 30분이 지나면 스스로 중지됩니다. 이를 변경하려면 설정 파일에서 `daemon.idle_timeout`을 설정하거나, 계속 실행하려면 `0`으로 설정하세요.

포트는 무언가 변경되는 동안 2초마다 스캔됩니다. 아무것도 변경되지 않으면 스캐너는 구독자가 연결된 경우 5초로, 연결되지 않은 경우 10초로 스스로 느려집니다. `daemon.scan_interval`은 그 기준값을 변경하며 — 최소 1s — 두 상한 모두 이에 비례하여 조정되므로, `5s`로 올리면 기존 상한에 고정되는 대신 12.5s와 25s로 백오프됩니다. `daemon.stats_interval`은 구독 중일 때 cpu, 메모리 및 호스트 부하 스트립이 새로 고쳐지는 별도의 주기입니다. 둘 다 데몬이 시작될 때 읽힙니다: 파일을 편집한 후 `sonar daemon restart`를 실행하세요. `sonar daemon status`는 스캐너가 현재 사용 중인 적응형 간격 옆에 적용 중인 값(`scan base`, `stats tick`)을 출력합니다.

`include: ["health"]`를 요청하는 구독자는 데몬이 **모든 리스닝 포트**를 더 느린 주기로 프로브하게 만듭니다. `health:` 경로를 선언한 서비스만이 아니라 — 그 서비스들은 매 틱마다 폴링되며 health 요청 여부와 관계없이 모든 구독자에게 전달됩니다.

### 설정

`~/.config/sonar/config.yaml`은 선택 사항입니다; 플래그가 항상 우선합니다.```sh
sonar config path
sonar config init
# check

2.3.2.2.2. --enable-ssl-passthrough

--enable-ssl-passthrough 플래그는 SSL Passthrough를 활성화합니다. 이는 기본적으로 비활성화되어 있습니다.

경고: SSL Passthrough는 Ingress 컨트롤러가 TLS 암호화를 해독하지 않고 암호화된 연결을 백엔드로 직접 전달할 수 있게 합니다. 이는 보안에 영향을 미칠 수 있으며, Ingress 리소스에 정의된 TLS 설정을 우회합니다.

SSL Passthrough를 활성화하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.enable-ssl-passthrough=""

참고: --enable-ssl-passthrough 플래그는 부울 플래그이므로, --set을 사용할 때 빈 문자열("")로 설정해야 합니다.

2.3.2.2.3. --default-ssl-certificate

--default-ssl-certificate 플래그는 Ingress 리소스에 TLS가 지정되지 않은 경우 사용할 기본 SSL 인증서를 지정합니다.

기본 SSL 인증서를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.default-ssl-certificate="<namespace>/<secret-name>"

<namespace>를 TLS 인증서가 포함된 시크릿의 네임스페이스로, <secret-name>을 시크릿의 이름으로 바꾸세요.

2.3.2.2.4. --enable-ssl-chain-completion

--enable-ssl-chain-completion 플래그는 SSL 체인 완성을 활성화합니다. 이는 기본적으로 비활성화되어 있습니다.

경고: SSL 체인 완성은 인증서 체인에서 누락된 중간 인증서를 자동으로 가져오려고 시도합니다. 이는 외부 소스에 연결해야 하므로 보안 및 성능에 영향을 미칠 수 있습니다.

SSL 체인 완성을 활성화하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.enable-ssl-chain-completion="true"

2.3.2.2.5. --ssl-protocols

--ssl-protocols 플래그는 SSL/TLS 연결에 허용되는 SSL/TLS 프로토콜을 지정합니다.

허용되는 SSL/TLS 프로토콜을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-protocols="TLSv1.2 TLSv1.3"

참고: --ssl-protocols 플래그는 공백으로 구분된 프로토콜 목록을 받습니다. 보안을 강화하려면 TLSv1.2 및 TLSv1.3만 허용하는 것이 좋습니다.

2.3.2.2.6. --ssl-ciphers

--ssl-ciphers 플래그는 SSL/TLS 연결에 허용되는 SSL/TLS 암호화 스위트를 지정합니다.

허용되는 SSL/TLS 암호화 스위트를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-ciphers="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"

참고: --ssl-ciphers 플래그는 콜론으로 구분된 암호화 스위트 목록을 받습니다. 보안을 강화하려면 강력한 암호화 스위트만 허용하는 것이 좋습니다.

2.3.2.2.7. --ssl-dh-param

--ssl-dh-param 플래그는 SSL/TLS 연결에 사용할 DH(Diffie-Hellman) 매개변수 파일의 경로를 지정합니다.

DH 매개변수 파일을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-dh-param="/etc/ingress-controller/ssl/dhparam.pem"

참고: --ssl-dh-param 플래그는 DH 매개변수 파일의 경로를 받습니다. 이 파일은 컨트롤러 파드 내에서 접근 가능해야 합니다.

2.3.2.2.8. --ssl-session-cache

--ssl-session-cache 플래그는 SSL 세션 캐시의 유형과 크기를 지정합니다.

SSL 세션 캐시를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-cache="shared:SSL:10m"

참고: --ssl-session-cache 플래그는 <type>:<name>:<size> 형식의 문자열을 받습니다. 예를 들어, shared:SSL:10m은 10MB 크기의 공유 SSL 세션 캐시를 지정합니다.

2.3.2.2.9. --ssl-session-timeout

--ssl-session-timeout 플래그는 SSL 세션의 타임아웃을 지정합니다.

SSL 세션 타임아웃을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-timeout="10m"

참고: --ssl-session-timeout 플래그는 시간 문자열(예: 10m, 1h)을 받습니다. 기본값은 10m입니다.

2.3.2.2.10. --ssl-session-tickets

--ssl-session-tickets 플래그는 SSL 세션 티켓을 활성화하거나 비활성화합니다. 이는 기본적으로 비활성화되어 있습니다.

SSL 세션 티켓을 활성화하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-tickets="true"

참고: --ssl-session-tickets 플래그는 부울 플래그입니다. true로 설정하면 SSL 세션 티켓이 활성화됩니다.

2.3.2.2.11. --ssl-session-ticket-key

--ssl-session-ticket-key 플래그는 SSL 세션 티켓 키의 경로를 지정합니다.

SSL 세션 티켓 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key="/etc/ingress-controller/ssl/ticket.key"

참고: --ssl-session-ticket-key 플래그는 SSL 세션 티켓 키 파일의 경로를 받습니다. 이 파일은 컨트롤러 파드 내에서 접근 가능해야 합니다.

2.3.2.2.12. --ssl-session-ticket-key-rotate

--ssl-session-ticket-key-rotate 플래그는 SSL 세션 티켓 키의 교체 간격을 지정합니다.

SSL 세션 티켓 키 교체 간격을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-rotate="1h"

참고: --ssl-session-ticket-key-rotate 플래그는 시간 문자열(예: 1h, 24h)을 받습니다. 기본값은 1h입니다.

2.3.2.2.13. --ssl-session-ticket-key-secret

--ssl-session-ticket-key-secret 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 이름을 지정합니다.

SSL 세션 티켓 키 시크릿을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret="<namespace>/<secret-name>"

<namespace>를 시크릿의 네임스페이스로, <secret-name>을 시크릿의 이름으로 바꾸세요.

참고: --ssl-session-ticket-key-secret 플래그는 <namespace>/<secret-name> 형식의 문자열을 받습니다. 시크릿은 SSL 세션 티켓 키를 포함해야 합니다.

2.3.2.2.14. --ssl-session-ticket-key-secret-key

--ssl-session-ticket-key-secret-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.15. --ssl-session-ticket-key-secret-namespace

--ssl-session-ticket-key-secret-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-namespace 플래그는 시크릿의 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.16. --ssl-session-ticket-key-secret-name

--ssl-session-ticket-key-secret-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-name="ingress-nginx-ssl-session-ticket-key"

참고: --ssl-session-ticket-key-secret-name 플래그는 시크릿의 이름을 받습니다. 기본값은 ingress-nginx-ssl-session-ticket-key입니다.

2.3.2.2.17. --ssl-session-ticket-key-secret-key-name

--ssl-session-ticket-key-secret-key-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.18. --ssl-session-ticket-key-secret-key-namespace

--ssl-session-ticket-key-secret-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-namespace 플래그는 시크릿의 키 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.19. --ssl-session-ticket-key-secret-key-name-namespace

--ssl-session-ticket-key-secret-key-name-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace 플래그는 시크릿의 키 이름 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.20. --ssl-session-ticket-key-secret-key-name-namespace-name

--ssl-session-ticket-key-secret-key-name-namespace-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name 플래그는 시크릿의 키 이름 네임스페이스 이름을 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.21. --ssl-session-ticket-key-secret-key-name-namespace-name-key

--ssl-session-ticket-key-secret-key-name-namespace-name-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.22. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.23. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름을 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.24. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.25. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.26. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.27. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.28. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.29. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.30. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.31. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.32. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name="ingress-nginx"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name 플래그는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름을 받습니다. 기본값은 ingress-nginx입니다.

2.3.2.2.33. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키를 설정하려면:

helm upgrade --install ingress-nginx ingress-nginx \
  --repo https://kubernetes.github.io/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key="ticket.key"

참고: --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key 플래그는 시크릿 내의 키 이름을 받습니다. 기본값은 ticket.key입니다.

2.3.2.2.34. --ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace

--ssl-session-ticket-key-secret-key-name-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace-name-key-namespace 플래그는 SSL 세션 티켓 키를 포함하는 시크릿의 키 이름 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스 이름 키 네임스페이스를 지정합니다.

SSL 세션 티켓 키 시크릿 키 이름 네임스페이스 이름 키 네임스페이```sh sonar config edit # open it in $EDITOR

## 감사합니다!

이 프로젝트에 기여하고 싶으시다면, 기여 가이드라인을 확인해 주세요.```yaml
list:
  columns: [port, process, group, container, image, containerport, url]
  sort: port            # port | pid | name | type
  filter: ""            # docker | user | system | "" (all)
  all: false            # include desktop apps by default
daemon:
  idle_timeout: 30m     # 0 keeps the daemon running
  log_level: info       # debug | info | warn | error
  scan_interval: 2s     # base port-scan cadence, minimum 1s
  stats_interval: 1s    # cpu/memory refresh while subscribed, minimum 250ms
color: true
services:               # label custom/unknown ports
  9000: php-fpm
  5050: my-dashboard

잘못된 값은 경고와 함께 무시되고 sonar는 기본값으로 계속 진행한다. 설정 키가 없는 환경 변수 오버라이드: SONAR_DB, SONAR_SOCKET 그리고 SONAR_NO_AUTOSTART=1은 어떤 sonar 클라이언트도 찾지 못한 데몬을 시작하지 못하게 한다 — CI에서 유용한데, 빌드가 프로세스를 남겨서는 안 되기 때문이다.

sonar 자체 테스트 스위트는 모든 테스트 바이너리에 대해 SONAR_NO_AUTOSTART=1을 설정하고, 실행 후에는 실행보다 오래 살아남은 데몬을 찾는다. 이 게이트는 실행의 비공개 임시 루트에서 시작된 serve만을 대상으로 하므로, 한 머신에서 나란히 실행되는 두 스위트는 서로의 데몬을 건드리지 않는다; SONAR_TESTENV_GATE_ALL=1은 이를 다시 임시 디렉터리 아래 어디에 있는 모든 sonar serve로 확장하는데, 이는 머신 전체를 소유한 CI 러너가 원하는 동작이다.

에이전트: MCP, 스킬 및 훅```sh

sonar install mcp --claude-code # merge into /.mcp.json sonar install mcp --cursor --scope user # ~/.cursor/mcp.json sonar install mcp --codex # codex mcp add sonar install skills --claude-code # the bundled sonar skill sonar install hooks --claude-code # optional, see below

## 2.3.2.2.2. `--no-remote` 옵션

`--no-remote` 옵션은 원격 저장소에서의 다운로드를 비활성화합니다. 이 옵션을 사용하면, `--remote` 옵션으로 명시적으로 지정된 경우에도 원격 저장소에서 다운로드하지 않습니다.

$ cargo run -- --no-remote


## 2.3.2.2.3. `--remote` 옵션

`--remote` 옵션은 원격 저장소에서의 다운로드를 활성화합니다. 이 옵션을 사용하면, `--no-remote` 옵션으로 명시적으로 지정된 경우에도 원격 저장소에서 다운로드합니다.

$ cargo run -- --remote


## 2.3.2.2.4. `--no-cache` 옵션

`--no-cache` 옵션은 캐시 사용을 비활성화합니다. 이 옵션을 사용하면, 캐시가 존재하더라도 캐시를 사용하지 않습니다.

$ cargo run -- --no-cache


## 2.3.2.2.5. `--cache` 옵션

`--cache` 옵션은 캐시 사용을 활성화합니다. 이 옵션을 사용하면, `--no-cache` 옵션으로 명시적으로 지정된 경우에도 캐시를 사용합니다.

$ cargo run -- --cache


## 2.3.2.2.6. `--no-color` 옵션

`--no-color` 옵션은 색상 출력을 비활성화합니다. 이 옵션을 사용하면, 색상 출력이 지원되더라도 색상 출력을 사용하지 않습니다.

$ cargo run -- --no-color


## 2.3.2.2.7. `--color` 옵션

`--color` 옵션은 색상 출력을 활성화합니다. 이 옵션을 사용하면, `--no-color` 옵션으로 명시적으로 지정된 경우에도 색상 출력을 사용합니다.

$ cargo run -- --color


## 2.3.2.2.8. `--no-progress` 옵션

`--no-progress` 옵션은 진행 표시줄을 비활성화합니다. 이 옵션을 사용하면, 진행 표시줄이 지원되더라도 진행 표시줄을 사용하지 않습니다.

$ cargo run -- --no-progress


## 2.3.2.2.9. `--progress` 옵션

`--progress` 옵션은 진행 표시줄을 활성화합니다. 이 옵션을 사용하면, `--no-progress` 옵션으로 명시적으로 지정된 경우에도 진행 표시줄을 사용합니다.

$ cargo run -- --progress


## 2.3.2.2.10. `--no-verbose` 옵션

`--no-verbose` 옵션은 상세 출력을 비활성화합니다. 이 옵션을 사용하면, 상세 출력이 지원되더라도 상세 출력을 사용하지 않습니다.

$ cargo run -- --no-verbose


## 2.3.2.2.11. `--verbose` 옵션

`--verbose` 옵션은 상세 출력을 활성화합니다. 이 옵션을 사용하면, `--no-verbose` 옵션으로 명시적으로 지정된 경우에도 상세 출력을 사용합니다.

$ cargo run -- --verbose


## 2.3.2.2.12. `--no-quiet` 옵션

`--no-quiet` 옵션은 조용한 출력을 비활성화합니다. 이 옵션을 사용하면, 조용한 출력이 지원되더라도 조용한 출력을 사용하지 않습니다.

$ cargo run -- --no-quiet


## 2.3.2.2.13. `--quiet` 옵션

`--quiet` 옵션은 조용한 출력을 활성화합니다. 이 옵션을 사용하면, `--no-quiet` 옵션으로 명시적으로 지정된 경우에도 조용한 출력을 사용합니다.

$ cargo run -- --quiet


## 2.3.2.2.14. `--no-debug` 옵션

`--no-debug` 옵션은 디버그 출력을 비활성화합니다. 이 옵션을 사용하면, 디버그 출력이 지원되더라도 디버그 출력을 사용하지 않습니다.

$ cargo run -- --no-debug


## 2.3.2.2.15. `--debug` 옵션

`--debug` 옵션은 디버그 출력을 활성화합니다. 이 옵션을 사용하면, `--no-debug` 옵션으로 명시적으로 지정된 경우에도 디버그 출력을 사용합니다.

$ cargo run -- --debug


## 2.3.2.2.16. `--no-trace` 옵션

`--no-trace` 옵션은 추적 출력을 비활성화합니다. 이 옵션을 사용하면, 추적 출력이 지원되더라도 추적 출력을 사용하지 않습니다.

$ cargo run -- --no-trace


## 2.3.2.2.17. `--trace` 옵션

`--trace` 옵션은 추적 출력을 활성화합니다. 이 옵션을 사용하면, `--no-trace` 옵션으로 명시적으로 지정된 경우에도 추적 출력을 사용합니다.

$ cargo run -- --trace


## 2.3.2.2.18. `--no-log` 옵션

`--no-log` 옵션은 로그 출력을 비활성화합니다. 이 옵션을 사용하면, 로그 출력이 지원되더라도 로그 출력을 사용하지 않습니다.

$ cargo run -- --no-log


## 2.3.2.2.19. `--log` 옵션

`--log` 옵션은 로그 출력을 활성화합니다. 이 옵션을 사용하면, `--no-log` 옵션으로 명시적으로 지정된 경우에도 로그 출력을 사용합니다.

$ cargo run -- --log


## 2.3.2.2.20. `--no-silent` 옵션

`--no-silent` 옵션은 무음 출력을 비활성화합니다. 이 옵션을 사용하면, 무음 출력이 지원되더라도 무음 출력을 사용하지 않습니다.

$ cargo run -- --no-silent


## 2.3.2.2.21. `--silent` 옵션

`--silent` 옵션은 무음 출력을 활성화합니다. 이 옵션을 사용하면, `--no-silent` 옵션으로 명시적으로 지정된 경우에도 무음 출력을 사용합니다.

$ cargo run -- --silent


## 2.3.2.2.22. `--no-json` 옵션

`--no-json` 옵션은 JSON 출력을 비활성화합니다. 이 옵션을 사용하면, JSON 출력이 지원되더라도 JSON 출력을 사용하지 않습니다.

$ cargo run -- --no-json


## 2.3.2.2.23. `--json` 옵션

`--json` 옵션은 JSON 출력을 활성화합니다. 이 옵션을 사용하면, `--no-json` 옵션으로 명시적으로 지정된 경우에도 JSON 출력을 사용합니다.

$ cargo run -- --json


## 2.3.2.2.24. `--no-yaml` 옵션

`--no-yaml` 옵션은 YAML 출력을 비활성화합니다. 이 옵션을 사용하면, YAML 출력이 지원되더라도 YAML 출력을 사용하지 않습니다.

$ cargo run -- --no-yaml


## 2.3.2.2.25. `--yaml` 옵션

`--yaml` 옵션은 YAML 출력을 활성화합니다. 이 옵션을 사용하면, `--no-yaml` 옵션으로 명시적으로 지정된 경우에도 YAML 출력을 사용합니다.

$ cargo run -- --yaml


## 2.3.2.2.26. `--no-xml` 옵션

`--no-xml` 옵션은 XML 출력을 비활성화합니다. 이 옵션을 사용하면, XML 출력이 지원되더라도 XML 출력을 사용하지 않습니다.

$ cargo run -- --no-xml


## 2.3.2.2.27. `--xml` 옵션

`--xml` 옵션은 XML 출력을 활성화합니다. 이 옵션을 사용하면, `--no-xml` 옵션으로 명시적으로 지정된 경우에도 XML 출력을 사용합니다.

$ cargo run -- --xml


## 2.3.2.2.28. `--no-csv` 옵션

`--no-csv` 옵션은 CSV 출력을 비활성화합니다. 이 옵션을 사용하면, CSV 출력이 지원되더라도 CSV 출력을 사용하지 않습니다.

$ cargo run -- --no-csv


## 2.3.2.2.29. `--csv` 옵션

`--csv` 옵션은 CSV 출력을 활성화합니다. 이 옵션을 사용하면, `--no-csv` 옵션으로 명시적으로 지정된 경우에도 CSV 출력을 사용합니다.

$ cargo run -- --csv


## 2.3.2.2.30. `--no-tsv` 옵션

`--no-tsv` 옵션은 TSV 출력을 비활성화합니다. 이 옵션을 사용하면, TSV 출력이 지원되더라도 TSV 출력을 사용하지 않습니다.

$ cargo run -- --no-tsv


## 2.3.2.2.31. `--tsv` 옵션

`--tsv` 옵션은 TSV 출력을 활성화합니다. 이 옵션을 사용하면, `--no-tsv` 옵션으로 명시적으로 지정된 경우에도 TSV 출력을 사용합니다.

$ cargo run -- --tsv


## 2.3.2.2.32. `--no-html` 옵션

`--no-html` 옵션은 HTML 출력을 비활성화합니다. 이 옵션을 사용하면, HTML 출력이 지원되더라도 HTML 출력을 사용하지 않습니다.

$ cargo run -- --no-html


## 2.3.2.2.33. `--html` 옵션

`--html` 옵션은 HTML 출력을 활성화합니다. 이 옵션을 사용하면, `--no-html` 옵션으로 명시적으로 지정된 경우에도 HTML 출력을 사용합니다.

$ cargo run -- --html


## 2.3.2.2.34. `--no-markdown` 옵션

`--no-markdown` 옵션은 Markdown 출력을 비활성화합니다. 이 옵션을 사용하면, Markdown 출력이 지원되더라도 Markdown 출력을 사용하지 않습니다.

$ cargo run -- --no-markdown


## 2.3.2.2.35. `--markdown` 옵션

`--markdown` 옵션은 Markdown 출력을 활성화합니다. 이 옵션을 사용하면, `--no-markdown` 옵션으로 명시적으로 지정된 경우에도 Markdown 출력을 사용합니다.

$ cargo run -- --markdown


## 2.3.2.2.36. `--no-text` 옵션

`--no-text` 옵션은 텍스트 출력을 비활성화합니다. 이 옵션을 사용하면, 텍스트 출력이 지원되더라도 텍스트 출력을 사용하지 않습니다.

$ cargo run -- --no-text


## 2.3.2.2.37. `--text` 옵션

`--text` 옵션은 텍스트 출력을 활성화합니다. 이 옵션을 사용하면, `--no-text` 옵션으로 명시적으로 지정된 경우에도 텍스트 출력을 사용합니다.

$ cargo run -- --text


## 2.3.2.2.38. `--no-binary` 옵션

`--no-binary` 옵션은 바이너리 출력을 비활성화합니다. 이 옵션을 사용하면, 바이너리 출력이 지원되더라도 바이너리 출력을 사용하지 않습니다.

$ cargo run -- --no-binary


## 2.3.2.2.39. `--binary` 옵션

`--binary` 옵션은 바이너리 출력을 활성화합니다. 이 옵션을 사용하면, `--no-binary` 옵션으로 명시적으로 지정된 경우에도 바이너리 출력을 사용합니다.

$ cargo run -- --binary


## 2.3.2.2.40. `--no-raw` 옵션

`--no-raw` 옵션은 원시 출력을 비활성화합니다. 이 옵션을 사용하면, 원시 출력이 지원되더라도 원시 출력을 사용하지 않습니다.

$ cargo run -- --no-raw


## 2.3.2.2.41. `--raw` 옵션

`--raw` 옵션은 원시 출력을 활성화합니다. 이 옵션을 사용하면, `--no-raw` 옵션으로 명시적으로 지정된 경우에도 원시 출력을 사용합니다.

$ cargo run -- --raw


## 2.3.2.2.42. `--no-format` 옵션

`--no-format` 옵션은 형식화된 출력을 비활성화합니다. 이 옵션을 사용하면, 형식화된 출력이 지원되더라도 형식화된 출력을 사용하지 않습니다.

$ cargo run -- --no-format


## 2.3.2.2.43. `--format` 옵션

`--format` 옵션은 형식화된 출력을 활성화합니다. 이 옵션을 사용하면, `--no-format` 옵션으로 명시적으로 지정된 경우에도 형식화된 출력을 사용합니다.

$ cargo run -- --format


## 2.3.2.2.44. `--no-pretty` 옵션

`--no-pretty` 옵션은 예쁜 출력을 비활성화합니다. 이 옵션을 사용하면, 예쁜 출력이 지원되더라도 예쁜 출력을 사용하지 않습니다.

$ cargo run -- --no-pretty


## 2.3.2.2.45. `--pretty` 옵션

`--pretty` 옵션은 예쁜 출력을 활성화합니다. 이 옵션을 사용하면, `--no-pretty` 옵션으로 명시적으로 지정된 경우에도 예쁜 출력을 사용합니다.

$ cargo run -- --pretty


## 2.3.2.2.46. `--no-compact` 옵션

`--no-compact` 옵션은 압축 출력을 비활성화합니다. 이 옵션을 사용하면, 압축 출력이 지원되더라도 압축 출력을 사용하지 않습니다.

$ cargo run -- --no-compact


## 2.3.2.2.47. `--compact` 옵션

`--compact` 옵션은 압축 출력을 활성화합니다. 이 옵션을 사용하면, `--no-compact` 옵션으로 명시적으로 지정된 경우에도 압축 출력을 사용합니다.

$ cargo run -- --compact


## 2.3.2.2.48. `--no-expand` 옵션

`--no-expand` 옵션은 확장 출력을 비활성화합니다. 이 옵션을 사용하면, 확장 출력이 지원되더라도 확장 출력을 사용하지 않습니다.

$ cargo run -- --no-expand


## 2.3.2.2.49. `--expand` 옵션

`--expand` 옵션은 확장 출력을 활성화합니다. 이 옵션을 사용하면, `--no-expand` 옵션으로 명시적으로 지정된 경우에도 확장 출력을 사용합니다.

$ cargo run -- --expand


## 2.3.2.2.50. `--no-collapse` 옵션

`--no-collapse` 옵션은 축소 출력을 비활성화합니다. 이 옵션을 사용하면, 축소 출력이 지원되더라도 축소 출력을 사용하지 않습니다.

$ cargo run -- --no-collapse


## 2.3.2.2.51. `--collapse` 옵션

`--collapse` 옵션은 축소 출력을 활성화합니다. 이 옵션을 사용하면, `--no-collapse` 옵션으로 명시적으로 지정된 경우에도 축소 출력을 사용합니다.

$ cargo run -- --collapse


## 2.3.2.2.52. `--no-wrap` 옵션

`--no-wrap` 옵션은 줄 바꿈 출력을 비활성화합니다. 이 옵션을 사용하면, 줄 바꿈 출력이 지원되더라도 줄 바꿈 출력을 사용하지 않습니다.

$ cargo run -- --no-wrap


## 2.3.2.2.53. `--wrap` 옵션

`--wrap` 옵션은 줄 바꿈 출력을 활성화합니다. 이 옵션을 사용하면, `--no-wrap` 옵션으로 명시적으로 지정된 경우에도 줄 바꿈 출력을 사용합니다.

$ cargo run -- --wrap


## 2.3.2.2.54. `--no-truncate` 옵션

`--no-truncate` 옵션은 잘림 출력을 비활성화합니다. 이 옵션을 사용하면, 잘림 출력이 지원되더라도 잘림 출력을 사용하지 않습니다.

$ cargo run -- --no-truncate


## 2.3.2.2.55. `--truncate` 옵션

`--truncate` 옵션은 잘림 출력을 활성화합니다. 이 옵션을 사용하면, `--no-truncate` 옵션으로 명시적으로 지정된 경우에도 잘림 출력을 사용합니다.

$ cargo run -- --truncate


## 2.3.2.2.56. `--no-ellipsis` 옵션

`--no-ellipsis` 옵션은 생략 부호 출력을 비활성화합니다. 이 옵션을 사용하면, 생략 부호 출력이 지원되더라도 생략 부호 출력을 사용하지 않습니다.

$ cargo run -- --no-ellipsis


## 2.3.2.2.57. `--ellipsis` 옵션

`--ellipsis` 옵션은 생략 부호 출력을 활성화합니다. 이 옵션을 사용하면, `--no-ellipsis` 옵션으로 명시적으로 지정된 경우에도 생략 부호 출력을 사용합니다.

$ cargo run -- --ellipsis


## 2.3.2.2.58. `--no-pager` 옵션

`--no-pager` 옵션은 페이저 출력을 비활성화합니다. 이 옵션을 사용하면, 페이저 출력이 지원되더라도 페이저 출력을 사용하지 않습니다.

$ cargo run -- --no-pager


## 2.3.2.2.59. `--pager` 옵션

`--pager` 옵션은 페이저 출력을 활성화합니다. 이 옵션을 사용하면, `--no-pager` 옵션으로 명시적으로 지정된 경우에도 페이저 출력을 사용합니다.

$ cargo run -- --pager


## 2.3.2.2.60. `--no-editor` 옵션

`--no-editor` 옵션은 편집기 출력을 비활성화합니다. 이 옵션을 사용하면, 편집기 출력이 지원되더라도 편집기 출력을 사용하지 않습니다.

$ cargo run -- --no-editor


## 2.3.2.2.61. `--editor` 옵션

`--editor` 옵션은 편집기 출력을 활성화합니다. 이 옵션을 사용하면, `--no-editor` 옵션으로 명시적으로 지정된 경우에도 편집기 출력을 사용합니다.

$ cargo run -- --editor


## 2.3.2.2.62. `--no-browser` 옵션

`--no-browser` 옵션은 브라우저 출력을 비활성화합니다. 이 옵션을 사용하면, 브라우저 출력이 지원되더라도 브라우저 출력을 사용하지 않습니다.

$ cargo run -- --no-browser


## 2.3.2.2.63. `--browser` 옵션

`--browser` 옵션은 브라우저 출력을 활성화합니다. 이 옵션을 사용하면, `--no-browser` 옵션으로 명시적으로 지정된 경우에도 브라우저 출력을 사용합니다.

$ cargo run -- --browser


## 2.3.2.2.64. `--no-clipboard` 옵션

`--no-clipboard` 옵션은 클립보드 출력을 비활성화합니다. 이 옵션을 사용하면, 클립보드 출력이 지원되더라도 클립보드 출력을 사용하지 않습니다.

$ cargo run -- --no-clipboard


## 2.3.2.2.65. `--clipboard` 옵션

`--clipboard` 옵션은 클립보드 출력을 활성화합니다. 이 옵션을 사용하면, `--no-clipboard` 옵션으로 명시적으로 지정된 경우에도 클립보드 출력을 사용합니다.

$ cargo run -- --clipboard


## 2.3.2.2.66. `--no-file` 옵션

`--no-file` 옵션은 파일 출력을 비활성화합니다. 이 옵션을 사용하면, 파일 출력이 지원되더라도 파일 출력을 사용하지 않습니다.

$ cargo run -- --no-file


## 2.3.2.2.67. `--file` 옵션

`--file` 옵션은 파일 출력을 활성화합니다. 이 옵션을 사용하면, `--no-file` 옵션으로 명시적으로 지정된 경우에도 파일 출력을 사용합니다.

$ cargo run -- --file


## 2.3.2.2.68. `--no-stdout` 옵션

`--no-stdout` 옵션은 표준 출력 출력을 비활성화합니다. 이 옵션을 사용하면, 표준 출력 출력이 지원되더라도 표준 출력 출력을 사용하지 않습니다.

$ cargo run -- --no-stdout


## 2.3.2.2.69. `--stdout` 옵션

`--stdout` 옵션은 표준 출력 출력을 활성화합니다. 이 옵션을 사용하면, `--no-stdout` 옵션으로 명시적으로 지정된 경우에도 표준 출력 출력을 사용합니다.

$ cargo run -- --stdout


## 2.3.2.2.70. `--no-stderr` 옵션

`--no-stderr` 옵션은 표준 오류 출력을 비활성화합니다. 이 옵션을 사용하면, 표준 오류 출력이 지원되더라도 표준 오류 출력을 사용하지 않습니다.

$ cargo run -- --no-stderr


## 2.3.2.2.71. `--stderr` 옵션

`--stderr` 옵션은 표준 오류 출력을 활성화합니다. 이 옵션을 사용하면, `--no-stderr` 옵션으로 명시적으로 지정된 경우에도 표준 오류 출력을 사용합니다.

$ cargo run -- --stderr


## 2.3.2.2.72. `--no-stdin` 옵션

`--no-stdin` 옵션은 표준 입력 출력을 비활성화합니다. 이 옵션을 사용하면, 표준 입력 출력이 지원되더라도 표준 입력 출력을 사용하지 않습니다.

$ cargo run -- --no-stdin


## 2.3.2.2.73. `--stdin` 옵션

`--stdin` 옵션은 표준 입력 출력을 활성화합니다. 이 옵션을 사용하면, `--no-stdin` 옵션으로 명시적으로 지정된 경우에도 표준 입력 출력을 사용합니다.

$ cargo run -- --stdin


## 2.3.2.2.74. `--no-tty` 옵션

`--no-tty` 옵션은 TTY 출력을 비활성화합니다. 이 옵션을 사용하면, TTY 출력이 지원되더라도 TTY 출력을 사용하지 않습니다.

$ cargo run -- --no-tty


## 2.3.2.2.75. `--tty` 옵션

`--tty` 옵션은 TTY 출력을 활성화합니다. 이 옵션을 사용하면, `--no-tty` 옵션으로 명시적으로 지정된 경우에도 TTY 출력을 사용합니다.

$ cargo run -- --tty


## 2.3.2.2.76. `--no-terminal` 옵션

`--no-terminal` 옵션은 터미널 출력을 비활성화합니다. 이 옵션을 사용하면, 터미널 출력이 지원되더라도 터미널 출력을 사용하지 않습니다.

$ cargo run -- --no-terminal


## 2.3.2.2.77. `--terminal` 옵션

`--terminal` 옵션은 터미널 출력을 활성화합니다. 이 옵션을 사용하면, `--no-terminal` 옵션으로 명시적으로 지정된 경우에도 터미널 출력을 사용합니다.

$ cargo run -- --terminal


## 2.3.2.2.78. `--no-console` 옵션

`--no-console` 옵션은 콘솔 출력을 비활성화합니다. 이 옵션을 사용하면, 콘솔 출력이 지원되더라도 콘솔 출력을 사용하지 않습니다.

$ cargo run -- --no-console


## 2.3.2.2.79. `--console` 옵션

`--console` 옵션은 콘솔 출력을 활성화합니다. 이 옵션을 사용하면, `--no-console` 옵션으로 명시적으로 지정된 경우에도 콘솔 출력을 사용합니다.

$ cargo run -- --console


## 2.3.2.2.80. `--no-shell` 옵션

`--no-shell` 옵션은 셸 출력을 비활성화합니다. 이 옵션을 사용하면, 셸 출력이 지원되더라도 셸 출력을 사용하지 않습니다.

$ cargo run -- --no-shell


## 2.3.2.2.81. `--shell` 옵션

`--shell` 옵션은 셸 출력을 활성화합니다. 이 옵션을 사용하면, `--no-shell` 옵션으로 명시적으로 지정된 경우에도 셸 출력을 사용합니다.

$ cargo run -- --shell


## 2.3.2.2.82. `--no-command` 옵션

`--no-command` 옵션은 명령 출력을 비활성화합니다. 이 옵션을 사용하면, 명령 출력이 지원되더라도 명령 출력을 사용하지 않습니다.

$ cargo run -- --no-command


## 2.3.2.2.83. `--command` 옵션

`--command` 옵션은 명령 출력을 활성화합니다. 이 옵션을 사용하면, `--no-command` 옵션으로 명시적으로 지정된 경우에도 명령 출력을 사용합니다.

$ cargo run -- --command


## 2.3.2.2.84. `--no-script` 옵션

`--no-script` 옵션은 스크립트 출력을 비활성화합니다. 이 옵션을 사용하면, 스크립트 출력이 지원되더라도 스크립트 출력을 사용하지 않습니다.

$ cargo run -- --no-script


## 2.3.2.2.85. `--script` 옵션

`--script` 옵션은 스크립트 출력을 활성화합니다. 이 옵션을 사용하면, `--no-script` 옵션으로 명시적으로 지정된 경우에도 스크립트 출력을 사용합니다.

$ cargo run -- --script


## 2.3.2.2.86. `--no-program` 옵션

`--no-program` 옵션은 프로그램 출력을 비활성화합니다. 이 옵션을 사용하면, 프로그램 출력이 지원되더라도 프로그램 출력을 사용하지 않습니다.

$ cargo run -- --no-program


## 2.3.2.2.87. `--program` 옵션

`--program` 옵션은 프로그램 출력을 활성화합니다. 이 옵션을 사용하면, `--no-program` 옵션으로 명시적으로 지정된 경우에도 프로그램 출력을 사용합니다.

$ cargo run -- --program


## 2.3.2.2.88. `--no-application` 옵션

`--no-application` 옵션은 애플리케이션 출력을 비활성화합니다. 이 옵션을 사용하면, 애플리케이션 출력이 지원되더라도 애플리케이션 출력을 사용하지 않습니다.

$ cargo run -- --no-application


## 2.3.2.2.89. `--application` 옵션

`--application` 옵션은 애플리케이션 출력을 활성화합니다. 이 옵션을 사용하면, `--no-application` 옵션으로 명시적으로 지정된 경우에도 애플리케이션 출력을 사용합니다.

$ cargo run -- --application


## 2.3.2.2.90. `--no-service` 옵션

`--no-service` 옵션은 서비스 출력을 비활성화합니다. 이 옵션을 사용하면, 서비스 출력이 지원되더라도 서비스 출력을 사용하지 않습니다.

$ cargo run -- --no-service


## 2.3.2.2.91. `--service` 옵션

`--service` 옵션은 서비스 출력을 활성화합니다. 이 옵션을 사용하면, `--no-service` 옵션으로 명시적으로 지정된 경우에도 서비스 출력을 사용합니다.

$ cargo run -- --service


## 2.3.2.2.92. `--no-daemon` 옵션

`--no-daemon` 옵션은 데몬 출력을 비활성화합니다. 이 옵션을 사용하면, 데몬 출력이 지원되더라도 데몬 출력을 사용하지 않습니다.

$ cargo run -- --no-daemon


## 2.3.2.2.93. `--daemon` 옵션

`--daemon` 옵션은 데몬 출력을 활성화합니다. 이 옵션을 사용하면, `--no-daemon` 옵션으로 명시적으로 지정된 경우에도 데몬 출력을 사용합니다.

$ cargo run -- --daemon


## 2.3.2.2.94. `--no-server` 옵션

`--no-server` 옵션은 서버 출력을 비활성화합니다. 이 옵션을 사용하면, 서버 출력이 지원되더라도 서버 출력을 사용하지 않습니다.

$ cargo run -- --no-server


## 2.3.2.2.95. `--server` 옵션

`--server` 옵션은 서버 출력을 활성화합니다. 이 옵션을 사용하면, `--no-server` 옵션으로 명시적으로 지정된 경우에도 서버 출력을 사용합니다.

$ cargo run -- --server


## 2.3.2.2.96. `--no-client` 옵션

`--no-client` 옵션은 클라이언트 출력을 비활성화합니다. 이 옵션을 사용하면, 클라이언트 출력이 지원되더라도 클라이언트 출력을 사용하지 않습니다.

$ cargo run -- --no-client


## 2.3.2.2.97. `--client` 옵션

`--client` 옵션은 클라이언트 출력을 활성화합니다. 이 옵션을 사용하면, `--no-client` 옵션으로 명시적으로 지정된 경우에도 클라이언트 출력을 사용합니다.

$ cargo run -- --client


## 2.3.2.2.98. `--no-host` 옵션

`--no-host` 옵션은 호스트 출력을 비활성화합니다. 이 옵션을 사용하면, 호스트 출력이 지원되더라도 호스트 출력을 사용하지 않습니다.

$ cargo run -- --no-host


## 2.3.2.2.99. `--host` 옵션

`--host` 옵션은 호스트 출력을 활성화합니다. 이 옵션을 사용하면, `--no-host` 옵션으로 명시적으로 지정된 경우에도 호스트 출력을 사용합니다.

$ cargo run -- --host


## 2.3.2.2.100. `--no-port` 옵션

`--no-port` 옵션은 포트 출력을 비활성화합니다. 이 옵션을 사용하면, 포트 출력이 지원되더라도 포트 출력을 사용하지 않습니다.

$ cargo run -- --no-port


## 2.3.2.2.101. `--port` 옵션

`--port` 옵션은 포트 출력을 활성화합니다. 이 옵션을 사용하면, `--no-port` 옵션으로 명시적으로 지정된 경우에도 포트 출력을 사용합니다.

$ cargo run -- --port


## 2.3.2.2.102. `--no-address` 옵션

`--no-address` 옵션은 주소 출력을 비활성화합니다. 이 옵션을 사용하면, 주소 출력이 지원되더라도 주소 출력을 사용하지 않습니다.

$ cargo run -- --no-address


## 2.3.2.2.103. `--address` 옵션

`--```sh
sonar install mcp --generic --print
sonar install skills --print
sonar install hooks --print
# check

---

[Read more](https://github.com/raskrebs/sonar)

카테고리