본문으로 건너뛰기

© 2026 Molayo

GH Trending릴리즈2026. 05. 06. 22:07

portless: 로컬 개발용 포트 번호를 안정적인 네임드 .localhost URL 로 교체합니다

요약

Portless는 로컬 개발 환경에서 일반적인 포트 번호(예: `localhost:3000`) 대신 안정적이고 명명된 `.localhost` URL을 사용하여 애플리케이션에 접근할 수 있게 해주는 도구입니다. 이 도구를 사용하면 개발 서버가 자동으로 HTTPS/HTTP2를 지원하며, 복잡한 프록시 설정 없이도 브라우저에서 `https://myapp.localhost`와 같은 형태로 앱에 접속할 수 있습니다. Portless는 프로젝트의 구조(워크스페이스)를 인식하여 애플리케이션 이름을 추론하고, 여러 패키지를 관리하는 대규모 모노레포 환경에서도 일관된 개발 경험을 제공합니다.

핵심 포인트

  • 로컬 포트 번호 대신 안정적인 네임드 `.localhost` URL (예: `myapp.localhost`) 사용
  • 자동으로 HTTPS/HTTP2를 활성화하고 로컬 CA를 생성하여 브라우저 경고 없이 안전한 환경 제공
  • 프록시 설정이 자동으로 이루어지며, 다양한 프레임워크의 개발 서버(Next.js, Vite 등)와 호환성을 유지함
  • 모노레포 구조(Workspaces)를 인식하여 여러 패키지의 개발 서버를 중앙에서 관리하고 일관된 도메인 네이밍을 제공함

로컬 개발을 위해 포트를 번호 대신 안정적이고 명명된 .localhost URL 로 대체합니다. 인간과 에이전트를 위해.

- "dev": "next dev" # http://localhost:3000
+ "dev": "portless run next dev" # https://myapp.localhost

글로벌 (권장):

npm install -g portless

또는 프로젝트 개발 의존성으로:

npm install -D portless

portless 는 1.0 이전 버전입니다. 프로젝트별로 설치되면 기여자들이 다른 버전을 실행할 수 있습니다. 릴리스 간 상태 디렉터리 형식이 변경될 수 있어 portless trust 를 다시 실행해야 할 수 있습니다.

portless myapp next dev
# -> https://myapp.localhost

HTTP/2 와 HTTPS 가 기본으로 활성화됩니다. 첫 실행 시 portless 는 로컬 CA 를 생성하고, 이를 신뢰하며, 포트 443 을 바인딩합니다 (macOS/Linux 에서 sudo 와 함께 자동 승격). --no-tls 를 사용하여 평문 HTTP 를 사용하세요.

프로кси는 앱을 실행할 때 자동으로 시작됩니다. 무작위 포트 (4000-4999) 는 PORT 환경 변수를 통해 할당됩니다. 대부분의 프레임워크 (Next.js, Express, Nuxt 등) 는 이를 자동으로 존중합니다. PORT 를 무시하는 프레임워크 (Vite, VitePlus, Astro, React Router, Angular, Expo, React Native) 의 경우 portless 는 올바른 --port 플래그를 자동 주입하고 필요시 일치하는 --host 플래그를 추가합니다.

자동 시작 시 portless 는 가장 최근 프로кси 실행의 설정 (포트, TLS, TLD) 을 재사용하므로 재시작 또는 부팅 후 기본값으로 침묵하여 되돌아가지 않습니다. 명시적인 환경 변수 (PORTLESS_PORT, PORTLESS_HTTPS 등) 는 항상 우선권을 갖습니다.

비상호작용 환경 (TTY 없음 또는 CI=1) 에서 portless 는 프롬프트 대신 설명 가능한 에러로 종료하므로 task runners 와 CI 스크립트가 명확한 메시지와 함께 초기 실패합니다.

Bare portless 는 바로 작동합니다. package.json"dev" 스크립트를 프로кси를 통해 실행하며, 패키지명, git 루트 또는 디렉터리에서 앱 이름을 추론합니다:

portless # -> "dev" 스크립트 실행, https://<project>.localhost

기본값을 오버라이드하기 위해 선택적인 portless.json 을 사용하세요:

{ "name": "myapp" }

portless # -> "dev" 스크립트 실행, https://myapp.localhost

스크립트는 기본값이 "dev" 입니다. 설정에서 명시되지 않으면 패키지명에서 이름을 추론합니다.

리포지토리 루트에 하나의 portless.json 은 모든 워크스페이스 패키지를 커버합니다. Portless 는 pnpm-workspace.yaml 또는 package.json"workspaces" 필드 (npm, yarn, bun) 에서 패키지를 발견합니다:

{
"apps": {
"apps/web": { "name": "myapp" },
...
portless # 리포지토리 루트에서: 모든 워크스페이스 패키지가 "dev" 스크립트를 실행합니다
`cd apps/web && portless # 하나의 패키지만 시작`

apps 맵은 선택적이며 이름 오버라이드에만 필요합니다. 목록되지 않은 패키지는 package.json 에서 추론된 이름으로 자동 발견됩니다.

apps 맵이 없는 경우 호스트명은 <package>.<project>.localhost 관습을 따릅니다. 프로젝트명은 워크스페이스 패키지의 가장 일반적인 npm 스펙트 (예: @myorg/web@myorg/apimyorg) 에서 비롯되며, 워크스페이스 루트 디렉터리 이름으로 후퇴합니다. 패키지의 짧은 이름이 프로젝트명과 일치하면 중복 없이 <project>.localhost 만 사용합니다.

FieldTypeDefaultDescription
name
stringinferredBase app name. Worktree prefix still applies.
script
string`

(mDNS/Bonjour 와 충돌) 및 .dev

(Google 소유, HSTS 를 통해 HTTPS 강제).

flowchart TD
Browser["Browser<br>myapp.localhost"]
Proxy["portless proxy<br>(port 80 또는 443)"]
...

프록시 시작: 앱을 실행할 때 자동 시작하거나, portless proxy start 를 명시적으로 실행하여 시작.

앱 실행: portless <name> <command>

무료 포트를 할당하고 프록시에 등록합니다.URL 을 통한 접근: https://<name>.localhost

프록시를 통해 앱으로 라우팅됩니다. HTTP/2 와 HTTPS 가 기본적으로 활성화됩니다. 브라우저는 호스트당 6 개 연결까지만 HTTP/1.1 을 제한하여, 많은 언바인드 파일 (Vite, Nuxt 등) 을 제공하는 개발 서버를 병목 현상 (bottleneck) 으로 만듭니다. HTTP/2 는 단일 연결을 통해 모든 요청을 멀티플렉싱합니다.

첫 실행 시, portless 는 로컬 CA 를 생성하고 시스템 신뢰 스토어에 추가합니다. 브라우저 경고 없음. 수동 설정 없음.

# 자체 인증서 사용 (예: mkcert 에서)
portless proxy start --cert ./cert.pem --key ./key.pem
# HTTPS 비활성화 (포트 80 의 평문 HTTP)
...

Linux 에서는 portless trust

Debian/Ubuntu, Arch, Fedora/RHEL/CentOS, 및 openSUSE 를 지원하며 (update-ca-certificates

또는 update-ca-trust

). Windows 는 CA 를 시스템 신뢰 스토어에 추가하기 위해 certutil

을 사용합니다.

portless proxy start --lan
portless proxy start --lan --https
portless proxy start --lan --ip 192.168.1.42

--lan

프록시를 mDNS 발견으로 전환합니다: 서비스는 <name>.local

로 광고되며, 동일한 네트워크의 모든 장치에서 접근 가능합니다. Portless 는 LAN IP 를 자동 감지하고 Wi-Fi/IP 변경을 자동으로 따릅니다. 그러나 --ip <address>

또는 PORTLESS_LAN_IP

을 내보내어 다른 주소를 고정할 수 있습니다. 쉘에 PORTLESS_LAN=1

(0/1 보울) 을 설정하면 프록시 시작 시마다 LAN 모드가 기본값이 됩니다.

Portless 는 proxy.lan

을 통해 LAN 모드를 기억하므로, LAN 프록시를 중지하고 다시 시작하면 LAN 모드에서 계속됩니다. 모든 프록시 설정 (포트, TLS, TLD, LAN) 은 자동 시작 시 명시적 플래그 또는 환경 변수로 덮어쓰지 않는 한 지속되어 재사용됩니다. PORTLESS_LAN=0

을 사용하여 .localhost

모드로 전환할 수 있습니다. 이미 다른 명시적 LAN/TLS/TLD 설정으로 프록시가 실행 중이라면, portless 는 경고하고 먼저 중지하라고 요청합니다.

LAN 모드는 portless 가 이미 스폰하는 시스템 mDNS 도구에 의존합니다: macOS 는 dns-sd

을 제공하며, Linux 는 avahi-utils

avahi-publish-address

을 사용합니다 (설치: sudo apt install avahi-utils

또는 해당 분포의 동등한 도구). 명령어가 누락되거나 네트워크에 접근할 수 없으면, portless proxy start --lan

은 관련 오류를 출력하고 종료합니다.

  • Next.js: .local

호스트네임을 allowedDevOrigins

에 추가하세요:// next.config.js 모듈 export = { allowedDevOrigins: ["myapp.local", "*.myapp.local"], };

  • Expo / React Native: portless 는 항상 --port

을 주입합니다. React Native 도 --host 127.0.0.1

을 받습니다. Expo 는 LAN 모드 외부에서 --host localhost

를 받지만, LAN 모드에서는 portless 는 Metro 를 기본 LAN 호스트 동작에 맡기고 --host

또는 HOST

을 강요하지 않습니다.

Tailscale 네트워크의 팀메이트와 개발 서버를 공유하세요:

portless myapp --tailscale next dev
# -> https://myapp.localhost (local)
# -> https://devbox.yourteam.ts.net (tailnet)

--tailscale

앱은 자체 Tailscale HTTPS 포트에서 루트 마운트되므로, 프레임워크 basePath

구성이 필요하지 않습니다. 첫 번째 앱은 포트 443 을 받으며, 이후 앱은 8443, 8444 등을 받습니다.

portless myapp --tailscale next dev # -> https://devbox.ts.net
portless api --tailscale pnpm start # -> https://devbox.ts.net:8443

Tailscale Funnel 을 사용하여 개발 서버를 공개 인터넷에 노출합니다:

portless myapp --funnel next dev
# -> https://devbox.yourteam.ts.net (public)

shell profile 또는 .envPORTLESS_TAILSCALE=1 을 설정하여 모든 앱을 기본적으로 공유할 수 있습니다. portless list 는 로컬 및 tailnet URL 을 모두 표시합니다. 앱이 종료될 때 Tailscale serve 등록은 자동으로 정리됩니다.

Tailscale CLI 가 설치되고 연결되어 있어야 합니다 (tailscale up).

portless # 개발 스크립트를 프록시 통해 실행
portless # monorepo 루트: 모든 workspace 패키지를 실행
portless run [--name <name>] [cmd] [args...] # 이름 추론, 프록시 통해 실행
...
-p, --port <number> 프록시 포트를 설정 (기본값: 443, 또는 --no-tls 와 함께 80)
--no-tls HTTPS 비활성화 (포트 80 에서 평문 HTTP 사용)
--https HTTPS 활성화 (기본값, 호환성을 위해 허용됨)
...
# 설정
PORTLESS_PORT=<number> 기본 프록시 포트를 덮어쓰기
PORTLESS_APP_PORT=<number> 앱에 고정된 포트 사용 (--app-port 와 동일)
...

run, get, alias, hosts, list, trust, clean, prune, 및 proxy 는 서브명령어이며 직접 앱 이름으로 사용할 수 없습니다. 프로젝트에서 이름을 추론하려면 portless run <cmd> 를 사용하거나, 예약된 이름 포함 모든 이름을 강제하려면 portless --name <name> <cmd> 를 사용하세요.

portless 데이터를 컴퓨터에서 제거하려면 (프록시 상태 (~/.portless), 시스템 상태 디렉터리, portless 가 설치할 때 OS 신뢰 스토어에서 로컬 CA, 및 /etc/hosts 의 portless 블록):

portless clean

macOS/Linux 는 sudo 를 요청할 수 있습니다. --cert--key 와 함께 전달된 커스텀 인증서 경로는 삭제되지 않습니다.

.localhost 서브도메인은 Chrome, Firefox, Edge 에서 자동으로 127.0.0.1 로 해결됩니다. Safari 는 시스템 DNS 해결기를 사용하며 모든 구성에서 .localhost 서브도메인을 처리하지 않을 수 있습니다.

Safari 가 .localhost URL 을 찾을 수 없는 경우:

portless hosts sync # 현재 라우트를 /etc/hosts 에 추가
portless hosts clean # 나중에 정리

기본적으로 /etc/hosts 를 라우트 호스트명으로 자동 동기화합니다 (.localhost, 커스텀 TLD, LAN .local). PORTLESS_SYNC_HOSTS=0 을 설정하여 비활성화할 수 있습니다.

프론트엔드 개발 서버 (예: Vite, webpack) 가 다른 portless 앱에 API 요청을 프록시하는 경우, 프록시가 Host 헤더를 재작성하도록 확인하세요. 이를 하지 않으면 portless 는 프론트엔드로 무한 루프로 요청을 라우팅합니다.

Vite (vite.config.ts):

server: {
proxy: {
"/api": {
...

webpack-dev-server (webpack.config.js):

devServer: {
proxy: [{
context: ["/api"],
...

Portless 는 자식 프로세스에서 NODE_EXTRA_CA_CERTS 를 자동으로 설정하여 Node.js 가 portless CA 를 신뢰하게 합니다. portless 외부에서 별도의 Node.js 프로세스를 실행하는 경우, CA 를 수동으로 지시하세요: NODE_EXTRA_CA_CERTS=~/.portless/ca.pem. 또는 평문 HTTP 를 위해 --no-tls 를 사용하세요.

Portless 는 이 잘못된 구성을 감지하고 해당 수정을 지시하는 메시지와 함께 508 Loop Detected 를 반환합니다.

이 저장소는 Turborepo 를 사용하는 pnpm workspace monorepo 입니다. publishable 패키지는 packages/portless/ 에 있습니다.

pnpm install # 모든 의존성 설치
pnpm build # 모든 패키지 빌드
pnpm test # 테스트 실행
...
  • Node.js 20+
  • macOS, Linux, 또는 Windows
  • Tailscale CLI (선택 사항, --tailscale--funnel 에 사용)

AI 자동 생성 콘텐츠

본 콘텐츠는 GitHub Trending TypeScript (weekly)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
1

댓글

0