로컬 개발 환경 관리자 — PHP/Python/Next.js 프로젝트를 Apache vhost + MariaDB/PostgreSQL로 관리합니다.
- 프로젝트 관리: PHP/Python/Next.js 프로젝트를
*.localhost도메인으로 등록/etc/hosts자동 수정- Apache 가상호스트 자동 생성 (
/etc/apache2/sites-available/) - PHP: DocumentRoot 직접 서빙 / Python·Next.js: ProxyPass로 dev server 포트로 포워드
- 하위 디렉토리 지원: 앱이 저장소 루트가 아니라 하위 폴더에 있어도 등록 가능
(예:
easyhost/app). PHP는 이 값이 DocumentRoot가 되므로 Laravel의public등에 쓸 수 있다 - Python 환경 자동 설정: venv 생성,
requirements.txt/pyproject.toml자동 감지, npm 프론트엔드 빌드 포함 - Next.js 지원: 폴더를 고르면 Next.js 앱 위치를 자동 감지해 타입·하위 디렉토리·실행 명령을 채운다
- lockfile을 보고 pnpm/yarn/bun/npm 중 맞는 것으로 의존성 설치 (매니저가 없으면 corepack 경유)
- HMR(웹소켓)까지 프록시하므로 개발 중 새로고침 없이 반영된다
- 서버 제어: dev server 시작/중지 (포트 자동 할당 5001~6000)
- 서비스 제어: Apache2, MariaDB, PostgreSQL systemctl 시작/중지
- 데이터베이스 관리: MariaDB/PostgreSQL DB 생성/삭제, 백업/복원, 사용자 관리
프로젝트 목록은 ~/.local/share/localman/projects.json에 저장됩니다.
[
{
"id": "myproject",
"name": "My Project",
"path": "/home/user/projects/myproject",
"domain": "myproject.localhost",
"project_type": "Python",
"port": 5001,
"start_command": "venv/bin/python3 run_server.py 5001"
},
{
"id": "easyhost",
"name": "easyhost",
"path": "/home/user/dev/easyhost",
"domain": "easyhost.localhost",
"project_type": "NextJs",
"port": 5002,
"start_command": "npx next dev --port 5002",
"app_dir": "app"
}
]project_type:Php|Python|NextJsapp_dir: 실행·DocumentRoot 기준이 되는 하위 디렉토리 (없으면path그대로)start_command는 셸을 거치지 않고 그대로 실행된다 →&&, 파이프,FOO=1 cmd같은 셸 문법은 쓸 수 없다- Next.js는 저장 시 명령어의
--port값이 할당된 포트에 자동으로 맞춰진다
다른 도구에서 이 파일을 읽어 포트/상태를 연동할 수 있습니다.
- Apache2 (
sudo apt install apache2)- Next.js의 HMR에는
proxy_wstunnel·rewrite모듈이 필요하다 (없으면 프로젝트 추가 시 자동 활성화)
- Next.js의 HMR에는
- MariaDB (
sudo apt install mariadb-server) - PostgreSQL (
sudo apt install postgresql postgresql-client) - Python3, Node.js/npm
- sudoers 설정 (Apache 제어용)
# install-sudoers.sh 실행으로 설정
sudo ./scripts/linux/install-sudoers.sh- Homebrew — httpd·PostgreSQL은 서비스 탭의 [설치하기]로
brew install된다 - DB 서버(MariaDB 자리): Homebrew가 Intel 맥용 MariaDB를 더 이상 미리 빌드해 주지 않아(Xcode 없이는 빌드 불가),
[설치하기]는 MySQL 8.4 LTS 공식 바이너리를
<데이터 폴더>/tools/mysql에 설치한다 (sudo 불필요, 3306·/tmp/mysql.sock, root 비밀번호root). 서비스 탭에는 "MySQL"로 보인다. Homebrew MariaDB가 이미 있으면 그것을 쓴다 - 개발 도구의 PHP는 Apache 모듈까지 본다 — MAMP 등의 php 명령만 있고 Homebrew
libphp가 없으면 "Apache 연결 안 됨" - Apache는 Homebrew
httpd를 80포트로 띄운다. vhost는$(brew --prefix)/etc/httpd/localman/*.conf에 생기고,httpd.conf끝에 localman 관리 블록(Listen 80, 실행 사용자, proxy·rewrite 모듈, mod_php, Include)이 들어간다 - PHP 프로젝트는
brew install php가 있으면 mod_php로 연결된다 - sudoers 설정 (80포트 httpd 제어, /etc/hosts, 로그):
sudo ./scripts/macos/install-sudoers.sh서비스 탭 아래 [개발 도구]에서 버전을 보고, 없거나 오래됐으면 설치한다.
- Node.js: nodejs.org 배포 목록에서 그때의 LTS를 골라 공식 SHA-256으로 검증한 뒤
<데이터 폴더>/tools/node에 푼다. sudo가 필요 없고 맥·리눅스가 같으며, 앱이 시작할 때 PATH 맨 앞에 둔다. 깔린 Node는 공식 릴리스 일정 (nodejs/Releaseschedule.json)으로 Active LTS · 유지보수 LTS(종료 시기) · LTS 아님 · 지원 종료를 구분해 보여준다 - Python 3, PHP, rsync: 맥은 Homebrew, 리눅스는 apt (
install-sudoers.sh에 고정된 패키지만 허용). PHP를 깔면 Apache에 mod_php를 연결한다
- 프로젝트 탭 위 검색창에서 도메인·이름·ID로 걸러낸다
- [용량 계산]: 사이트마다 파일(그중 node_modules·venv 등 의존성 몫)·DB·합계, 맨 위에 전체 합계. 앱을 켤 때 한 번 잰다
- 사이트의 DB는 수정 화면에서 지정한다. 비우면 라이믹스
files/config/config.php,.env(DB_DATABASE·DATABASE_URL)에서 자동으로 찾는다 (비밀번호는 읽지 않는다)
프로젝트 ⋯ → [서버 배포·가져오기]. 서버(주소·포트·사용자·웹 경로, 비밀번호 또는 SSH 키)와 서버 DB(호스트·포트·이름·사용자·비밀번호)를 넣는다.
호스팅 DB가 외부 접속을 막으면 **[SSH로 서버에 들어가서 접속]**을 켠다 — 서버 안에서 mysqldump/mysql을 돌린다.
서버에서 가져오기 (↓) — 실서버를 로컬로 받아 테스트할 때
- [받을 파일 보기] / [파일 가져오기]: 서버 웹 경로를 프로젝트 폴더로 rsync (서버에 rsync가 없으면 tar로 전부).
로컬에만 있는 파일은 지우지 않고, 로컬 설정 파일(
files/config/,.env,wp-config.php…)이 이미 있으면 덮지 않는다 - [DB 가져오기]: 서버 DB를 받아(
<데이터 폴더>/pull-backups/에 보관) 고른 로컬 DB에 넣는다. 같은 이름의 로컬 DB는 먼저 백업한다. 사이트에 DB를 연결하고, 사이트 설정에 적힌 DB 계정도 만든다 - [모두 가져오기]: 파일 → DB 순서로 한 번에
서버로 올리기 (↑)
- [연결 시험]: SSH·웹 경로·서버 rsync·서버 DB 접속 확인
- [미리보기]: 올라갈 파일 목록만 본다 (
rsync --dry-run) - [파일 올리기]: 바뀐 파일만 rsync로 올린다. 서버에만 있는 파일은 지우지 않는다.
.git/,node_modules/,.env등은 기본으로 빼며(라이믹스는files/config/·files/cache/등도) 목록은 고칠 수 있다 - [DB 올리기]: 확인을 한 번 더 받은 뒤, 서버 DB를 이 PC의
<데이터 폴더>/deploy-backups/에 먼저 백업하고 로컬 DB로 덮어쓴다. 백업이 실패하면 덮어쓰지 않는다
rsync가 없거나 명령 실행이 막힌 호스팅(카페24 등)은 자동으로 SFTP를 쓴다 — 폴더를 훑어 크기·수정 시각이 다른 파일만
8개씩 동시에 주고받는다. [연결 시험]은 웹 경로를 못 찾으면 홈 아래(www, public_html …)에서 찾아 저장한다.
서버 DB 호스트는 이 PC에서 본 주소다 — 사이트 설정의 localhost가 아니라 서버 주소(카페24는 사이트 도메인 + 외부 접속 허용 IP 등록)를 넣는다.
넣을 로컬 DB가 없으면 새로 만든다.
SSH 비밀번호는 SSH_ASKPASS로 넘겨 명령줄에 남기지 않는다. 키 로그인이면 ssh-copy-id -p 포트 사용자@서버로 한 번 등록한다.
접속 정보는 deploy.json(권한 600)에 저장되고 백업 묶음에는 담기지 않는다. 기록은 ⋯ 메뉴의 이전 기록에 "↓ 가져옴"·"↑ 배포"로 남는다.
프로젝트 ⋯ 메뉴 (사이트 종류를 알아보면 나온다)
- [관리자 비밀번호]: 관리자 목록을 보여 주고 사이트 DB의 비밀번호를 바로 바꾼다 (라이믹스·XE bcrypt, 워드프레스 MD5 → 다음 로그인 때 워드프레스가 다시 저장)
- [로컬 DB로 설정 바꾸기]:
files/config/config.php(라이믹스)·files/config/db.config.php(XE)·wp-config.php의 DB 접속을 localhost·로컬 DB로 바꾸고, 그 계정을 이 PC의 DB에 만든다. 사이트 주소(설정 파일·라이믹스domains·워드프레스siteurl/home)도 로컬 도메인으로. 처음 바꿀 때 서버 원본을<파일>.localman-server로 보관하고 [서버 원본으로 되돌리기]로 되돌릴 수 있다. 서버로 올릴 때 이 설정 파일과 백업은 자동으로 뺀다
설정 → 자동화. AI나 스크립트가 사이트 목록, 서버 켜기·끄기, 프로젝트 추가, DB 목록·만들기·백업, 사이트 DB 계정 만들기, 서버에서 가져오기를 할 수 있다. DB·프로젝트 삭제와 서버로 올리기는 열지 않는다.
# Claude Code 에 MCP 서버로 등록 (설정 화면의 [Claude Code 명령 복사]와 같다) — `-s user` 가 없으면 등록한 폴더에서만 보인다
# 다른 프로젝트에서 쓰는 법: docs/mcp-guide.md
claude mcp add -s user localman -- /Applications/LocalMan.app/Contents/MacOS/LocalMan --mcp
# HTTP API — 설정에서 켜면 앱이 떠 있는 동안 127.0.0.1:47810 에서만 듣는다 (토큰 필요)
curl -H "Authorization: Bearer <토큰>" http://127.0.0.1:47810/api/tools
curl -H "Authorization: Bearer <토큰>" -X POST http://127.0.0.1:47810/api/tools/pull_from_server \
-d '{"id":"eond","files":true,"db":true}'프로젝트 폴더의 ops/*.plist 를 그 프로젝트의 예약 작업으로 보고, 프로젝트 ⋯ → [예약 작업]에서 켜기·끄기·지금 실행·로그를 본다
(MCP·API: list_jobs·set_job·run_job). 켜면 ~/Library/LaunchAgents 로 복사해 launchctl bootstrap, 끄면 내리고 복사본을 지운다.
projects.json 은 바꾸지 않는다. 구현: src/domain/jobs.rs.
eondctl 도구로 eond.com 배포 도구를 부른다 — eondctl(action="status"|"dry-run"|"quick"|"full"|…), eondctl_status 로 어느 길로 부를지 확인.
eondctl 로컬 API(127.0.0.1:47811, 토큰은 ~/.config/eondctl/config.json)가 떠 있으면 API 로, 아니면 설치된 ~/.local/bin/eondctl CLI 로 실행한다.
거꾸로 eondctl 은 이 앱의 HTTP API(start_project·stop_project)로 로컬 서버를 켜고 끈다 — 그래서 HTTP API 를 켜 두면 eondctl 로컬 탭의 켜기·끄기가 LocalMan 을 거친다.
구현: src/domain/eondctl.rs.
서비스 탭의 [HTTPS 켜기]로 모든 프로젝트를 https://<도메인>으로도 연다 (http도 그대로 열려 있다).
- Let's Encrypt는
*.localhost에 발급하지 않으므로 mkcert와 같은 방식을 쓴다: localman 전용 인증기관을 만들어 OS에 한 번 신뢰시키고, 도메인마다 인증서(1년)를 발급한다. 만료 30일 전이면 앱 시작 때와 6시간마다 자동으로 다시 발급하고 Apache를 reload한다 - 인증기관은 이름 제약으로
localhost와*.localhost에만 발급할 수 있다 — 키가 새도 다른 사이트를 위조할 수 없다 - 인증기관 키는 이 PC에만 있다(
<데이터 폴더>/tls/, 권한 600). 백업 묶음에도 담지 않는다 - 신뢰 등록: 맥은 로그인 키체인(암호 확인 창이 뜬다), 리눅스는 시스템 저장소 + 크롬·파이어폭스 NSS DB
(
libnss3-tools의certutil필요) - 앱 안에서는
X-Forwarded-Proto: https가 붙어 프록시 뒤 앱도 https로 접속된 걸 안다
- 받는 PC: [백업·이전] → [받기 대기]. 6자리 코드가 나온다
- 보내는 PC: [받는 PC 찾기] → PC 선택(못 찾으면 IP 입력) → 코드 입력 → [보내기]
- 보낼 것: 전체 또는 프로젝트 하나. 프로젝트 목록의 더보기(⋯) → [다른 PC로 보내기]로도 시작한다
- 함께 옮길 DB와 DB 접속 정보는 [함께 옮길 데이터베이스]에서 고른다
- rsync처럼 크기·수정 시각을 비교해 바뀐 파일만 보낸다. 받는 쪽에만 있는 파일은 지우지 않는다
node_modules,venv,.venv,__pycache__,.next등 의존성·빌드 폴더는 보내지 않는다 → 받은 PC에서 패키지 설치- 받는 쪽 파일이 더 최신이면 기본으로 건너뛰고 결과에 알린다 (덮어쓰기를 켜면 보내는 쪽 기준)
- 받는 PC는 자기 홈 폴더 안에만 쓴다. 보내는 쪽 홈 밖의 프로젝트는
~/localman-received/<id>로 받는다 - 설정·DB는 아래 백업 파일과 같은 형식으로 함께 보내고, 받는 PC가 같은 가져오기 규칙으로 적용한다
- 보안: 코드로 SPAKE2 키 합의 후 ChaCha20-Poly1305로 암호화. 엿본 트래픽으로 코드를 대입해 풀 수 없고, 받는 쪽은 틀린 코드 3번이면 멈춘다. 코드는 한 번만 쓴다
- 포트: 발견 UDP 47800, 전송 TCP 47801. 맥은 처음 받을 때 방화벽이 수신 연결 허용을 물을 수 있다
- 보내고 받은 기록은 [최근 이전 기록]과 프로젝트 줄("↗ 10/03 14:20 맥북(으)로 보냄"), 더보기 메뉴에 남는다
MAMP가 있으면 [백업·이전]에 나타난다. MAMP의 가상호스트(폴더가 남은 것)와 MySQL DB를 골라 로컬맨 백업 파일과 같은 형식으로 만든다 → [이 PC로 바로 가져오기] 또는 리눅스로 옮겨 가져오기.
- DB 크기는 MAMP 데이터 폴더에서 바로 잰다 (MySQL을 켜지 않는다)
- MAMP MySQL이 꺼져 있으면 덤프하는 동안만 포트 없이 소켓으로 띄웠다가 다시 내린다 (로컬맨 MySQL 3306과 충돌 없음)
- MAMP 폴더는 읽기만 한다. MAMP MySQL 계정 기본값 root/root
[백업·이전] 탭에서 한 파일(localman-backup-<호스트>-<날짜>.tar.gz)로 내보내고 다른 PC에서 가져온다.
- 담기는 것: 프로젝트 설정(
projects.json), 고른 DB의 덤프, (선택) DB 접속 정보 — 비밀번호가 평문이므로 파일 관리에 주의 - 담기지 않는 것: 프로젝트 소스 폴더. git 등으로 따로 옮긴다
- 가져올 때 경로 앞부분을 바꾼다 (기본값: 만든 PC의 홈 → 이 PC의 홈, 예
/home/dell→/Users/eond) - 이미 있는 프로젝트·DB는 기본으로 건너뛰고, 덮어쓰기를 켜면 바꾼다. 포트가 겹치면 빈 포트로 옮긴다
- 프로젝트마다 이 PC 방식으로 vhost·hosts를 새로 만든다 (Apache가 설치돼 있어야 한다)
- Python venv는 OS 간 호환되지 않는다. 다른 OS에서 온 venv는 지우고 패키지 설치를 다시 한다
cargo build --release
./target/release/localman또는 개발 실행 (리눅스, kime 입력기 충돌 회피용 X11 강제):
./scripts/linux/run.sh맥 — Homebrew로 설치 (Intel·Apple Silicon 겸용):
brew install --cask eondcom/tap/localman
sudo /Applications/LocalMan.app/Contents/Resources/install-sudoers.sh # 처음 한 번LocalMan은 Apple 미서명 앱이라 처음 실행할 때 우클릭 → 열기, 또는 시스템 설정 → 개인정보 보호 및 보안에서 허용한다. 화면 언어는 설정 탭에서 한국어·English·日本語(기본: 시스템 언어)로 바꾼다.
소스에서 맥 앱으로 설치 (/Applications/LocalMan.app, 아이콘·임시 서명 포함, 다시 실행하면 덮어쓴다):
./scripts/macos/make-app.sh맥에서 리눅스 대상 타입 검사: ./scripts/check-linux.sh
맥북 팬 관리(mac-fan-control)와 같은 EOND UI App 0.2 체계를 쓴다 — src/ui/theme.rs.
- 테두리 대신 밝기로 층을 나눈다 (바탕 → 카드 c1 → 겹침 c2 → 선택 c3). 색은 토큰으로만 쓴다 (HeroUI 팔레트)
- 다크/라이트/시스템 (설정 탭). 시스템이면 맥은 화면 모드, 리눅스는 GNOME color-scheme 을 따른다
- 글꼴 Pretendard, 아이콘 Lucide 를 앱에 넣는다 (
assets/fonts/, 각 LICENSE 포함). iced 가 화면을 직접 그리므로 맥·리눅스가 같은 화면이다 (창 테두리·파일 선택 창만 OS 것)
- Rust + Iced GUI
- Apache2 vhost 기반 로컬 도메인
- MariaDB/PostgreSQL CLI 래핑