launchd는 스크립트와 프로세스를 시작, 중지, 관리하는 macOS의 핵심 도구입니다. 리눅스를 사용해 본 경험이 있다면 cron에 익숙할 텐데, launchd는 쉽게 말해 macOS 버전의 cron이라고 할 수 있습니다.
데몬(Daemon)이란 무엇인가?
데몬(daemon)은 백그라운드에서 실행되는 스크립트를 의미합니다. 일반 애플리케이션과 달리 데몬은 프로세스 형태로 동작하며, 사용자나 다른 프로그램의 직접적인 제어를 받지 않습니다. macOS에서는 launchd 프레임워크가 데몬을 관리하며, 언제 시작하고 종료할지를 결정합니다.
흥미롭게도 '데몬'이라는 이름은 열역학 사고 실험에서 분자를 분류하는 상상 속 존재인 '맥스웰의 악마(Maxwell's demon)'에서 유래했습니다.
스크립트 작성하기
launchd를 통해 데몬을 실행하려면 먼저 실행할 스크립트를 작성해야 합니다. 가장 널리 쓰이는 스크립팅 언어는 bash입니다. bash 스크립팅을 처음 접한다면 초보자용 bash 스크립팅 가이드를 참고하는 것을 추천합니다.
launchd 사용하기
launchd에서 스크립트는 특정 디렉터리에 저장된 .plist 파일 형태의 '작업 정의(job definition)'에 의해 실행됩니다. 이 XML 파일은 작업에 고유한 이름을 부여하고, 실행할 스크립트의 경로를 지정하며, 스크립트가 언제 실행될지 정의합니다. 스크립트를 작성한 후에는 이를 적절한 시점에 실행하는 작업 정의를 만들어 로드하면 됩니다.
작업 정의는 대략 다음과 같은 형태입니다:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.restart</string>
<key>Program</key>
<string>/Users/user/Scripts/restart.sh</string>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
필요에 맞게 내용을 수정한 뒤 .plist 확장자를 가진 텍스트 파일로 저장하고, 올바른 디렉터리에 배치하면 됩니다(아래 설명 참조).
작업 정의의 핵심 요소는 다음과 같습니다:
- Label: launchd 내에서 해당 작업을 식별하는 이름입니다. 각 작업마다 반드시 고유해야 하며, 역방향 도메인 표기법(reverse domain notation)으로 작성합니다. 개인 용도의 에이전트라면 "local"이 좋은 도메인입니다.
- Program: 이 작업 정의가 실행하는 스크립트의 전체 경로입니다.
- RunAtLoad: 스크립트의 실행 시점을 지정합니다. 몇 가지 옵션이 있습니다:
- RunAtLoad: 작업 정의가 로드되는 즉시 실행합니다. 로드당 한 번만 실행됩니다.
- StartInterval: n초마다 작업을 실행합니다. 아래 예제는 7200초, 즉 2시간 간격으로 작업을 실행합니다.
<key>StartInterval</key> <integer>7200</integer>
- StartCalendarInterval: 특정 날짜와 시간에 작업을 실행합니다. 아래 코드는 매일 오전 9시에 작업을 실행합니다.
<key>StartCalendarInterval</key> <dict> <key>Hour</key> <integer>9</integer> <key>Minute</key> <integer>0</integer> </dict>
에이전트(Agent) vs. 데몬(Daemon)
작업 정의를 작성했다면 이제 적절한 디렉터리에 저장해야 합니다.
launchd는 에이전트(agent)와 데몬(daemon)을 구분합니다. 에이전트는 로그인한 사용자를 대신하여 실행되는 반면, 데몬은 root(root) 사용자 권한으로 실행됩니다. 따라서 특정 사용자 계정에서만 스크립트가 동작하도록 제한하려면 에이전트를, 누가 로그인했든 상관없이 항상 실행되길 원한다면 데몬을 사용하면 됩니다.
에이전트와 데몬의 차이는 저장 위치에서 비롯됩니다:
- "~/Library/LaunchAgents": 로그인한 사용자를 대신하여 실행
- "/Library/LaunchDaemons": root 사용자를 대신하여 실행
plist 파일은 반드시 목적에 맞는 올바른 위치에 저장해야 합니다.
launchctl에 작업 로드하기
스크립트를 작성하고 에이전트를 올바른 위치에 저장했다면, 이제 launchctl에 로드해야 합니다. 한 번 로드하면 이후 로그인 시 자동으로 등록됩니다.
현재 launchctl에서 실행 중인 항목을 확인하려면 터미널에서 launchctl list 명령어를 사용하세요. 출력되는 방대한 목록에서 자신의 스크립트를 찾으려면 다음과 같이 라벨을 기준으로 grep을 활용하면 편리합니다:
launchctl list | grep local.restart
스크립트를 로드하려면 터미널을 열고 다음 명령어를 입력합니다:
launchctl load ~/Library/LaunchAgents/local.restart.plist
launchctl 큐에서 스크립트를 제거하려면 unload 명령어를 사용합니다:
launchctl unload ~/Library/LaunchAgents/local.restart.plist
작업을 로드하면 launchd 큐에 등록되며, 설정된 실행 조건에 따라 자동으로 실행됩니다. 조건과 무관하게 즉시 스크립트를 실행하고 싶다면 "start" 명령어를 사용하세요:
launchctl start local.restart
이 명령어는 작업의 라벨을 인수로 받으며, 해당 작업이 이미 launchctl에 로드되어 있어야만 정상적으로 동작합니다.
마무리
launchd를 활용하면 파일 자동 정리, 서버 주기적 재시작, 특정 파일이 생성될 때 애플리케이션 실행 등 다양한 작업을 자동화할 수 있습니다. launchd에 대해 더 깊이 알고 싶다면 launchd 공식 튜토리얼을 참고해 보세요.