0Pricing
DevOps Bootcamp · 강의

set -euo pipefail을 활용한 엄격 모드

즉시 실패 동작을 활성화하고 각 엄격 모드 플래그가 포착하거나 놓치는 오류를 정확히 이해합니다.

set -euo pipefail을 활용한 엄격 모드은(는) CoddyKit의 무료 DevOps Bootcamp 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DevOps Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

Bash가 기본적으로 조용히 실패하는 이유

기본적으로 Bash는 명령이 실패해도 계속 실행합니다. 이로 인해 운영 스크립트에서 미묘하고 디버깅하기 어려운 장애가 발생합니다.

백업을 생성하려는 다음 스크립트를 생각해 보십시오.

  • 경로의 오타로 cp가 실패합니다.
  • Bash는 실패를 무시하고 계속 진행합니다.
  • 데이터가 실제로 백업되지 않았는데도 스크립트는 성공을 보고합니다.

이것이 조용한 실패 문제입니다. 엄격 모드를 사용하면 Bash가 컴파일 언어처럼 동작하여 문제가 발생하는 즉시 중지하므로 이 문제를 해결할 수 있습니다.

#!/usr/bin/env bash
# Without strict mode — dangerous default behavior

cp /important/data /backups/data   # fails (path doesn't exist)
echo "Backup complete"              # still prints — false confidence!
rm -rf /tmp/staging                # still runs — potentially destructive

세 가지 핵심 플래그: set -euo pipefail

모든 스크립트의 앞부분 가까이에 다음 줄을 넣어 엄격 모드를 활성화합니다.

set -euo pipefail

이 명령은 서로 다른 세 가지 보호 기능을 활성화합니다.

  • -e — 어떤 명령이든 0이 아닌 상태를 반환하면 즉시 종료합니다.
  • -u — 설정되지 않은 변수를 오류로 처리합니다(빈 문자열로 확장하지 않음).
  • -o pipefail — 파이프라인의 마지막 명령만이 아니라 파이프라인 안의 어떤 명령이든 실패하면 파이프라인을 실패로 처리합니다.

이 세 가지를 함께 사용하면 신뢰성이 중요한 Bash 스크립트에서 표준 방어용 헤더가 됩니다. 각 플래그는 서로 다른 종류의 버그를 잡아냅니다.

#!/usr/bin/env bash
set -euo pipefail

echo "Strict mode is now active"
echo "Every command failure will abort the script"

set -e(errexit) 이해하기

set -e(set -o errexit라고도 씁니다)는 명령이 0이 아닌 상태로 종료되면 스크립트를 즉시 종료합니다.

다음과 같은 주요 동작을 알아 두십시오.

  • 일반 명령인 false, grep pattern file(일치 항목 없음), ls /nonexistent는 모두 종료를 유발합니다.
  • 스크립트의 마지막 명령의 종료 코드가 스크립트의 종료 코드가 됩니다.
  • if 조건 안의 명령은 예외입니다. -e는 테스트 표현식에 적용되지 않습니다.
  • || true가 뒤따르는 명령도 예외입니다(다음 장면 참조).

-e를 실패 후 조용히 계속 실행되는 것을 막는 첫 번째 방어선이라고 생각하십시오.

#!/usr/bin/env bash
set -e

echo "Before failure"
ls /this/path/does/not/exist   # exits here with code 2
echo "This line never runs"

set -u(nounset) 이해하기

set -u(set -o nounset이라고도 씁니다)는 설정되지 않은 변수를 참조하면 Bash가 이를 치명적인 오류로 처리하게 합니다.

-u가 없으면 $FILENAME 대신 $FLENAME처럼 오타를 내도 빈 문자열로 조용히 확장됩니다. 그 결과 명령이 예상하지 못한 방식으로, 또는 위험하게 동작할 수 있습니다. 예를 들어 $TMPDIR이 설정되지 않은 상태에서 rm -rf "$TMPDIR/"를 실행하는 경우입니다.

중요한 예외는 다음과 같습니다.

  • ${VAR:-default} — 안전한 기본값 치환이므로 -u를 발동시키지 않습니다.
  • ${VAR:+value} — 조건부 확장이므로 역시 안전합니다.
  • 위치 인수가 전달되지 않은 경우 "$@"와 "$*"는 예외로 처리됩니다.
#!/usr/bin/env bash
set -euo pipefail

# Safe: provide a default for optional vars
OUTPUT_DIR="${1:-/tmp/output}"
LOG_LEVEL="${LOG_LEVEL:-info}"

echo "Writing to: $OUTPUT_DIR"
echo "Log level: $LOG_LEVEL"

# This would abort the script:
# echo "$UNDEFINED_VAR"   # bash: UNDEFINED_VAR: unbound variable

-o pipefail 이해하기

pipefail이 없으면 파이프라인의 종료 상태는 마지막 명령만으로 결정됩니다. 앞선 명령의 실패는 조용히 묻힙니다.

pipefail이 없는 경우의 예:

  • cat /missing/file | wc -l
  • cat은 종료 코드 1로 실패하지만 wc -l은 코드 0으로 성공합니다.
  • 데이터가 손실되었는데도 파이프라인은 0, 즉 성공을 반환합니다.

pipefail을 활성화하면 Bash는 실패한 가장 오른쪽 명령의 종료 코드를 반환합니다. 따라서 파이프라인 실패를 확인하고 처리할 수 있습니다.

참고: pipefail은 단일 문자 플래그가 아니므로 -o pipefail을 사용하여 설정해야 합니다.

#!/usr/bin/env bash
set -euo pipefail

# With pipefail: this aborts if grep finds nothing (exit 1)
# grep returns 1 when no match found
ps aux | grep "[n]ginx" | awk '{print $2}'

echo "If we reach here, nginx is running"

의도적인 명령 실패 — || true 사용하기

때로는 명령이 실패해도 허용해야 합니다. set -e를 사용하면 허용되는 실패임을 명시해야 하며, 그렇지 않으면 스크립트가 중단됩니다.

관용적인 해결 방법은 항상 성공하는 대체 동작을 덧붙이는 || true입니다.

  • command || true — 실패를 완전히 무시합니다.
  • command || echo "Warning: step failed, continuing" — 기록을 남기고 계속합니다.
  • command || { echo "fatal"; exit 1; } — 사용자 지정 실패 처리를 수행합니다.

이 패턴은 코드에서 의도를 명확히 보여 줍니다. 일반 명령은 "반드시 성공해야 함"을 뜻하고, || true는 "실패할 수 있으며 괜찮음"을 뜻합니다.

#!/usr/bin/env bash
set -euo pipefail

# Remove temp dir if it exists — OK if it doesn't
rm -rf /tmp/my_workspace || true
mkdir -p /tmp/my_workspace

# Check if a service is running — OK if not
if systemctl is-active --quiet nginx 2>/dev/null || true; then
  echo "nginx is active"
fi

# Grep that may find nothing — OK
grep 'ERROR' /var/log/app.log || true

echo "Done"

set -e가 포착하지 못하는 것

set -e에는 잘 알려진 예외와 함정이 있습니다. 이를 이해하면 잘못된 확신을 피할 수 있습니다.

  • if / while / until 조건 안의 명령 — 테스트 표현식은 설계상 예외입니다.
  • !로 부정된 명령 — ! false는 종료를 유발하지 않습니다.
  • || 앞의 마지막 명령 — 예를 들어 false || handle_error입니다.
  • 특정 상황에서의 서브셸 종료 상태 — 일부 Bash 버전에서 VAR=$(failing_command)와 같은 경우입니다.
  • 함수 반환값 — 함수의 마지막 명령만 반영됩니다.

엄격 모드는 명시적인 오류 확인을 대신하는 기능이 아닙니다. 우발적인 실패의 대부분을 잡아내는 안전망입니다.

#!/usr/bin/env bash
set -euo pipefail

# These do NOT trigger -e:
if false; then echo "never"; fi         # -e exempt in conditions
! false                                  # negation exempts
false || echo "handled"                 # || exempts the left side

# This DOES trigger -e (no condition, no ||):
# false

echo "Script continues after exempted failures"

엄격 모드에서 서브셸과 함수 사용하기

엄격 모드 설정은 서브셸에 상속되지만 함수와 명령 치환에서는 미묘하게 다르게 동작합니다.

주요 규칙은 다음과 같습니다.

  • 함수는 호출한 셸에서 -e, -u, pipefail을 상속합니다.
  • 함수가 0이 아닌 값을 반환하면 -e가 설정된 경우 호출한 쪽이 종료됩니다. 단, 호출이 조건 안에 있거나 || 뒤에 있으면 예외입니다.
  • 명령 치환 $(): 이전 Bash에서는 $() 내부의 실패한 명령이 부모의 -e를 발동시키지 않을 수 있으므로, 안전하게 처리하려면 값을 할당한 뒤 별도로 사용하십시오.
  • 명시적인 서브셸 ()은 모든 플래그를 상속합니다.
#!/usr/bin/env bash
set -euo pipefail

setup_workspace() {
  local dir="$1"
  mkdir -p "$dir"          # fails here if permissions denied
  cd "$dir"
  echo "Ready in $(pwd)"
}

# Safe pattern: assign result then use it
TODAY=$(date +%Y-%m-%d)   # capture separately
WORKDIR="/tmp/run_${TODAY}"

setup_workspace "$WORKDIR"
echo "Workspace: $WORKDIR"

엄격 모드와 오류 트랩 결합하기

엄격 모드는 Bash에 언제 중지할지를 알려 줍니다. ERR에 trap을 설정하면 스크립트가 종료되기 전에 정리 또는 진단 작업을 실행할 수 있습니다.

일반적인 패턴은 다음과 같습니다.

  • 처음에 엄격 모드를 설정합니다.
  • cleanup 또는 on_error 함수를 정의합니다.
  • trap 'on_error' ERR로 등록합니다.
  • 성공 또는 실패와 관계없이 정리를 보장하려면 선택적으로 EXIT에도 트랩을 설정합니다.

중요: set -E(대문자 E, errtrace라고도 함)를 사용해야 ERR 트랩이 함수와 서브셸에도 상속됩니다. 이를 사용하지 않으면 트랩은 주 셸 본문에서만 실행됩니다.

#!/usr/bin/env bash
set -Eeuo pipefail

on_error() {
  local exit_code=$?
  local line_number=${BASH_LINENO[0]}
  echo "ERROR: command failed with code ${exit_code} at line ${line_number}" >&2
}

cleanup() {
  echo "Cleaning up temporary files..." >&2
  rm -rf /tmp/my_run_dir 2>/dev/null || true
}

trap on_error ERR
trap cleanup EXIT

mkdir -p /tmp/my_run_dir
echo "hello" > /tmp/my_run_dir/output.txt
cat /tmp/my_run_dir/output.txt
echo "Done"

엄격 모드를 로컬에서 비활성화하기

때로는 코드 블록을 의도적으로 "거칠게" 실행해야 합니다. 예를 들어 선택적 도구가 있는지 확인하거나 오류가 아닌 이유로 0이 아닌 값을 반환하는 오래된 명령을 실행하는 경우입니다. 이때 잠시 엄격 모드를 비활성화한 뒤 복원할 수 있습니다.

안전한 패턴은 다음과 같습니다.

  • set +e로 상태를 저장하고(-e 비활성화), 블록을 실행한 다음 set -e로 다시 활성화합니다.
  • 또는 서브셸 ( set +e; ... )을 사용하여 부모 셸의 플래그가 전혀 영향을 받지 않게 합니다.
  • 위험한 블록이 끝나는 즉시 항상 플래그를 다시 활성화하십시오. 플래그를 끈 채로 두는 것은 버그의 흔한 원인입니다.

여러 명령이 포함된 블록이라면 종료 시 플래그를 자동으로 복원하는 서브셸 형식을 우선 사용하십시오.

#!/usr/bin/env bash
set -euo pipefail

# Probe for optional tools without aborting
HAS_JQ=false
(
  set +e
  command -v jq > /dev/null 2>&1
  [[ $? -eq 0 ]] && echo "jq_found"
) && HAS_JQ=true || true

if [[ "$HAS_JQ" == "true" ]]; then
  echo "jq is available — using JSON output"
else
  echo "jq not found — using plain text"
fi

완전한 엄격 모드 스크립트 템플릿

이 레슨에서 다룬 엄격 모드 모범 사례를 모두 결합한 운영 환경용 템플릿은 다음과 같습니다.

  • set -Eeuo pipefail — errtrace를 포함한 네 가지 플래그입니다.
  • IFS=$'\n\t' — 더 안전한 단어 분할을 사용합니다(공백 기준 분할 방지).
  • 진단 및 정리를 위한 ERR + EXIT 트랩
  • 선택적 매개변수에 대한 명시적 기본값
  • 변수 범위를 제한하기 위한 readonly 및 local

복잡하지 않은 스크립트를 제외한 모든 Bash 스크립트의 시작 부분에 이 템플릿을 복사하면, 즉시 실패 시 빠르게 중지하는 동작과 추적 가능한 오류의 이점을 얻을 수 있습니다.

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'

# ── Constants ────────────────────────────────────────────
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"

# ── Trap handlers ────────────────────────────────────────
err_handler() {
  echo "[${SCRIPT_NAME}] ERROR on line ${BASH_LINENO[0]}: exit ${?}" >&2
}
cleanup() {
  echo "[${SCRIPT_NAME}] Exiting" >&2
}
trap err_handler ERR
trap cleanup EXIT

# ── Defaults ─────────────────────────────────────────────
ENV="${1:-production}"
MAX_RETRIES="${MAX_RETRIES:-3}"

# ── Main ─────────────────────────────────────────────────
main() {
  echo "Running in env=${ENV}, max_retries=${MAX_RETRIES}"
  echo "Script dir: ${SCRIPT_DIR}"
}

main "$@"

확인 문제: pipefail 동작

pipefail이 파이프라인의 종료 코드에 어떤 영향을 미치는지 이해했는지 확인해 보십시오.

복습: set -euo pipefail을 사용한 엄격 모드

이 단원에서는 엄격 모드를 사용하여 Bash 스크립트가 신속하고 명확하게 실패하도록 만드는 방법을 배웠습니다.

세 플래그와 각 플래그가 방지하는 문제:

  • -e (errexit) — 0이 아닌 명령 상태가 반환되면 종료합니다. 단, 조건문과 || 뒤에서는 예외입니다.
  • -u (nounset) — 설정되지 않은 변수를 참조하면 중단합니다. 선택적 변수에는 ${VAR:-default}를 사용하십시오.
  • -o pipefail — 마지막 단계뿐 아니라 어느 단계에서든 실패하면 전체 파이프라인이 실패하도록 합니다.

함께 사용하면 좋은 방법:

  • -E (errtrace)를 추가하여 ERR 트랩이 함수 안에서도 전파되도록 합니다.
  • ERR 및 EXIT에 trap을 사용하여 진단과 정리를 수행합니다.
  • 실패를 의도적으로 허용하려면 || true를 사용합니다.
  • 기존 코드나 탐색 코드에서는 서브셸 안에서 set +e를 사용하여 일시적으로 비활성화합니다.

엄격 모드가 모든 문제를 해결해 주는 것은 아니므로 예외를 알아 두어야 합니다. 하지만 안정적이고 방어적인 Bash 스크립트를 작성하는 데 가장 효과적인 단일 습관입니다.

자주 묻는 질문

“set -euo pipefail을 활용한 엄격 모드” 강의는 무료인가요?

네 — “set -euo pipefail을 활용한 엄격 모드” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DevOps Bootcamp 강의 전체를 잠금 해제할 수 있습니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

“set -euo pipefail을 활용한 엄격 모드”에서 뭘 배우나요?

즉시 실패 동작을 활성화하고 각 엄격 모드 플래그가 포착하거나 놓치는 오류를 정확히 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 DevOps Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

DevOps Bootcamp을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 DevOps Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“set -euo pipefail을 활용한 엄격 모드” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 DevOps Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 DevOps Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. set -euo pipefail을 활용한 엄격 모드
  2. 정리 및 신호 처리를 위한 트랩 핸들러
  3. 안전한 임시 파일 및 잠금 디렉터리
  4. 멱등적 스크립트와 지수 백오프 재시도 로직
← DevOps Bootcamp(으)로 돌아가기