infra
Platform

모듈 맵

[Cloud] IaC와 Terraform — 인프라를 코드로 선언하기

0 / 22 완료

펼치기
0 / 22 완료0%

클라우드 엔지니어링 · 12 / 22

[Cloud] IaC와 Terraform — 인프라를 코드로 선언하기

콘솔 클릭 대신 인프라를 코드로 선언하는 IaC의 개념, Terraform의 plan/apply와 상태(state) 파일, 그리고 팀 협업 시 주의점을 정리합니다

🚨INCIDENT ALERT
HIGH

운영 환경은 잘 도는데, 스테이징을 새로 만들라는 지시가 떨어졌습니다. 그런데 운영을 만든 사람은 퇴사했고, 콘솔 어디를 어떻게 클릭했는지 기록이 없습니다. 보안그룹 규칙 하나, 서브넷 하나가 다르면 "운영에선 됐는데 스테이징에선 안 되는" 지옥이 시작됩니다. 손으로 만든 인프라는 재현이 안 됩니다. IaC는 인프라를 코드로 적어, 같은 환경을 버튼 하나로 다시 찍어냅니다.

이번 챕터에서 배울 것
  • 1IaC가 인프라를 코드화해 얻는 가치(재현·리뷰·버전관리)를 안다
  • 2Terraform의 plan/apply 흐름을 설명할 수 있다
  • 3상태(state) 파일의 역할과 안전한 관리법을 이해한다
  • 4팀 협업 시 원격 백엔드·상태 잠금이 왜 필요한지 안다
  • 5드리프트(코드와 실제의 차이) 개념을 안다

콘솔 클릭을 코드로 바꾸기

💡개념

IaC — 인프라를 git에 올리는 순간 달라지는 것

IaC는 VPC·서브넷·인스턴스·DB 같은 인프라를 선언적 코드로 적습니다. 그러면:

  • 재현: 같은 코드로 dev/stg/prod를 동일하게 생성
  • 리뷰: 인프라 변경을 PR로 리뷰(PR과 코드 리뷰)
  • 버전 관리: 누가 언제 무엇을 바꿨는지 git에 기록
  • 재해 복구: 통째로 날아가도 코드로 재구축

"콘솔에서 누군가 손으로 만든, 아무도 모르는" 스노우플레이크 인프라가 사라집니다.

💡개념

선언적 — '어떻게'가 아니라 '무엇을'

Terraform 코드는 절차(클릭 순서)가 아니라 원하는 최종 상태를 적습니다. "이 VPC, 이 서브넷 2개, 이 보안그룹이 존재해야 한다"라고 선언하면, Terraform이 현재 상태와 비교해 차이만큼만 만들거나 바꿉니다.

HCL
resource "aws_instance" "web" {
  ami           = "ami-0abc123"
  instance_type = "t3.micro"
  subnet_id     = aws_subnet.public.id
  tags = { Name = "web", Env = "prod" }
}

Terraform plan→apply와 상태 파일 — terraform plan은 코드(.tf)와 상태 파일(state)·실제 인프라를 비교해 변경 예정을 미리 보여주고, apply가 그 차이를 실제로 적용한 뒤 state를 갱신. state는 코드와 실제 자원의 매핑을 기록하므로 원격 백엔드(S3+잠금)로 공유·보호해야 드리프트·충돌을 막음확대

Terraform 상태 파일 — 코드·state·실제 인프라 관계확대 위 그림처럼 tfstate는 코드와 실제 인프라를 잇는 매핑 테이블입니다. 팀 협업 시 S3+DynamoDB 원격 백엔드로 상태를 공유하고 잠금으로 충돌을 막습니다.

IaC 워크플로 — Git PR → Plan → Apply확대 위 그림처럼 인프라 변경도 코드 리뷰를 거칩니다. PR에서 plan 출력을 검토하고 승인된 뒤에만 apply가 실행됩니다.

💡개념

plan → apply, 그리고 state

  • plan: 코드와 실제 인프라(state 기준)를 비교해 +생성 / ~변경 / -삭제를 미리 보여줌. 적용 안 함.
  • apply: plan의 변경을 실제로 적용.
  • state: Terraform이 만든 자원과 코드의 매핑 기록. 다음 plan의 차이 계산 근거.

핵심 습관: apply 전에 plan을 반드시 읽는다. 특히 -(삭제)강제 재생성(replace)이 보이면 의도와 맞는지 확인 — DB 같은 자원이 무심코 재생성되면 데이터가 날아갈 수 있습니다.

💡개념

terraform apply를 누르면 실제로 무슨 일이 일어나나 — 파싱부터 state 갱신까지 6단계

terraform apply 한 줄. Enter를 누르면 잠깐 멈췄다가 Apply complete!가 뜨고 클라우드에 자원이 생깁니다. 이 짧은 순간에 Terraform은 코드를 읽어 그래프를 만들고 → 실제와 state를 대조(refresh)하고 → 차이를 계산(plan)하고 → 승인을 받아 → provider API로 적용하고 → state를 갱신하는 6단계를 순서대로 밟습니다. 이 흐름을 알면 "왜 삭제가 뜨지", "apply가 중간에 죽으면 어떻게 되지", "왜 잠겼지"를 단계로 좁혀 이해할 수 있습니다. apply는 마법이 아니라 아래 6단계의 결과입니다.

TEXT
[내 PC]  terraform apply
   │
   ① 코드 파싱 + 그래프 구성        (.tf 읽기 → 자원 의존성 DAG 생성)
   │
   ② refresh: state ↔ 실제 대조     (provider API로 현재 상태를 읽어 state와 비교)
   │
   ③ plan: diff 산출                (원하는 상태 - 실제 = +생성 / ~변경 / -삭제)
   │
   ④ 승인 대기 + state 잠금(lock)    (yes 입력 — -auto-approve면 건너뜀)
   │
   ⑤ provider API 호출로 적용        (의존성 순서대로 create / update / delete)
   │
   ⑥ state 갱신                      (적용 결과·실제 ID를 tfstate에 기록)
   ▼
[결과]  Apply complete! 2 added, 1 changed, 0 destroyed

각 단계에서 무슨 일이 일어나고, 틀어지면 어떤 증상인가:

단계하는 일여기서 틀어지면
① 파싱·그래프HCL 문법 검사, 변수·모듈 해석, 자원 간 참조로 실행 의존성 그래프(DAG)를 만든다문법 오류·순환 참조 → Error: Cycle … 또는 파싱 에러로 즉시 중단
② refreshprovider API로 실제 자원의 현재 값을 읽어 state와 대조. 콘솔에서 손으로 바꾼 변경(드리프트)이 이 단계에서 드러난다콘솔 수동 변경 → plan에 ~변경(드리프트)으로 표시 / 자격증명 만료 → refresh에서 인증 실패
③ plan코드가 선언한 '원하는 상태'에서 refresh한 '실제'를 빼서 무엇을 +생성·~변경·-삭제할지 계산변경 불가 속성(예: 서브넷) 수정 → ~가 아니라 강제 재생성(-/+ replace)으로 계산
④ 승인·잠금사람이 diff를 읽고 yes. 이 실행이 state 잠금(lock)을 점유해 동시 apply를 막는다동료가 이미 apply 중 → Error acquiring the state lock으로 대기
⑤ 적용의존성 순서대로(VPC → 서브넷 → 인스턴스) provider API 호출. 서로 무관한 자원은 병렬로중간에 API 실패·타임아웃 → 일부만 적용된 채 중단(부분 적용)
⑥ state 갱신성공한 자원의 실제 ID·속성을 tfstate에 기록해 다음 plan의 기준으로 삼는다⑤가 중간에 죽어도 이미 만든 자원은 state에 반영, 못 만든 건 누락 → 다음 plan이 남은 차이를 다시 계산

즉 apply 실패가 치명적이지 않은 이유는 이 6단계가 이렇게 분리돼 있기 때문입니다. ⑤에서 절반만 적용되고 죽어도 ⑥이 성공분을 state에 남기므로, 다시 apply하면 ②refresh가 현재 실제 상태를 새로 읽어 '남은 차이'만 이어서 적용합니다(멱등성). 그래서 부분 적용은 대개 재실행으로 수렴하고, 진짜 위험한 건 apply 실패 자체가 아니라 ③plan을 읽지 않고 넘긴 -삭제·replace입니다. 잠금(④)은 이 6단계가 두 사람에게서 동시에 돌아 state가 엉키는 것을 막는 장치이고, 드리프트(②)는 apply 밖에서 벌어진 변경을 plan이 되잡아 오는 지점입니다.

Terraform 운영 판단 — plan 출력·state·협업
plan에 -(삭제)나 replace가 보임멈추고 의도 확인 — DB·디스크가 재생성되면 데이터 소실. lifecycle prevent_destroy로 보호하거나, 왜 재생성하는지(변경 불가 속성?) 파악. 'apply 전 plan 정독'
state를 어디에 둘까로컬 금지 — 팀이면 원격 백엔드(S3+DynamoDB 락, GCS). 로컬 state는 분실·충돌·비밀 노출 위험. '혼자라도 원격이 안전'
Error acquiring the state lock동료가 동시에 apply 중이거나 이전 실행이 락을 안 풀고 죽음. 진짜 실행 중이면 대기, 유령 락이면 force-unlock(신중히). '락은 동시 손상 방지 장치'
콘솔에서 누가 수동 변경(드리프트)terraform plan -refresh-only로 코드-실제 차이 탐지 → 코드로 되돌리거나(apply) 코드에 반영(import). '콘솔 수동 변경은 IaC를 깨뜨림'
이미 만든 자원을 코드로 가져오기terraform import로 기존 자원을 state에 등록 후 코드 작성. 안 하고 apply하면 '이미 존재' 에러 또는 중복 생성. '기존 자원은 import 먼저'
민감값(비밀번호·키)을 코드에평문 금지 — 변수+비밀 저장소(Vault·Secrets Manager) 참조. state에도 평문으로 남으니 state 자체를 암호화·접근 제한. 'state는 비밀을 담는다'
위험 명령어-auto-approve는 변경 미리보기를 건너뛰고 즉시 적용합니다. 코드 한 줄 실수(서브넷 변경 등)로 DB·운영 자원이 삭제·재생성되어 데이터가 영구 유실될 수 있습니다.

terraform apply — plan을 안 보고 실행하지 말 것

안전한 실행 조건: 운영 환경에서는 반드시 terraform plan을 사람이 읽고 -(삭제)·replace 항목을 확인한 뒤 apply. -auto-approve는 plan을 이미 검토한 CI 파이프라인 등 통제된 환경에서만.

실행 전 반드시 확인

  • plan 출력에서 -(삭제)·replace 항목이 의도한 것인가
  • DB·스토리지 등 상태가 있는 자원이 재생성 대상은 아닌가
  • 대상 환경(workspace/계정/리전)이 맞는가 — prod에 dev 코드 적용 아닌가
  • state가 최신이고 다른 사람이 동시에 apply 중은 아닌가(잠금)
terraform apply -auto-approve

위 항목을 모두 확인한 후 복사할 수 있습니다

1변경 미리보기 — plan

무엇이 바뀔지 먼저 봅니다. 숫자(+/-/~)와 특히 삭제·재생성을 확인합니다.

로컬 터미널
terraform plan
OUTPUT
Plan: 2 to add, 1 to change, 0 to destroy.
  + aws_subnet.private_b
  ~ aws_instance.web   (tags 변경)
terraform plan
2실제 자원과 코드의 차이(드리프트) 탐지

누군가 콘솔에서 손으로 바꾼 변경이 있는지 확인합니다. 차이가 나오면 코드와 실제가 어긋난 것입니다.

로컬 터미널
terraform plan -refresh-only
OUTPUT
Note: Objects have changed outside of Terraform
  ~ aws_security_group.web  ingress(22) 가 콘솔에서 추가됨   ← 드리프트
terraform plan -refresh-only
🔍실행 후 확인할 것
  • plan의 'to destroy' 숫자 — 0이 아니면 무엇이 삭제되는지 한 줄씩 확인. DB·스토리지면 특히 경계
  • ~replace(강제 재생성) 표시 — 변경이 아니라 삭제 후 재생성. 상태 있는 자원이면 데이터 유실 위험
  • refresh-only에서 드리프트 발견 — 콘솔 수동 변경이 있었다는 신호. 코드에 반영하거나 되돌려 일관성 유지
  • state 파일이 git/로컬에 평문으로 있는지 — 민감정보 유출 위험. 원격 백엔드+암호화+잠금으로 이전

상황: 여러 명이 같은 인프라에 동시에 apply해 state 충돌·잠금 오류.

원인: state를 공유 원격 백엔드에 두고 잠금을 걸지 않으면, 동시 apply가 같은 자원을 두고 경합합니다. 로컬 state면 서로의 변경을 아예 모릅니다.

진단: 백엔드 설정 확인(원격인지 로컬인지) → 잠금 메커니즘 유무 확인 → 누가 apply 중인지 확인.

해결: state를 원격 백엔드(예: S3)에 두고 상태 잠금(예: DynamoDB lock)을 활성화해 한 번에 한 명만 apply. CI 파이프라인에서만 apply하도록 통제하면 더 안전합니다. 잠금이 비정상 종료로 남았으면 신중히 force-unlock(정말 아무도 실행 안 할 때만).

심화 — state가 커질수록 달라지는 것

💡개념

심화: 하나의 state가 전부를 담으면 — 분할과 무중단 리팩터링

IaC가 자리 잡으면 다음 병목은 코드가 아니라 state의 크기입니다. 자원 수백 개가 한 state에 들어 있으면:

  • plan이 느려집니다: plan은 state의 모든 자원을 실제와 대조(refresh)하므로, 자원 수에 비례해 수 분~수십 분까지 늘어납니다.
  • 폭발 반경이 커집니다: 태그 하나 고치려는 apply가 같은 state의 DB·네트워크까지 건드릴 수 있는 위치에 놓입니다. 잠금도 state 단위라, 사소한 변경 하나가 팀 전체의 apply를 막습니다.

그래서 성숙한 팀은 state를 변경 주기와 위험도로 분할합니다 — 네트워크(거의 안 바뀜)/데이터(위험)/애플리케이션(자주 바뀜)을 별도 state로 두고, 계층 간 참조는 output을 데이터 소스로 읽어 연결합니다.

하나 더 — 코드를 정리(리소스 이름 변경·모듈로 묶기)하면 Terraform은 다른 자원으로 인식해 삭제+재생성을 계획합니다. 실제 자원은 그대로 두고 state 안의 주소만 바꾸는 도구가 moved 블록(또는 terraform state mv)입니다. 리팩터링 plan에서 destroy가 보이면 이것부터 의심합니다.

상황: 정리 목적의 리팩터링 PR. 동작 변경이 없다고 생각해 가볍게 apply하려다, plan에서 운영 DB 서브넷 그룹·보안그룹까지 삭제 대상에 올라온 것을 발견했습니다.

원인: Terraform은 자원을 '코드상 주소'(예: aws_instance.webmodule.web.aws_instance.this)로 추적합니다. 이름을 바꾸거나 모듈로 옮기면 기존 주소의 자원은 '삭제할 것', 새 주소는 '만들 것'으로 계산합니다. 실제 인프라는 그대로인데 state의 주소만 어긋난 상태입니다.

진단: plan의 destroy와 add 목록을 짝지어 봅니다 — 같은 자원이 옛 주소에서 지워지고 새 주소로 생성되면 리팩터링발 오탐입니다. terraform state list로 현재 state의 주소 목록을 확인합니다.

해결: 코드에 moved 블록으로 '옛 주소 → 새 주소'를 선언하면(구버전은 terraform state mv) plan이 0 to destroy로 바뀝니다. 그리고 리팩터링 PR은 plan 결과가 무변경(no changes)임을 확인한 뒤에만 머지하는 것을 규칙으로 삼습니다 — CI가 plan을 PR에 코멘트로 붙여주면(CI/CD 파이프라인) 리뷰어가 바로 잡아냅니다.

💼
실무 맥락
현업 패턴

IaC는 현대 인프라 엔지니어의 필수 역량입니다. 면접에서 "인프라를 어떻게 관리하나요?"에 "콘솔에서 직접"이라 답하면 감점, "Terraform으로 코드화하고 PR 리뷰 후 CI에서 plan/apply, state는 원격+잠금"이면 합격선입니다.

실무 성숙도: ① 모듈화(재사용 가능한 VPC/EKS 모듈), ② 환경별 분리(workspace/디렉터리), ③ CI에서 plan을 PR에 코멘트로 보여주고 머지 시 apply(CI/CD 파이프라인), ④ 드리프트 정기 점검. 이 트랙에서 배운 VPC(VPC와 서브넷)·DB(관리형 데이터베이스(RDS))·컨테이너(매니지드 컨테이너)를 전부 코드로 선언하면 환경 재현이 버튼 하나가 됩니다.

관련 모듈로 더 깊이:

다음 모듈에서는 이렇게 만든 자원들이 새어 나가는 돈 — 클라우드 비용 모델과 최적화 를 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

IaC(Infrastructure as Code)의 핵심 가치로 가장 정확한 것은?

Q2

Terraform의 'plan'과 'apply'의 관계로 옳은 것은?

Q3

Terraform '상태(state) 파일'이 하는 역할은?

Q4

팀이 Terraform을 함께 쓸 때 'state를 로컬 파일로 각자 보관'하면 생기는 문제는?

Q5

Terraform이 '선언적(declarative)'이라는 것의 실무적 의미는?

Q6

누군가 콘솔에서 인프라를 손으로 바꿨다(드리프트). Terraform으로 이를 알아내려면?

Q7

[심화] 자원 수백 개가 하나의 state에 들어 있을 때 생기는 문제와, 성숙한 팀의 올바른 대응은?

Q8

[심화] 리소스를 모듈로 묶는 정리성 리팩터링 후 동작은 아무것도 안 바꿨는데 plan에 '23 to add, 23 to destroy'가 떴다. 원인과 올바른 해결은?

0 / 8 답변

🧪 실습으로 확인하기

GCP Compute Engine 인스턴스 생성 및 SSH 접속

초급

Google Cloud Console과 gcloud CLI로 VM 인스턴스를 생성하고, SSH 접속·파일 전송·방화벽 규칙 설정까지 GCP 기본 흐름을 익힌다.

45📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요