Output이 하는 일: 모듈 간 데이터 전달과 CLI 노출
Terraform에서 Output Values는 두 가지 핵심 역할을 한다. 첫째, terraform apply 후 사용자에게 중요한 값(IP 주소, 엔드포인트 URL 등)을 표시한다. 둘째, 모듈 간 데이터를 전달하는 인터페이스 역할을 한다. Terraform 공식 문서는 Output을 “자식 모듈이 리소스 속성의 부분 집합을 부모 모듈에 노출하거나, 루트 모듈이 CLI 출력으로 특정 값을 표시하는 방법”으로 정의한다.
Output을 제대로 설계하면 모듈 재사용성이 높아지고, terraform_remote_state를 통한 프로젝트 간 참조도 가능해진다. 이 글에서는 Output의 기본 문법부터 sensitive 마스킹, depends_on, precondition, 모듈 간 전달 패턴, 그리고 remote state 연동까지 공식 문서 기반으로 정리한다.
Output 기본 문법과 속성
# 기본 Output
output "instance_ip" {
description = "EC2 인스턴스의 퍼블릭 IP"
value = aws_instance.web.public_ip
}
# 복합 값 Output
output "db_connection" {
description = "DB 연결 정보"
value = {
host = aws_db_instance.main.address
port = aws_db_instance.main.port
database = aws_db_instance.main.db_name
}
}
| 속성 | 필수 | 역할 | 비고 |
|---|---|---|---|
value |
✅ | 출력할 값 (모든 타입 가능) | 표현식, 리소스 속성, 함수 모두 가능 |
description |
❌ | 문서화 (terraform output, registry) | 공개 모듈에서는 사실상 필수 |
sensitive |
❌ | CLI 출력에서 값 마스킹 | state 파일에는 평문 저장됨 |
depends_on |
❌ | 명시적 의존성 | 드물게 사용, 숨겨진 의존성 해결 |
precondition |
❌ | 값에 대한 검증 조건 (1.2+) | 조건 실패 시 에러 |
sensitive: CLI 마스킹과 그 한계
output "db_password" {
description = "데이터베이스 루트 비밀번호"
value = aws_db_instance.main.password
sensitive = true
}
# terraform output 실행 시:
# db_password = <sensitive>
# 값을 확인하려면 명시적으로:
# terraform output -raw db_password
# terraform output -json db_password
중요한 한계: sensitive = true는 CLI 출력만 마스킹한다. state 파일에는 평문으로 저장된다. 공식 문서는 이를 명시하며, state 파일의 암호화(S3 SSE, GCS CMEK 등)를 별도로 설정하라고 권장한다.
| sensitive 동작 | 마스킹 여부 |
|---|---|
terraform output |
✅ <sensitive>로 표시 |
terraform output -raw |
❌ 평문 출력 |
terraform output -json |
❌ JSON에 포함 |
terraform plan 로그 |
✅ (known after apply) 또는 마스킹 |
| state 파일 (tfstate) | ❌ 평문 저장 |
| 다른 모듈에서 참조 | ✅ 참조 측도 sensitive 전파 |
sensitive 전파 규칙
# 자식 모듈의 output이 sensitive면,
# 부모 모듈에서 참조하는 값도 자동으로 sensitive로 처리됨
# child/outputs.tf
output "secret_key" {
value = tls_private_key.main.private_key_pem
sensitive = true
}
# parent/main.tf
module "child" {
source = "./child"
}
# 이 값은 자동으로 sensitive — 다시 output하려면 sensitive = true 필요
output "child_secret" {
value = module.child.secret_key
sensitive = true # 생략하면 에러 발생
}
모듈 간 Output 전달: 모듈의 공개 인터페이스
자식 모듈의 Output은 부모 모듈에서 module.<NAME>.<OUTPUT>으로 참조한다. Output이 없으면 모듈 내부의 리소스 속성에 접근할 방법이 없다. 즉, Output은 모듈의 공개 API다.
# modules/vpc/outputs.tf
output "vpc_id" {
description = "생성된 VPC의 ID"
value = aws_vpc.main.id
}
output "private_subnet_ids" {
description = "프라이빗 서브넷 ID 목록"
value = aws_subnet.private[*].id
}
output "public_subnet_ids" {
description = "퍼블릭 서브넷 ID 목록"
value = aws_subnet.public[*].id
}
# modules/ecs/variables.tf
variable "vpc_id" {
type = string
description = "ECS 클러스터를 배포할 VPC ID"
}
variable "subnet_ids" {
type = list(string)
description = "ECS 태스크를 배포할 서브넷 ID 목록"
}
# root/main.tf — 모듈 간 연결
module "vpc" {
source = "./modules/vpc"
cidr = "10.0.0.0/16"
}
module "ecs" {
source = "./modules/ecs"
vpc_id = module.vpc.vpc_id # VPC 모듈의 output 참조
subnet_ids = module.vpc.private_subnet_ids # 리스트 output
}
terraform_remote_state: 프로젝트 간 Output 참조
별도의 Terraform 프로젝트(state)에서 다른 프로젝트의 Output을 읽을 수 있다. 네트워크 팀이 VPC를 관리하고, 애플리케이션 팀이 그 위에 서비스를 배포하는 구조에서 유용하다.
# 네트워크 프로젝트 (state: s3://tf-state/network)
# 이미 apply된 상태에서 output이 state에 저장됨
output "vpc_id" {
value = aws_vpc.main.id
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
}
# 애플리케이션 프로젝트 — 네트워크 state 참조
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "tf-state"
key = "network"
region = "ap-northeast-2"
}
}
resource "aws_ecs_service" "app" {
# remote state의 output 참조
network_configuration {
subnets = data.terraform_remote_state.network.outputs.private_subnet_ids
}
}
| 참조 방식 | 사용 시점 | 장점 | 단점 |
|---|---|---|---|
module.x.output |
같은 프로젝트 내 모듈 간 | 직접 의존성, 타입 안전 | 같은 state에 있어야 함 |
terraform_remote_state |
다른 프로젝트(state) 간 | 팀 간 분리, 독립 apply | 전체 state 읽기 권한 필요 |
data source (대안) |
다른 프로젝트 간 | 특정 리소스만 읽기, 최소 권한 | 리소스 식별자를 알아야 함 |
공식 문서 권장: terraform_remote_state는 전체 state에 대한 읽기 접근이 필요하므로, 민감한 output이 포함된 state에는 주의가 필요하다. 대안으로 특정 리소스만 읽는 data source(예: aws_vpc, aws_subnet)를 사용할 수 있다.
precondition: Output 값 검증 (Terraform 1.2+)
output "api_endpoint" {
description = "API Gateway 엔드포인트"
value = aws_apigatewayv2_api.main.api_endpoint
precondition {
condition = length(aws_apigatewayv2_api.main.api_endpoint) > 0
error_message = "API endpoint가 비어 있습니다. API Gateway 생성을 확인하세요."
}
}
output "cluster_version" {
description = "EKS 클러스터 버전"
value = aws_eks_cluster.main.version
precondition {
condition = tonumber(split(".", aws_eks_cluster.main.version)[1]) >= 28
error_message = "EKS 버전이 1.28 미만입니다. 업그레이드하세요."
}
}
precondition은 terraform apply 시 Output 값이 기대 조건을 만족하는지 검증한다. 실패하면 apply가 에러로 중단되므로, 후속 모듈이 잘못된 값을 받는 것을 방지한다.
Output 활용 패턴: CLI와 스크립트 연동
# 특정 output 값만 출력 (스크립트용)
terraform output -raw instance_ip
# JSON으로 모든 output 출력 (파이프라인용)
terraform output -json
# jq와 조합
terraform output -json | jq -r '.private_subnet_ids.value[]'
# Shell 변수에 할당
DB_HOST=$(terraform output -raw db_host)
echo "Connecting to $DB_HOST"
# CI/CD 파이프라인에서 Output을 다음 단계로 전달
# GitHub Actions 예시
- name: Get Terraform outputs
id: tf
run: |
echo "api_url=$(terraform output -raw api_endpoint)" >> $GITHUB_OUTPUT
echo "cluster_name=$(terraform output -raw cluster_name)" >> $GITHUB_OUTPUT
- name: Deploy application
run: |
kubectl --context ${{ steps.tf.outputs.cluster_name }} apply -f k8s/
depends_on: 숨겨진 의존성 선언
# Output이 참조하는 값 외에 추가 의존성이 있는 경우
output "app_url" {
description = "애플리케이션 URL (DNS 전파 후 접근 가능)"
value = "https://${aws_route53_record.app.fqdn}"
# DNS 레코드뿐 아니라 ACM 인증서 검증도 완료되어야 유효
depends_on = [aws_acm_certificate_validation.app]
}
공식 문서에 따르면 depends_on은 “Output이 참조하지 않는 리소스에 간접적으로 의존할 때”만 사용한다. 대부분의 경우 Terraform이 value 표현식에서 의존성을 자동 추론하므로, depends_on이 필요한 상황은 드물다.
실전 체크리스트: Output 설계 7단계
- 모듈의 공개 인터페이스로 설계 — 다른 모듈이 필요로 하는 값만 Output으로 노출, 내부 구현 상세는 숨긴다
- description 항상 작성 —
terraform output과 Terraform Registry 문서에 표시된다 - sensitive 표시 — 비밀번호, 프라이빗 키, 토큰에는 반드시
sensitive = true - state 파일 암호화 — sensitive output이 있으면 backend 암호화(S3 SSE 등) 필수
- 타입 일관성 — 리스트 output은
[*]splat, 맵 output은 명시적 구조체 사용 - precondition으로 계약 검증 — 후속 모듈이 의존하는 값에 조건 추가 (1.2+)
- remote_state 최소화 — 가능하면 data source로 대체해 state 접근 범위를 줄인다
흔한 실수 4가지와 방지법
실수 1: sensitive output을 참조하는 측에서 sensitive를 생략
증상: Error: Output refers to sensitive values. 자식 모듈의 sensitive output을 부모에서 다시 output할 때 sensitive = true를 빠뜨렸다.
방지: sensitive 값을 참조하는 모든 output에 sensitive = true를 명시한다. Terraform은 sensitive 전파를 강제한다.
실수 2: sensitive = true로 state 파일이 안전하다고 오해
증상: state 파일을 Git에 커밋했는데 비밀번호가 평문으로 노출.
방지: sensitive는 CLI 마스킹일 뿐이다. state 파일에는 평문 저장된다. remote backend + 암호화를 반드시 사용하고, .gitignore에 *.tfstate를 추가한다.
실수 3: 모듈 output을 정의하지 않고 리소스를 참조하려 함
증상: module.vpc.aws_vpc.main.id로 접근하려 하면 에러. 모듈 내부 리소스는 직접 참조할 수 없다.
방지: 모듈 외부에서 필요한 값은 반드시 output으로 선언한다. Output이 모듈의 유일한 공개 인터페이스다.
실수 4: terraform_remote_state로 순환 의존성 생성
증상: 프로젝트 A가 B의 remote_state를 읽고, B도 A의 remote_state를 읽는다. 둘 다 apply할 수 없는 교착 상태.
방지: remote_state 참조는 단방향으로 설계한다. 인프라 계층(네트워크 → 컴퓨팅 → 앱)의 방향을 정하고, 하위 계층이 상위를 참조하지 않도록 한다.
마무리
Terraform Output은 단순한 값 출력을 넘어 모듈의 공개 인터페이스이자 프로젝트 간 데이터 전달 매커니즘이다. sensitive로 CLI 마스킹을 하되 state 파일 암호화를 잊지 말고, precondition으로 출력 값의 계약을 검증하며, terraform_remote_state는 단방향으로만 설계하는 것이 안전한 운영의 기본이다. 이 글의 모든 내용은 Terraform 공식 문서(Output Values, Remote State Data Source)를 근거로 한다.