
여러분이 몇 달 내내 공을 들여 개발한 프로젝트 MVP가 드디어 완성되고 배포 단계에 진입했다고 가정해보자. EC2 생성하는 대시보드부터 뭐가 그렇게 복잡한지 클로드한테 뭘 눌러야 하냐고 물어봐야 한다. 안 되면 왜 안 되냐고 하면서 겨우겨우 세팅을 완료했다.
그런데 세상에? 팀장님이 "방금 staging 환경 그대로 prod로 한 개 더 만들어주세요"라고 메시지를 보냈네? EC2부터 VPC, Subnet, 보안그룹, RDS, ALB, IAM Role까지 또 설정해야 하는데 잘 기억도 나지 않아서 처음부터 하는 심정으로 해야 한다. 그러다가 중간에 클릭 실수나 오타가 발생하면? 다른 요청이 들어올 때마다 이 짓거리를 반복해야 하나?
이쯤 되면 아래와 같은 의문이 생길 것이다.

🧩 IaC
콘솔 관리 vs IaC
배포 방식에는 크게 두 가지가 있다.
- 콘솔 관리: AWS나 GCP 같은 클라우드 회사가 제공하는 웹 화면에서 마우스로 클릭하며 리소스를 만드는 방식으로, 위에 있는 스크린샷이다. 웹 대시보드 말고도 AWS CLI를 사용할 수도 있지만 코드로 관리하지는 않는다.
- IaC(Infrastructure as Code): 말 그대로 인프라 구성을 코드로 작성해 파일로 관리하는 방식이다. 같은 코드를 다른 환경에 그대로 쓸 수도 있고, 변경 이력을 Git으로 관리할 수도 있다.
| 구분 | 콘솔 관리 (수동 방식) | IaC (코드형 인프라) |
| 방식 | 웹 GUI(AWS, GCP 등)에서 직접 클릭하여 리소스 생성 및 수정 | 스크립트/템플릿 코드를 작성하여 API 호출로 리소스 자동 생성 및 관리 |
| 버전 관리 | ❌ 불가 (수정 이력 추적 어려움) | ✅ 가능 (Git으로 이력 추적 및 롤백 용이) |
| 일관성 | ❌ 휴먼 에러 발생 확률 높음 | ✅ 동일한 환경 보장 (코드 기반의 규격화) |
| 재사용성 | ❌ 불가능 (처음부터 다시 작업해야 함) | ✅ 가능 (변수 처리 및 모듈화로 환경 복제) |
선언적 접근 방식 vs 명령형 접근 방식
IaC에 대한 접근 방식은 다음 두 가지로 나뉜다.
- 선언적 접근(Declarative): 필요한 리소스와 해당 리소스가 갖춰야 할 속성을 포함해 원하는 시스템 상태를 정의하면 IaC 도구가 시스템을 자동으로 구성한다. 시스템 오브젝트의 현 상태를 추적함으로써 인프라 제거를 더 간편하게 관리할 수 있도록 한다.
- 명령적 접근(Imperative): 원하는 구성을 수행하는 데 필요한 특정 커맨드를 정의한다. (올바른 순서대로 실행되야 함)
대표적인 IaC 도구
- Terraform: 특정 클라우드에 종속되지 않는 선언적 방식의 도구. 인프라 상태를 코드로 정의하고 현재 상태와 비교해 필요한 부분만 변경한다.
- Ansible: 서버 구성 관리 및 배포에 특화된 도구
- 클라우드 네이티브 도구: 특정 클라우드 전용 (예: AWS CloudFormation, AWS CDK, Azure ARM 등)
IaC 도입 시 얻을 수 있는 이점
- 비용 절감: 수동 배포 설정에 소요되던 수 시간의 작업을 수 분 이내로 단축해 인건비를 줄일 뿐만 아니라, 불필요한 리소스 제거를 통해 실제 사용 중인 리소스에 대해서만 비용을 지불하게 한다.
- 휴먼 에러 방지: 클릭 실수로 인한 보안 홀이나 설정 누락을 예방하고, 장애 발생 시 코드를 통한 빠른 롤백이 다능해 복구 비용을 절감한다.
- 속도 및 확장성: 개발, 테스트, 운영 환경을 코드를 통해 단 몇 분 만에 동일하게 복제할 수 있다.
- 인프라의 자산화: 인프라 구성을 문서 대신 코드 자체로 보관하여 인수인계가 수월하다.
IaC 도입의 과제
- 보안 취약점: 하드코딩된 비밀 정보, 템플릿 내에서 과도한 권한을 갖는 IAM Role 또는 기본 이미지 내의 취약점은 인프라 전반에 쉽게 확산되어 보안 위험을 초래할 수 있다.
- 표준화 부족: 명확한 가이드라인이 없다면 팀이 IaC를 중구난방으로 구현하여 일관성 없는 관행과 코드 중복, 유지 보수의 복잡성으로 이어질 수 있다.
- 복잡한 파이프라인 디버깅: IaC가 오류를 줄이기는 하지만 발생하는 오류는 수동 프로세스를 사용할 때 발생하는 오류보다 디버깅하기가 더 까다로울 수 있으며, 특히 팀이 IaC에 익숙하지 않은 경우 더욱 그렇다.
좋은 IaC의 5가지 조건
| 조건 | 왜 필요한가 | 어떻게 달성하는가 |
| 선언적인가 | "어떤 상태가 되어야 하는지"만 표현 | resource 블록 사용 |
| 재현 가능한가 | 같은 코드로 같은 결과 | Terraform 자체 동작 |
| 검토 가능한가 | 변경 전 영향 파악 | terraform plan |
| 협업 가능한가 | 팀원과 동시 작업 | remote backend + locking |
| 안전한가 | 사고 시 복구 가능 | Git 이력 + 백업 |
IaC vs IaD?
IaC와 IaD는 자주 혼용되는 개념들이지만 미묘하게 다르다.
- IaC(Infra as Code): 인프라를 '어떻게' 만들지 코드로 작성하는 방식 (예: Terraform)
- IaD(Infra as Data): 인프라의 '원하는 상태'를 데이터(YAML)로 선언하고 시스템이 자율적으로 그 상태에 수렴하게 만드는 방식 (예: Kubernetes 매니페스트)
실무에서는 Terraform으로 AKS 클러스터를 만들고(IaC) 그 위에 올라가는 앱은 Kubernetes YAML로 관리(IaD)하는 식으로 둘을 함께 쓴다고 한다.
🧩 Terraform
💡 Terraform은 인프라를 코드로 선언하고, 현재 상태와 비교한 뒤, 변경을 안전하게 반영하는 도구다.
Terraform은 HashiCorp에서 오픈소스로 개발중인 클라우드 및 온프레미스 인프라를 코드로 관리할 수 있는 IaC 도구이다.
2023년 8월 이후 Terraform의 상업적 이용에 제한을 두기 시작했고, 이에 대한 반발로 Terraform을 포크한 OpenTofu라는 도구가 나왔다.
왜 Terraform일까?
IaC 도구에는 AWS CloudFormation, Azure Bicep, Ansible, Pulumi 등등 여러가지가 있다. 그런데 어쩌다가 Terraform이 IaC의 대명사 격이 된 것일까?
- 클라우드에 종속되지 않음: AWS, Azure, GCP, Kubernetes 등 수많은 Provider를 지원한다.
- 거대한 커뮤니티와 생태계: 전 세계 수만 개의 조직에서 사용하는 사실상 IaC 표준으로 공식 문서, 커뮤니티 모듈, 레퍼런스 아키텍처가 풍부하다.
- 선언형 언어로 직관적이다: HCL이라는 자체 언어를 사용해 "어떻게 만들지"가 아니라 "어떤 상태가 되어야 하는지"만 기술하면 Terraform이 알아서 현재 상태와 비교해서 필요한 작업을 수행한다.
provider "aws" {
region = "ap-northeast-2"
}
HCL은 JSON이나 YAML처럼 인간 친화적인 구조를 가지고 있는데, 얼마나 직관적이냐면 HCL이라는 코드 자체를 처음 본 필자조차도 위 코드를 보고 "아, AWS를 쓰고 리전이 서울이구나"라고 바로 알 수 있을 정도다.
Terraform의 기본 동작 구조

- write : 인프라 환경을 HCL 언어로 리소스 정의
- plan : 정의된 리소스들을 생성, 수정, 삭제하기 위해 실행 계획을 확인
- apply : 확인된 실행 계획을 리소스 종속성을 고려하여 올바른 순서대로 수행
AWS 내에서 필요한 서비스들과 하위 리소스 설정들을 모듈화 하여 설정들을 정의하고(write), 리소스를 추가 생성, 수정, 삭제하기 위한 실행 계획을 확인하고(plan), 리소스간 종속성을 고려하여 테라폼이 자동으로 실행 순서를 정해서 리소스들을 배포(apply)하는 과정을 반복한다.
Terraform의 핵심 개념
| 개념 | 설명 |
| Provider | Terraform과 외부 서비스를 연결하는 플러그인 |
| Resource | 만들거나 관리할 대상 |
| State | 실제 인프라와 코드를 매핑한 기록 |
| Plan/Apply | 변경 계획 수립과 반영 |
Provider
Terraform 자체는 AWS의 EC2가 무엇인지 모르고, Provider가 그 역할을 한다. AWS Provider, GCP Provider, Kubernetes Provider 등이 있고, 각 Provider는 자신이 담당하는 서비스의 리소스 타입과 데이터 소스를 정의한다.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-2"
}
Resourece
Terraform이 만들고 관리할 실제 대상이다. EC2 인스턴스, S3 버킷, IAM Role 등이 해당한다.
resource "aws_s3_bucket" "logs" {
bucket = "my-app-logs-2026"
}
위에서 aws_s3_bucket은 리소스 타입, logs는 Terraform 내부에서 이 리소스를 부르는 이름이다.
State
Terraform이 무엇을 만들었는지 기록한 파일이다. 코드와 실제 인프라를 연결하는 기준이 된다.
Terraform은 매번 AWS에 가서 "이 버킷이 실제로 있나요?"라고 물어보지 않고, State 파일을 보고 자신이 지난 번에 만든 결과를 확인한 뒤에 필요한 경우에만 Refresh한다.
- 리소스 매핑: 코드 이름과 실제 리소스 연결
- 메타데이터: 의존성, 속성 등
- 성능 캐시: 큰 인프라에서 refresh 비용 절감
Plan과 Apply
- terraform plan: 코드와 현재 state를 비교해 무엇이 바뀔지 미리보기
- terraform apply: 그 계획을 실제 인프라에 반영
실무에서는 apply보다 plan을 해석하는 부분이 더 중요하다.
+ aws_instance.web ← 새로 생성됨 (Create)
~ aws_s3_bucket.logs ← 수정됨 (Update)
- aws_eip.legacy ← 삭제됨 (Destroy)
특히 - 표시가 의도와 다르게 나오면 apply하면 안 된다.
Terraform will perform the following actions:
# aws_instance.web will be destroyed
- resource "aws_instance" "web" {
- ami = "ami-0abc12345"
- instance_type = "t3.medium"
}
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-0abc12345"
+ instance_type = "t3.large"
}
Plan: 1 to add, 0 to change, 1 to destroy.
위 Plan 결과를 보면 기존 t3.medium 인스턴스를 삭제하고 t3.large 인스턴스를 새로 만들 것이라는 걸 알 수 있다.
Terraform의 동작 흐름 요약
- .tf 파일 작성
- terraform init: Provider 다운로드
- terraform plan: 변경 계획 생성
- terraform apply: 실제 반영
- 실제 인프라 변경 + state 업데이트
- 다시 plan하면 "No changes" 출력
Terraform의 기본 명령어와 동작 흐름
| 명령어 | 역할 | 언제 쓰나 |
| terraform init | 작업 디렉터리 초기화, Provider 다운로드 | 프로젝트 시작 시, Provider 변경 시 |
| terraform fmt | 코드 포맷 정리 | 커밋 전 |
| terraform validate | 문법 검증 | 커밋 전, CI에서 |
| terraform plan | 변경 계획 미리 보기 | apply 전 항상 |
| terraform apply | 실제 인프라 반영 | plan 검토 후 |
| terraform destroy | 관리 중인 리소스 전체 삭제 | 테스트 환경 정리 |
terraform init
init을 가장 먼저 실행해야 하며, 다른 명령은 그 다음에 동작한다.
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "~> 5.0"...
- Installing hashicorp/aws v5.30.0...
Terraform has been successfully initialized!
| 단계 | 내용 |
| 작업 디렉터리 준비 | .terraform 폴더 생성 |
| Provider 다운로드 | .terraform/providers에 Provider 플러그인 설치 |
| Backend 설정 | state 저장 위치 초기화 |
plan 결과 읽는 방법
| 기호 | 의미 | 영향 |
| + | 새로 생성 | 신규 비용 발생 |
| ~ | 속성 수정 | 일부 속성은 재생성 유발 |
| -/+ | 삭제 후 재생성 | 다운타임 발생 가능 |
| - | 삭제 | 데이터 손실 위험 |
특히 -/+ (특정 속성 변경을 위해 삭제 후 재생성을 한 뒤 데이터가 사라지지 않는가?) 와 - (의도된 삭제인가?) 를 반드시 확인해야 한다.
apply 이후
apply가 도중에 실패하면 state는 일부만 업데이트된 상태로 남는다. 코드와 실제 인프라가 상이할 수 있기 때문에 다음 plan 결과를 신중히 봐야 한다.
Terraform 사용해보기
- Terraform 설치 (참고)
- main.tf 작성
terraform {
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}
# 무작위 ID를 생성하는 리소스
resource "random_id" "server" {
byte_length = 8
}
# 생성된 ID를 출력
output "server_id" {
value = random_id.server.hex
}
- terraform init

- init이 성공하면 .terraform 디렉토리와 .terraform.lock.hcl이 생성된다.

- terraform plan

- terraform apply

- apply 후 디렉토리에 terraform.tfstate 파일이 생성된다. (후술하겠지만 절대 Git에 커밋하면 안됨)
{
"version": 4,
"resources": [
{
"type": "random_id",
"name": "server",
"instances": [
{
"attributes": {
"byte_length": 8,
"hex": "a3f7b2c9e1d4..."
}
}
]
}
]
}
- terraform destroy

Terraform과 AWS 연결하기
작성한 tf 파일을 토대로 AWS 리소스를 생성하고 관리하기 앞서 적절한 자격 증명이 필요하다.
AWS IAM 설정
Terraform에서 AWS 서비스 리소스들을 접근할 수 있도록 별도의 AdministratorAccess 권한을 가진 IAM 계정과 그룹을 생성한다.
- IAM > IAM 사용자 > 사용자 생성

- IAM 사용자 > terraform(사용자명) > 보안 자격 증명 > 액세스 키 만들기

AWS CLI 자격증명
- AWS CLI 설치 (참고)
- AWS Config 설정

- 이후 provider를 aws로만 설정해주면 Terraform은 알아서 ~/.aws/credentials에 적힌 Key를 통해 자격증명을 진행한다.
State와 Backend
앞에서 State는 Terraform이 무엇을 만들었는지 기록한 파일이라고 했다. 이 기록이 꼬이거나 사라지면 다음 상황이 발생한다.
| 상황 | 결과 |
| state 파일 분실 | Terraform이 기존 리소스를 모르게 됨 |
| state와 실제 차이 발생 | plan 결과가 예상과 어긋남 |
| 두 명이 동시에 apply | state 파일이 손상됨 |
| state에 secret 노출 | Git 등에 secret이 평문으로 남음 |
특히 초보자들이 자주하는 실수가 state를 Git에 커밋하는 것이다. 언뜻 봤을 때는 "state 이력도 같이 추적해야 나중에 장애 발생했을 때 복구하기 더 편하지 않나?"라는 생각이 든다. 하지만 state 파일에는 RDS 비밀번호, IAM Secret Key, 인증 토큰 등 민감한 정보들이 평문으로 그대로 저장된다.
{
"resources": [{
"type": "aws_db_instance",
"instances": [{
"attributes": {
"username": "admin",
"password": "MyPass1234!"
}
}]
}]
}
또한 위의 예시에서는 간단하게 작성했지만, 실무 수준에서 .tfstate는 방대한 JSON 파일이기 떄문에 변경 사항을 Git 히스토리로 추적하기 어렵다고 한다.
"보안이 문제라면 Private Repository에 올리면 되는거 아닌가?" 라는 생각도 들었지만, 다음과 같은 이유로 권장되지 않는다고 한다.
- 동시성 제어 불가: Git은 상태 잠금(State Locking) 기능이 없다. 팀원 A와 B가 동시에 인프라를 수정하고 푸시하면 코드 충돌이 나거나, 다른 사람이 변경한 실제 인프라 상태를 덮어씌워 서비스 장애로 이어진다.
- 접근 권한 기반의 보안 위협: 비공개 저장소라도 개발자, CI/CD 봇 등 저장소에 접근할 수 있는 모든 주체에게 민감한 정보(DB 비밀번호, API 키 등)가 평문으로 노출된다.
- 불필요한 Git 충돌(Conflict): 테라폼을 실행할 때마다 JSON 파일인 tfstate의 메타데이터나 리소스 순서가 미세하게 바뀐다. 코드가 완벽히 같아도 무의미한 Git 충돌이 계속 발생해 협업이 피곤해진다.
이러한 이유 때문에 Remote Backend가 필요하다.
Remote Backend는 주로 Terraform과 같은 인프라 관리 도구에서 사용되는 개념으로, 인프라의 현재 상태를 기록한 State(상태) 파일을 로컬 PC가 아닌 중앙 서버(원격 저장소)에 저장하는 방식을 말한다.
여러 명이 함께 작업을 하려면 state를 공용 저장소에 둬야 한다. Terraform이 이를 위해 제공하는 기능이 Remote Backend다.
| 종류 | 저장 위치 | 협업 가능 여부 |
| Local Backend | 로컬 파일 | 1인 작업만 가능 |
| Remote Backend | S3, GCS, Terraform Cloud 등 | 팀 협업 가능 |
가장 흔히 쓰는 조합은 AWS S3(State 파일 저장) + DynamoDB(작업 락 관리)라고 한다. (참고)
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-2"
encrypt = true # 저장 시 암호화
dynamodb_table = "terraform-state-lock" # 동시 실행 방지
}
}
| 환경 | 자주 보이는 조합 |
| 초기 스타트업 | Terraform + S3 backend + GitHub Actions |
| 중간 규모 회사 | Terraform + Atlantis + Terragrunt |
| 대규모 회사 | Terraform Cloud 또는 자체 플랫폼 |
| 금융권 | 자체 호스팅 + 자체 모듈 레지스트리 |
State Locking
한 사람이 Apply 중일 때 다른 사람이 동시에 apply하지 못하도록 락을 거는 기능이다.

Drift
코드에 없는 변경이 실제 인프라에 가해진 상태를 말한다. 쉽게 말하자면 코드 ≠ 인프라인 상태다.

- Terraform으로 EC2 생성 (코드: t3.medium)
- 누군가 콘솔에서 t3.large로 수동 변경
- 코드와 실제가 상이해짐 (drift 발생)
- 다음 terraform plan 실행 시: ~ aws_instance.web ~ instance_type: "t3.large" → "t3.medium"
- 모르고 apply하면 t3.medium으로 되돌려짐
Draft를 막으려면 Terraform으로 만든 리소스를 콘솔에서 수정하면 안 된다. 만약 긴급 상황으로 콘솔에서 수정했다면, 같은 변경을 코드에도 반드시 반영해야 한다.
변수, 출력, 모듈
variable
resource "aws_db_instance" "main" {
identifier = "prod-db"
instance_class = "db.t3.large"
allocated_storage = 100
}
위 코드를 dev 환경에서도 적용하면 다음과 같은 문제가 생긴다.
- 식별자 충돌: prod-db라는 이름이 dev에도 만들어짐
- 비용 낭비: dev에서도 db.t3.large로 운영
- 유연성 부족: 환경마다 코드를 따로 만들어야 함
환경 별로 다른 값을 처리하기 위해서 variable로 값을 분리한다.
variable "environment" {
type = string
description = "배포 환경 (dev, staging, prod)"
}
variable "db_instance_class" {
type = string
description = "RDS 인스턴스 타입"
default = "db.t3.micro"
}
variable "db_storage_gb" {
type = number
default = 20
}
resource "aws_db_instance" "main" {
identifier = "${var.environment}-db"
instance_class = var.db_instance_class
allocated_storage = var.db_storage_gb
}
환경별 값은 tfvars 파일로 분리한다.
# dev.tfvars
environment = "dev"
db_instance_class = "db.t3.micro"
db_storage_gb = 20
# prod.tfvars
environment = "prod"
db_instance_class = "db.t3.large"
db_storage_gb = 100
terraform apply -var-file="dev.tfvars"
terraform apply -var-file="prod.tfvars"
변수를 분리함으로써 코드 하나로 두 개 이상의 환경에 대해 작업을 할 수 있다.
output
인프라 배포가 완료된 후 생성된 리소스의 중요 정보(IP 주소, ID, 엔드포인트 등)를 화면에 표시하거나 다른 Terraform 모듈에서 참조할 수 있도록 외부에 노출하는 기능이다.
output "db_endpoint" {
value = aws_db_instance.main.endpoint
description = "애플리케이션에서 사용할 DB 엔드포인트"
}
output "db_password" {
value = aws_db_instance.main.password
sensitive = true # 화면 출력 시 마스킹
}
sensitive를 true로 설정하면 plan/apply 출력에서 값이 마스킹되지만, state 파일에는 여전히 평문으로 저장된다.
module
여러 개의 인프라 리소스를 하나로 묶어 재사용 가능하게 만든 코드 패키지다. 프로그래밍 언어의 함수나 라이브러리와 유사한 개념으로, 복잡한 인프라 코드를 규격화해 중복을 줄이고 유지보수성을 높일 수 있다. 쉽게 말하면 같은 구성을 여러 곳에서 반복해야 할 때 묶는 재사용 단위다.
같은 모듈을 두 번 호출해, 환경만 다른 두 벌의 인프라를 만들 수 있다.
project/
├── main.tf
├── variables.tf
└── modules/
└── web-server/
├── main.tf
├── variables.tf
└── outputs.tf
# project/main.tf
module "web_dev" {
source = "./modules/web-server"
environment = "dev"
instance_type = "t3.micro"
}
module "web_prod" {
source = "./modules/web-server"
environment = "prod"
instance_type = "t3.large"
}
하지만 그렇다고 모듈을 너무 잘게 쪼개면 오히려 관리가 어려워진다.
| 함정 | 결과 |
| 모든 리소스를 모듈로 | 코드가 분산되어 흐름 파악이 어려움 |
| 너무 일반화된 모듈 | 변수가 수십 개로 늘어남 |
| 모듈 안의 모듈의 모듈 | 디버깅이 어려워짐 |
처음에는 main.tf 하나로 시작하고, 같은 패턴이 반복될 때 모듈로 분리하는 것이 안전하다.
좋은 모듈 설계 3원칙
캡슐화: 항상 함께 배포되는 것만 묶자
모듈에 너무 많은 리소스를 넣으면 쓰기는 편하지만, 모듈의 목적과 요구사항이 무엇인지 이해하기 어려워진다. 항상 함께 배포되는 것들끼리 묶는 것이 자연스럽다. 예를 들어 네트워크 모듈이 VNet, Subnet, NSG를 함께 만드는 것은 자연스럽지만, 애플리케이션 서버까지 만들기 시작하면 모듈의 목적이 흐려지고 나중에 분리하기 어려워진다.
권한 경계: 권한이 다른 리소스는 분리하자
네트워크 팀이 관리하는 VNet 설정과 개발팀이 관리하는 VM을 같은 모듈에 넣으면 개발팀이 의도치 않게 네트워크 설정을 건드릴 수 있다.
변동성: 자주 바뀌는 것과 안 바뀌는 것을 분리하자
수명이 긴 인프라와 짧은 인프라를 같은 모듈에 묶으면 안 된다.
| 낮은 변동성 | 높은 변동성 |
| VNet, 서브넷 등 네트워크 | 애플리케이션 서버 |
| DB | 오토스케일링 설정 |
| Key Vault, IAM 정책 | 배포 관련 설정 |
애플리케이션 서버는 하루에도 수십 번 배포될 수 있다. 이걸 DB와 같은 모듈에 넣으면 서버 배포할 때마다 DB 설정에 불필요한 변경 위험이 생길 수 있다.
HashiCorp 공식 문서에 따르면 다음과 같은 모듈 구조를 권장하고 있다.
terraform-module-name/
├── main.tf # 핵심 리소스 정의 (필수)
├── variables.tf # 입력 변수 정의
├── outputs.tf # 출력값 정의
├── README.md # 모듈 설명 문서
├── modules/ # 중첩 모듈 (선택)
│ └── sub-module/
└── examples/ # 사용 예시 (선택)
└── basic/
Terraform 코드에 절대 남기면 안 되는 정보
절대 코드에 남기면 안되는 정보
AWS Access Key / Secret Key
RDS 비밀번호
API Token
Private Key (.pem, .key)
JWT secret
OAuth client secret
DB 접속 문자열 (계정 정보 포함)
사용자 개인정보
사실 Terraform 뿐만 아니라 Github에 올라가면 안되는 것들이다.
Provider에 키를 직접 적기
# 절대 이렇게 하면 안 됨
provider "aws" {
region = "ap-northeast-2"
access_key = "AKIAIOSFODNN7EXAMPLE"
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
환경 변수에 저장하거나 위에서 설정했던 자격 증명을 이용하자.
| 방식 | 어떻게 |
| 환경 변수 | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
| 자격 증명 파일 | ~/.aws/credentials |
| IAM Role | EC2/EKS에서 IAM Role 부여 |
| AWS SSO | 임시 자격 증명 사용 |
비밀번호 등 sensitive 값 처리
variable "db_password" {
type = string
sensitive = true
}
resource "aws_db_instance" "main" {
username = "admin"
password = var.db_password
}
값 자체는 다음 경로 중 하나로 주입한다.
| 경로 | 사용 시기 |
| 환경 변수 (TF_VAR_db_password) | 로컬 작업 |
| AWS Secrets Manager / SSM Parameter Store | 운영 환경 |
| HashiCorp Vault | 대규모 환경 |
| CI/CD secret 저장소 | GitHub Actions, GitLab 등 |
tfvars 파일에 secret을 적는 경우도 있지만, 그럴 경우 해당 파일을 반드시 .gitignore에 등록해야 한다.
.gitignore
.terraform/
*.tfstate
*.tfstate.*
*.tfvars
*.tfvars.json
crash.log
override.tf
override.tf.json
마무리
다시 처음으로 돌아가보자. 팀장님이 "방금 staging 환경 그대로 prod로 한 개 더 만들어주세요"라고 메시지를 보냈었다.
- 스테이징을 콘솔로 만들었기 때문에 terraform 코드가 없는 상황이다.
| 방법 | 설명 | 추천도 |
| terraform import | 기존 콘솔 리소스를 state로 가져옴 | 작은 규모일 때 |
| 새로 코드 작성 | 스테이징 설정을 보고 코드로 다시 작성 | 권장 (장기적으로 안전) |
- 코드 구조 설계
infra/
├── modules/
│ └── service-stack/
│ ├── main.tf # VPC, Subnet, SG, RDS, EC2, ALB
│ ├── variables.tf
│ └── outputs.tf
├── envs/
│ ├── staging/
│ │ ├── main.tf # module 호출, staging 변수
│ │ └── backend.tf # staging state 저장 위치
│ └── prod/
│ ├── main.tf # module 호출, prod 변수
│ └── backend.tf # prod state 저장 위치
- 모듈 작성
# modules/service-stack/variables.tf
variable "environment" { type = string }
variable "instance_type" { type = string }
variable "db_instance_class" { type = string }
# modules/service-stack/main.tf
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "${var.environment}-vpc"
Environment = var.environment
}
}
# ... (subnet, sg, rds, ec2, alb 생략)
- staging 환경 적용: 기존 콘솔 staging은 그대로 두고, 새 staging을 Terraform으로 만든 뒤 트래픽을 옮기는 방식이 가장 안전하다.
# envs/staging/main.tf
module "service" {
source = "../../modules/service-stack"
environment = "staging"
instance_type = "t3.medium"
db_instance_class = "db.t3.medium"
}
cd envs/staging
terraform init
terraform plan
terraform apply
- prod 환경 적용: 같은 모듈을 사용했으므로 구조는 동일하고, 값만 prod 사양으로 적용된다.
# envs/prod/main.tf
module "service" {
source = "../../modules/service-stack"
environment = "prod"
instance_type = "t3.xlarge"
db_instance_class = "db.r6g.large"
}
cd envs/prod
terraform init
terraform plan # 변경 사항 신중히 검토
terraform apply
- 검증
| 확인 항목 | 방법 |
| 리소스 존재 | AWS 콘솔에서 확인 |
| 서비스 동작 | 애플리케이션 헬스 체크 |
| 환경 일치 | staging과 prod의 구조 비교 |
| 재현 가능성 | destroy 후 다시 apply해도 동일하게 만들어지는가 |
[참고자료]
- 내일배움캠프 Terraform 강의자료
- Redhat 기술블로그
코드형 인프라(Infrastructure as Code)란?
코드형 인프라(Infrastructure as Code, IaC)는 수동 프로세스가 아닌 코드를 통한 인프라 관리 및 프로비저닝을 뜻합니다.
www.redhat.com
- 사람인 기술블로그
Terraform IaC 도구를 활용한 AWS 웹콘솔 클릭 노가다 해방기
테라폼이란?
saramin.github.io
- AWS 가이드 문서 (PDF)
IaC 구현의 이점 - AWS 권장 가이드
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
- Kt Cloud 기술 블로그
[도입가이드] 인프라 관리의 표준, IaC의 본질과 도입 가치
[ kt cloud Azure전환팀 변세림 님 ] 📋 요약 이 글에서는 클라우드 인프라를 코드로 관리하는 IaC의 개념과 도입 가치를 다룹니다.수동 관리로 인한 설정 불일치와 복구 지연을 줄이고, 운영 안정성
tech.ktcloud.com
[설계가이드] Terraform 모듈 설계, 원칙 없이 만들면 반드시 무너진다
[ kt cloud Azure전환팀 변세림 님 ] 📋 요약 이 글에서는 Terraform 모듈 설계 원칙과 표준 구조, 실무 적용 기준을 다룹니다.안정적인 인프라 운영을 위해 모듈 복잡도와 변경 위험을 줄이는 방향을
tech.ktcloud.com
'내일배움캠프' 카테고리의 다른 글
| [내일배움캠프] AWS ECS (0) | 2026.06.21 |
|---|---|
| [내일배움캠프] 무중단 배포 (0) | 2026.06.20 |
| [내일배움캠프] Spring Batch (0) | 2026.06.15 |
| [내일배움캠프] RAG (0) | 2026.06.10 |
| [내일배움캠프] Spring AI (0) | 2026.06.09 |