본문으로 건너뛰기

macOS 전용이에요?

· 약 4분
JS Koo
Developer
시리즈 안내

이 글은 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 │
└─────────────┴──────────────────┘

네 개 플랫폼, 전부 된다.

사내 메신저에 공유했더니 따봉들이 날아왔다. 그게 좋았다.

배운 것

  1. WSL은 특별하다. Linux처럼 보이지만 Linux가 아니다. 별도 처리가 필요하다.

  2. WSLENV를 기억하라. WSL에서 Windows로 환경변수 넘길 때 필수다.

  3. stdin이 답이다. 복잡한 데이터는 파이프로 넘겨라. 인자나 환경변수보다 낫다.

  4. 에러는 조용히. 알림이 안 되면 그냥 넘어가라. 에러 메시지 출력하면서 죽지 마라.

  5. 감지 순서가 중요하다. WSL을 먼저, Linux를 나중에.

그래서

"macOS 전용이에요?"

이제 아니다.

다음 글에서는 이걸 공식 플러그인으로 PR한 얘기를 할 거다. 코드 리뷰를 받으면서 또 많이 고쳤다.


시리즈 목차:


문서: Claude Code Notifier Docs

GitHub: https://github.com/js-koo/claude-code-notifier