RSS듀오랩스
개발 도구

드라이런과 멱등성: 사고를 막는 시점의 차이

작성자
듀오랩스 대표·7분 읽기

배포 스크립트에 안전장치를 넣는다고 할 때 두 가지가 자주 같이 나옵니다.

./deploy.sh --dry-run     # 무엇이 일어날지만 본다
PUT /orders/9f31          # 여러 번 보내도 결과가 같다

둘 다 "실수해도 괜찮게" 만드는 장치라 한 묶음으로 이해하기 쉽습니다. 그런데 이 둘은 막는 시점이 다릅니다. 드라이런은 아직 아무 일도 안 일어났을 때 쓰고, 멱등성은 이미 한 번 일어난 뒤에 쓰입니다.

이 차이를 놓치면 둘 중 하나만 갖춰 놓고 다 갖췄다고 생각하게 됩니다.

부작용만 끄는 드라이런

흔히 「아무것도 안 하고 결과를 예측해 보여 주는 기능」으로 이해합니다. 절반만 맞습니다.

제대로 된 드라이런은 실행할 때와 똑같은 경로를 탑니다. 같은 설정을 읽고, 같은 목록을 만들고, 같은 검증을 통과합니다. 마지막의 쓰기 한 번만 건너뜁니다.

if (COMMIT) await api(`/edits/${id}/listings/${lang}`, { method: 'PUT', body: listing });
console.log(`${COMMIT ? '·' : '(미리보기)'} ${lang} 「${listing.title}」`);

「예측」이 아니라는 점이 중요합니다. 예상 결과를 별도 코드로 계산해 보여 주는 방식은 실제 경로와 갈라지고, 갈라진 뒤에도 미리보기는 여전히 그럴듯하게 나옵니다. 그래서 드라이런의 값어치는 정확히 「실행 경로를 얼마나 그대로 탔는가」에 비례합니다.

부작용을 허용하되 겹쳐도 같게 만드는 멱등성

멱등성은 반대쪽입니다. 실제로 씁니다. 다만 두 번 써도 최종 상태가 한 번 쓴 것과 같습니다.

응답까지 같아야 하는 것은 아닙니다. DELETE 를 두 번 보내면 첫 번째는 204, 두 번째는 404 가 올 수 있는데 그래도 멱등합니다. 자원이 없다는 최종 상태가 같기 때문입니다. 이 구분은 따로 정리해 둔 글이 있습니다.

서로를 대신하지 못하는 이유

드라이런 멱등성
언제 실행 실행 중·후
부작용 없음 있음 (겹쳐도 같음)
막는 사고 틀린 것을 하는 것 맞는 것을 두 번 하는 것
누가 쓰나 명령을 내리는 사람 재시도하는 기계

드라이런은 「이게 맞나」에 답하고, 멱등성은 「또 해도 되나」에 답합니다.

멱등한 API 도 대상을 잘못 고르면 잘못된 대상에 멱등하게 씁니다. 몇 번을 보내도 틀린 곳이 틀린 상태로 정확히 수렴합니다. 반대로 드라이런으로 확인한 명령도 네트워크가 끊겨 재시도하면, 멱등하지 않은 경우 두 번 반영됩니다.

소방이 아니라 폭격 쪽일 가능성이 큰 유래

dry run 이라는 말은 컴퓨터에서 나오지 않았습니다.

널리 퍼진 설명은 소방입니다. 소방차 출동 훈련에서 물을 쓰지 않는 연습을 dry run, 실제 방수를 하는 것을 wet run 이라 불렀다는 것입니다.

다른 설명은 군용 항공입니다. 폭탄을 떨어뜨리지 않는 연습 비행이 dry run 이고 실탄 투하가 live run 입니다.

사전들이 이 표현의 등장을 1940년대 초로 잡습니다. 그 시기라면 2차대전 항공 쪽이 자연스럽습니다. 소방 이야기는 훨씬 자주 인용되는데 출처를 대기는 더 어렵습니다. 저도 어느 쪽이 먼저인지는 확신이 없습니다. 다만 두 설명이 공통으로 말하는 것은 있습니다. 실제와 똑같이 움직이되 나가는 것만 뺀다는 구조입니다. 소방차는 출동하고 폭격기는 목표 상공까지 갑니다.

더 중요한 것은 기본값의 위치

드라이런을 붙이는 방식이 둘로 갈립니다.

tool deploy -n        # 기본은 실행. -n 을 붙여야 미리보기
tool deploy --commit  # 기본은 미리보기. --commit 을 붙여야 실행

앞쪽은 -n기억해야 안전하고, 뒤쪽은 잊어버려도 안전합니다. make -n, rsync -n, git --dry-run 이 전부 앞쪽입니다. 관례가 그쪽으로 굳었고, 대부분의 도구에서는 그게 맞습니다.

다만 한 번의 실수가 되돌리기 어려운 명령이라면 뒤쪽이 낫습니다. 스토어 등록정보를 바꾸거나, 프로덕션 트랙에 릴리스를 올리거나, 라이브 DNS 를 갈아 끼우는 종류입니다. 플래그 하나 더 치는 비용과 잘못 나간 것을 되돌리는 비용이 비교가 안 됩니다.

드라이런의 약점

미리보기와 실행이 별개 실행이라는 점입니다.

tool deploy            # 목록을 읽고 보여 준다
tool deploy --commit   # 목록을 다시 읽고 실행한다

그 사이에 무엇이 바뀌면 본 것과 다른 것이 나갑니다. 혼자 쓰는 스크립트에서는 부딪힐 일이 거의 없지만, 여럿이 같은 자원을 만지는 곳에서는 실재하는 틈입니다.

Terraform 의 planapply 가 그 틈을 메웁니다. plan 이 계획을 파일로 저장하고 apply 가 그 파일을 먹습니다. 검토한 것과 실행되는 것이 같은 물건입니다.

여기까지가 확실한 부분입니다. 저장된 계획을 쓰는 방식이 항상 옳은지는 저도 판단이 서지 않습니다. 계획이 낡으면 그건 그것대로 문제고, 그래서 Terraform 도 apply 시점에 상태를 다시 확인합니다. 틈을 없앤 게 아니라 좁힌 것에 가깝습니다.

설계할 때 무엇이 달라지나

한 문장으로 갈립니다. 드라이런은 사람을 위한 것이고, 멱등성은 기계를 위한 것입니다.

사람이 명령을 내리는 자리에는 드라이런이 필요합니다. 명령을 내리기 전에 무엇이 일어날지 봐야 하니까요. 기계가 재시도하는 자리에는 멱등성이 필요합니다. 재시도하는 쪽은 미리보기를 읽지 않으니까요.

CLI 를 만든다면 앞쪽을, API 를 만든다면 뒤쪽을 먼저 봐야 합니다. 둘 다 있는 자리도 있습니다. 배포 파이프라인이 그렇습니다. 사람이 트리거하고 기계가 재시도하니까요.

마지막 수정:

공유하실 때는 출처(Duolabs)와 원문 주소를 표시해 주세요.