Terraform vs OpenTofu vs Pulumi, 2026년에 우리는 뭘 골랐나
작년 이맘때만 해도 이 얘기를 꺼내면 팀 슬랙 채널이 뜨거워졌다. HashiCorp가 BSL로 라이선스를 바꾼 뒤 OpenTofu 포크가 나왔고, 마침 우리 팀은 Pulumi로 옮겨야 하나 하는 논의도 있었다. 1년 반 정도가 흘렀고, 세 도구 모두 조금씩 다른 방향으로 자라났다. OpenTofu는 최근 1.9까지 오면서 state 암호화 같은 걸 Terraform보다 먼저 넣었고, Terraform은 HCP 통합을 계속 강화하고 있고, Pulumi는 여전히 "진짜 프로그래밍 언어로 IaC"라는 자리를 지키고 있다.
우리 팀은 6개월 동안 세 개를 실제 프로젝트에 나눠 쓰면서 비교해봤다. 결론부터 말하면 하나로 통일하지 못했다. 상황마다 다르더라. 이 글은 그 과정에서 느낀 것들이다.
라이선스와 거버넌스, 이제는 얘기 안 하기로 했다
솔직히 이 얘기는 이제 그만하고 싶다. 하지만 짚고 넘어가야 하는 부분은 있다.
Terraform은 BSL 1.1이다. 대기업이 관리형 서비스로 재판매하지 않는 이상 실무에서는 문제될 게 거의 없다. OpenTofu는 MPL 2.0이고 Linux Foundation이 관리한다. Pulumi는 Apache 2.0이지만 Pulumi Cloud라는 SaaS로 수익을 낸다.
우리 팀 내부 논의 끝에 결론은 "라이선스만 이유로 갈아탈 필요는 없다"였다. HCL 스택이 200개 넘는데 그걸 다 바꾸는 리스크가, BSL 조항 하나로 잡히는 리스크보다 훨씬 크다. 다만 신규 프로젝트에서 굳이 Terraform을 새로 도입할 이유도 없어졌다. 이 미묘한 뉘앙스가 팀마다 다르게 나오는 것 같다.
HCL이냐 진짜 언어냐, 이게 진짜 갈림길
Terraform과 OpenTofu는 사실상 같은 도구다. HCL 문법이 같고, state 파일도 호환된다. 우리도 일부 스택을 OpenTofu로 옮겨봤는데 terraform 명령어를 tofu로 바꾸는 것 말고는 아무것도 안 했다. terraform init 하다가 tofu init 하는 것뿐이었다.
그런데 Pulumi는 완전히 다른 세계다.
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const env = config.require("env");
// 환경별로 인스턴스 타입을 프로그래밍 방식으로 결정
const instanceType = env === "prod" ? "m6i.2xlarge" : "t3.medium";
const cluster = new aws.eks.Cluster(`app-${env}`, {
version: "1.31",
instanceType,
tags: {
ManagedBy: "pulumi",
Env: env,
},
});
// 조건부 리소스 생성이 그냥 if문
if (env === "prod") {
new aws.route53.Record("app-alias", { /* ... */ });
}
이걸 HCL로 쓰려면 count = var.env == "prod" ? 1 : 0 같은 걸 매번 해야 한다. 익숙해지면 되긴 하는데, 팀에 새로 들어온 백엔드 개발자가 "이거 for_each랑 count 언제 어떤 걸 써야 해요?" 물어볼 때마다 매번 설명하기 지친다.
Pulumi는 그런 걸 물어볼 필요가 없다. TypeScript면 TypeScript, Python이면 Python 문법 그대로 쓴다. 대신 다른 문제가 있다. 아무나 다 IaC를 짤 수 있게 되니까 아무렇게나 짜는 사람도 늘어난다. 상태를 잘못 만들면 HCL보다 훨씬 복잡한 방식으로 망가진다.
실제로 6개월 써보고 느낀 것
Terraform (기존 스택 유지용)
이미 짜놓은 게 많으면 그냥 쓰는 게 답이다. 우리는 EKS, VPC, IAM 같은 코어 인프라를 여전히 Terraform으로 관리한다. 안정적이고, 팀원 전원이 다룰 줄 안다. Terraform Cloud를 안 쓰고 S3 backend + DynamoDB lock만 쓰는데 이걸로도 충분했다.
단점은 새 기능이 좀 느리다. State 암호화도 OpenTofu가 먼저 넣었고, provider mocking 같은 것도 아직 미묘하다.
OpenTofu (신규 프로젝트에서 채택)
작년 4분기부터 신규 인프라 스택 두 개를 OpenTofu로 시작했다. 매끄러웠다. Terraform Registry의 provider도 다 잘 쓰인다. State 암호화는 실제로 유용했는데, 우리는 정책상 원격 state 파일에 KMS 옆에서 한 번 더 암호화를 걸어야 하는 요구사항이 있었다. Terraform이었으면 external tool로 우회했을 텐데 OpenTofu는 그냥 설정 몇 줄로 됐다.
terraform {
encryption {
key_provider "aws_kms" "state_key" {
kms_key_id = "arn:aws:kms:ap-northeast-2:xxx:key/xxx"
region = "ap-northeast-2"
}
method "aes_gcm" "state_method" {
keys = key_provider.aws_kms.state_key
}
state {
method = method.aes_gcm.state_method
}
}
}
애매한 점: 커뮤니티 provider 중 아주 마이너한 것들은 Terraform Registry에만 있고 OpenTofu Registry에 미러링이 안 된 경우가 몇 개 있었다. 우리 경우엔 문제 없었지만 이건 확인이 필요하다.
Pulumi (내부 플랫폼 팀 쪽에서 채택)
우리 팀에서 내부 개발자 플랫폼(IDP)을 만드는 서브팀이 Pulumi를 골랐다. 이유는 명확했다. 자동화 API가 필요했기 때문이다. 개발자가 웹 콘솔에서 "새 마이크로서비스 만들기" 버튼 누르면 백엔드에서 Pulumi Automation API로 EKS namespace, IAM role, Route53 레코드가 프로그래밍적으로 생성돼야 했다. 이걸 Terraform으로 하려면 별도로 terraform CLI를 shell exec하거나 Terraform Cloud API를 물어야 하는데 둘 다 어색하다.
Pulumi Automation API로는 그냥 함수 호출이다.
const stack = await LocalWorkspace.createOrSelectStack({
stackName: `service-${serviceName}`,
projectName: "internal-platform",
program: async () => {
const namespace = new k8s.core.v1.Namespace(serviceName, { /* ... */ });
const role = new aws.iam.Role(`${serviceName}-role`, { /* ... */ });
},
});
const result = await stack.up({ onOutput: console.log });
대신 학습 곡선이 있다. TypeScript 문법은 알아도 Pulumi의 "resource output이 사실은 Promise 비슷한 것"이라는 걸 이해하는 데 시간이 좀 걸린다. .apply()와 pulumi.interpolate 언제 쓰는지 헷갈리는 상태로 3주 정도 갔던 것 같다.
그래서 뭘 골라야 하는가
정리하면 이렇다.
Terraform — 이미 쓰고 있고 큰 문제 없으면 그냥 쓴다. 새로 시작할 이유는 별로 없다.
OpenTofu — 신규 HCL 기반 스택은 이걸로 시작한다. Terraform과 호환되니까 마음 바뀌면 언제든 돌아갈 수 있다는 게 심리적으로 편하다.
Pulumi — IaC를 코드에서 프로그래밍적으로 다뤄야 하는 유스케이스(플랫폼, 셀프서비스, 동적 프로비저닝)에만 골랐다. 그 외에는 팀 학습 비용이 이득보다 크다고 판단했다.
물론 이건 우리 팀 기준이다. 만약 신규 팀이라면 나는 아마 Pulumi로 시작할 것 같다. HCL의 어색함을 처음부터 우회할 수 있고, 요즘 백엔드 개발자들은 대부분 TypeScript나 Python은 쓰니까. 하지만 이건 아직 검증 중이고, 팀에 SRE가 아닌 인프라 담당자(예: 데이터 엔지니어)가 많으면 오히려 HCL이 진입장벽이 낮을 수도 있다.
혹시 세 개 다 안 쓰고 Crossplane 같이 다른 방식으로 가시는 분 있으면 댓글로 이야기 들어보고 싶다.