테라폼 - State는 왜 나눠야 할까?
Terraform State 분리와 의존성 관리, 그리고 destroy 안정성
이전 글에서는 Terraform에서 의존성 문제와 순환 참조가 왜 발생하는지,
그리고 복잡한 인프라를 Create → Ready → Attach → Activate 흐름으로 나누어 보는 관점을 정리했다.
이번 글에서는 Terraform을 운영할 때 반드시 중요해지는 또 다른 주제를 다뤄보려 한다.
Terraform State
Terraform을 처음 사용할 때는 state를 크게 신경 쓰지 않아도 된다.
로컬에서 terraform apply를 실행하면 자연스럽게 terraform.tfstate 파일이 생기고,
Terraform은 이 파일을 기준으로 인프라를 관리한다.
하지만 인프라 규모가 커지고, 여러 리소스가 서로 의존하기 시작하면 state 설계가 매우 중요해진다.
특히 다음과 같은 문제가 생긴다.
- 하나의 state에 너무 많은 리소스가 들어간다.
- 작은 변경에도 전체 리소스가 plan 대상이 된다.
- destroy 범위가 너무 커진다.
- 협업 시 state lock 충돌이 잦아진다.
- 의존성 방향이 코드 구조만으로는 명확하지 않다.
이 글에서는 왜 Terraform State를 분리해야 하는지, 어떤 기준으로 나누면 좋은지, 그리고 state 분리가 CI/CD와 destroy 안정성에 어떤 영향을 주는지 정리해본다.
목차
- 1. Terraform State란 무엇인가
- 2. State가 하나일 때 생기는 문제
- 3. State 분리의 핵심 목적
- 4. State 분리 기준
- 5. 계층 기반 State 분리 예시
- 6. State 간 참조는 어떻게 할까?
- 7. State 분리와 destroy 안정성
- 8. State 분리와 CI/CD
- 9. State 분리 시 주의할 점
- 10. 마무리
1. Terraform State란 무엇인가
Terraform State는 Terraform이 현재 인프라를 어떻게 바라보고 있는지를 저장하는 파일이다.
Terraform은 코드만 보고 인프라를 관리하지 않는다. 현재 인프라 상태를 기록한 state와 현재 코드를 비교해서 어떤 변경이 필요한지 계산한다.
현재 Terraform 코드
+
현재 Terraform State
=
변경 계획(plan)
예를 들어 다음과 같은 리소스를 생성했다고 하자.
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
Terraform apply가 성공하면 Terraform은 실제 AWS에 생성된 VPC의 ID를 state에 저장한다.
aws_vpc.main.id = vpc-xxxxxxxx
이후 다른 리소스가 aws_vpc.main.id를 참조하면,
Terraform은 state에 저장된 값을 기반으로 리소스 간 관계를 추적할 수 있다.
즉, state는 단순한 결과 파일이 아니라 Terraform이 인프라를 관리하기 위한 기준점이다.
Terraform State는 Terraform이 인프라를 기억하는 방식이다.
2. State가 하나일 때 생기는 문제
처음에는 하나의 state로 시작하는 것이 자연스럽다.
terraform/
├── main.tf
├── variables.tf
├── outputs.tf
└── terraform.tfstate
소규모 프로젝트에서는 이 방식이 단순하고 편하다.
하지만 리소스가 많아지고 다음과 같은 구성이 들어가기 시작하면 문제가 생긴다.
VPC
Subnet
Security Group
RDS
ECS
ALB
CloudFront
Route53
Lambda
EventBridge
Monitoring
이 모든 리소스가 하나의 state에 들어가면 다음 문제가 발생한다.
2.1 변경 범위가 커진다
작은 app 설정을 바꾸는 작업인데도 전체 state를 기준으로 plan이 계산된다.
예를 들어 ECS Service의 desired_count나 task definition 관련 변경을 보려는데, 같은 state 안에 RDS, VPC, CloudFront까지 모두 포함되어 있다면 plan 결과가 커지고 확인하기 어려워진다.
작은 변경
→ 큰 plan
→ 검토 어려움
→ 실수 가능성 증가
2.2 destroy 범위가 너무 커진다
하나의 state에 모든 리소스가 들어가 있으면 terraform destroy의 범위도 전체가 된다.
terraform destroy
→ VPC, RDS, ECS, CloudFront, S3 모두 삭제 대상
특히 다음과 같은 리소스는 실수로 삭제되면 위험하다.
- RDS
- S3 Bucket
- Secrets Manager
- KMS Key
- Route53 Hosted Zone
운영 환경에서는 app 리소스만 삭제하고 싶은데 data 리소스까지 함께 삭제 대상이 되는 구조는 위험하다.
하나의 state는 하나의 생명주기를 공유한다.
2.3 협업 시 lock 충돌이 커진다
협업 환경에서는 보통 remote backend와 locking을 사용한다.
한 사람이 Terraform apply를 실행하면 state lock이 걸리고, 다른 사람은 같은 state에 대해 동시에 apply를 실행할 수 없다.
이 자체는 안전한 동작이다. 하지만 모든 리소스가 하나의 state에 있으면 lock 범위가 너무 커진다.
network 작업 중
→ app 작업도 대기
monitoring 변경 중
→ edge 변경도 대기
즉, 서로 관련 없는 작업도 같은 state를 사용한다는 이유로 막힐 수 있다.
2.4 의존성 방향이 흐려진다
하나의 state 안에서는 모든 리소스가 서로 참조 가능하다.
이것은 편해 보이지만, 규모가 커질수록 구조를 흐리게 만든다.
network가 app을 참조
app이 data를 참조
edge가 app을 참조
data가 다시 app을 참조
이런 식으로 의존성 방향이 꼬이면 구조를 이해하기 어려워지고, 순환 참조나 삭제 실패 가능성이 커진다.
3. State 분리의 핵심 목적
State를 나누는 이유는 단순히 파일을 깔끔하게 나누기 위해서가 아니다.
핵심 목적은 다음과 같다.
1. 변경 범위 줄이기
2. destroy 범위 제한하기
3. 의존성 방향 강제하기
4. 협업 충돌 줄이기
5. CI/CD 파이프라인 분리하기
3.1 변경 범위 줄이기
app 리소스를 수정할 때는 app state만 plan/apply하면 된다.
app 변경
→ app.tfstate만 영향
network나 data 리소스를 매번 함께 확인하지 않아도 된다.
3.2 destroy 범위 제한하기
state를 나누면 특정 영역만 삭제할 수 있다.
app destroy
→ ECS, Lambda 등 app 리소스만 삭제
data 유지
→ RDS, S3, Secrets 보존
즉, state 분리는 실수의 폭발 범위를 줄이는 역할을 한다.
State 분리는 blast radius를 줄이는 설계다.
3.3 의존성 방향 강제하기
state를 계층별로 나누면 의존성 방향을 구조적으로 강제할 수 있다.
network → data → app → edge
이 구조에서는 app이 network를 참조할 수는 있지만, network가 app을 참조하지 않도록 설계할 수 있다.
즉, state 분리는 단순한 저장 방식이 아니라 아키텍처 경계를 만드는 방법이기도 하다.
4. State 분리 기준
그렇다면 state는 어떤 기준으로 나누면 좋을까?
정답이 하나로 정해져 있는 것은 아니지만, 일반적으로 다음 기준을 고려할 수 있다.
4.1 변경 빈도
자주 바뀌는 리소스와 거의 바뀌지 않는 리소스는 분리하는 것이 좋다.
거의 안 바뀜:
- VPC
- Subnet
- Route Table
- IAM 기본 Role
자주 바뀜:
- ECS Service
- Lambda
- Task Definition
- EventBridge Schedule
변경 빈도가 다른 리소스를 같은 state에 넣으면, 자주 바뀌는 리소스 때문에 안정적인 기반 리소스까지 매번 plan 대상에 포함된다.
4.2 위험도
삭제되면 위험한 리소스는 별도 state로 관리하는 것이 좋다.
위험도 높음:
- RDS
- S3
- KMS
- Secrets Manager
- Route53 Hosted Zone
이런 리소스는 app과 같은 state에 넣기보다 data 또는 foundation 성격의 state로 분리하는 것이 안전하다.
4.3 의존성 방향
하위 계층을 상위 계층이 참조하는 구조로 나누면 이해하기 쉽다.
network → platform → data → app → edge
예를 들어 app은 VPC, Subnet, Security Group, RDS endpoint 등을 참조할 수 있다. 하지만 network가 app을 참조하면 구조가 이상해진다.
의존성 방향이 자연스럽게 흐르도록 state를 나누는 것이 중요하다.
4.4 소유권
팀이 나뉘어 있다면 소유권 기준으로 state를 나눌 수도 있다.
network 팀
platform 팀
application 팀
security 팀
팀별로 관리 책임이 다르면 state도 분리하는 것이 협업에 유리하다.
5. 계층 기반 State 분리 예시
개인 프로젝트나 소규모 서비스라면 너무 복잡하게 나눌 필요는 없다.
하지만 일정 규모 이상이 되면 다음과 같은 구조를 고려할 수 있다.
terraform/
├── backend/
├── network/
├── platform/
├── data/
├── app/
├── edge/
└── monitoring/
각 계층의 역할은 다음과 같다.
5.1 backend
Terraform state를 저장하기 위한 기반 리소스다.
예:
- S3 backend bucket
- state lock 설정
- backend 관련 IAM
backend는 다른 Terraform 코드가 state를 저장하기 위한 기반이므로 가장 먼저 준비되어야 한다.
보통 backend 자체는 별도 bootstrap 과정으로 관리한다.
5.2 network
네트워크 기반 리소스다.
예:
- VPC
- Subnet
- Route Table
- Internet Gateway
- NAT Gateway
- 기본 Security Group
대부분의 리소스는 network 위에 올라간다. 따라서 network는 하위 계층에 가깝다.
5.3 platform
공통 플랫폼 리소스다.
예:
- IAM Role
- ECR
- 공통 Security Group
- 공통 SSM Parameter
- 공통 Secrets
여러 app이나 worker가 공통으로 사용하는 리소스를 이 계층에 둘 수 있다.
5.4 data
상태를 가지는 리소스다.
예:
- RDS
- S3 data bucket
- Redis
- OpenSearch
- RabbitMQ
data 계층은 destroy에 특히 주의해야 한다.
app을 삭제하더라도 data는 유지해야 하는 경우가 많다.
5.5 app
애플리케이션 실행 리소스다.
예:
- ECS Service
- Lambda
- Task Definition
- Worker
- EventBridge Rule
변경이 가장 자주 일어나는 계층이다.
따라서 network나 data와 분리하면 plan과 apply 범위를 줄일 수 있다.
5.6 edge
외부 진입점에 가까운 리소스다.
예:
- ALB Listener
- CloudFront
- Route53 Record
- WAF
- ACM
edge 계층은 app과 연결되는 경우가 많다.
트래픽 유입과 관련되기 때문에 적용 순서와 destroy 순서가 중요하다.
5.7 monitoring
관측과 알림을 위한 계층이다.
예:
- CloudWatch Alarm
- Log Group
- Grafana
- Prometheus
- Loki
- Dashboard
monitoring은 app이나 data를 관찰하는 계층이다.
운영 중에는 마지막까지 남겨두는 것이 유용한 경우가 많다.
6. State 간 참조는 어떻게 할까?
state를 분리하면 한 가지 문제가 생긴다.
다른 state의 값을 어떻게 가져올 것인가?
예를 들어 app 계층은 network 계층에서 만든 Subnet ID가 필요하다.
network state
→ subnet_id output
app state
→ subnet_id 참조
이때 사용할 수 있는 방법 중 하나가 terraform_remote_state다.
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "my-terraform-state-bucket"
key = "network/terraform.tfstate"
region = "ap-northeast-2"
}
}
그리고 output 값을 사용할 수 있다.
subnet_ids = data.terraform_remote_state.network.outputs.private_subnet_ids
하지만 remote_state를 너무 많이 사용하면 state 간 결합도가 높아질 수 있다.
따라서 다음 원칙을 지키는 것이 좋다.
하위 계층 output만 상위 계층에서 참조한다.
상위 계층이 하위 계층을 역참조하지 않는다.
remote_state 참조는 최소화한다.
7. State 분리와 destroy 안정성
State 분리가 중요한 이유 중 하나는 destroy 안정성이다.
단일 state에서는 terraform destroy의 범위가 너무 넓다.
terraform destroy
→ 모든 리소스 삭제 대상
반면 state를 분리하면 특정 계층만 destroy할 수 있다.
app destroy
→ ECS, Lambda 삭제
data 유지
→ RDS, S3 유지
network 유지
→ VPC, Subnet 유지
이 구조는 특히 운영 환경에서 중요하다.
예를 들어 애플리케이션 배포를 다시 구성하기 위해 app 리소스를 제거해야 할 수 있다. 하지만 그 과정에서 RDS까지 삭제되면 안 된다.
State를 분리하면 이런 위험을 줄일 수 있다.
7.1 lifecycle로 추가 보호하기
중요 리소스는 state 분리와 함께 lifecycle 설정으로 보호할 수도 있다.
resource "aws_db_instance" "main" {
identifier = "app-db"
lifecycle {
prevent_destroy = true
}
}
이 설정을 사용하면 Terraform이 해당 리소스를 삭제하려고 할 때 오류가 발생한다.
물론 모든 리소스에 무조건 넣을 필요는 없다. 하지만 RDS, S3, KMS, 중요한 Secret 같은 리소스에는 고려할 만하다.
8. State 분리와 CI/CD
State를 나누면 CI/CD 파이프라인도 나눌 수 있다.
network pipeline
platform pipeline
data pipeline
app pipeline
edge pipeline
각 pipeline은 자기 state만 plan/apply한다.
예를 들어 app 코드가 바뀌었는데 network까지 plan할 필요는 없다.
app 변경
→ app pipeline 실행
→ app.tfstate만 변경
이렇게 하면 배포 속도도 빨라지고, 변경 영향 범위도 줄어든다.
8.1 apply 순서
state를 분리하면 apply 순서가 중요해진다.
backend
→ network
→ platform
→ data
→ app
→ edge
→ monitoring
하위 계층이 먼저 생성되어야 상위 계층에서 참조할 수 있다.
예를 들어 app이 Subnet ID를 필요로 한다면 network가 먼저 apply되어야 한다.
8.2 destroy 순서
destroy는 일반적으로 반대 방향으로 진행하는 것이 안전하다.
monitoring
→ edge
→ app
→ data
→ platform
→ network
→ backend
다만 실제 destroy에서는 단순히 순서를 반대로 하는 것만으로 충분하지 않을 수 있다.
이전 글에서 설명한 것처럼 먼저 연결을 끊어야 하는 경우가 있다.
Deactivate
→ Detach
→ Destroy
예를 들어 CloudFront에 연결된 WAF를 삭제하려면 먼저 CloudFront에서 WAF 연결을 해제해야 한다.
9. State 분리 시 주의할 점
State 분리는 장점이 많지만, 무조건 많이 나누는 것이 정답은 아니다.
너무 잘게 나누면 오히려 관리가 어려워질 수 있다.
9.1 작은 프로젝트에서는 과할 수 있다
리소스가 몇 개 없는 프로젝트라면 하나의 state로 시작해도 충분하다.
VPC
Subnet
Security Group
EC2
이 정도 규모에서는 state를 과하게 나누면 오히려 복잡도만 올라간다.
9.2 remote_state 의존성이 늘어난다
state를 나누면 각 state 간 output 참조가 필요하다.
이 참조가 많아지면 state 간 결합도가 커질 수 있다.
app → network
app → data
edge → app
monitoring → app
monitoring → data
따라서 output은 꼭 필요한 값만 노출하는 것이 좋다.
9.3 orchestration이 필요하다
state가 나뉘면 apply 순서를 관리해야 한다.
즉, Terraform 코드만 나누는 것이 아니라 CI/CD에서도 실행 순서를 정의해야 한다.
network apply 완료
→ data apply
→ app apply
→ edge apply
이 순서를 관리하지 않으면 상위 계층이 필요한 output을 찾지 못하는 문제가 발생할 수 있다.
9.4 이름 규칙이 중요해진다
state가 여러 개로 나뉘면 디렉토리와 state key 이름 규칙이 중요하다.
network/terraform.tfstate
platform/terraform.tfstate
data/terraform.tfstate
app/terraform.tfstate
edge/terraform.tfstate
처음부터 일관된 규칙을 정해두는 것이 좋다.
10. 마무리
Terraform State는 단순한 결과 파일이 아니다.
Terraform이 인프라를 관리하기 위해 사용하는 기준점이다.
처음에는 하나의 state로 시작해도 괜찮다. 하지만 인프라 규모가 커지고 리소스 간 의존성이 복잡해지면 state 분리를 고려해야 한다.
State를 분리하면 다음 장점이 있다.
변경 범위 감소
destroy 범위 제한
의존성 방향 강제
협업 충돌 감소
CI/CD 분리 가능
반대로 단점도 있다.
remote_state 관리 필요
apply 순서 관리 필요
구조 복잡도 증가
따라서 중요한 것은 무조건 나누는 것이 아니라, 리소스의 생명주기와 위험도, 변경 빈도, 의존성 방향을 기준으로 나누는 것이다.
한 줄 정리
State를 나누는 것은 구조를 위한 선택이 아니라, 안전성을 위한 설계다.
다음 글에서는 Terraform과 CI/CD의 역할을 어떻게 나누면 좋은지, 그리고 GitHub Actions에서 Terraform을 어떻게 적용할 수 있는지 정리해본다.
'테라폼' 카테고리의 다른 글
| 4-0. 테라폼 - 초보자를 위한 기본 코드 작성법 (0) | 2026.05.11 |
|---|---|
| 3-4. 테라폼 - CI/CD에서 Terraform은 어디까지 맡아야 할까? (1) | 2026.05.05 |
| 3-2. 테라폼 - 순환 참조는 왜 발생하고 어떻게 끊어야 할까? (0) | 2026.05.05 |
| 3-1. 테라폼 - apply는 성공했는데 왜 서비스는 동작하지 않을까? (0) | 2026.05.05 |
| 3-0. 테라폼 - 리소스의 분류 및 구현 (0) | 2026.05.05 |