오늘은 인프라 운영의 핵심인 IaC(Infrastructure as Code) 를 실무에 적용하면서 겪었던 시행착오와, 이를 해결하기 위해 도입한 Terragrunt 활용기를 공유해보려 합니다. 단순히 도구를 사용하는 법을 넘어, 왜 이 조합을 선택했는지에 대한 고민을 담았습니다.

Terraform, Terragrunt

본격적인 이야기에 앞서 Terraform과 Terragrunt가 무엇이고 어떻게 동작하는지 짚어볼 필요가 있습니다. Terraform은 ‘선언적(Declarative)’ 방식을 취할 수 있는 IAC 도구입니다. 선언적 방식이라 함은 일반적인 프로그래밍의 언어처럼 “어떤 과정을 거쳐서 만들어라”와 같이 로직을 포함하는 How를 가지고 있는 것이 아니라 최종적으로 어떤 형태(What)가 나와야 하는지에 대한 코드를 작성하는 방식입니다. 고수준으로 추상화되어 있는 Terraform의 언어의 특성상 가능한 방식입니다.

# 1. 어떤 클라우드를 쓸지 선언합니다 (Provider)
provider "aws" {
  region = "ap-northeast-2"
}
 
# 2. 최종적으로 원하는 상태를 선언합니다 (Resource)
resource "aws_instance" "web_server" {
  count         = 3  # "난 항상 3대의 서버가 있길 원해"
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
 
  tags = {
    Name = "Terraform-Web-Server"
  }
}

Terraform을 통해 이 선언적 방식으로 구성할 인프라를 작성하면, Go로 작성된 Terraform 내부의 Core 로직에서는 tf 설정 파일을 읽고 리소스 간의 의존성 그래프를 생성합니다. 이렇게 생성된 의존성 그래프는 각 클라우드 서비스에 어떻게 배포해야 할 지가 문제일 텐데요, Terrform에서는 이를 RPC를 통해 각각의 Provider들에게 명령하여 인프라를 구축할 수 있도록 합니다.

“서버 3대를 만들어줘”라고 코드를 작성하면, Terraform은 현재 상태와 비교하여 부족한 부분을 채우거 나 남는 부분을 삭제해 최종 결과물을 만들어냅니다.

이 과정에서 가장 중요한 것이 바로 State(.tfstate) 파일입니다. State는 실제 클라우드 리소스와 우리가 작성한 코드 사이의 매핑 정보를 담고 있는 단일 진실 공급원(Single Source of truth)입니다. Terraform은 이 State를 바탕으로 변경 사항을 계산(Plan)하고 적용(Apply)하며 인프라의 일관성을 유지합니다.

순수 Terraform만으론 부족했던 이유: 마주한 문제들

이렇게 코드를 통해 인프라를 관리하면서 직접 AWS에 들어가서 딸깍 딸깍을 몇시간이나 하지 않아도 코드를 통해 관리할 수 있게 되면서 인프라 구축은 보다 수월해졌습니다. 하지만 실제 프로젝트에서 여러 개의 환경(Dev, Staging, Prod)을 운영하다 보니 몇 가지 치명적인 불편함이 발생했습니다.

코드 중복

각 환경은 설정값만 다를 뿐 구조는 거의 동일한데, Terraform은 디렉토리마다 동일한 모듈 호출 코드를 복사해서 붙여넣어야 했습니다.

백엔드 서정의 경직성

State 파일을 안전하게 보관하기 위한 S3 백엔드 설정에는 변수를 사용할 수 없어서, 수십 개의 디렉토리에 버킷 이름을 일일이 하드코딩해야 했습니다. 결국 나중에 유지보수성이 떨어지고, 별도로 관리하자니 위에서 언급했던 코드 중복의 문제가 다시금 생겼습니다.

의존성 관리의 어려움

인프라에서도 의존성 관리는 중요합니다. AWS 기준으로 얘기를 해보자면, 클라우드 서비스에서 제공하는 서비스는 정말 많습니다. 이러한 서비스들을 VPC 내부에서 백본 네트워크를 통해 밖으로 트래픽을 내보내지 않고 처리할 수 도 있지만, 이런 VPC가 만들어진 후 해당 VPC와 인터페이스를 뚫어놔야지, 순서가 역전되어버리면 의도치않게 보안에도 구멍이 생기는 등 제대로 된 인프라 구성이라고 보기엔 어려울 수 있습니다.

하지만 각 리소스를 디렉토리별로 분리해두면 이를 순서대로 실행하는 것은 온전히 엔지니어의 ‘기억력’이나 별도의 ‘쉘 스크립트’ 를 통해 해당 순서를 제어해야만 했습니다. 사용하는 서비스가 많아지면 많아질 수록 여러 서비스에 의존하는 다른 서비스에 대해서 처리할 수 있도록 스크립트 로직을 짜는 것은 곧 Terraform으로 찾았던 선언적 방식을 다시 뺏는 것과 차이가 없다고 생각합니다.

Terraform의 가려운 곳을 긁어줄 수 있는 Terragrunt

이러한 고충을 해결해준 도구가 바로 Terragrunt였습니다. Terragrunt는 Terraform을 한 번 더 감싸는 ‘래퍼(Wrapper)’ 도구로, DRY(Don’t Repeat Yourself) 원칙을 철저히 지킬 수 있게 해줍니다.

우리는 Terragrunt를 도입하며 인프라 로직은 modules/ 폴더에 딱 한 번만 정의하고, 실제 배포 설정은 live/ 폴더 내의 terragrunt.hcl 파일로 관리했습니다. 이제 공통적인 서버에 대한 설정이나 Provider 정의는 루트 폴더에서 한 번만 작성하면 하위 모든 모듈에 자동으로 상속됩니다. 또한, dependency 블록을 통해 모듈 간의 출력값을 변수로 주고받을 수 있게 되면서, 복잡한 인프라 생성 순서를 코드로 명확히 정의할 수 있게 되었습니다.

실제 사용 사례

저희 회사에서는 여러 AWS 계정을 소유하고 있습니다. 상황에 따라 유동적으로 계정을 바꿔가면서 인프라를 구축해야 하는 환경이었죠. 어느 날, 여러 POC를 진행한 계정에서 갑작스럽게 인프라를 처음부터 다른 계정에 구축해야 한다는 끔찍한 소식을 들었습니다. 저는 여기서 그 인프라를 구축하는 담당이 되었습니다.(D-3은 덤..)

기존에 떠 있던 인프라들의 리소스 갯수는 20개를 넘었습니다. 의존성 또한 복잡하게 얽혀 있어 하나를 삭제하더라도 제대로 삭제가 안되는 경우도 정말 많았습니다.

하지만 그러다가 개발하시던 분께서 남겨놓은 Terraform과 Terragrunt를 통해 빠르게 S3에 올라와있는 tfState 파일로 기존의 리소스들을 모두 정리하고, 약간의 수정을 거친 뒤에 빠르게 다시금 다른 계정에 안정적인 인프라를 구축할 수 있었습니다.

실제 사용하며 느낀 장점과 한계

직접 운영해보니 장점은 명확했습니다. 위에서 말했던 장점들을 직접 몸으로 느껴보니 경험하지 못한 신세계였습니다. 매번 인프라를 구축하기 위해 AWS를 들락날락하지 않아도 된다는 점이 저에게는 큰 장점으로 다가왔습니다. 인간이 하는 데는 한계가 있기 마련인데, 인프라쪽이 특히 그랬던 것 같습니다. 띄워야 할 서비스가 많다 보니 인프라 작업을 하고 있다가도 중간에 어디까지 했는지 까먹으면 다시 서비스 들어가서 확인하고 진행하고의 연속이었습니다. 무엇보다 의존성이 있는 인프라 서비스를 이용하려면 다른 의존성에 대해서도 미리 다 확인을 했어야 했고, 특정 서비스에 assumerole 한번 하려면 띄워져있는 서비스에 다시 가서 arn 받고 role생성해서 resource명시하는 것만 해도 지쳤던 저에게는 너무나도 맛있는 주제였죠.

그 중에서도 가장 만족도가 높았던 부분은 유지보수 효율이었습니다. 인프라 구조를 변경해야 할 때 모듈 한 곳만 수정하면 모든 환경에 일괄 적용할 수 있었고, run-all plan 명령어로 전체 환경의 변경 사항을 한눈에 파악하는 것도 가능해졌습니다. 또한 AI의 발전으로 claude code에게 IAC 코드를 주고 인프라를 설명해달라고 하면 코드 기반으로 답해주니 정확도도 높고 다른 팀원이 온보딩 할 때 걸리던 시간을 반 이상 획기적으로 줄일 수 있었습니다.

하지만 장점만 있는 것은 아니었습니다. 우선 팀원들이 Terraform뿐만 아니라 Terragrunt라는 도구의 문법과 동작 방식을 추가로 학습해야 하는 학습 곡선이 존재합니다. 또한, 추상화 층이 하나 더 생기다 보니 문제가 발생했을 때 이것이 Terraform의 문제인지 Terragrunt의 설정 오류인지 파악하는 데 시간이 더 걸리기도 했습니다. 그렇지만 프로그래밍을 어느정도 한 사람이라면 당장 조금만 봐도 어떤 구조인지 빠르게 파악이 가능했고, 직접 코드를 건드리지 않는 사람들에게는 더없이 유용했으며, 수정하더라도 빠르게 문법만 짚고 클로드 코드를 통해 유지보수가 가능해지면서 단점들을 흐린눈할 만큼의 유용성을 보여주었습니다.

어떤 팀에게 필요할까?

결론만 짧게 요약하자면 소규모 인프라를 운영한다면 순수 Terraform만으로도 충분하지만, 환경의 분리가 필요하다면 Terragrunt도 생각해보는 것도 좋다. 입니다.

막상 새로운 언어를 배운다는 두려움 때문에 이전에는 제대로 건들지도 못했지만, 막상 제대로 체험하고 난 후에는 계속해서 Terraform을 배우고 코드들을 더 발전시키고 싶겠다고 생각할 만큼 매력적인 기술이었습니다. 특히 AI가 발전하는 현 상황에서 IAC를 통해 빠르게 인프라 프로토타이핑과 이를 조금만 보완해서 인프라를 빠르게 구축할 수 있다는 점에서 더 매력을 얻는 것 같습니다. 직접 AI에게 aws cli를 통해 조작하는 것은 위험하지만 코드로 구현하고 이를 검증하여 인간이 올림으로써 보다 안전한 배포가 되기 때문입니다. 안 써보신 분들을 한 번쯤은 체험해보시는것을 강력히 추천드립니다.