ch12. 시간 기반 트리거 -- Cron Job (place-info, naver-points)
특정 시간에 셸 스크립트를 자동으로 실행하는 Time 트리거를 배운다. 8개 매크로를 통해 Cron Job 아키텍처와 에러 처리 전략을 익힌다.
이런 불편함, 겪어보셨나요?
매일 오후 4시에 주식 데이터를 수집해야 한다. 일요일 오전 10시에 휴장일 목록을 갱신해야 한다. 네이버 포인트 적립을 위해 매일 같은 시간에 특정 페이지를 방문해야 한다.
이런 반복 작업은 사람이 기억하고 실행하는 것 자체가 리스크다. 잊어버리면 데이터가 빠지고, 일정이 밀린다.
macOS에는 cron과 launchd가 있다.
그러나 이들은 설정이 복잡하고, 실행 결과를 확인하기 어렵다.
Keyboard Maestro의 Time 트리거는 GUI에서 시간을 지정하고, 실행 로그를 바로 확인할 수 있다.
셸 스크립트와 결합하면, macOS 위에서 돌아가는 개인 Cron 시스템을 만들 수 있다.
이 챕터에서는 8개의 시간 기반 매크로를 세 그룹으로 나누어 분석한다.
| 그룹 | 매크로 | 실행 주기 |
|---|---|---|
| Cron Job | get-place-info, get-naver-points-mobile, naver-cookie-event | 매일 반복 |
| Stock | 휴장일 업데이트, PostgreSQL 백업, 주식 데이터 수집, 실시간 매매 실행 | 평일/일요일 특정 시간 |
| Common | npm - 글로벌 패키지 업데이트 | 주기적 유지보수 |
공통 아키텍처: 컴퓨터 식별 + 조건 실행 패턴
8개 매크로 중 7개가 동일한 구조를 공유한다. 이 패턴을 먼저 이해해야 개별 매크로 분석이 수월하다.
1. SetVariable — computerName = %MacName%
2. IfThenElse — computerName이 특정 값인지 확인
├── Then: ExecuteShellScript 실행
└── Else: (아무것도 안 함)
왜 이런 구조가 필요한가.
Keyboard Maestro 매크로는 iCloud나 파일 동기화를 통해 여러 Mac에 공유된다.
주식 매매 스크립트가 회사 맥북에서 실행되면 문제가 생긴다.
%MacName% 변수로 현재 컴퓨터를 식별하고, 지정된 컴퓨터에서만 스크립트를 실행하는 안전장치다.
이 패턴은 멀티 Mac 환경에서 매크로를 관리할 때 필수 기법이다.
매크로 분석: get-place-info (Cron Job 그룹 대표)
무엇을 하는 매크로인가
지정된 시간에 장소 정보를 크롤링하는 셸 스크립트를 자동 실행한다.
외부 스크립트(crawl.sh)가 실제 크롤링 로직을 담당하고, KM은 스케줄러 역할만 한다.
트리거
| 트리거 타입 | 값 | 설명 |
|---|---|---|
| Time | 지정 시간 | 매일 정해진 시간에 자동 실행 |
액션 흐름
그림 12-1. get-place-info 매크로의 Cron Job 공통 패턴 흐름도
- SetVariableToText --
computerName변수에%MacName%(현재 Mac 이름)을 저장한다. - IfThenElse --
computerName이 지정된 Mac 이름과 일치하는지 확인한다. - Then: ExecuteShellScript -- 조건이 참이면
/Users/jaesik.lee/Downloads/multi-chrome-browser-test/crawl.sh를 실행한다. - Else -- 조건이 거짓이면 아무 동작 없이 종료한다.
핵심 기술 해설
Time 트리거의 동작 원리
KM의 Time 트리거는 macOS의 시스템 시계를 기준으로 동작한다. 트리거 설정에서 실행 시간, 요일, 반복 주기를 지정할 수 있다. Mac이 슬립 상태였다가 깨어나면, 놓친 트리거를 즉시 실행하는 옵션도 있다.
cron과 비교했을 때 KM Time 트리거의 장점은 세 가지다.
| 항목 | cron / launchd | KM Time 트리거 |
|---|---|---|
| 설정 방식 | 터미널에서 crontab -e 편집 |
GUI에서 시간/요일 선택 |
| 실행 로그 | 별도 로그 파일 설정 필요 | KM 로그 창에서 바로 확인 |
| 에러 알림 | 메일 설정 또는 별도 스크립트 | Notification 액션 추가로 해결 |
외부 셸 스크립트 호출 패턴
ExecuteShellScript 액션에 스크립트 전체를 넣을 수도 있지만, 이 매크로는 외부 .sh 파일을 호출한다.
이 방식의 장점은 명확하다.
- 스크립트를 Git으로 버전 관리할 수 있다.
- 터미널에서 직접 실행하여 디버깅할 수 있다.
- KM 매크로와 스크립트 로직을 분리하여 유지보수가 쉽다.
활용 팁
- 스크립트 실행 결과를 KM 변수에 저장하면, 후속 액션에서 성공/실패를 판단할 수 있다.
ExecuteShellScript의 "Save results to variable" 옵션을 활용하라.- 실패 시 Notification 액션으로 알림을 보내는 에러 처리를 추가하면 더 안정적이다.
Cron Job 그룹 비교: 3개 매크로
get-naver-points-mobile과 naver-cookie-event도 get-place-info와 동일한 구조다. 차이는 호출하는 셸 스크립트뿐이다.
| 매크로 | 셸 스크립트 | 목적 |
|---|---|---|
| get-place-info | crawl.sh |
장소 정보 크롤링 |
| get-naver-points-mobile | naver-mobile-click.sh |
네이버 포인트 적립 자동화 |
| naver-cookie-event | run.sh |
네이버 시리즈 쿠키 이벤트 참여 |
세 매크로 모두 "KM은 스케줄러, 셸 스크립트가 실행자"라는 역할 분리를 따른다. 이 패턴의 핵심은 KM이 복잡한 로직을 직접 처리하지 않는다는 점이다. 크롤링, API 호출, 브라우저 자동화 같은 무거운 작업은 외부 스크립트에 위임한다. KM은 "언제 실행할 것인가"만 관리한다.
매크로 분석: 주식 데이터 수집 (평일 16:00)
무엇을 하는 매크로인가
평일 오후 4시, 한국 주식시장이 마감된 직후에 당일 주식 데이터를 자동으로 수집한다.
수집 로직은 collect_daily.sh 스크립트가 담당한다.
트리거
| 트리거 타입 | 값 | 설명 |
|---|---|---|
| Time | 평일 16:00 | 월~금 장 마감 후 실행 |
액션 흐름
그림 12-2. 주식 데이터 수집 매크로 흐름도 (평일 16:00 자동 실행)
- SetVariableToText --
computerName에 현재 Mac 이름을 저장한다. - IfThenElse -- 지정된 Mac에서만 실행되도록 확인한다.
- Then: ExecuteShellScript --
cd명령으로 프로젝트 디렉토리로 이동한 뒤./scripts/collect_daily.sh를 실행한다.
#!/bin/bash
cd "/Users/jaesik.lee/jaesik.lee/real/korean-stock-trading" && ./scripts/collect_daily.sh
스크립트 경로를 절대 경로로 지정하고, cd로 작업 디렉토리를 먼저 설정하는 것이 포인트다.
KM의 ExecuteShellScript는 기본 작업 디렉토리가 루트(/)이므로, 상대 경로에 의존하는 스크립트는 반드시 cd를 먼저 실행해야 한다.
핵심 기술 해설
평일만 실행하는 Time 트리거 설정
Time 트리거의 Days 옵션에서 월요일부터 금요일까지만 선택한다. 그러나 공휴일은 Time 트리거만으로 처리할 수 없다. 이 문제를 해결하는 것이 바로 같은 Stock 그룹의 "휴장일 업데이트" 매크로다. 일요일 10시에 미리 휴장일 목록을 갱신하고, 데이터 수집 스크립트 내부에서 휴장일 여부를 확인하는 방식이다.
이처럼 여러 매크로가 서로 연계되어 하나의 시스템을 구성하는 것이 고급 자동화의 핵심이다.
활용 팁
- 장 마감 직후인 15:30이 아니라 16:00으로 설정한 이유는, 증권사 API의 데이터 반영에 시간차가 있기 때문이다. 여유를 두는 것이 안전하다.
- 데이터 수집 실패 시 재시도 로직은 셸 스크립트 내부에 구현하는 것이 좋다. KM에서 복잡한 재시도 로직을 만드는 것보다 스크립트에서 처리하는 편이 유지보수에 유리하다.
Stock 그룹 비교: 4개 매크로
Stock 그룹의 4개 매크로는 주식 자동매매 시스템의 파이프라인을 구성한다.
| 매크로 | 실행 시간 | 스크립트 | 역할 |
|---|---|---|---|
| 휴장일 업데이트 | 일요일 10:00 | update_holidays.sh |
다음 주 휴장일 목록 갱신 |
| PostgreSQL 백업 | 매일 (설정 시간) | backup_postgres.sh |
주식 DB 백업 |
| 주식 데이터 수집 | 평일 16:00 | collect_daily.sh |
장 마감 후 데이터 수집 |
| 실시간 매매 실행 | 평일 08:50 | live_trading.sh live --strategy ValueMomentum |
장 시작 전 매매 전략 실행 |
실행 순서에 주목하라. 일요일에 휴장일을 확인하고, 평일 아침 8시 50분에 매매를 시작하고, 오후 4시에 데이터를 수집하고, 매일 DB를 백업한다. 4개의 독립적인 매크로가 시간 순서로 연결되어 하나의 자동매매 시스템을 이룬다.
특히 live_trading.sh에 전달되는 --strategy ValueMomentum 인자에 주목하라.
셸 스크립트에 커맨드라인 인자를 전달하여 매매 전략을 지정하는 방식이다.
전략을 바꾸고 싶으면 KM 매크로의 스크립트 내용만 수정하면 된다.
매크로 분석: npm - 글로벌 패키지 업데이트
무엇을 하는 매크로인가
Node.js의 글로벌 npm 패키지를 주기적으로 최신 버전으로 업데이트한다. 개발 환경 유지보수를 자동화하는 매크로다.
트리거
| 트리거 타입 | 값 | 설명 |
|---|---|---|
| Time | 지정 시간 | 주기적으로 자동 실행 |
액션 흐름
이 매크로는 다른 7개와 달리 컴퓨터 식별 분기 없이 바로 셸 스크립트를 실행한다. 액션이 단 1개다.
- ExecuteShellScript -- nvm으로 Node.js 22 버전을 활성화한 뒤, 지정된 글로벌 패키지를 업데이트한다.
#!/bin/zsh
set -euo pipefail
export NVM_DIR="$HOME/.nvm"
source "$NVM_DIR/nvm.sh"
nvm use 22
echo "Node: $(node -v)"
echo "npm: $(npm -v)"
packages=(
"npm"
"corepack"
"@googl..." # 이하 패키지 목록
)
핵심 기술 해설
셸 환경 초기화 문제
KM의 ExecuteShellScript는 로그인 셸이 아닌 비-인터랙티브 셸로 실행된다.
따라서 .zshrc나 .bash_profile에 설정된 환경 변수가 자동으로 로드되지 않는다.
이 매크로에서 export NVM_DIR과 source "$NVM_DIR/nvm.sh"를 명시적으로 실행하는 이유가 여기에 있다.
nvm, pyenv, rbenv 등 버전 관리 도구를 KM 셸 스크립트에서 사용하려면, 스크립트 상단에서 해당 도구를 직접 초기화해야 한다.
set -euo pipefail
이 한 줄은 셸 스크립트의 에러 처리 수준을 크게 높인다.
| 옵션 | 의미 |
|---|---|
-e |
명령어가 실패하면 즉시 스크립트 종료 |
-u |
정의되지 않은 변수 사용 시 에러 |
-o pipefail |
파이프라인 중 하나라도 실패하면 전체 실패로 처리 |
시간 기반 매크로는 사용자가 모니터링하지 않는 상태에서 실행된다.
조용히 실패하는 것이 가장 위험하다.
set -euo pipefail은 문제가 생기면 즉시 멈추게 하여, 잘못된 상태로 계속 진행하는 것을 방지한다.
활용 팁
- Homebrew 패키지 업데이트(
brew update && brew upgrade)도 같은 패턴으로 자동화할 수 있다. - Python 가상환경이나 Ruby 버전 관리 도구도 동일한 초기화 패턴이 필요하다.
직접 만들어보기
가장 기본적인 Cron Job 패턴을 직접 만들어보자. 매일 오전 9시에 셸 스크립트를 실행하는 매크로를 구성한다.
- KM 에디터에서 새 매크로 그룹 Cron Job 을 만든다.
- 새 매크로를 만들고 이름을 "Daily Health Check"로 설정한다.
- 트리거를 추가한다: Time of Day Trigger 를 선택하고, 오전 9:00, 매일 반복으로 설정한다.
- 첫 번째 액션을 추가한다: Set Variable to Text -- 변수명
computerName, 값%MacName%. - 두 번째 액션을 추가한다: If Then Else -- 조건을 "Variable computerName is (자신의 Mac 이름)"으로 설정한다.
- Then 블록 안에 Execute Shell Script 액션을 추가한다:
#!/bin/bash
set -euo pipefail
echo "=== Daily Health Check ==="
echo "Date: $(date '+%Y-%m-%d %H:%M:%S')"
echo "Uptime: $(uptime)"
echo "Disk: $(df -h / | tail -1)"
echo "Memory: $(vm_stat | head -5)"
- 실행 결과를 확인하려면, 셸 스크립트 액션의 "Save trimmed results to variable" 옵션에서 결과 변수를 지정하고, 그 뒤에 Notification 액션을 추가하여 결과를 표시한다.
- 테스트: 매크로를 선택하고 하단의 "Try" 버튼을 눌러 수동 실행한다. 알림으로 시스템 상태가 표시되는지 확인한다.
시간 기반 매크로 설계 원칙
8개 매크로를 분석하면서 드러난 설계 원칙을 정리한다.
1. KM은 스케줄러, 스크립트가 실행자
KM 매크로 안에 복잡한 로직을 넣지 않는다. Time 트리거로 "언제"를 정하고, 외부 스크립트가 "무엇을"을 처리한다. 이렇게 분리하면 스크립트를 터미널에서 독립적으로 테스트할 수 있다.
2. 멀티 Mac 안전장치
%MacName% 변수로 컴퓨터를 식별하고, 지정된 Mac에서만 실행한다.
매크로 파일이 동기화되어 여러 Mac에 배포되더라도, 의도하지 않은 실행을 방지한다.
3. 에러를 조용히 삼키지 않는다
set -euo pipefail로 스크립트 실패를 즉시 감지한다.
실행 결과를 변수에 저장하고, 필요하면 알림을 보낸다.
로그 파일에 기록하는 것도 좋은 방법이다.
4. 매크로 간 연계로 시스템을 구성한다
Stock 그룹처럼 여러 매크로가 시간 순서로 연결되어 하나의 파이프라인을 이룬다. 각 매크로는 독립적으로 실행되지만, 전체적으로는 하나의 워크플로우를 구성한다.
이 챕터에서 배운 것
- Time 트리거로 특정 시간, 특정 요일에 매크로를 자동 실행하는 방법
%MacName%변수를 활용한 멀티 Mac 환경 안전장치 패턴- KM의 ExecuteShellScript에서 셸 환경 초기화가 필요한 이유와 해결법
set -euo pipefail을 통한 셸 스크립트 에러 처리 전략- KM을 스케줄러로, 외부 스크립트를 실행자로 분리하는 아키텍처
다음 챕터 예고
ch13에서는 이벤트 기반 트리거를 다룬다. 모니터가 연결되었을 때, 로그인했을 때, 주말과 평일을 구분하여 자동으로 다른 작업을 수행하는 매크로를 만든다. 시간이 아닌 "상황"에 반응하는 자동화를 배운다.