3계층 Terraform 아키텍처
Source: Dev.to
사전 요구 사항
- Terraform ≥ 1.0
- S3 버킷, DynamoDB 테이블, KMS 키, VPC, 서브넷, IGW 및 라우트 테이블을 생성할 수 있는 권한을 가진 AWS 자격 증명
폴더 구조
프로젝트 루트에서 다음 디렉터리 레이아웃을 생성합니다:
terraform-3layer-aws/
├── composition/
│ ├── remote-backend/
│ │ └── us-east-2/
│ │ └── prod/
│ └── vpc/
│ └── us-east-2/
│ └── prod/
├── infra/
│ ├── remote-backend/
│ └── vpc/
└── resource-modules/
├── storage/
│ └── s3-backend/
├── dynamodb/
│ └── backend/
├── kms/
│ └── backend/
└── vpc/
└── basic/
리소스 모듈 (가장 낮은 레이어)
2.1 S3 백엔드 버킷 모듈
경로: resource-modules/storage/s3-backend/main.tf
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
force_destroy = var.force_destroy
tags = var.tags
}
resource "aws_s3_bucket_versioning" "this" {
bucket = aws_s3_bucket.this.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = var.kms_key_arn
}
}
}
resource "aws_s3_bucket_public_access_block" "this" {
bucket = aws_s3_bucket.this.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
variable "bucket_name" { type = string }
variable "force_destroy" { type = bool }
variable "kms_key_arn" { type = string }
variable "tags" { type = map(string) }
output "bucket_name" { value = aws_s3_bucket.this.id }
output "bucket_arn" { value = aws_s3_bucket.this.arn }
2.2 DynamoDB 백엔드 테이블 모듈
경로: resource-modules/dynamodb/backend/main.tf
resource "aws_dynamodb_table" "this" {
name = var.table_name
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
tags = var.tags
}
variable "table_name" { type = string }
variable "tags" { type = map(string) }
output "table_name" { value = aws_dynamodb_table.this.name }
output "table_arn" { value = aws_dynamodb_table.this.arn }
2.3 KMS 키 모듈
경로: resource-modules/kms/backend/main.tf
resource "aws_kms_key" "this" {
description = var.description
deletion_window_in_days = 10
enable_key_rotation = true
tags = var.tags
}
variable "description" { type = string }
variable "tags" { type = map(string) }
output "key_id" { value = aws_kms_key.this.id }
output "key_arn" { value = aws_kms_key.this.arn }
2.4 기본 VPC 모듈
경로: resource-modules/vpc/basic/main.tf
resource "aws_vpc" "this" {
cidr_block = var.cidr_block
tags = merge(var.tags, { Name = "${var.project}-${var.environment}-vpc" })
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = var.tags
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnet_cidr
map_public_ip_on_launch = true
availability_zone = var.az
tags = var.tags
}
resource "aws_subnet" "private" {
vpc_id = aws_vpc.this.id
cidr_block = var.private_subnet_cidr
availability_zone = var.az
tags = var.tags
}
resource "aws_subnet" "database" {
vpc_id = aws_vpc.this.id
cidr_block = var.database_subnet_cidr
availability_zone = var.az
tags = var.tags
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.this.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.this.id
}
tags = var.tags
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
variable "project" { type = string }
variable "environment" { type = string }
variable "cidr_block" { type = string }
variable "az" { type = string }
variable "public_subnet_cidr" { type = string }
variable "private_subnet_cidr" { type = string }
variable "database_subnet_cidr" { type = string }
variable "tags" { type = map(string) }
output "vpc_id" { value = aws_vpc.this.id }
output "public_subnet_id" { value = aws_subnet.public.id }
output "private_subnet_id" { value = aws_subnet.private.id }
output "database_subnet_id" { value = aws_subnet.database.id }
Infra 레이어 – 원격 백엔드 파사드
이 레이어는 세 개의 백엔드 관련 리소스 모듈을 묶어 단일, 간단한 인터페이스를 제공합니다.
경로: infra/remote-backend/variables.tf
variable "project" { type = string }
variable "environment"{ type = string }
variable "region" { type = string }
resource "random_integer" "suffix" {
min = 1000
max = 9999
}
module "kms_backend" {
source = "../../resource-modules/kms/backend"
description = "${var.project}-${var.environment} backend key"
tags = {
Project = var.project
Environment = var.environment
}
}
module "s3_backend" {
source = "../../resource-modules/storage/s3-backend"
bucket_name = "${var.project}-${var.environment}-${random_integer.suffix.result}"
force_destroy = true
kms_key_arn = module.kms_backend.key_arn
tags = {
Project = var.project
Environment = var.environment
}
}
module "dynamodb_backend" {
source = "../../resource-modules/dynamodb/backend"
table_name = "${var.project}-${var.environment}-lock"
tags = {
Project = var.project
Environment = var.environment
}
}
output "backend_bucket_name" { value = module.s3_backend.bucket_name }
output "backend_dynamodb_table_name" { value = module.dynamodb_backend.table_name }
output "backend_kms_key_arn" { value = module.kms_backend.key_arn }
Composition – 원격 백엔드 (us-east-2 / prod)
특정 리전/환경에 원격 백엔드를 생성하기 위한 진입점.
경로: composition/remote-backend/us-east-2/prod/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# IMPORTANT: keep backend local for the first run.
# After the bucket & table exist, uncomment the block below.
# backend "s3" {
# bucket = ""
# key = "terraform.tfstate"
# region = "us-east-2"
# dynamodb_table = ""
# encrypt = true
# }
}
provider "aws" {
region = var.region
}
module "remote_backend" {
source = "../../../infra/remote-backend"
project = var.project
environment = var.environment
region = var.region
}
output "backend_bucket_name" { value = module.remote_backend.backend_bucket_name }
output "backend_dynamodb_table_name" { value = module.remote_backend.backend_dynamodb_table_name }
output "backend_kms_key_arn" { value = module.remote_backend.backend_kms_key_arn }
variable "project" { type = string }
variable "environment"{ type = string }
variable "region" { type = string }
# Example values
# project = "my-demo"
# environment= "prod"
# region = "us-east-2"
원격 백엔드 스택 실행
cd composition/remote-backend/us-east-2/prod
terraform init
terraform apply
버킷과 DynamoDB 테이블이 생성된 후, 다른 스택에서 원격 백엔드를 다음과 같이 사용할 수 있습니다:
backend "s3" {
bucket = ""
key = "path/to/terraform.tfstate"
region = "us-east-2"
dynamodb_table = ""
encrypt = true
}
Infra – VPC 파사드
기본 VPC 리소스 모듈을 래핑합니다.
경로: infra/vpc/locals.tf
variable "project" { type = string }
variable "cidr_block" { type = string }
variable "public_subnet_cidr" { type = string }
variable "private_subnet_cidr" { type = string }
variable "database_subnet_cidr" { type = string }
variable "az" { type = string }
variable "tags" { type = map(string) }
module "vpc_basic" {
source = "../../resource-modules/vpc/basic"
project = var.project
environment = var.environment
cidr_block = var.cidr_block
az = var.az
public_subnet_cidr = var.public_subnet_cidr
private_subnet_cidr = var.private_subnet_cidr
database_subnet_cidr = var.database_subnet_cidr
tags = var.tags
}
output "vpc_id" { value = module.vpc_basic.vpc_id }
output "public_subnet_id" { value = module.vpc_basic.public_subnet_id }
output "private_subnet_id" { value = module.vpc_basic.private_subnet_id }
output "database_subnet_id" { value = module.vpc_basic.database_subnet_id }
Composition – VPC (us-east-2 / prod)
인프라 파사드를 사용해 VPC를 생성합니다.
경로: composition/vpc/us-east-2/prod/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# OPTIONAL: after the remote backend exists, configure it here.
# backend "s3" { ... }
}
provider "aws" {
region = var.region
}
module "vpc" {
source = "../../../infra/vpc"
project = var.project
environment = var.environment
region = var.region
cidr_block = var.cidr_block
az = var.az
public_subnet_cidr = var.public_subnet_cidr
private_subnet_cidr = var.private_subnet_cidr
database_subnet_cidr= var.database_subnet_cidr
tags = {
Project = var.project
Environment = var.environment
}
}
output "vpc_id" { value = module.vpc.vpc_id }
output "public_subnet_id" { value = module.vpc.public_subnet_id }
output "private_subnet_id" { value = module.vpc.private_subnet_id }
output "database_subnet_id" { value = module.vpc.database_subnet_id }
variable "project" { type = string }
variable "environment" { type = string }
variable "region" { type = string }
variable "cidr_block" { type = string }
variable "az" { type = string }
variable "public_subnet_cidr" { type = string }
variable "private_subnet_cidr" { type = string }
variable "database_subnet_cidr" { type = string }
# Example values
# project = "my-demo"
# environment = "prod"
# region = "us-east-2"
# cidr_block = "10.0.0.0/16"
# az = "us-east-2a"
# public_subnet_cidr = "10.0.1.0/24"
# private_subnet_cidr = "10.0.2.0/24"
# database_subnet_cidr = "10.0.3.0/24"
VPC 스택 실행
cd composition/vpc/us-east-2/prod
terraform init
terraform apply
이 구조가 3‑계층 아키텍처와 일치하는 방식
| 레이어 | 목적 | 예시 모듈 |
|---|---|---|
| Resource modules | 환경 로직이 없는 순수 AWS 리소스 | s3-backend, dynamodb-backend, kms-backend, vpc-basic |
| Infra modules (facades) | 관련 리소스를 재사용 가능한 단위로 묶음 | infra/remote-backend (S3 + DynamoDB + KMS), infra/vpc (VPC + subnets) |
| Composition layer | 환경‑특정 진입점 (리전 + env) | composition/remote-backend/us-east-2/prod, composition/vpc/us-east-2/prod |
이는 강의에서 소개한 “resource → infra → composition” 패턴을 그대로 반영하면서도, 교육용 및 실제 사용에 적합한 간결하고 가독성 높은 코드 구조를 제공합니다.