테라폼

4-0. 테라폼 - 초보자를 위한 기본 코드 작성법

pininini 2026. 5. 11. 16:01

테라폼 - 초보자를 위한 기본 코드 작성법

Terraform 파일 구조, 기본 문법, 실행 흐름 이해하기


이번 글부터는 Terraform을 실제로 어떻게 작성하는지 정리해보려 한다.

앞선 글들에서는 Terraform 리소스를 어떻게 바라볼지, 의존성 문제를 어떻게 이해할지, State와 CI/CD를 어떻게 나눌지에 대해 정리했다.

이번 4번 시리즈에서는 조금 더 실전적으로 들어가서, AWS 기준으로 자주 사용하는 리소스들을 Terraform 코드로 구현하는 방법을 다룰 예정이다.

이번 글은 그 시작점이다.

Terraform 코드는 어떤 파일로 구성되는가?
resource는 어떻게 작성하는가?
variable과 output은 왜 사용하는가?
terraform init / plan / apply는 어떤 흐름인가?

Terraform을 처음 접하는 사람이라면 이 글부터 보는 것이 좋다.


목차

  • 1. Terraform 코드는 무엇을 표현하는가
  • 2. 기본 파일 구조
  • 3. versions.tf
  • 4. provider.tf
  • 5. main.tf
  • 6. variables.tf
  • 7. terraform.tfvars
  • 8. outputs.tf
  • 9. locals.tf
  • 10. resource, data, module 차이
  • 11. 기본 실행 명령어
  • 12. 초보자가 자주 하는 실수
  • 13. 마무리

1. Terraform 코드는 무엇을 표현하는가

Terraform은 인프라를 코드로 관리하는 도구다.

여기서 중요한 점은 Terraform 코드가 “명령어 순서”를 작성하는 방식이 아니라는 것이다.

Terraform 코드는 다음을 표현한다.

내가 원하는 인프라의 최종 상태

예를 들어 다음 코드는 “VPC를 하나 생성하고 싶다”는 선언이다.

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

이 코드는 “VPC 생성 명령을 실행하라”라기보다는, Terraform에게 다음 상태를 알려주는 것이다.

aws_vpc.main 이라는 VPC가 존재해야 한다.
CIDR은 10.0.0.0/16 이어야 한다.

Terraform은 현재 State와 코드를 비교해서, 이 상태가 되기 위해 어떤 작업이 필요한지 계산한다.

현재 코드
+
현재 State
=
terraform plan
Terraform 코드는 실행 절차가 아니라 원하는 인프라 상태를 선언하는 방식이다.

2. 기본 파일 구조

Terraform을 처음 시작할 때는 다음 정도의 파일 구조로 시작하면 충분하다.

terraform/
├── versions.tf
├── provider.tf
├── main.tf
├── variables.tf
├── terraform.tfvars
├── outputs.tf
└── locals.tf

각 파일의 역할은 다음과 같다.

파일 역할
versions.tf Terraform 버전과 Provider 버전 정의
provider.tf AWS Provider 설정
main.tf 실제 리소스 정의
variables.tf 입력 변수 정의
terraform.tfvars 변수 값 입력
outputs.tf 생성된 리소스 값 출력
locals.tf 반복해서 사용할 내부 값 정의

처음부터 파일을 너무 많이 나눌 필요는 없다. 하지만 위 구조는 초보자가 Terraform의 역할을 이해하기에 좋다.


3. versions.tf

versions.tf는 Terraform 버전과 Provider 버전을 정의하는 파일이다.

예시는 다음과 같다.

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

여기서 required_version은 Terraform CLI 버전을 제한한다.

required_version = ">= 1.6.0"

이 설정은 Terraform 1.6.0 이상에서 실행하겠다는 의미다.

required_providers는 사용할 Provider를 정의한다. AWS 리소스를 만들 것이므로 AWS Provider를 사용한다.

required_providers {
  aws = {
    source  = "hashicorp/aws"
    version = "~> 5.0"
  }
}

Provider 버전을 고정하지 않으면 시간이 지나면서 Provider 동작이 바뀌어 예기치 않은 문제가 생길 수 있다.

Terraform 프로젝트에서는 Terraform 버전과 Provider 버전을 명시하는 것이 좋다.

4. provider.tf

provider.tf는 어떤 클라우드 Provider를 사용할지 설정하는 파일이다.

AWS 기준으로는 다음처럼 작성한다.

provider "aws" {
  region = var.aws_region
}

여기서 var.aws_region은 변수다.

즉, region 값을 코드에 직접 고정하지 않고 변수로 받겠다는 의미다.

region = var.aws_region

초보자라면 처음에는 이렇게 직접 작성할 수도 있다.

provider "aws" {
  region = "ap-northeast-2"
}

하지만 프로젝트가 커지면 region이나 환경별 값은 변수로 분리하는 것이 좋다.


5. main.tf

main.tf는 실제 리소스를 정의하는 파일이다.

예를 들어 VPC와 Subnet을 생성하려면 다음처럼 작성할 수 있다.

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = {
    Name = "${var.project_name}-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id
  cidr_block = var.public_subnet_cidr

  tags = {
    Name = "${var.project_name}-public-subnet"
  }
}

여기서 중요한 부분은 다음이다.

resource "aws_vpc" "main"

Terraform resource는 보통 다음 구조를 가진다.

resource "리소스 타입" "로컬 이름" {
  설정 값
}

예를 들어 aws_vpc는 AWS VPC 리소스 타입이고, main은 Terraform 코드 안에서 사용하는 이름이다.

따라서 다음 표현은 main이라는 이름의 VPC 리소스를 의미한다.

aws_vpc.main

그리고 VPC의 ID는 다음처럼 참조한다.

aws_vpc.main.id

Subnet은 VPC 안에 생성되어야 하므로 VPC ID가 필요하다.

vpc_id = aws_vpc.main.id

이렇게 참조를 걸면 Terraform은 VPC를 먼저 만들고 Subnet을 나중에 만든다.

Terraform은 참조 관계를 기반으로 의존성을 계산한다.

6. variables.tf

variables.tf는 입력 변수를 정의하는 파일이다.

변수를 사용하면 코드에 직접 값을 박아 넣지 않고, 환경이나 상황에 따라 값을 바꿀 수 있다.

variable "aws_region" {
  description = "AWS region"
  type        = string
}

variable "project_name" {
  description = "Project name"
  type        = string
}

variable "vpc_cidr" {
  description = "VPC CIDR block"
  type        = string
}

variable "public_subnet_cidr" {
  description = "Public subnet CIDR block"
  type        = string
}

각 변수는 다음 구조를 가진다.

variable "변수명" {
  description = "설명"
  type        = 타입
}

초보자 입장에서 가장 많이 쓰는 타입은 다음과 같다.

string
number
bool
list(string)
map(string)

예를 들어 CIDR은 문자열이므로 string 타입을 사용한다.

type = string

7. terraform.tfvars

terraform.tfvars는 변수에 실제 값을 넣는 파일이다.

예시는 다음과 같다.

aws_region         = "ap-northeast-2"
project_name       = "demo"
vpc_cidr           = "10.0.0.0/16"
public_subnet_cidr = "10.0.1.0/24"

variables.tf가 변수의 이름과 타입을 정의하는 파일이라면, terraform.tfvars는 그 변수에 실제 값을 넣는 파일이다.

variables.tf
→ 어떤 변수가 필요한지 정의

terraform.tfvars
→ 그 변수에 어떤 값을 넣을지 정의

단, 주의할 점이 있다.

terraform.tfvars에는 비밀번호, Access Key, Secret 같은 민감정보를 직접 넣지 않는 것이 좋다.

password = "my-secret-password"  # 권장하지 않음

민감정보는 AWS Secrets Manager, SSM Parameter Store 등을 사용하는 것이 더 안전하다.


8. outputs.tf

outputs.tf는 Terraform apply 이후 필요한 값을 출력하는 파일이다.

예를 들어 생성된 VPC ID와 Subnet ID를 출력할 수 있다.

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "public_subnet_id" {
  description = "Public Subnet ID"
  value       = aws_subnet.public.id
}

apply가 끝나면 Terraform은 다음처럼 output 값을 보여준다.

vpc_id = "vpc-xxxxxxxx"
public_subnet_id = "subnet-xxxxxxxx"

output은 다음 상황에서 유용하다.

  • 생성된 리소스 ID 확인
  • 다른 Terraform stack에서 참조
  • CI/CD에서 후속 작업에 사용

예를 들어 network stack에서 만든 subnet ID를 app stack에서 사용할 수 있다.

output은 Terraform 리소스 간 값을 연결하는 출구 역할을 한다.

9. locals.tf

locals.tf는 코드 내부에서 반복해서 사용할 값을 정의하는 파일이다.

예를 들어 공통 태그를 정의할 수 있다.

locals {
  common_tags = {
    Project = var.project_name
    Managed = "terraform"
  }
}

그리고 리소스에서 사용할 수 있다.

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-vpc"
  })
}

locals는 변수와 비슷해 보이지만 용도가 다르다.

구분 역할
variable 외부에서 입력받는 값
locals 코드 내부에서 계산하거나 재사용하는 값

즉, 외부에서 바뀌는 값은 variable, 내부에서 반복 사용하는 값은 locals로 두는 것이 좋다.


10. resource, data, module 차이

Terraform을 작성하다 보면 resource, data, module을 자주 보게 된다.

처음에는 헷갈릴 수 있으므로 간단히 구분해보자.


10.1 resource

resource는 Terraform이 직접 생성하고 관리하는 리소스다.

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

이 경우 Terraform이 VPC를 생성하고 state로 관리한다.


10.2 data

data는 Terraform이 직접 생성하지 않고, 이미 존재하는 값을 조회할 때 사용한다.

예를 들어 현재 사용 가능한 Amazon Linux AMI를 조회할 수 있다.

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

이 값은 EC2를 만들 때 사용할 수 있다.

resource "aws_instance" "app" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
}

즉, data는 기존 정보를 가져오는 역할이다.


10.3 module

module은 여러 리소스를 묶어서 재사용하는 단위다.

예를 들어 VPC 구성을 모듈로 만들어두면 여러 환경에서 재사용할 수 있다.

module "vpc" {
  source = "./modules/vpc"

  vpc_cidr = "10.0.0.0/16"
}

초보자라면 처음부터 module을 사용하기보다, 먼저 resource와 variable, output을 충분히 이해한 뒤 module로 넘어가는 것이 좋다.


11. 기본 실행 명령어

Terraform의 기본 실행 흐름은 다음과 같다.

terraform init
terraform fmt
terraform validate
terraform plan
terraform apply
terraform destroy

11.1 terraform init

Terraform 프로젝트를 초기화한다.

terraform init

Provider를 다운로드하고 backend 설정을 초기화한다.


11.2 terraform fmt

Terraform 코드 포맷을 정리한다.

terraform fmt

코드 스타일을 자동으로 맞춰주기 때문에 자주 실행하는 것이 좋다.


11.3 terraform validate

Terraform 코드 문법과 기본 구성이 올바른지 검사한다.

terraform validate

11.4 terraform plan

실제로 무엇이 변경될지 미리 확인한다.

terraform plan

운영 환경에서는 plan 결과를 반드시 확인하는 것이 좋다.


11.5 terraform apply

plan 결과를 실제 인프라에 반영한다.

terraform apply

실행하면 Terraform이 변경 사항을 보여주고 적용 여부를 묻는다.


11.6 terraform destroy

Terraform이 관리하는 리소스를 삭제한다.

terraform destroy

주의해야 한다.

특히 RDS, S3, Route53, KMS 같은 리소스가 포함되어 있다면 실수로 삭제하지 않도록 조심해야 한다.


12. 초보자가 자주 하는 실수

12.1 terraform.tfstate를 Git에 올림

State 파일은 Git에 올리면 안 된다.

State에는 리소스 정보와 민감한 값이 포함될 수 있다.

terraform.tfstate
terraform.tfstate.backup
.terraform/

이런 파일은 .gitignore에 추가해야 한다.

.terraform/
*.tfstate
*.tfstate.backup
*.tfvars

단, *.tfvars를 Git에 제외할지는 프로젝트 정책에 따라 다를 수 있다. 민감정보가 들어간다면 반드시 제외해야 한다.


12.2 Access Key를 코드에 직접 작성

다음처럼 Access Key를 코드에 직접 쓰면 안 된다.

provider "aws" {
  access_key = "..."
  secret_key = "..."
  region     = "ap-northeast-2"
}

AWS CLI profile, 환경 변수, IAM Role, GitHub Actions OIDC 등을 사용하는 것이 좋다.


12.3 plan을 확인하지 않고 apply

Terraform은 강력한 도구이기 때문에 실수하면 실제 리소스가 삭제되거나 교체될 수 있다.

따라서 terraform plan 결과를 확인한 뒤 apply하는 습관이 중요하다.


12.4 모든 값을 main.tf에 하드코딩

처음에는 괜찮지만 프로젝트가 커지면 관리가 어려워진다.

region = "ap-northeast-2"
cidr_block = "10.0.0.0/16"
instance_type = "t3.micro"

반복되거나 환경별로 달라질 수 있는 값은 variable로 분리하는 것이 좋다.


12.5 민감정보를 variable로만 숨겼다고 생각

Terraform variable에 민감정보를 넣었다고 해서 완전히 안전한 것은 아니다.

값이 state에 저장될 수 있기 때문이다.

비밀번호, 토큰, Secret은 Terraform 코드에 직접 넣기보다 Secrets Manager나 SSM Parameter Store를 사용하는 것이 좋다.


13. 마무리

이번 글에서는 Terraform을 처음 작성할 때 알아야 할 기본 구조를 정리했다.

핵심은 다음과 같다.

versions.tf       → Terraform / Provider 버전
provider.tf       → AWS Provider 설정
main.tf           → 리소스 정의
variables.tf      → 입력 변수 정의
terraform.tfvars  → 변수 값 입력
outputs.tf        → 결과 값 출력
locals.tf         → 내부 공통 값 정의

Terraform은 처음에는 파일도 많고 개념도 낯설게 느껴질 수 있다.

하지만 기본 구조를 이해하면 이후 VPC, EC2, RDS, ECS 같은 리소스를 구현할 때 훨씬 쉽게 접근할 수 있다.


한 줄 정리

Terraform 코드는 리소스를 선언하고, 변수로 바꾸고, output으로 연결한다.


다음 글에서는 이 기본 구조를 바탕으로, 초보자가 바로 실행해볼 수 있는 AWS 기본 Terraform 프로젝트 템플릿을 만들어본다.