macOS 전용이에요?
이 글은 Claude Code Notifier 개발기 시리즈의 3편입니다. (총 4편)
"그거 macOS 전용이에요?"
이 한마디가 모든 걸 바꿨다.
시작
사내에 Claude Code 워크플로우를 소개하고 싶었다. 내가 쓰는 방식이 꽤 괜찮다고 생각했고, 팀원들도 쓰면 좋겠다 싶었다. 내가 만든 걸로 다른 사람들이 편해지면 그게 좋은 거니까.
근데 막상 보여주니까 돌아온 반응이 이랬다.
"저 Ubuntu 쓰는데요." "저희 팀 WSL 쓰거든요." "Windows에서도 되면 좋겠는데..."
그렇게 크로스 플랫폼 지원이 시작됐다.
네 개의 세계
지원해야 할 플랫폼이 네 개였다.
macOS - 내가 쓰는 것
Linux - 백엔드 팀이 쓰는 것
Windows - 일부가 쓰는 것
WSL - 제일 많이 쓰는 것
macOS는 이미 됐다. 문제는 나머지 셋이었다.
Linux는 쉬웠다
notify-send라는 게 있었다. 대부분의 Linux 배포판에 있는 알림 도구다.
notify-send "Claude Code" "작업 완료"
끝. 다섯 분 만에 됐다.
문제는 notify-send가 없는 경우였다. 설치 안 된 사람도 있으니까. 그래서 없으면 그냥 넘어가게 했다.
if ! command -v notify-send &> /dev/null; then
exit 0 # 없으면 말고
fi
알림은 있으면 좋은 거지 필수는 아니다. 에러를 내면서 죽는 것보다 조용히 넘어가는 게 낫다.
Windows도 어렵지 않았다
Windows에는 Toast 알림이라는 게 있다. PowerShell로 보낼 수 있다.
$xml = @"
<toast>
<visual>
<binding template="ToastText02">
<text id="1">Claude Code</text>
<text id="2">작업 완료</text>
</binding>
</visual>
</toast>
"@
$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Claude Code")
$notifier.Show($toast)
XML이 좀 지저분하긴 한데 동작은 잘 됐다. 여기까지는 순조로웠다.
WSL이 문제였다
WSL. Windows Subsystem for Linux. Linux인데 Linux가 아니고 Windows인데 Windows가 아닌 것.
처음엔 간단할 줄 알았다. Linux니까 notify-send 쓰면 되지 않나?
안 됐다.
WSL에서 notify-send를 실행하면 알림이 어디로 가는가? 아무 데도 안 간다. WSL은 기본적으로 GUI가 없다. Windows 알림을 써야 했다.
그래서 WSL에서 powershell.exe를 호출하기로 했다. WSL에서는 Windows 프로그램을 실행할 수 있으니까.
powershell.exe -File ./windows.ps1
안 됐다.
시도 1: 경로
WSL과 Windows는 경로 체계가 다르다.
WSL: /home/user/data/file.txt
Windows: C:\Users\user\data\file.txt
WSL 경로를 PowerShell에 넘기면 못 찾는다. Windows 경로로 바꿔야 한다.
# wslpath가 해준다
WIN_PATH=$(wslpath -w "$LINUX_PATH")
이건 금방 해결했다.
시도 2: 환경변수
이게 진짜 문제였다.
Bash에서 환경변수를 설정하고 PowerShell을 호출하면 어떻게 될까?
export MY_VAR="hello"
powershell.exe -Command 'echo $env:MY_VAR'
아무것도 안 나온다.
WSL에서 Windows 프로그램을 호출하면 환경변수가 전달이 안 된다. 나는 이걸 몰랐다. 세 시간을 썼다.
"왜 비어있지?" "분명 설정했는데?" "뭐가 문제야?"
스택오버플로우를 뒤지다가 답을 찾았다. WSLENV라는 게 있었다.
export WSLENV="MY_VAR"
export MY_VAR="hello"
powershell.exe -Command 'echo $env:MY_VAR'
이제 된다.
WSLENV에 전달할 변수 이름을 적어주면 그 변수들만 Windows로 넘어간다. 이걸 왜 자동으로 안 해주는지 모르겠다.
export WSLENV="MIN_DURATION_SECONDS:NOTIFY_MESSAGE:NOTIFICATION_TYPE:DATA_DIR_WIN"
콜론으로 구분한다. 보기 싫게 생겼다. 하지만 동작한다.
시도 3: 데이터 전달
Hook에서 받은 JSON 데이터를 PowerShell로 넘겨야 했다.
처음엔 명령줄 인자로 시도했다.
powershell.exe -Command "& { param($j) ... }" -j "$JSON"
특수문자 때문 에 터졌다. JSON에는 따옴표, 중괄호, 콜론이 잔뜩 있다. 이스케이프 지옥이었다.
환경변수로 해봤다.
export JSON_DATA="$JSON"
export WSLENV="JSON_DATA"
크기 제한에 걸렸다. 긴 JSON은 잘렸다.
결국 stdin으로 해결했다.
echo "$JSON" | powershell.exe -File ./windows.ps1
$json = [Console]::In.ReadToEnd()
이게 제일 깔끔했다. 처음부터 이렇게 할 걸.
OS 감지
네 개의 플랫폼을 구분해야 했다. 순서가 중요했다.
# 1. Windows Native (Git Bash)
if [ "$OS" = "Windows_NT" ]; then
# Windows 처리
# 2. WSL - 반드시 Linux보다 먼저!
elif grep -qi microsoft /proc/version 2>/dev/null; then
# WSL 처리
# 3. macOS
elif [ "$(uname)" = "Darwin" ]; then
# macOS 처리
# 4. Linux
else
# Linux 처리
fi
WSL을 Linux보다 먼저 체크해야 한다. WSL에서 uname을 치면 Linux가 나온다. 순서가 바뀌면 WSL이 Linux로 잘못 분류된다.
이것도 한 시간 걸렸다. "왜 WSL에서 Linux 알림이 가지?"하면서.
결과
┌─────────────┬──────────────────┐
│ 플랫폼 │ 알림 방식 │
├─────────────┼──────────────────┤
│ macOS │ osascript │
│ Linux │ notify-send │
│ Windows │ Toast (WinRT) │
│ WSL │ Toast via PS │
└─────────────┴──────────────────┘
네 개 플랫폼, 전부 된다.
사내 메신저에 공유했더니 따봉들이 날아왔다. 그게 좋았다.
배운 것
-
WSL은 특별하다. Linux처럼 보이지만 Linux가 아니다. 별도 처리가 필요하다.
-
WSLENV를 기억하라. WSL에서 Windows로 환경변수 넘길 때 필수다.
-
stdin이 답이다. 복잡한 데이터는 파이프로 넘겨라. 인자나 환경변수보다 낫다.
-
에러는 조용히. 알림이 안 되면 그냥 넘어가라. 에러 메시지 출력하면서 죽지 마라.
-
감지 순서가 중요하다. WSL을 먼저, Linux를 나중에.
그래서
"macOS 전용이에요?"
이제 아니다.
다음 글에서는 이걸 공식 플러그인으로 PR한 얘기를 할 거다. 코드 리뷰를 받으면서 또 많이 고쳤다.
시리즈 목차:
- 1편: Claude Code, 알림이 필요해
- 2편: Hook이 전부다
- 3편: macOS 전용이에요? (현재 글)
- 4편: Create pull request
