테라폼 기초 강좌 #9 원격 state와 협업: S3 백엔드, 네이티브 잠금, 민감 정보 보호
#5의 결론은 “로컬 디스크는 state의 보관처로 부적합하다"였습니다. 기초 시리즈의 마지막 편에서 그 숙제를 풉니다. state를 S3로 옮기고 잠금을 걸어, 혼자 쓰던 테라폼을 팀이 함께 쓸 수 있는 상태로 만드는 것입니다. 이 한 편을 마치면 실전 시리즈에서 인프라 전체를 세울 준비가 끝납니다.
로컬 state의 세 가지 문제 #
terraform.tfstate가 노트북에만 있을 때 팀에는 세 가지 문제가 생깁니다.
- 공유가 안 됩니다: 동료가 같은 코드를 받아 apply 하면, 동료의 빈 state 기준으로 테라폼이 모든 리소스를 또 만들려고 합니다. state가 없는 코드는 반쪽입니다.
- 동시 실행을 막을 수 없습니다: 두 사람이 같은 인프라에 동시에 apply 하면 각자의 계산이 서로를 모른 채 진행되어 state가 어긋나거나 리소스가 중복됩니다.
- 잃어버리기 쉽습니다: 노트북 분실, 디스크 고장, 실수로 삭제. #5에서 본 대로 state를 잃으면 import라는 노동이 기다립니다.
이 셋을 한 번에 푸는 것이 원격 백엔드(backend)입니다. state를 팀이 공유하는 저장소에 두고, 테라폼이 실행 때마다 거기서 읽고 쓰게 만드는 것입니다. AWS 환경의 표준 선택은 S3입니다.
state 버킷 준비 #
먼저 state를 담을 버킷이 필요합니다. 이 버킷 하나는 닭과 달걀 문제(state를 담을 버킷을 테라폼으로 만들면 그 state는 어디에)를 피하기 위해 콘솔이나 CLI로 만드는 것이 일반적입니다.
aws s3api create-bucket --bucket my-team-tfstate \
--region ap-northeast-2 \
--create-bucket-configuration LocationConstraint=ap-northeast-2
aws s3api put-bucket-versioning --bucket my-team-tfstate \
--versioning-configuration Status=Enabled버저닝은 선택이 아니라 필수입니다. state가 손상되거나 잘못된 조작으로 덮어써졌을 때, 버저닝이 켜져 있으면 이전 버전으로 되돌릴 수 있습니다. state 사고의 마지막 보험입니다. S3 기본 암호화(SSE-S3)는 요즘 버킷에 자동 적용되지만, 조직 정책에 따라 KMS 키 암호화로 올릴 수도 있습니다.
backend 블록과 마이그레이션 #
백엔드는 terraform 블록 안에 선언합니다.
terraform {
required_version = ">= 1.15.0"
backend "s3" {
bucket = "my-team-tfstate"
key = "myapp/prod/terraform.tfstate"
region = "ap-northeast-2"
use_lockfile = true
}
}key는 버킷 안에서 이 프로젝트의 state가 놓일 경로입니다. 버킷 하나에 여러 프로젝트·환경의 state를 key로 구분해 담는 것이 일반적이므로, 프로젝트/환경/terraform.tfstate 같은 규칙을 처음부터 정해 두는 것이 좋습니다. 블록을 추가하고 terraform init을 실행하면 테라폼이 기존 로컬 state를 발견하고 물어봅니다.
Initializing the backend...
Do you want to copy existing state to the new backend?
Enter a value: yesyes라고 답하면 로컬 state가 S3로 복사되고, 이후의 모든 plan·apply는 S3의 state를 읽고 씁니다. 로컬의 terraform.tfstate는 더 이상 쓰이지 않으므로 확인 후 지워도 됩니다. 이 시점부터 동료도 같은 코드로 init만 하면 같은 state를 보게 되어, 문제 1과 3이 풀렸습니다.
use_lockfile: 동시 실행 잠금 #
남은 것은 동시 실행입니다. 위 설정의 use_lockfile = true가 그 답입니다. apply가 시작되면 테라폼이 state 파일 옆에 .tflock 객체를 만들어 잠금을 표시하고, 끝나면 지웁니다. 잠금이 있는 동안 다른 사람이 실행하면 이렇게 거부됩니다.
Error: Error acquiring the state lock
Lock Info:
Who: bob@laptop
Created: 2026-08-30 09:12:44 UTC이 S3 네이티브 잠금은 테라폼 1.11에서 정식(GA)이 되었습니다. 그 전에는 잠금 전용 DynamoDB 테이블을 따로 만들어 dynamodb_table 인수로 연결하는 방식이 표준이었고, 검색하면 아직 그 구성이 많이 나옵니다. DynamoDB 방식은 폐기 예고 상태이며 새 구성에서는 use_lockfile만으로 충분합니다. 팀 전원의 테라폼이 1.11 이상이어야 한다는 조건만 확인하면 됩니다.
잠금 에러를 만났을 때의 원칙도 알아 두어야 합니다. 대부분은 동료의 apply가 진행 중이라는 정상 신호이므로 기다리는 것이 정답입니다. apply가 강제 종료되어 잠금만 남는 경우가 가끔 있는데, 그때만 terraform force-unlock <잠금ID>로 풀어 줍니다. Lock Info의 Who를 확인해 실행 중인 사람이 정말 없는지 확인하고 쓰는 최후 수단이지, 에러가 뜨자마자 반사적으로 치는 명령이 아닙니다.
state 버킷의 접근 제어 #
state가 팀 공유 저장소에 올라온 순간, #5에서 본 평문 민감 정보 문제가 팀 문제가 됩니다. state 버킷을 읽을 수 있는 사람은 그 안의 데이터베이스 비밀번호도 읽을 수 있습니다. 그래서 state 버킷에는 최소 접근 원칙을 적용합니다. 퍼블릭 차단은 물론이고, 테라폼을 실행하는 역할에만 해당 key 경로의 읽기·쓰기를 허용하는 IAM 정책으로 좁힙니다. 민감 정보를 state에 아예 넣지 않는 설계(Secrets Manager 참조 등)는 실전 시리즈의 시크릿 편에서 다루겠습니다.
기초 시리즈를 마치며 #
아홉 편을 정리하면 이렇습니다. 인프라를 선언으로 적고(#1), HCL로 표현하고(#2), 값을 변수로 빼고(#3), 기존 것을 읽어 오고(#4), state라는 중심을 이해하고(#5), 의존과 반복을 다루고(#6), 수명주기를 제어하고(#7), 모듈로 구조화하고(#8), 오늘 팀 협업의 토대를 만들었습니다. 문법과 원리는 갖춰졌고, 남은 것은 실제 인프라를 세우는 경험입니다.
이번 글에서 다룬 내용입니다.
- 로컬 state는 공유 불가, 동시 실행 충돌, 분실 위험이라는 세 가지 문제를 갖고, 원격 백엔드가 이를 한 번에 풉니다
- state 버킷은 테라폼 밖에서 만들고 버저닝을 반드시 켭니다. 사고 시 이전 버전 복원이 마지막 보험입니다
backend "s3"블록 추가 후 init이 기존 state의 마이그레이션을 안내합니다. key 경로 규칙은 처음부터 정합니다- 잠금은
use_lockfile = true가 현행 표준입니다(1.11 GA). DynamoDB 테이블 방식은 폐기 예고 상태이고, force-unlock은 실행자가 없음을 확인한 뒤의 최후 수단입니다 - state 버킷은 평문 민감 정보를 담으므로 IAM으로 접근을 최소화합니다
다음은 테라폼 실전 강좌입니다. VPC 네트워크부터 도메인과 HTTPS까지, 웹 서비스 인프라 하나를 열 편에 걸쳐 처음부터 끝까지 코드로 세우겠습니다.