Back to the blog

The posts on this blog are written in Korean. There is no English version. Open the Korean page

기본값이 지운 '빠짐'과 '끔'의 구분: 54일 된 배포 안전장치

기본값을 채운 설정값으로는 '빠짐'과 '일부러 끔'을 가를 수 없다. 배포 안전장치가 끄는 배포까지 막던 구조와, 없음, 끔, 켬 세 칸으로 고친 과정.

Published 0 views

TL;DR

  • 설정값을 false 로 바꾸면 배포가 54일 동안 항상 멈추는 구조였다
  • 안전장치가 기본값 'false' 를 채운 값을 읽어 '없음'과 '끔'이 같았다
  • 원래 값을 따로 넘겨 false 만 통과시키고 테스트 3건으로 고정했다
  • 세 칸 원칙: 없음, 끔, 켬. 판단은 기본값을 채우기 전에 한다

글쓰기 커뮤니티 서비스 하나를 운영하고 있다. 여러 서비스가 같이 쓰는 중앙 로그인(한 계정으로 여러 서비스에 들어가는 방식, SSO)을 붙였다가 보류하기로 했다. 그 로그인을 쓰는 서비스가 하나뿐이었기 때문이다. 할 일은 설정값 하나를 true 에서 false 로 바꾸고 배포하는 것이었다. 그런데 배포가 그 자리에서 멈췄다.

끄는 배포를 멈춘 안전장치

배포 스크립트에 이런 검사가 있었다. 이 글에서는 이런 검사를 안전장치라고 부른다. 위험한 상태를 보면 배포를 멈추는 코드다.

if [ "$prev_sso" = "true" ] && [ "$NIMUNI_SSO_BUILD_FLAG" != "true" ]; then
  echo 'ERROR: 서버는 NEXT_PUBLIC_NIMUNI_SSO_ENABLED=true 인데 러너 빌드는 꺼진 상태다.' >&2
  exit 1
fi

화면에 중앙 로그인 버튼을 보일지는 빌드할 때 정해져서 결과물에 박힌다. 서버 설정 파일을 나중에 고쳐도 되돌릴 수 없다. 그래서 2026-08-19 에 이 안전장치를 넣었다. 서버는 켜져 있는데 새 빌드가 꺼진 채로 올라오면 로그인 버튼이 경고 없이 사라진다. GitHub 저장소에 등록한 설정값(repository variable) 하나가 지워지기만 해도 그렇게 된다.

빌드 쪽 값은 이렇게 받았다. || 뒤의 'false' 는 값이 비었을 때 대신 쓰는 기본값이다.

NIMUNI_SSO_BUILD_FLAG: ${{ vars.PROD_NIMUNI_SSO_ENABLED || 'false' }}

설정값을 false 로 바꾸고 배포를 돌렸다. 안전장치가 위 오류를 내고 멈췄다. 안내 문구는 "variable 을 true 로 설정한 뒤 다시 배포하라" 였다. 끄려는 사람에게 다시 켜라고 안내한 셈이다.

세 칸 원칙

설정값에는 없음, 끔, 켬 세 칸이 있다. 기본값을 채우는 순간 없음과 끔이 한 칸으로 합쳐진다. 그러니 '빠졌는지'를 묻는 판단은 기본값을 채우기 전에 한다.

GitHub Actions 에서 등록하지 않은 설정값은 빈 문자열이 된다. || 'false' 는 그 빈 문자열을 false 로 바꾼다. 일부러 넣은 false 도 그대로 false 다. 안전장치에 도착할 때 둘은 같은 글자다.

flowchart LR
  A[없음] -->|빈 문자열| F["기본값 false 채우기"]
  B[끔 false] --> F
  C[켬 true] --> T[true]
  F --> X[false]
  X --> G{안전장치}
  T --> G
  G -->|false 면 멈춤| S[배포 중단]
  G -->|true 면 통과| P[배포 진행]

안전장치는 '없음'을 막으려고 만들었다. 그런데 '없음'만 골라낼 정보가 안전장치까지 오지 않아서 '끔'까지 같이 막았다. 이 안전장치가 있는 동안 중앙 로그인을 끄는 배포는 한 번도 성공할 수 없었다.

54일 동안 이 사실이 드러나지 않았다. 그동안 아무도 끄려고 하지 않았고 이 안전장치를 실제로 돌려 보는 테스트도 0건이었다. 만들 때 생각한 경우는 '없음' 하나였다.

같은 모양은 GitHub Actions 밖에도 있다.

코드없음끔안전장치가 보는 값
${{ vars.X || 'false' }}falsefalse같음
process.env.X ?? 'false'falsefalse같음
config.get('X', 'false')falsefalse같음

셋 다 기본값을 채우는 쪽에서는 편하다. 문제는 그 값을 받아서 '빠졌는지' 판단하는 코드가 뒤에 붙을 때 생긴다.

원래 값을 따로 넘기는 처리

기본값 채우기를 없애지는 않았다. 빌드와 서버 설정은 여전히 true 나 false 하나를 받아야 한다. 대신 안전장치만을 위해 기본값을 채우지 않은 원래 값을 하나 더 넘겼다.

NIMUNI_SSO_BUILD_FLAG: ${{ vars.PROD_NIMUNI_SSO_ENABLED || 'false' }}
# 기본값 없는 원래 값. 비어 있으면 '없음', false 면 '일부러 끔'이다.
DECLARED_NIMUNI_SSO_FLAG: ${{ vars.PROD_NIMUNI_SSO_ENABLED }}
if [ "$prev_sso" = "true" ] && [ "$NIMUNI_SSO_BUILD_FLAG" != "true" ] \
   && [ "$DECLARED_NIMUNI_SSO_FLAG" != "false" ]; then

원래 값이 비어 있으면 지금처럼 멈춘다. false 라고 적혀 있을 때만 통과한다. 끄는 사람은 설정값을 지우지 말고 false 로 적으면 된다. 이 절차는 배포 문서에도 적었다.

안전장치를 직접 돌리는 테스트

이 안전장치에는 테스트가 없었다. 옆 안전장치들의 테스트도 배포 설정 파일에 특정 문자열이 있는지만 봤다. 그렇게 보면 어떤 입력에서 멈추는지 알 수 없다. 그래서 배포 설정 파일에서 if ... fi 부분을 잘라 bash 로 직접 돌리게 했다.

const cases = [
  { what: "variable missing",  declared: "",      build: "false", status: 1 },
  { what: "explicit disable",  declared: "false", build: "false", status: 0 },
  { what: "still enabled",     declared: "true",  build: "true",  status: 0 },
];

없음, 끔, 켬 세 칸을 하나씩 넣었다. 고치기 전 설정에서는 두 번째 줄(끔)이 실패했다. 고친 뒤에는 세 줄 모두 통과했다. dev 와 prod 배포 설정 두 개에 같은 검사를 걸었고 배포 관련 테스트 47건이 전부 통과했다.

고치기 전과 후

고치기 전고친 뒤
설정값이 없을 때멈춤멈춤
설정값이 false 일 때멈춤배포
안전장치를 돌리는 테스트0건3건

고친 뒤 dev 와 prod 모두 false 로 배포됐다. 두 서버 설정에 NIMUNI_SSO_ENABLED=false 가 들어간 것을 확인했다. 로그인 화면에는 이메일 입력란과 Google 버튼이 다시 나왔다.

남은 것

화면 전용으로 값을 덮어쓰는 설정값이 하나 더 있다. vars.X_NEXT_PUBLIC_... || vars.X || 'false' 꼴이다. 이 값만 true 로 두고 기본 설정값을 false 로 두면 화면은 켜지고 서버는 꺼진 채로 배포된다. 지금은 그 설정값을 등록하지 않아서 고치지 않았다. 등록하는 날 두 값이 다르면 멈추는 검사를 같이 넣어야 한다.

다음부터

실수로 꺼지는 것을 막는 안전장치를 만들 때는 기본값을 채우기 전의 원래 값으로 판단하고, 그날 없음, 끔, 켬 세 칸을 모두 테스트에 넣는다.

Comments

No comment is visible yet.

Write a comment

A comment shows after the administrator approves it. Your email stays private.

Agreement to the collection and use of personal information (required)
  • Items: name, email, comment
  • Purpose: to show and manage comments, and to contact you about your comment
  • Retention: the name and the comment while the comment is on the site, the email for 1 year. The site deletes a comment that it does not show after 90 days.
  • To stop spam, the site keeps your IP address for 90 days. This is based on the legitimate interest of the operator, not on your agreement.
  • You have the right to refuse. If you refuse, you cannot write a comment. See the privacy policy for details.