어떤 일이 있었나
openclaw 프로젝트의 환경 설정을 자동화하기 위한 셋업 스크립트를 작성하고 있었습니다. 코드를 깔끔하게 관리하기 위해 현대적인 Linux 환경에서 자주 사용하던 연관 배열(Associative Arrays) 기능을 사용하기로 했습니다.
제 Ubuntu 개발 장비에서는 스크립트가 완벽하게 작동했습니다. 하지만 동료의 MacBook에서 스크립트를 실행하자마자 다음과 같은 알 수 없는 에러와 함께 중단되었습니다.
./setup.sh: line 12: declare: -A: invalid option
에러가 발생한 12번 라인은 설정 맵을 초기화하는 부분이었습니다.
declare -A CONFIG_MAP
근본 원인
원인은 macOS의 잘 알려져 있지만 잊기 쉬운 특징 때문이었습니다. macOS는 기본적으로 Bash 3.2 버전을 탑재하고 있습니다.
Bash 3.2는 2007년에 출시되었습니다. 연관 배열 기능(declare -A)은 2009년에 출시된 Bash 4.0에서 처음 도입되었습니다. Apple은 GPLv3 라이선스 변경 문제로 인해 Bash 3.2 이후 버전을 업데이트하지 않았고, 최신 macOS 버전에서도 /bin/bash는 여전히 20년 가까이 된 3.2 버전을 유지하고 있습니다.
저는 #!/usr/bin/env bash가 항상 최신 버전의 쉘을 가리킬 것이라고 막연히 가정하는 “내 컴퓨터에서는 잘 되는데” 함정에 빠졌던 것입니다. macOS 사용자가 직접 Homebrew를 통해 최신 Bash를 설치하고 PATH를 설정하지 않는 한, 시스템 기본값인 고대 유물을 사용하게 됩니다.
해결 방법
이 문제를 해결하기 위해 스크립트를 Bash 3.2(가급적 POSIX sh)와 호환되도록 리팩토링해야 했습니다. 연관 배열 대신 인덱스 배열과 명명 규칙을 조합하여 사용했습니다.
기존 코드:
declare -A mymap
mymap["key"]="value"
echo ${mymap["key"]}
수정된 방식:
# 변수 이름을 키로 사용
CONFIG_KEY="value"
# 또는 순서가 중요한 경우 인덱스 배열 사용
KEYS=("key1" "key2")
VALUES=("val1" "val2")
더 복잡한 데이터의 경우 설정을 별도의 텍스트 파일로 분리하고 grep이나 awk를 사용하여 값을 가져오는 방식을 택했습니다. 이 방식은 다양한 Unix 계열 시스템에서 훨씬 더 높은 이식성을 보장합니다.
배운 점
- macOS의 Bash 버전을 절대 과신하지 마세요. macOS 사용자를 위한 스크립트를 작성한다면 Bash 3.2를 기준으로 하거나, 현대적인 macOS의 기본 쉘인
zsh를 타겟으로 삼는 것이 좋습니다. - POSIX sh는 훌륭한 대안입니다. 고급 Bash 기능이 꼭 필요하지 않다면, 순수 POSIX 호환 쉘 스크립트를 작성하는 것이 가장 안전한 이식성 확보 방법입니다.
- 런타임 버전 체크. 만약 스크립트가 반드시 Bash 4+ 기능을 필요로 한다면, 시작 부분에 버전 체크 로직을 추가하여 사용자에게 친절한 안내 메시지를 보여주어야 합니다.
if [[ ${BASH_VERSINFO[0]} -lt 4 ]]; then echo "Error: 이 스크립트는 Bash 4.0 이상의 버전이 필요합니다." exit 1 fi - ShellCheck 활용. ShellCheck와 같은 도구를 사용하여 호환성 문제를 조기에 발견하세요. 특정 쉘 버전을 타겟으로 설정하여 검사할 수 있습니다.
예방 조치
앞으로는 다음과 같은 원칙을 개발 워크플로우에 추가하기로 했습니다.
- 깨끗한 macOS 환경(또는 이를 모방한 컨테이너)에서 스크립트 테스트.
- macOS 전용 자동화에는
zsh를 기본으로 사용. - 쉘 이식성이 복잡해지는 로직에는 Python이나 Go 사용 고려.
호환성은 단순히 OS의 문제가 아니라, OS가 제공하는 도구의 문제입니다. 2007년의 쉘이 2026년의 자동화를 망치게 두지 마세요.
