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' }} | false | false | 같음 |
process.env.X ?? 'false' | false | false | 같음 |
config.get('X', 'false') | false | false | 같음 |
셋 다 기본값을 채우는 쪽에서는 편하다. 문제는 그 값을 받아서 '빠졌는지' 판단하는 코드가 뒤에 붙을 때 생긴다.
원래 값을 따로 넘기는 처리
기본값 채우기를 없애지는 않았다. 빌드와 서버 설정은 여전히 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 로 두면 화면은 켜지고 서버는 꺼진 채로 배포된다. 지금은 그 설정값을 등록하지 않아서 고치지 않았다. 등록하는 날 두 값이 다르면 멈추는 검사를 같이 넣어야 한다.
다음부터
실수로 꺼지는 것을 막는 안전장치를 만들 때는 기본값을 채우기 전의 원래 값으로 판단하고, 그날 없음, 끔, 켬 세 칸을 모두 테스트에 넣는다.