Terraform과 IaC의 핵심 구조
Terraform은 인프라를 HCL 코드와 State, DAG 기반 실행으로 재현 가능한 변경 문제로 바꾸는 IaC 도구다. 이 스크립트는 State 관리, 환경 분리, 모듈화, 드리프트, 도구 선택과 주요 실패 모드를 함께 정리한다.
Script Companion
오디오와 함께 스크립트 보기
- 01
Terraform을 이해할 때 출발점은 수동 콘솔 작업의 한계다. AWS 콘솔에서 VPC, ECS, RDS를 직접 만들면 그 순간의 클릭은 지나가 버리고, 다음 사람이 같은 환경을 똑같이 재현하기 어렵다. AWS CLI 스크립트도 실행 당시 무엇을 했는지는 남길 수 있지만, 이미 존재하는 리소스와 코드의 일대일 매핑, 삭제와 교체 순서, 팀 단위 동시 변경 제어까지 안정적으로 보장하기는 어렵다. Terraform은 이 문제를 인프라 변경을 재현 가능한 그래프 계산 문제로 바꾸는 방식으로 푼다.
- 02
IaC, Infrastructure as Code는 인프라 구성을 사람이 읽을 수 있는 코드 파일로 관리하는 방식이다. Terraform에서는 HCL, HashiCorp Configuration Language로 리소스를 선언하고, 이 코드를 실행해 실제 인프라를 만든다. 처음 Terraform 코드를 읽을 때는 variable, locals, output을 입력, 내부 계산, 반환값으로 잡으면 구조가 훨씬 선명해진다. 콘솔 작업이 수작업 요리라면 Terraform 코드는 레시피에 가깝고, 그 레시피가 Git에 남기 때문에 누가 언제 무엇을 바꿨는지도 추적할 수 있다.
- 03
Terraform의 기본 흐름은 Init, Plan, Apply다. Init은 필요한 Provider를 준비하고, Plan은 현재 코드와 State, 실제 인프라를 비교해 무엇이 바뀔지 검토하며, Apply는 실제 생성, 수정, 삭제를 수행한다. 내부적으로는 Terraform Core가 State 관리, 의존성 계산, 변경 사항 결정을 맡고, Provider 플러그인이 AWS, GCP, Azure 같은 플랫폼 API와 통신한다. Plan이 실행될 때는 방향성 비순환 그래프인 DAG를 만들고, VPC, Subnet, Security Group, EC2처럼 순서가 필요한 리소스는 의존성을 보장하며, 독립적인 리소스는 병렬로 처리한다.
- 04
State는 Terraform의 기억이다. terraform.tfstate 파일은 단순한 백업이 아니라, aws_s3_bucket.logs 같은 Terraform 주소가 실제 AWS의 어떤 버킷 ID를 가리키는지 기록하는 매핑 테이블이다. 이 매핑이 없으면 Terraform은 기존 리소스를 같은 대상으로 인식하지 못해 새로 만들 수도 있고, 반대로 State에는 있는데 코드에서 빠진 리소스를 삭제 대상으로 볼 수도 있다. State에는 성능을 위한 속성 캐시와 의존성 메타데이터도 들어가며, DB 초기 비밀번호나 API 토큰 같은 민감 값이 포함될 수 있으므로 Git에 커밋하면 안 된다.
- 05
팀에서 Terraform을 쓸 때 State는 로컬 파일이 아니라 Remote Backend에 저장해 공유해야 한다. Terraform 1.9 이하의 레거시 구성에서는 S3와 DynamoDB Lock 조합을 많이 썼고, Terraform 1.10부터는 S3 use_lockfile 방식으로 DynamoDB 없이 State Lock을 구현할 수 있다. 이때 State 객체와 별도로 tflock 객체 접근 권한이 필요하며, State 복구를 위해 S3 버킷 Versioning을 켜는 것이 권장된다. 핵심은 여러 사람이 동시에 같은 State를 바꾸지 못하게 막고, 변경 기록을 안전하게 보존하는 것이다.
- 06
Terraform의 State reconciliation은 더 넓은 패턴과 연결된다. 원하는 상태를 선언하고, 현재 상태를 읽고, 차이가 있으면 수렴시키는 구조는 Terraform, Kubernetes, Argo CD, Flux, DB 마이그레이션에서 모두 보인다. Terraform은 tf 파일과 tfstate를 기준으로 AWS API 응답과 비교하고, 사람이 Apply를 실행하는 명령형 트리거 방식이다. 반면 Kubernetes, Argo CD, Flux는 컨트롤러가 상시 감시하는 지속 reconciliation 루프에 가깝다. Terraform Cloud의 Drift Detection은 이 간극을 좁히려는 시도지만, Argo CD의 Self-heal처럼 완전 자동은 아니다.
- 07
이 패턴 전이는 코드리뷰에서도 유용하다. 어떤 시스템을 처음 볼 때 원하는 상태를 선언하는 파일이나 리소스가 있는지, 현재 상태를 읽어오는 Read 또는 Observe 단계가 있는지, 차이가 있을 때 자동 또는 반자동으로 수렴시키는 액션이 있는지를 보면 된다. 세 질문에 모두 예라고 답할 수 있다면 같은 제어 이론의 구현으로 볼 수 있다. 실무에서는 Terraform으로 EKS 클러스터와 VPC 같은 플랫폼 레이어를 만들고, Argo CD나 Flux가 Deployment와 Service 같은 앱 레이어를 GitOps로 관리하는 계층 분리가 중요하다.
- 08
Module은 자주 쓰는 인프라 구성을 재사용 가능한 패키지로 만든 것이다. 팀이 합의한 VPC 모듈이나 ECS 모듈을 만들면 신규 환경을 만들 때 표준 인프라를 반복해서 찍어낼 수 있다. 프로덕션 Terraform 코드베이스는 단순히 tf 파일을 나열하지 않고, 모듈을 관심사별로 나누고 환경별 설정을 격리한다. 특히 dev, staging, prod가 독립적인 State 파일을 갖도록 분리하는 것이 핵심이다. dev의 State와 prod의 State를 공유하면 한 환경의 변경이 다른 환경에 영향을 줄 수 있기 때문이다.
- 09
EKS 영역에서는 AWS 공식 EKS Blueprints가 중요한 프로덕션 패턴으로 언급된다. EKS Blueprints는 ArgoCD, Karpenter, KEDA, AWS Load Balancer Controller 같은 Add-on을 포함해 프로덕션 수준 EKS 클러스터를 Terraform으로 구성하는 패턴 모음이다. EKS 클러스터를 만들 때 권한 부여 방식도 변화했다. 기존 IRSA, IAM Roles for Service Accounts는 OIDC Provider와 Service Account 어노테이션을 사용했지만, EKS Pod Identity는 각 Node에서 실행되는 Pod Identity Agent가 자격증명을 대신 발급하는 방식이다. 2025년 기준 신규 클러스터의 기본값으로 권장된다.
- 10
환경 분리는 Workspace와 디렉토리 분리 중 하나를 고르는 문제다. Workspace는 같은 설정을 여러 번 배포하되 State만 나누는 기능이고, 동일한 루트 모듈의 여러 인스턴스로 이해해야 한다. 팀이 작고 환경 차이가 이미지 태그나 레플리카 수 정도라면 Workspace가 맞을 수 있다. 반대로 팀이 커지고 dev, staging, prod, dr처럼 환경이 늘거나, prod만 Multi-AZ RDS, 별도 AWS 계정, 별도 IAM Role, 승인 게이트를 쓴다면 디렉토리 분리가 더 안전하다. 선택을 잘못하면 성장 후 마이그레이션 비용이 커진다.
- 11
Terraform 운영에서 자주 쓰는 제어 장치도 함께 봐야 한다. Drift는 누군가 AWS 콘솔에서 직접 리소스를 수정해 Terraform State와 실제 인프라가 달라진 상태다. 근본적인 예방책은 Terraform으로 관리하는 리소스는 Terraform으로만 변경하고, 긴급하게 콘솔에서 바꿨다면 이후 코드에도 반영하는 것이다. lifecycle은 prevent_destroy, create_before_destroy, ignore_changes처럼 리소스 생성, 수정, 삭제 동작을 제어한다. import block은 Terraform 1.5부터 기존 리소스를 선언적으로 State에 등록하게 해 코드 리뷰 가능한 import를 돕고, data source는 이미 존재하는 리소스 정보를 동적으로 읽어온다.
- 12
Terragrunt는 여러 환경과 여러 리전을 관리할 때 반복되는 backend 설정, provider 설정, 공통 변수를 줄이는 Terraform 래퍼 도구다. Terraform만으로 dev, staging, prod를 나누면 각 환경 디렉토리에 비슷한 설정이 쌓이기 쉬운데, Terragrunt는 공통 설정을 한 곳에 두고 환경별 차이만 정의하게 한다. 다만 Terraform 자체가 항상 정답은 아니다. 일회성 프로토타입이나 PoC는 State 초기화와 Remote Backend 설정이 목적에 비해 무거울 수 있고, 팀 5명 이하의 단일 서비스라면 CloudFormation이나 AWS CDK로 시작한 뒤 성장하면서 Terraform으로 옮기는 경로가 현실적일 수 있다.
- 13
도구 선택은 팀 규모와 운영 모델에 따라 달라진다. 팀 10명 이하의 단일 클라우드라면 Terraform 단독이 학습 곡선, 커뮤니티, Provider 생태계 측면에서 강하다. 팀 10명에서 50명 규모의 멀티 환경이라면 Terraform과 Terragrunt 조합이 환경별 State 격리와 중복 제거에 맞다. 팀 50명 이상 멀티 클라우드에서는 Pulumi 또는 Terraform과 Terragrunt를 비교하게 되고, K8s 네이티브 플랫폼 팀은 Crossplane도 선택지가 된다. HashiCorp가 2023년에 Terraform 라이선스를 BSL로 바꾼 뒤 OpenTofu가 CNCF 프로젝트로 성장했고, 2026년 기준 기본 문법은 거의 같지만 라이선스와 일부 기능 차이가 생기고 있다.
- 14
2025년에서 2026년 사이의 생태계 변화도 선택 기준에 들어간다. Terraform Stacks는 VPC, EKS 클러스터, RDS, 애플리케이션 인프라처럼 여러 레이어로 구성된 인프라를 하나의 Stack으로 묶어 배포하는 기능이다. OpenTofu는 MPL-2.0 기반의 오픈소스 선택지이며, OpenTofu 1.11의 Ephemeral Resources는 AWS Secrets Manager의 비밀번호처럼 Apply 실행 중에만 필요하고 State에 남기면 안 되는 값을 다루는 기능이다. 반대로 AWS CDKTF, CDK for Terraform은 2025년 12월 공식 deprecated와 GitHub 저장소 archived로 정리되었으므로 Pulumi 또는 순수 HCL Terraform이 권장된다.
- 15
실패 모드는 State와 경계 관리에서 많이 나온다. Error acquiring the state lock은 이전 Apply가 강제 종료되어 Lock이 남았을 때 발생할 수 있고, 실제로 누군가 실행 중인지 확인하지 않은 force unlock은 State를 꼬이게 만들 수 있다. ResourceAlreadyExistsException은 AWS에는 리소스가 있지만 State에는 없어 Terraform이 새로 만들려 할 때 나타나며, 반대로 삭제된 리소스가 State에 남으면 Plan에서 다시 만들려 할 수 있다. Plan is stale은 Plan 이후 다른 Apply로 State가 바뀌어 승인된 Plan이 더 이상 유효하지 않은 상황이다.
- 16
마지막으로 Terraform의 경계는 분명히 잡아야 한다. Kubernetes 오브젝트를 Terraform kubernetes_deployment로 관리하면 VPA나 HPA가 바꾼 값을 Terraform이 다시 덮어쓰는 충돌이 생길 수 있고, Argo CD나 Flux의 지속 reconciliation 루프와도 맞지 않는다. Crossplane으로 AWS RDS나 S3를 이미 CRD로 관리한다면 Terraform이 같은 리소스를 함께 관리해 State 충돌이 생길 수 있다. 정리하면 Terraform은 HCL로 인프라를 선언하고, State로 실제 리소스와 코드를 매핑하며, Plan과 Apply로 변경을 통제하는 도구다. 중요한 것은 어떤 레이어를 Terraform이 맡고, 어떤 레이어를 다른 reconciler에 맡길지 분리하는 것이다.
같은 레이어