처음엔 Claude /deploy 스킬이 편했다. 명령 한 줄이면 SSH 접속부터 docker compose 재시작까지 다 해줬으니까. 그런데 배포할 때마다 토큰이 나갔다. 스킬 실행, 서버 상태 확인, 오류 나면 대응, 재시도. 몇 번 반복하면 한 번 배포에 의미 없는 토큰을 꽤 쓰고 있었다.
self-hosted runner 삽질부터 시작됐다
trading_mvp에 CI/CD를 처음 붙일 때는 GitHub 사이트에서 self-hosted runner를 등록했다. GitHub Actions 탭 들어가서 설치 스크립트 돌리고, 서버에 러너 데몬 등록하고, 서비스로 상주시키는 과정이었다. 설정이 어렵진 않았는데 서버 구조가 바뀌어버렸다. 평소엔 ssh hong으로 들어가서 직접 보던 흐름인데, 러너가 별도 컨텍스트로 돌면서 어디서 뭐가 실행되는지 파악이 어려워졌다.
그 상태에서 slowloop-lab 배포를 붙이려고 찾다가 appleboy/ssh-action을 알게 됐다.
yml + secrets 4개 + authorized_keys 한 줄
찾아보니 동작 방식이 단순했다. GitHub 클라우드 러너가 서버로 SSH 접속해서 명령을 실행하는 것뿐이다. 서버에는 authorized_keys에 배포 전용 키 한 줄만 추가된다. 러너 데몬 없고, 서비스 등록 없고, 서버 내부 구조 안 바뀐다. 평소 ssh hong 하던 흐름 그대로다.
설정은 gh CLI로 한 세션에 끝냈다:
# 전용 키 생성
ssh-keygen -t ed25519 -f /tmp/deploy_앱 -N "" -C "gha-deploy-앱"
# 서버에 공개키 등록
cat /tmp/deploy_앱.pub | ssh hong 'cat >> ~/.ssh/authorized_keys'
# 레포에 secrets 등록 (GitHub 사이트 안 들어가고)
gh secret set SSH_HOST --body "korat.iptime.org"
gh secret set SSH_USER --body "hong"
gh secret set SSH_PORT --body "22"
gh secret set SSH_PRIVATE_KEY < /tmp/deploy_앱
# 로컬 키 삭제
rm /tmp/deploy_앱 /tmp/deploy_앱.pub
워크플로우 yml에 appleboy/ssh-action@v1을 붙이고 push 트리거 브랜치를 설정하면 끝이다. slowloop-lab에서 2026-06-24에 검증하고, 다음 날 hansaiam에 그대로 적용했다. hansaiam /deploy 스킬은 삭제했다.
한 번 걸렸던 것
slowloop-lab 초기에 secrets 등록 전에 워크플로우 파일만 먼저 올렸다. 그랬더니 모든 run이 “missing server host”로 10초 만에 죽었는데, Actions 탭 직접 열어보기 전까지는 아무도 몰랐다. 전환 직후 첫 run 로그는 반드시 확인해야 한다. secrets 미등록은 조용히 실패한다.
써보고 나서 정리된 것
Claude 스킬로 배포하는 건 “AI가 해주니까 편하다”는 느낌인데, 실제로는 그 편함에 토큰이 들어가고 있었다. 스킬이 맞는 건 한 번만 하는 것, 상황 판단이 필요한 것이다. 매번 하는 배포는 거기 해당하지 않는다.
SSH-action은 yml 하나에 secrets 4개. runner 방식보다 훨씬 단순하고, 서버가 깔끔하게 유지된다.