Skip to content
Published on

Terraform 상태 관리와 모듈 설계 실전 가이드: Remote Backend·State Locking·모듈화 패턴과 Drift Detection

Authors
  • Name
    Twitter
Terraform State Management

들어가며

인프라를 코드로 관리하는 IaC(Infrastructure as Code) 도구 중 Terraform은 가장 널리 사용되는 도구이다. 그러나 Terraform을 프로덕션 환경에서 운영할 때 가장 복잡하고 중요한 영역이 바로 상태(State) 관리이다. 상태 파일이 손상되거나 충돌이 발생하면 전체 인프라 운영이 마비될 수 있으며, 잘못된 모듈 설계는 유지보수 비용을 기하급수적으로 증가시킨다.

이 글에서는 Terraform 상태 파일의 내부 구조부터 Remote Backend 설정, State Locking 메커니즘, 모듈 설계 패턴, Drift Detection 전략, 그리고 실제 장애 사례와 복구 절차까지 프로덕션 환경에서 필요한 모든 것을 심층적으로 다룬다. 특히 Terraform 1.10+ 이후 도입된 S3 네이티브 잠금과 같은 최신 변화도 함께 반영하였다.

Terraform State 아키텍처

상태 파일의 역할

Terraform은 terraform.tfstate라는 JSON 파일에 현재 관리 중인 인프라 리소스의 상태를 기록한다. 이 파일은 다음과 같은 핵심 역할을 수행한다.

  • 리소스 매핑: HCL 설정 파일의 리소스 정의와 실제 클라우드 리소스 간의 매핑 정보를 저장한다
  • 변경 계획 계산: terraform plan 실행 시 현재 상태와 원하는 상태의 차이를 계산하는 기준이 된다
  • 메타데이터 관리: 리소스 의존성, 프로바이더 정보, 모듈 경로 등의 메타데이터를 포함한다
  • 성능 최적화: 매번 클라우드 API를 호출하지 않고 캐시된 상태 정보를 활용하여 성능을 향상시킨다

상태 파일 구조

상태 파일의 내부 구조는 다음과 같다.

{
  "version": 4,
  "terraform_version": "1.10.3",
  "serial": 42,
  "lineage": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "outputs": {
    "vpc_id": {
      "value": "vpc-0abc123def456789",
      "type": "string"
    }
  },
  "resources": [
    {
      "mode": "managed",
      "type": "aws_vpc",
      "name": "main",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 1,
          "attributes": {
            "id": "vpc-0abc123def456789",
            "cidr_block": "10.0.0.0/16",
            "tags": {
              "Name": "production-vpc"
            }
          }
        }
      ]
    }
  ]
}
  • version: 상태 파일 포맷 버전 (현재 4)
  • serial: 상태 변경 시마다 증가하는 시퀀스 번호로, 충돌 감지에 사용된다
  • lineage: 상태 파일의 고유 식별자로, 서로 다른 상태 파일이 혼합되는 것을 방지한다
  • resources: 실제 관리 중인 리소스의 속성과 메타데이터

Plan 동작 원리

terraform plan은 세 가지 정보를 비교하여 실행 계획을 생성한다.

  1. 설정 파일(.tf): 사용자가 정의한 원하는 상태(Desired State)
  2. 상태 파일(.tfstate): 마지막으로 적용된 상태(Known State)
  3. 실제 인프라: 클라우드 프로바이더에서 조회한 현재 상태(Actual State)

Terraform은 먼저 실제 인프라를 조회(refresh)하여 상태 파일을 갱신하고, 갱신된 상태와 설정 파일을 비교하여 변경 계획을 산출한다.

Remote Backend 구성

로컬 Backend의 한계

기본적으로 Terraform은 상태 파일을 로컬 파일시스템에 저장한다. 이는 개인 프로젝트에서는 문제없지만, 팀 환경에서는 다음과 같은 심각한 한계가 있다.

  • 팀원 간 상태 파일 공유가 불가능하다
  • 동시 실행 시 상태 파일 충돌이 발생한다
  • 로컬 디스크 장애 시 상태 파일이 유실된다
  • 민감한 정보가 평문으로 로컬에 저장된다

S3 + DynamoDB Backend 설정

AWS 환경에서 가장 일반적인 구성이다. S3는 상태 파일 저장소로, DynamoDB는 상태 잠금용으로 사용된다.

먼저 Backend 인프라를 부트스트랩한다.

# backend-bootstrap/main.tf
resource "aws_s3_bucket" "terraform_state" {
  bucket = "my-company-terraform-state"

  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_versioning" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
  bucket = aws_s3_bucket.terraform_state.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.terraform_state.arn
    }
  }
}

resource "aws_s3_bucket_public_access_block" "terraform_state" {
  bucket                  = aws_s3_bucket.terraform_state.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-state-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

resource "aws_kms_key" "terraform_state" {
  description             = "KMS key for Terraform state encryption"
  deletion_window_in_days = 30
  enable_key_rotation     = true
}

이후 프로젝트에서 Backend를 설정한다.

# terraform block
terraform {
  required_version = ">= 1.10.0"

  backend "s3" {
    bucket         = "my-company-terraform-state"
    key            = "production/network/terraform.tfstate"
    region         = "ap-northeast-2"
    encrypt        = true
    kms_key_id     = "arn:aws:kms:ap-northeast-2:123456789012:key/abcd-1234"
    dynamodb_table = "terraform-state-locks"
  }
}

S3 네이티브 잠금 (Terraform 1.10+)

Terraform 1.10.0부터 S3 네이티브 상태 잠금이 도입되어 더 이상 DynamoDB가 필요하지 않다. S3의 조건부 쓰기(Conditional Write) 기능을 활용한다.

terraform {
  backend "s3" {
    bucket       = "my-company-terraform-state"
    key          = "production/network/terraform.tfstate"
    region       = "ap-northeast-2"
    encrypt      = true
    use_lockfile = true
  }
}

use_lockfile = true 설정만으로 DynamoDB 없이 S3에서 직접 잠금을 관리한다. 기존 DynamoDB 기반 잠금에서 마이그레이션할 때는 dynamodb_table 설정을 제거하고 use_lockfile = true를 추가한 뒤 terraform init -migrate-state를 실행하면 된다.

GCS Backend 설정

GCP 환경에서는 Google Cloud Storage를 사용한다. GCS Backend는 잠금 기능이 내장되어 있어 별도의 잠금 테이블이 필요 없다.

terraform {
  backend "gcs" {
    bucket = "my-company-terraform-state"
    prefix = "production/network"
  }
}

Terraform Cloud / HCP Terraform

HashiCorp의 관리형 서비스로, 상태 관리뿐만 아니라 실행 환경, 정책 관리, 감사 로그 등을 제공한다.

terraform {
  cloud {
    organization = "my-company"

    workspaces {
      name = "production-network"
    }
  }
}

State Locking과 동시성 제어

잠금 메커니즘의 필요성

여러 사용자나 CI/CD 파이프라인이 동시에 terraform apply를 실행하면 상태 파일 충돌이 발생하여 인프라 불일치, 리소스 중복 생성, 최악의 경우 상태 파일 손상이 일어날 수 있다. State Locking은 이를 방지하는 핵심 메커니즘이다.

DynamoDB 기반 잠금 동작 흐름

  1. terraform plan 또는 terraform apply 실행
  2. Terraform이 DynamoDB 테이블에 잠금 항목을 생성한다 (LockID = 상태 파일 경로)
  3. 잠금 획득 성공 시 작업을 수행한다
  4. 다른 사용자가 같은 상태에 대해 작업 시도 시 잠금 충돌 오류가 발생한다
  5. 작업 완료 후 잠금을 해제한다

잠금 충돌 해결

잠금이 걸린 상태에서 작업을 시도하면 다음과 같은 오류가 발생한다.

Error: Error acquiring the state lock
Lock Info:
  ID:        a1b2c3d4-e5f6-7890
  Path:      my-company-terraform-state/production/network/terraform.tfstate
  Operation: OperationTypeApply
  Who:       user@hostname
  Version:   1.10.3
  Created:   2026-03-11 09:15:30.123456 +0000 UTC

정상적인 경우 작업이 완료될 때까지 기다려야 한다. 비정상적으로 잠금이 남아있는 경우(프로세스 크래시, 네트워크 단절 등) 강제 해제를 수행한다.

# 잠금 강제 해제 (반드시 다른 작업이 실행 중이 아닌지 확인 후)
terraform force-unlock a1b2c3d4-e5f6-7890

주의: 실제로 다른 사용자가 apply 중인 상태에서 강제 해제하면 상태 파일이 손상될 수 있으므로, 반드시 해당 잠금의 소유자와 확인한 후 실행해야 한다.

모듈 설계 패턴

모듈 구조 표준

HashiCorp이 권장하는 표준 모듈 구조는 다음과 같다.

modules/
  vpc/
    main.tf          # 리소스 정의
    variables.tf     # 입력 변수
    outputs.tf       # 출력 값
    versions.tf      # 프로바이더 및 Terraform 버전 제약
    README.md        # 모듈 사용 문서
    examples/
      simple/
        main.tf
      complete/
        main.tf
    tests/
      vpc_test.go    # Terratest 기반 테스트

Composition 패턴

작은 단위 모듈을 조합하여 상위 수준 인프라를 구성하는 패턴이다. 각 모듈은 단일 책임 원칙을 따르며, 의존성은 인자로 주입받는다.

# environments/production/main.tf
module "vpc" {
  source  = "../../modules/vpc"
  name    = "production"
  cidr    = "10.0.0.0/16"
  azs     = ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
}

module "security_groups" {
  source = "../../modules/security-groups"
  vpc_id = module.vpc.vpc_id
  environment = "production"
}

module "eks" {
  source             = "../../modules/eks"
  cluster_name       = "production-cluster"
  vpc_id             = module.vpc.vpc_id
  subnet_ids         = module.vpc.private_subnet_ids
  security_group_ids = [module.security_groups.eks_sg_id]
}

module "rds" {
  source             = "../../modules/rds"
  identifier         = "production-db"
  vpc_id             = module.vpc.vpc_id
  subnet_ids         = module.vpc.database_subnet_ids
  security_group_ids = [module.security_groups.rds_sg_id]
}

Facade 패턴

복잡한 내부 모듈 조합을 단순한 인터페이스 뒤에 숨기는 패턴이다. 소비자(Consumer)에게 간결한 API를 제공하면서 내부 복잡성을 캡슐화한다.

# modules/web-application/main.tf
# 내부에서 VPC, ALB, ECS, RDS 모듈을 조합
module "vpc" {
  source = "../vpc"
  cidr   = var.vpc_cidr
}

module "alb" {
  source    = "../alb"
  vpc_id    = module.vpc.vpc_id
  subnet_ids = module.vpc.public_subnet_ids
}

module "ecs" {
  source       = "../ecs"
  cluster_name = var.app_name
  vpc_id       = module.vpc.vpc_id
  alb_arn      = module.alb.alb_arn
}

# 소비자는 간단하게 사용
# environments/production/main.tf
module "web_app" {
  source   = "../../modules/web-application"
  app_name = "my-web-app"
  vpc_cidr = "10.0.0.0/16"
}

Registry를 통한 버전 관리

Private Registry를 활용하면 모듈의 버전 관리와 팀 간 공유가 용이하다.

module "vpc" {
  source  = "app.terraform.io/my-company/vpc/aws"
  version = "~> 3.2.0"

  name = "production"
  cidr = "10.0.0.0/16"
}

버전 제약 패턴은 다음과 같다.

  • = 3.2.1: 정확한 버전 고정
  • ~> 3.2.0: 3.2.x 범위 내 최신 (패치 업데이트 허용)
  • >= 3.2.0, < 4.0.0: 3.x 범위 내 최신 (마이너 업데이트 허용)

Drift Detection과 Remediation

Drift란 무엇인가

Drift(드리프트)는 Terraform이 관리하는 실제 인프라가 상태 파일과 불일치하는 상태를 의미한다. 주요 원인은 다음과 같다.

  • AWS 콘솔에서 수동으로 리소스를 변경한 경우
  • 다른 자동화 도구(Ansible, 스크립트 등)가 리소스를 수정한 경우
  • 클라우드 프로바이더의 자동 업데이트(예: RDS 마이너 버전 업그레이드)
  • 긴급 장애 대응 시 수동 변경을 적용한 경우

terraform plan을 통한 감지

가장 기본적인 Drift Detection 방법은 terraform plan을 정기적으로 실행하는 것이다.

# 상태만 새로고침하여 drift 확인 (인프라 변경 없음)
terraform plan -refresh-only

# 상세 출력으로 변경 사항 확인
terraform plan -refresh-only -detailed-exitcode
# 종료 코드: 0 = 변경 없음, 1 = 오류, 2 = drift 감지

CI/CD 파이프라인에 Drift Detection 통합

#!/bin/bash
# drift-detection.sh
set -euo pipefail

SLACK_WEBHOOK_URL="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX"

echo "Running drift detection..."
terraform init -backend=true -input=false

# -detailed-exitcode: 종료 코드 2는 변경 사항 존재
if terraform plan -refresh-only -detailed-exitcode -input=false > plan_output.txt 2>&1; then
  echo "No drift detected."
  exit 0
fi

EXIT_CODE=$?

if [ "$EXIT_CODE" -eq 2 ]; then
  echo "Drift detected! Sending notification..."
  DRIFT_SUMMARY=$(grep -E "^  #|^  ~|^  -|^  \+" plan_output.txt | head -20)

  curl -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d "{\"text\": \"Drift detected in production infrastructure:\n\`\`\`\n${DRIFT_SUMMARY}\n\`\`\`\"}"
  exit 2
else
  echo "Error running terraform plan"
  exit 1
fi

Drift Remediation 전략

Drift를 발견했을 때 대응 방법은 크게 세 가지이다.

  1. Terraform으로 원복: terraform apply를 실행하여 코드에 정의된 상태로 인프라를 되돌린다
  2. 코드에 반영: 수동 변경이 의도된 것이라면 HCL 코드를 업데이트하여 현재 인프라 상태를 반영한다
  3. 상태 파일 갱신: terraform apply -refresh-only를 실행하여 상태 파일만 현재 인프라에 맞게 갱신한다

State 마이그레이션 전략

로컬에서 Remote Backend로 마이그레이션

# 1. backend 설정 추가 후
terraform init -migrate-state

# 2. 마이그레이션 확인 프롬프트에 yes 입력
# 3. 로컬 상태 파일 삭제
rm terraform.tfstate terraform.tfstate.backup

terraform state mv 활용

리소스 이름 변경이나 모듈 리팩토링 시 사용한다.

# 리소스 이름 변경
terraform state mv aws_instance.old_name aws_instance.new_name

# 모듈로 이동
terraform state mv aws_vpc.main module.network.aws_vpc.main

# 다른 상태 파일로 이동
terraform state mv -state-out=other.tfstate aws_s3_bucket.data aws_s3_bucket.data

terraform import로 기존 리소스 가져오기

# 기존 리소스를 Terraform 관리 하에 둠
terraform import aws_instance.web i-1234567890abcdef0

# 모듈 내 리소스 import
terraform import module.vpc.aws_vpc.main vpc-0abc123def456789

Terraform 1.5+에서는 import 블록을 사용한 선언적 Import도 가능하다.

import {
  to = aws_instance.web
  id = "i-1234567890abcdef0"
}

moved 블록을 사용한 리팩토링 (Terraform 1.1+)

moved {
  from = aws_instance.old_name
  to   = aws_instance.new_name
}

moved {
  from = aws_vpc.main
  to   = module.network.aws_vpc.main
}

moved 블록을 사용하면 terraform state mv와 달리 코드에 선언적으로 리팩토링 이력을 남길 수 있고, 팀원이 terraform plan 실행 시 자동으로 상태가 마이그레이션된다.

비교 분석

Backend 비교표

항목S3 + DynamoDBS3 네이티브 (1.10+)GCSTerraform Cloud
잠금 방식DynamoDB 테이블S3 조건부 쓰기GCS 객체 잠금내장
추가 인프라S3 버킷 + DynamoDB 테이블S3 버킷만GCS 버킷만없음 (SaaS)
암호화SSE-S3/SSE-KMSSSE-S3/SSE-KMSGoogle 관리 키/CMEKHashiCorp Vault
버전 관리S3 버전 관리S3 버전 관리GCS 객체 버전자동 버전 관리
접근 제어IAM 정책IAM 정책IAM 정책팀/조직 기반 RBAC
비용S3 + DynamoDB 비용S3 비용만GCS 비용무료 티어 + 유료
설정 복잡도중간낮음낮음매우 낮음
멀티 클라우드AWS 전용AWS 전용GCP 전용클라우드 무관

모듈 설계 패턴 비교

패턴적합한 상황장점단점
Flat (단일 구성)소규모 프로젝트, 프로토타입단순함, 빠른 시작재사용 불가, 코드 중복
Composition중대규모 프로젝트, 팀 협업재사용성, 테스트 용이초기 설계 비용
Facade복잡한 인프라, 셀프서비스 플랫폼사용 편의성, 일관성유연성 감소, 추상화 유지 비용
Registry대규모 조직, 멀티팀거버넌스, 버전 관리운영 오버헤드

운영 시 주의사항

상태 파일에 민감 정보가 포함된다

Terraform 상태 파일에는 데이터베이스 비밀번호, API 키, 인증서 등 민감한 정보가 평문으로 저장될 수 있다. 반드시 다음을 준수해야 한다.

  • 상태 파일을 Git에 커밋하지 않는다 (.gitignore*.tfstate 추가)
  • Remote Backend에 저장 시 서버 측 암호화를 활성화한다
  • Backend 접근 권한을 최소 권한 원칙에 따라 설정한다
  • sensitive 속성을 출력 값에 적용한다
output "database_password" {
  value     = aws_db_instance.main.password
  sensitive = true
}

상태 파일 분리 전략

대규모 인프라에서는 단일 상태 파일에 모든 리소스를 관리하면 다음과 같은 문제가 발생한다.

  • terraform plan이 수분에서 수십 분 소요된다
  • 네트워크 변경이 애플리케이션에 영향을 미칠 수 있다 (Blast Radius 확대)
  • 팀 간 잠금 충돌이 빈번해진다

권장되는 분리 구조는 다음과 같다.

environments/
  production/
    network/          # VPC, 서브넷, NAT GW (인프라팀)
    security/         # IAM, KMS, Security Group (보안팀)
    database/         # RDS, ElastiCache (DBA)
    application/      # ECS, Lambda, API GW (개발팀)
    monitoring/       # CloudWatch, Datadog (SRE)
  staging/
    ...

각 디렉토리는 독립된 상태 파일을 가지며, terraform_remote_state 데이터 소스를 통해 다른 상태의 출력 값을 참조한다.

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "my-company-terraform-state"
    key    = "production/network/terraform.tfstate"
    region = "ap-northeast-2"
  }
}

resource "aws_instance" "web" {
  subnet_id = data.terraform_remote_state.network.outputs.private_subnet_ids[0]
}

terraform plan 자동 실행은 주의가 필요하다

CI/CD에서 terraform plan을 자동 실행하는 것은 권장되지만, terraform apply -auto-approve를 자동 실행하는 것은 프로덕션 환경에서 매우 위험하다. 반드시 승인 프로세스를 거쳐야 한다.

장애 사례와 복구 절차

사례 1: 상태 파일 손상

상태 파일이 손상되는 가장 흔한 원인은 네트워크 장애 중 terraform apply가 중단되는 것이다.

증상: terraform plan 실행 시 JSON 파싱 오류가 발생한다.

복구 절차:

# 1. S3 버전 관리에서 이전 버전 복원
aws s3api list-object-versions \
  --bucket my-company-terraform-state \
  --prefix production/network/terraform.tfstate

# 2. 정상적인 이전 버전을 다운로드
aws s3api get-object \
  --bucket my-company-terraform-state \
  --key production/network/terraform.tfstate \
  --version-id "VERSION_ID_HERE" \
  restored-state.tfstate

# 3. 상태 파일 검증
terraform show restored-state.tfstate

# 4. 복원된 상태 파일 업로드
aws s3 cp restored-state.tfstate \
  s3://my-company-terraform-state/production/network/terraform.tfstate

# 5. refresh로 최신 상태 동기화
terraform apply -refresh-only

사례 2: 잠금이 해제되지 않음 (Stuck Lock)

CI/CD 파이프라인이 비정상 종료되면서 잠금이 남아있는 경우이다.

증상: Error acquiring the state lock 오류가 지속적으로 발생한다.

복구 절차:

# 1. 실제로 다른 작업이 실행 중인지 확인
# DynamoDB에서 잠금 항목 조회
aws dynamodb get-item \
  --table-name terraform-state-locks \
  --key '{"LockID": {"S": "my-company-terraform-state/production/network/terraform.tfstate"}}'

# 2. 잠금 소유자와 시간 확인 후 강제 해제
terraform force-unlock LOCK_ID_HERE

# 3. 상태 무결성 확인
terraform plan

사례 3: State 충돌 (Serial Mismatch)

두 명의 사용자가 거의 동시에 apply를 수행하여 serial 번호가 불일치하는 경우이다.

증상: Error saving state: serial number mismatch 오류가 발생한다.

복구 절차:

# 1. 현재 원격 상태 다운로드
terraform state pull > remote-state.json

# 2. serial 번호 확인
python3 -c "import json; print(json.load(open('remote-state.json'))['serial'])"

# 3. refresh로 상태 동기화
terraform apply -refresh-only

# 4. 변경 사항 재적용
terraform plan
terraform apply

프로덕션 체크리스트

Terraform을 프로덕션에 도입할 때 반드시 확인해야 할 항목을 정리한다.

Remote Backend 설정

  • Remote Backend(S3, GCS 또는 Terraform Cloud)가 설정되어 있는가
  • 상태 파일 암호화가 활성화되어 있는가 (SSE-KMS 또는 CMEK)
  • S3/GCS 버전 관리가 활성화되어 있는가
  • Backend 접근에 최소 권한 IAM 정책이 적용되어 있는가
  • Public Access Block이 설정되어 있는가

State Locking

  • State Locking이 활성화되어 있는가 (DynamoDB 또는 S3 네이티브)
  • 잠금 테이블의 비용 모드가 적절한가 (PAY_PER_REQUEST 권장)
  • 잠금 강제 해제 절차가 문서화되어 있는가

모듈 관리

  • 모듈이 표준 구조를 따르고 있는가 (main.tf, variables.tf, outputs.tf)
  • 모듈 버전이 고정(pinning)되어 있는가
  • 모듈에 사용 예제와 테스트가 포함되어 있는가
  • Private Registry 또는 Git 태그를 통한 버전 관리가 적용되어 있는가

Drift Detection

  • 정기적인 terraform plan -refresh-only 스케줄이 설정되어 있는가
  • Drift 감지 시 알림(Slack, PagerDuty 등)이 설정되어 있는가
  • Drift 해결 프로세스가 문서화되어 있는가

운영 프로세스

  • .gitignore*.tfstate, .terraform/ 등이 포함되어 있는가
  • CI/CD 파이프라인에서 terraform plan 결과를 PR에 코멘트로 게시하는가
  • 프로덕션 apply에 승인 프로세스가 적용되어 있는가
  • 상태 파일 백업 및 복구 절차가 검증되어 있는가
  • Terraform 버전 업그레이드 절차가 문서화되어 있는가

참고자료

마치며

Terraform 상태 관리는 IaC 운영의 근간이다. 상태 파일이 올바르게 관리되지 않으면 아무리 잘 작성된 HCL 코드라도 프로덕션 환경에서 안정적으로 동작할 수 없다. 이 글에서 다룬 내용을 정리하면 다음과 같다.

첫째, Remote Backend는 필수이다. 로컬 상태 파일은 팀 환경에서 사용할 수 없으며, S3, GCS 또는 Terraform Cloud를 통해 안전하게 상태를 관리해야 한다. Terraform 1.10 이상이라면 S3 네이티브 잠금을 활용하여 DynamoDB 의존성을 제거할 수 있다.

둘째, State Locking은 동시성 제어의 핵심이다. 잠금 없이 여러 사용자가 동시에 apply하면 상태 파일 손상이 불가피하다. 잠금 강제 해제는 반드시 현재 실행 중인 작업이 없음을 확인한 후 수행해야 한다.

셋째, 모듈 설계는 재사용성과 유지보수성의 기반이다. Composition 패턴으로 작은 모듈을 조합하고, Registry를 통해 버전을 관리하면 대규모 조직에서도 일관된 인프라를 유지할 수 있다.

넷째, Drift Detection은 지속적으로 실행해야 한다. 수동 변경은 언제든 발생할 수 있으며, 이를 조기에 감지하고 코드에 반영하지 않으면 Terraform과 실제 인프라의 괴리가 누적되어 결국 Terraform 자체를 사용할 수 없게 된다.

마지막으로, 장애는 반드시 발생한다는 전제 하에 복구 절차를 미리 검증해두어야 한다. S3 버전 관리를 통한 상태 파일 복원, 잠금 강제 해제, serial 충돌 해결 등의 절차를 팀 전체가 숙지하고 있어야 실제 장애 시 빠르게 대응할 수 있다.

Terraform 상태 관리를 견고하게 구축하면, 인프라 변경을 자신 있게 실행할 수 있는 기반이 만들어진다. 이는 DevOps 문화에서 추구하는 빠르고 안전한 배포의 핵심 전제 조건이다.