devAlice
← Mac

Mac 개발 환경 정기 유지관리 — brew·언어 툴체인·SDK를 한 명령으로

주 1회 한 번의 실행으로 Homebrew, Mac App Store, npm globals, rustup, cargo binaries, pipx, Flutter, CocoaPods, oh-my-zsh를 모두 갱신하는 셸 스크립트 1편.

신규 PC를 세팅할 때 환경 구성에는 시간을 쓰지만, 그 다음부터는 다들 갱신을 잊는다. 두 달 뒤에 brew install 한 번 하면 의존성 30개가 줄줄이 따라 올라오고, 네이티브 빌드가 SDK 버전 불일치로 깨지고, 보안 인증서(ca-certificates)는 묵묵히 만료를 향해 간다.

이 글은 그걸 막는 9단계 통합 스크립트다. 주 1회 한 번 실행하면 끝.

TL;DR

  • brew + mas + npm -g + rustup + cargo install-update + pipx + flutter + pod + omz 9개를 순서대로 돌린다
  • 각 단계는 도구가 없으면 자동 SKIP, 한 단계 실패해도 나머지는 진행
  • 끝에 macOS 시스템 업데이트 알림만 (자동 설치는 재부팅 위험이라 제외)
  • 약 100줄짜리 단일 bash 스크립트

왜 필요한가

1. 보안 인증서 만료

ca-certificates는 분기마다 새 번들이 나온다. 두 분기 묵히면 일부 HTTPS 호출이 인증 실패한다. 본인은 쓸 일 없다고 생각해도 npm registry, brew 자체, GitHub API가 다 영향권.

2. 모바일 빌드 SDK drift

Flutter·iOS 작업하는 머신은 특히 위험하다. Flutter는 한 달에 3-4번 stable이 나오고, CocoaPods의 spec repo는 일주일 안 갱신하면 pod install이 "spec not found"를 토하기 시작한다. 주 1회 pod repo update만 돌려도 80%는 막힌다.

3. 언어 툴체인 누적

rustup·pipx·cargo로 깐 글로벌 도구들은 각자 따로 갱신해야 한다. 잊으면 어느 날 cargo install 새 버전이 안 깔려서야 알게 된다 (rust-version 미스매치).

사전 조건

  • macOS 12+ + Homebrew (Mac 초기 셋업)
  • 9단계 전부 채우려면: mas, pipx, cargo-update, flutter, cocoapods, oh-my-zsh 미리 설치
  • 도구 일부가 없어도 OK — 해당 단계만 SKIP하고 진행

사전 설치 (한 번만)

brew install mas pipx cocoapods
brew install --cask flutter
cargo install cargo-update
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
pipx ensurepath

cargo install cargo-update는 컴파일 의존이라 처음엔 2-3분 걸린다. Flutter는 약 1GB 다운로드.

9단계 갱신 대상

#단계명령비고
1Homebrewbrew update && brew upgrade && brew cleanupformulae + casks
2Mac App Storemas upgradeApp Store 앱 (Xcode 등)
3npm globalsnpm update -g글로벌 npm 패키지
4Rust toolchainrustup updatestable channel
5Cargo binariescargo install-update -acargo-update 확장 필요
6pipxpipx upgrade-allPython global tools
7Flutter SDKflutter upgrade현재 채널의 최신
8CocoaPods repopod repo updateiOS 빌드 spec 동기화
9oh-my-zsh~/.oh-my-zsh/tools/upgrade.shzsh 프레임워크

통합 스크립트

저장: ~/bin/update-system.sh 같은 위치. chmod +x 후 사용.

#!/usr/bin/env bash
#
# 시스템 도구 일괄 업데이트 — macOS
# 사용법: ./update-system.sh
#
# 갱신 범위:
#   1. Homebrew (formulae + casks)
#   2. Mac App Store (mas, 있을 때만)
#   3. npm globals
#   4. Rust toolchain (rustup)
#   5. Cargo binaries (cargo install-update -a, cargo-update 필요)
#   6. pipx (Python global tools)
#   7. Flutter SDK (flutter upgrade)
#   8. CocoaPods repo (pod repo update — iOS 빌드 의존)
#   9. oh-my-zsh (omz update)
#
# 종료 시 softwareupdate -l 결과 알림 (자동 설치 X — 재부팅 위험)
#
 
set -uo pipefail
 
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m'
 
TOTAL=9
STEP=0
 
step() {
    STEP=$((STEP + 1))
    printf "\n${CYAN}[%d/%d] %s${NC}\n" "$STEP" "$TOTAL" "$1"
}
 
ok()   { printf "  ${GREEN}OK${NC} %s\n" "$1"; }
skip() { printf "  ${GRAY}SKIP${NC} %s\n" "$1"; }
warn() { printf "  ${YELLOW}WARN${NC} %s\n" "$1"; }
fail() { printf "  ${RED}FAIL${NC} %s\n" "$1"; }
 
printf "${CYAN}━━━ 시스템 업데이트 (macOS) ━━━${NC}\n"
 
# fnm 환경 초기화 (npm 단계에서 노드 경로 잡기 위함)
if command -v fnm >/dev/null 2>&1; then
    eval "$(fnm env --use-on-cd --shell bash 2>/dev/null)" 2>/dev/null || true
fi
 
# 1. Homebrew
step "Homebrew (formulae + casks)"
if command -v brew >/dev/null 2>&1; then
    if brew update && brew upgrade && brew cleanup; then
        ok "brew update / upgrade / cleanup"
    else
        fail "brew 단계 일부 실패 — 위 로그 확인"
    fi
else
    skip "brew 미설치"
fi
 
# 2. Mac App Store
step "Mac App Store (mas)"
if command -v mas >/dev/null 2>&1; then
    mas upgrade && ok "mas upgrade" || fail "mas upgrade"
else
    skip "mas 미설치 (brew install mas)"
fi
 
# 3. npm globals
step "npm globals"
if command -v npm >/dev/null 2>&1; then
    npm update -g && ok "npm update -g" || fail "npm update -g"
else
    skip "npm 미설치"
fi
 
# 4. Rust toolchain
step "Rust toolchain (rustup)"
if command -v rustup >/dev/null 2>&1; then
    rustup update && ok "rustup update" || fail "rustup update"
else
    skip "rustup 미설치"
fi
 
# 5. Cargo binaries
step "Cargo binaries (cargo install-update -a)"
if command -v cargo >/dev/null 2>&1; then
    if cargo install-update --version >/dev/null 2>&1; then
        cargo install-update -a && ok "cargo install-update -a" || fail "cargo install-update -a"
    else
        warn "cargo-update 미설치 (설치: cargo install cargo-update)"
    fi
else
    skip "cargo 미설치"
fi
 
# 6. pipx
step "pipx (Python global tools)"
if command -v pipx >/dev/null 2>&1; then
    pipx upgrade-all && ok "pipx upgrade-all" || fail "pipx upgrade-all"
else
    skip "pipx 미설치 (brew install pipx)"
fi
 
# 7. Flutter SDK
step "Flutter SDK (flutter upgrade)"
if command -v flutter >/dev/null 2>&1; then
    flutter upgrade && ok "flutter upgrade" || fail "flutter upgrade"
else
    skip "flutter 미설치"
fi
 
# 8. CocoaPods repo
step "CocoaPods repo (pod repo update)"
if command -v pod >/dev/null 2>&1; then
    pod repo update && ok "pod repo update" || fail "pod repo update"
else
    skip "pod 미설치 (brew install cocoapods)"
fi
 
# 9. oh-my-zsh
step "oh-my-zsh (omz update)"
if [ -d "$HOME/.oh-my-zsh" ]; then
    OMZ_UPDATER="$HOME/.oh-my-zsh/tools/upgrade.sh"
    if [ -x "$OMZ_UPDATER" ]; then
        zsh "$OMZ_UPDATER" && ok "oh-my-zsh 업데이트" || fail "oh-my-zsh"
    else
        skip "oh-my-zsh upgrade.sh 없음"
    fi
else
    skip "oh-my-zsh 미설치"
fi
 
# macOS 시스템 업데이트 — 알림만
printf "\n${CYAN}━━━ macOS 시스템 업데이트 점검 (알림만) ━━━${NC}\n"
if command -v softwareupdate >/dev/null 2>&1; then
    SU_OUT=$(softwareupdate -l 2>&1)
    if echo "$SU_OUT" | grep -qi "no new software\|No updates"; then
        printf "${GRAY}  최신 — 시스템 업데이트 없음${NC}\n"
    else
        printf "${YELLOW}  ⚠ 사용 가능한 시스템 업데이트가 있습니다:${NC}\n"
        echo "$SU_OUT" | sed 's/^/    /'
        printf "${GRAY}  설치하려면: sudo softwareupdate -ia --restart  (재부팅됨)${NC}\n"
    fi
fi
 
printf "\n${CYAN}━━━ 완료 ━━━${NC}\n"
printf "${GRAY}점검: brew doctor / brew outdated / npm outdated -g / flutter doctor${NC}\n"

실행 결과 예시

처음 돌리면 길어 보이지만 (brew upgrade 13개면 5분 정도), 두 번째부터는 1-2분.

━━━ 시스템 업데이트 (macOS) ━━━

[1/9] Homebrew (formulae + casks)
==> Upgrading 13 outdated packages:
ca-certificates 2026-03-19 -> 2026-05-14
ruby 4.0.3 -> 4.0.4
sqlite 3.53.0 -> 3.53.1
python@3.14 3.14.4_1 -> 3.14.5
...
==> This operation has freed approximately 18.8MB of disk space.
  OK brew update / upgrade / cleanup

[2/9] Mac App Store (mas)
  OK mas upgrade

[3/9] npm globals
  OK npm update -g

[4/9] Rust toolchain (rustup)
  stable-aarch64-apple-darwin unchanged - rustc 1.95.0
  OK rustup update

...

[9/9] oh-my-zsh (omz update)
Hooray! Oh My Zsh has been updated!
  OK oh-my-zsh 업데이트

━━━ macOS 시스템 업데이트 점검 (알림만) ━━━
  최신 — 시스템 업데이트 없음

━━━ 완료 ━━━

자동화 — 어디까지 자동으로

전부 자동화는 비추한다. brew 메이저 버전 점프(예: vercel-cli 53→54)는 가끔 깨지는 변경이 들어가는데, 사람이 보고 있어야 즉시 인지한다.

대신 "잊지 않게 알림만" 정도가 적절:

~/Library/LaunchAgents/local.update-reminder.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>local.update-reminder</string>
  <key>ProgramArguments</key>
  <array>
    <string>osascript</string>
    <string>-e</string>
    <string>display notification "주간 update-system 점검 시간" with title "Dev Maintenance"</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict>
    <key>Weekday</key><integer>5</integer>
    <key>Hour</key><integer>10</integer>
    <key>Minute</key><integer>0</integer>
  </dict>
</dict>
</plist>

활성화:

launchctl load -w ~/Library/LaunchAgents/local.update-reminder.plist

매주 금요일 오전 10시 데스크톱 알림 한 번. 실행은 사람이 결정.

함정

  • macOS 시스템 업데이트는 자동 설치 X: softwareupdate -ia --restart는 강제 재부팅이 들어간다. 작업 중인 IDE·터미널·도커 컨테이너 모두 날아간다. 스크립트는 softwareupdate -l사용 가능 목록만 알림, 설치는 사람이 결정.
  • pod repo update는 느리다 (5분+ 가능): trunk repo가 거대해서. 갱신 자주 안 해뒀으면 더 길어진다.
  • oh-my-zsh 자체 자동 갱신 옵션도 있음: .zshrczstyle ':omz:update' mode auto. 스크립트와 중복이지만 무해.
  • Cargo 컴파일 의존: cargo install-update -a로 신버전 받은 바이너리는 그 자리에서 컴파일된다. 첫 실행 시 수 분 걸릴 수 있음.

Windows 사용자도?

같은 발상으로 MSYS2·winget·언어 툴체인을 한 번에 갱신하는 PowerShell 스크립트 → Windows 개발 환경 정기 유지관리.

정리

  • 도구 1개씩 손으로 갱신 → 잊는다
  • 통합 스크립트 1개 → 잊어도 한 번 돌리면 끝
  • 자동화는 알림까지만, 실행은 사람이
  • 매주 1회면 brew 13개·SDK drift·인증서 만료 모두 막을 수 있다

댓글