대부분의 Terraform 코드를 그대로 두고 OpenTofu 1.x로 넘어가기 — 바이너리 교체부터 HCL 네이티브 테스트로 IaC 품질을 검증하는 법
2023년 8월, HashiCorp가 Terraform 라이선스를 MPL v2.0에서 BSL(Business Source License)로 전환하면서 IaC 생태계에 작은 지각 변동이 일었습니다. BSL의 경쟁 조항을 꼼꼼히 읽고 나서야 사안의 무게감이 다르게 느껴졌는데, 그 직후 Gruntwork·Spacelift·Harness 등 주요 툴체인 업체들이 뭉쳐 만든 것이 OpenTofu입니다. 2025년 2월 IBM의 HashiCorp 인수가 완료되면서 이 흐름은 더 가속화됐습니다.
이 글에서 다룰 핵심은 세 가지입니다. OpenTofu가 Terraform과 얼마나 호환되는지, 실제 마이그레이션이 어떻게 진행되는지, 그리고 tofu test로 IaC 코드를 얼마나 체계적으로 검증할 수 있는지입니다.
한 가지 먼저 솔직하게 짚겠습니다. 제목에 "대부분의 코드를 그대로"라고 표현한 이유가 있습니다. 프로바이더 소스를 완전 경로(registry.terraform.io/hashicorp/...)로 명시한 코드는 수정이 필요합니다. 반면 축약형(hashicorp/aws)을 이미 쓰는 코드베이스 — 대부분의 경우 — 는 수정 없이 그대로 넘어갑니다. 이 차이를 미리 알고 들어가면 마이그레이션의 실제 난이도가 훨씬 명확하게 보입니다.
결론부터 말씀드리면, 마이그레이션의 본질은 기술적 재작성이 아니라 바이너리·파이프라인 교체와 조직적 전환 관리에 가깝습니다. Fidelity Investments가 50,000개 이상의 상태 파일, 400만 개 이상의 리소스를 이전하면서 남긴 말이 이를 잘 설명해줍니다. "기술적 장벽보다 조직적 전환 관리가 더 큰 과제였다."
핵심 개념
OpenTofu는 어디서 온 프로젝트인가
OpenTofu는 Terraform 1.5.x의 마지막 MPL 코드베이스를 분기점으로 만들어진 완전한 오픈소스 포크입니다. MPL v2.0 라이선스를 유지하며, Linux Foundation 산하에서 다중 조직 거버넌스로 운영됩니다.
채택률 수치를 보면 흐름이 보입니다. Scalr의 2026년 조사에 따르면 IaC 실무자 중 약 12%가 OpenTofu를 사용하고, 27%가 추가 도입을 검토 중입니다(출처). 연간 다운로드 성장률 300%, 누적 다운로드 약 980만 건(encore.dev, 2026). 단순 포크 수준을 넘어 독자적인 제품으로 분기하고 있다는 게 숫자로도 드러납니다.
버전 로드맵 — 1.7부터 고유 기능이 쌓이기 시작
포크 초기에는 "Terraform이랑 뭐가 달라?" 소리가 많았는데, 1.7부터 고유 기능이 쌓이면서 지금은 꽤 명확히 갈라졌습니다.
| 버전 | 출시 | 주요 기능 |
|---|---|---|
| 1.7 | 2024 | 상태 파일 AES-GCM 암호화, import 블록 for_each, 동적 프로바이더 정의 함수 |
| 1.8 | 2024 | 백엔드 구성 포함 변수 조기 평가, 테스트 resource overrides |
| 1.9 | 2025 | 프로바이더 iteration with for_each, -exclude 플래그 |
| 1.10 | 2025 | OCI 레지스트리 지원, OpenTelemetry 트레이싱, S3 네이티브 잠금 |
| 1.11 | 2025 | ephemeral(임시) 리소스·값, write-only 속성, enabled 메타인수 |
| 1.12 | 2026 | 동적 prevent_destroy, destroy = false 라이프사이클, 동시 프로바이더 설치 속도 향상 |
S3 네이티브 잠금(DynamoDB 없는 잠금)은 OpenTofu 1.10이 먼저 도입했고, Terraform도 1.10에서 use_lockfile 옵션으로 동일한 기능을 추가했습니다. AWS 환경에서 DynamoDB 테이블 관리 부담을 없앤다는 방향성 자체는 두 도구 모두 수렴하고 있는 상황입니다.
전환 여부를 판단하는 의사결정 흐름
"그래서 지금 바꿔야 하나, 말아야 하나?" 상황에 따라 판단이 달라지기 때문에, 아래 흐름도가 출발점이 될 수 있습니다.
Terraform Stacks는 HCP Terraform 전용 기능이고 OpenTofu에는 없습니다. 이걸 핵심 워크플로에서 쓰고 있다면 Atlantis·Spacelift 같은 대안 조합을 먼저 검토하고 나서 전환하는 순서가 자연스럽습니다.
실전 마이그레이션
마이그레이션은 다섯 단계로 진행됩니다. 소스 경로 수정(2단계)은 완전 경로를 명시한 코드가 있을 때만 해당합니다.
1단계. OpenTofu 설치
macOS 기준으로는 Homebrew 한 줄입니다.
brew install opentofuLinux 환경이라면 공식 설치 스크립트를 사용합니다.
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh | sh설치 후 버전 확인:
tofu version
# OpenTofu v1.12.x
# on linux_amd642단계. 프로바이더 소스 경로 확인 (해당 코드베이스만)
프로바이더를 완전 경로로 명시한 코드가 있으면 tofu init에서 오류가 납니다. 대규모 모노레포라면 먼저 목록을 뽑아두세요.
grep -r "registry.terraform.io" .해당 항목이 있으면 축약형으로 일괄 수정합니다.
# 수정 전 — 완전 경로 명시
terraform {
required_providers {
aws = {
source = "registry.terraform.io/hashicorp/aws"
version = "~> 5.0"
}
}
}
# 수정 후 — 축약형
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}완전 경로를 쓰지 않는 코드베이스라면 이 단계는 건너뜁니다.
3단계. tofu init — 마이그레이션의 핵심 첫 단계
기존 Terraform 작업 디렉터리에서 그대로 실행합니다.
tofu init
# 기존 lock 파일을 갱신하려면
tofu init -upgrade.terraform.lock.hcl이 남아 있는 상태에서 tofu init을 실행하면 registry.opentofu.org 기반으로 재생성됩니다. 처음 이전할 때 lock 파일을 삭제하고 새로 생성하는 방법도 있지만, 삭제 시 프로바이더 버전 pin이 풀리면서 init이 최신 버전을 당겨올 수 있습니다. 의도하지 않은 프로바이더 버전으로 전환되는 상황을 피하려면 -upgrade 플래그를 활용하거나, lock 파일을 삭제했다면 반드시 갱신된 버전을 검토한 뒤 커밋하세요.
4단계. tofu plan으로 드리프트 없음 확인
상태 파일 형식이 Terraform과 완전히 공유되므로, 기존 terraform apply로 만들어진 리소스들을 OpenTofu가 그대로 인식합니다.
tofu plan
# Plan: 0 to add, 0 to change, 0 to destroy.
# 이 메시지가 나오면 마이그레이션 준비 완료5단계. CI/CD 파이프라인 교체
GitHub Actions 기준으로 setup-terraform을 opentofu/setup-opentofu로 교체합니다.
# .github/workflows/tofu.yml
jobs:
tofu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup OpenTofu
uses: opentofu/setup-opentofu@v1
with:
tofu_version: "1.12.x"
- name: Tofu Init
run: tofu init
- name: Tofu Plan
run: tofu plan -out=tfplan
- name: Tofu Apply
if: github.ref == 'refs/heads/main'
run: tofu apply tfplan상태 암호화 — 오픈소스 Terraform 대비 차별점
오픈소스 Terraform에는 없는 기능입니다(Terraform Enterprise/HCP Terraform은 별도 암호화 기능을 제공합니다). S3에 저장되는 상태 파일을 AES-GCM으로 암호화하는 설정을 HCL 안에서 바로 할 수 있습니다.
# main.tf
terraform {
encryption {
key_provider "pbkdf2" "my_passphrase" {
passphrase = var.state_passphrase
}
method "aes_gcm" "my_method" {
keys = key_provider.pbkdf2.my_passphrase
}
state {
method = method.aes_gcm.my_method
}
}
backend "s3" {
bucket = "my-tofu-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
}
}프로덕션 환경이라면 passphrase 대신 AWS KMS를 키 프로바이더로 쓰는 것이 더 자연스럽습니다.
key_provider "aws_kms" "my_kms" {
kms_key_id = "arn:aws:kms:ap-northeast-2:123456789012:key/your-key-id"
region = "ap-northeast-2"
}tofu test — HCL 네이티브 테스팅 프레임워크
HCL 네이티브 테스팅(terraform test / tofu test)은 Terraform 1.6에서 먼저 도입된 개념이고, OpenTofu가 독점하는 기능이 아닙니다. 그러나 이미 OpenTofu로 전환하는 맥락이라면, tofu test는 Go 환경 없이 HCL만으로 즉시 IaC 테스팅 문화를 도입할 수 있는 현실적인 진입점입니다. Terratest를 쓰려면 Go를 알아야 하고 로컬에 Go 환경도 세팅해야 하는데, tofu test는 그 진입 비용 없이 같은 디렉터리에서 바로 씁니다.
테스트 유형을 먼저 이해하면 작성이 훨씬 쉬워집니다.
.tftest.hcl 파일을 tests/ 디렉터리에 두고 tofu test를 실행하면 됩니다.
Mock 프로바이더로 오프라인 단위 테스트
CI에서 AWS 자격증명 없이도 실행됩니다. 아래 예시는 다음과 같은 모듈을 가정합니다. aws_vpc.main은 단일 VPC 리소스이고, aws_subnet.private는 count = 3으로 선언된 서브넷 리소스입니다.
# tests/vpc.tftest.hcl
mock_provider "aws" {
mock_resource "aws_vpc" {
defaults = {
id = "vpc-offline-test-01"
cidr_block = "10.0.0.0/16"
}
}
mock_resource "aws_subnet" {
defaults = {
id = "subnet-offline-test-01"
availability_zone = "ap-northeast-2a"
}
}
}
run "vpc_cidr_is_correct" {
command = plan
assert {
condition = aws_vpc.main.cidr_block == "10.0.0.0/16"
error_message = "VPC CIDR가 10.0.0.0/16이어야 합니다"
}
}
# aws_subnet.private가 count = 3으로 선언된 경우에 동작합니다
run "subnet_count_is_three" {
command = plan
assert {
condition = length(aws_subnet.private) == 3
error_message = "프라이빗 서브넷은 3개여야 합니다"
}
}실제 클라우드에 Apply 후 검증하는 통합 테스트
리소스를 실제로 만들고, assert로 상태를 검증하고, 테스트가 끝나면 자동으로 정리합니다.
# tests/s3_integration.tftest.hcl
# aws_s3_bucket.main과 aws_s3_bucket_versioning.main이 정의된 모듈을 가정합니다
variables {
bucket_name = "my-test-bucket-tofu-ci"
environment = "test"
}
run "bucket_exists_and_versioned" {
command = apply
assert {
condition = aws_s3_bucket.main.bucket == var.bucket_name
error_message = "버킷 이름이 변수와 일치하지 않습니다"
}
assert {
condition = aws_s3_bucket_versioning.main.versioning_configuration[0].status == "Enabled"
error_message = "버킷 버저닝이 활성화되어 있어야 합니다"
}
}1.8+의 override_resource — 특정 리소스만 mock으로 교체
전체 프로바이더가 아니라 특정 리소스만 mock으로 바꾸고 나머지는 실제를 쓰고 싶을 때 유용합니다. aws_db_instance.main이 해당 모듈에 정의되어 있어야 합니다.
# tests/mixed.tftest.hcl
run "use_real_vpc_mock_rds" {
command = plan
override_resource {
target = aws_db_instance.main
values = {
endpoint = "mock-rds.cluster.local"
port = 5432
}
}
assert {
condition = aws_db_instance.main.port == 5432
error_message = "RDS 포트가 5432여야 합니다"
}
}실행은 단순합니다.
# 전체 테스트 실행
tofu test
# 특정 파일만 실행
tofu test -filter=tests/vpc.tftest.hcl
# 상세 로그 출력
tofu test -verbose장단점 분석
객관적인 비교표
| 항목 | OpenTofu | Terraform OSS |
|---|---|---|
| 라이선스 | MPL v2.0 (OSI 승인) | BSL 1.1 (경쟁 조항 포함) |
| 거버넌스 | Linux Foundation | IBM/HashiCorp |
| 비용 | 완전 무료 | CLI 무료, HCP Terraform 유료 |
| 상태 파일 암호화 | 네이티브 지원 (1.7+) | 미지원 |
| 프로바이더 for_each | 지원 (1.9+) | 미지원 |
| OCI 레지스트리 | 지원 (1.10+) | 미지원 |
| DynamoDB 없는 S3 잠금 | 지원 (1.10+) | 지원 (1.10+, use_lockfile) |
| HCL 네이티브 테스트 | tofu test | terraform test (1.6+, 유사) |
| Terraform Stacks | 미지원 | HCP Terraform 전용 |
| Remote Run / Sentinel | 미지원 (별도 도구 필요) | HCP Terraform 전용 |
| 시장 점유율 | 약 12% (Scalr, 2026) | 33% 이상 (조사마다 다름) |
| 커뮤니티 자료량 | 성장 중 | 절대적으로 많음 |
실무에서 흔히 겪는 실수
프로바이더 소스 경로 미수정. registry.terraform.io/hashicorp/... 형태를 명시적으로 쓴 코드가 있으면 tofu init에서 오류가 납니다. 대규모 모노레포라면 grep -r "registry.terraform.io" 로 미리 목록을 뽑아두세요.
lock 파일 삭제 후 버전 검토 생략. .terraform.lock.hcl을 삭제하고 tofu init으로 재생성하면 pin이 풀려 최신 프로바이더 버전이 당겨올 수 있습니다. -upgrade 플래그를 대신 활용하거나, 삭제-재생성 후 반드시 갱신된 lock 파일의 버전을 팀이 검토하고 커밋하는 절차를 거치세요.
테스트 apply 후 리소스 미정리. tofu test는 apply 테스트 후 자동 정리를 해주는데, CI 환경에서 실패로 중단되면 리소스가 남는 경우가 있습니다. CI 파이프라인 cleanup 훅에 tofu destroy를 별도로 걸어두면 안전합니다.
Terraform Cloud 상태 백엔드 마이그레이션 누락. HCP Terraform의 remote 백엔드를 쓰고 있다면 S3, GCS, Azure Blob 같은 자체 관리 백엔드로 먼저 이전한 뒤 OpenTofu로 넘어가는 순서가 자연스럽습니다.
마치며
정리하면 이렇습니다. OpenTofu로의 전환은 기술적 마이그레이션보다 조직적 전환 관리에 더 많은 에너지가 필요하고, 기술적으로는 바이너리 교체와 파이프라인 업데이트가 대부분입니다. Fidelity의 사례처럼 먼저 사용량이 많은 팀에서 성공 사례를 만들고, 그 결과를 조직 신호로 활용하는 접근이 실제로 효과적입니다.
상태 암호화·프로바이더 for_each·OCI 레지스트리·ephemeral 리소스 같은 고유 기능들은 오픈소스 Terraform과의 기능 격차를 만들어가고 있습니다. 단순 포크라고 보기는 이미 어려운 단계입니다.
지금 시작해볼 수 있는 3단계를 제안합니다.
1단계 — 사이드 프로젝트 하나에 tofu init 실행해보기. 기존 코드 그대로 두고 바이너리만 교체해서 plan이 동일하게 나오는지 확인하는 것만으로도 마이그레이션의 체감 난이도를 파악할 수 있습니다.
2단계 — 가장 변경이 잦은 모듈에 .tftest.hcl 작성해보기. mock_provider로 자격증명 없이 plan 레벨 단위 테스트 하나를 작성하면, HCL 네이티브 테스트 프레임워크의 가치를 바로 느낄 수 있습니다.
3단계 — CI 파이프라인 하나를 opentofu/setup-opentofu로 교체해보기. setup-terraform에서 액션 이름 하나 바꾸는 것부터 시작하면, 실제 파이프라인 레벨에서의 영향이 어느 정도인지 확인됩니다.
참고 자료
- OpenTofu 공식 마이그레이션 문서
- OpenTofu Migration Guide (공식)
- OpenTofu 1.7.0 릴리스 공지 — 상태 암호화
- What's new in OpenTofu 1.9?
- What's new in OpenTofu 1.12?
- tofu test 명령어 공식 문서
- State and Plan Encryption — OpenTofu 공식 문서
- Fidelity Investments 마이그레이션 사례 (OpenTofu 공식 블로그)
- Fidelity Migration: A DevOps Success Story (Harness)
- Spacelift — How to Migrate From Terraform to OpenTofu
- Spacelift — OpenTofu vs Terraform 비교
- env0 — Terratest vs OpenTofu Test 비교
- env0 — OpenTofu v1.7 상태 암호화 심층 분석
- Scalr — OpenTofu vs Terraform (2026)
- Scalr — OpenTofu 네이티브 테스트 프레임워크 완전 가이드
- ControlMonkey — Terraform to OpenTofu Migration Guide 2025
- encore.dev — OpenTofu vs Terraform in 2026