HCL
고친 사람 github-actions[bot]
HCL 은 사람이 인프라 설정을 읽고 고치기 쉽게 적도록 만든 설정 언어입니다. 테라폼 같은 도구가 이 언어로 적은 파일을 읽어 클라우드 자원을 만듭니다. 원하는 모습은 블록과 속성으로 적습니다. 값이 바뀌는 곳에는 간단한 식을 씁니다.
쉽고 빠른 이해
HCL 은 「서버 한 대, 저장소 하나가 있어야 한다」 같은 설정을 글자 파일로 적는 문법입니다. 테라폼의 .tf 파일이 이 문법으로 적혀 있습니다.
설정을 흔한 데이터 포맷인 JSON(JavaScript Object Notation, 자바스크립트 객체 표기법)이나 YAML(YAML Ain't Markup Language)로 적으면 불편한 데가 생깁니다. JSON 은 메모를 못 남깁니다. 따옴표와 쉼표가 많아 손으로 쓰기도 번거롭습니다. JSON 도 YAML 도 값을 계산하는 방법이 없어 비슷한 설정을 여러 번 베껴 적게 됩니다. HCL 은 읽기 쉬운 모양에 작은 계산 기능을 붙여 이 둘을 줄입니다.
어떻게 도나:
- 중괄호로 묶은 블록 안에
이름 = 값을 적습니다 - 값에는 다른 설정을 가리키는 이름이나 간단한 계산을 쓸 수 있습니다
- 테라폼 같은 도구가 파일을 읽어 계산을 끝낸 뒤 그 값대로 일합니다
대가도 있습니다. 반복문과 함수 만들기가 없어 복잡한 논리를 적기 어렵습니다. 어떤 블록을 쓸 수 있는지는 도구마다 달라서 문법만 알아서는 파일을 다 읽지 못합니다.
상세
이 절은 테라폼 설정 한 덩이로 HCL 을 풀어 봅니다. 이 언어가 생긴 까닭, 블록과 속성, 값과 식, HCL 과 도구의 경계, 이 언어가 못 하는 일, 다른 포맷과의 비교 순서입니다.
이름과 하는 일
HCL 은 HashiCorp Configuration Language 의 줄임말입니다. 우리말로는 해시코프 설정 언어입니다. HashiCorp 는 테라폼을 만든 회사입니다. 이 언어도 그 회사가 만들었습니다.
설정 언어는 프로그램에 줄 설정을 적는 언어입니다. 어떤 서버를 몇 대 띄울지, 어느 포트를 열지 같은 값을 적습니다. 파일을 실행하는 것이 아니라 다른 프로그램이 읽어 가는 것입니다.
HCL 로 적은 파일은 평범한 글자 파일입니다. 테라폼은 확장자 .tf 를 씁니다. 다른 도구는 .hcl 을 쓰기도 합니다.
이 언어가 없으면 생기는 일
설정은 흔히 JSON(JavaScript Object Notation, 자바스크립트 객체 표기법)이나 YAML(YAML Ain't Markup Language, 「YAML 은 마크업 언어가 아니다」)로 적습니다. 두 포맷 모두 데이터를 담는 데는 충분합니다. 그런데 사람이 인프라 설정을 오래 고쳐 가며 쓰기에는 모자란 데가 있습니다.
JSON 은 주석을 못 답니다. 「이 방화벽 규칙은 왜 열었나」를 파일 안에 남길 방법이 없습니다. 이름과 문자열마다 따옴표를 쳐야 합니다. 쉼표 하나만 빠져도 파일 전체가 안 읽힙니다.
YAML 은 주석을 달 수 있고 따옴표도 덜 씁니다. 대신 구조를 들여쓰기로 나타내서 빈칸 하나가 뜻을 바꿉니다. 그리고 둘 다 값을 계산하는 방법이 없습니다.
계산이 안 되면 비슷한 설정을 베껴 적게 됩니다. 개발용과 운영용 설정은 이름 앞머리만 다릅니다. 그래도 파일 두 벌을 따로 둡니다. 그래서 YAML 위에 템플릿 엔진을 덧씌워 값을 끼워 넣는 일이 흔합니다. 쿠버네티스 설정 파일을 찍어 내는 Helm 이 그런 도구입니다.
HCL 은 이 사이를 노립니다. 중괄호로 구조를 보입니다. 주석도 달 수 있습니다. 그리고 값을 적는 곳에 작은 식을 쓸 수 있어 템플릿을 덧씌우지 않아도 됩니다.
블록과 속성
HCL 파일은 두 가지로만 이루어집니다. 속성과 블록입니다.
속성은 이름 = 값 한 줄입니다. 블록은 이름표를 달고 중괄호로 묶은 덩이입니다. 블록 안에는 다시 속성과 블록이 들어갑니다.
아래는 테라폼으로 AWS(Amazon Web Services)에 파일 저장소 하나를 만드는 설정입니다.
# 로그를 모아 둘 저장소
resource "aws_s3_bucket" "logs" {
bucket = "app-logs"
tags = {
team = "platform"
}
lifecycle {
prevent_destroy = true
}
}
맨 윗줄 # 뒤는 주석입니다. 파일을 읽는 도구는 이 줄을 건너뜁니다. // 로 한 줄 주석을, /* */ 로 여러 줄 주석을 달 수도 있습니다.
`resource` 부터 마지막 중괄호까지가 블록 하나입니다. 블록은 세 부분으로 나뉩니다.
| 부분 | 이 예에서 | 하는 일 |
|---|---|---|
| 블록 타입 | resource |
이 덩이가 무엇인지 밝힌다 |
| [[라벨 (HCL) | 라벨]] | "aws_s3_bucket" · "logs" |
| 본문 | 중괄호 안 | 속성과 블록을 담는다 |
본문 안의 bucket = "app-logs" 가 속성입니다. tags = { ... } 도 속성입니다. 이름 뒤에 = 가 있으면 속성입니다. tags 의 값은 중괄호 안에 이름 = 값 쌍을 여럿 묶은 것인데, 이것을 맵이라고 부릅니다.
lifecycle { ... } 는 = 가 없으니 블록입니다. 블록 안에 블록이 든 모양입니다. = 가 있나 없나가 속성과 블록을 가르는 표시입니다.
flowchart TD
F["HCL 파일"] --> A1["속성 · 이름 = 값"]
F --> BL["블록"]
subgraph BLK["블록 한 덩이"]
T["블록 타입"] --> L["라벨 · 0개 이상"]
L --> B1["중괄호 안 본문"]
end
BL --> T
B1 --> A2["속성"]
B1 --> BL2["또 다른 블록"]
그림처럼 블록의 본문에는 파일 맨 위와 똑같이 속성과 블록이 들어갑니다. 그래서 블록은 몇 겹이든 겹쳐 넣을 수 있습니다.
값과 식
속성의 오른쪽에는 값이 옵니다. 값에는 몇 가지 종류가 있습니다.
| 종류 | 적는 법 | 예 |
|---|---|---|
| 문자열 | 큰따옴표 | "app-logs" |
| 숫자 | 그냥 숫자 | 3 · 0.5 |
| 참거짓 | true · false |
true |
| 목록 | 대괄호 | ["a", "b"] |
| 맵 | 중괄호 안에 이름 = 값 |
{ team = "platform" } |
| 비어 있음 | null |
null |
값 대신 식을 쓸 수도 있습니다. 식은 도구가 파일을 읽을 때 계산되어 값이 되는 글입니다. HCL 이 JSON·YAML 과 가장 크게 다른 점입니다.
variable "env" {
default = "dev"
}
resource "aws_s3_bucket" "logs" {
bucket = "${var.env}-app-logs"
}
resource "aws_s3_bucket_versioning" "logs" {
bucket = aws_s3_bucket.logs.id
versioning_configuration {
status = var.env == "prod" ? "Enabled" : "Suspended"
}
}
이 설정에 쓴 식은 넷입니다.
var.env 는 다른 블록에서 정한 값을 이름으로 가리키는 참조입니다. aws_s3_bucket.logs.id 는 다른 자원이 만들어진 뒤에 생기는 값을 가리킵니다.
"${var.env}-app-logs" 는 문자열 안에 식을 끼워 넣은 것입니다. 이것을 보간이라고 부릅니다. env 가 dev 면 이 값은 dev-app-logs 가 됩니다.
조건 ? 참일 때 : 거짓일 때 는 조건식입니다. aws_s3_bucket_versioning 블록은 저장소가 파일을 덮어써도 옛 판을 남겨 두는 기능을 켜고 끕니다. 이 기능을 버전 보관이라고 합니다. 조건식 덕에 운영 환경일 때만 버전 보관이 켜집니다. 같은 파일 한 벌로 개발용과 운영용을 함께 다루는 방법입니다.
flowchart TD
V["variable env · 값 dev"]
V -->|"보간"| N["logs 저장소의 이름 · dev-app-logs"]
V -->|"조건식"| S["버전 보관 설정의 status · Suspended"]
N --> ID["logs 저장소가 만들어진 뒤 생기는 id"]
ID -->|"참조"| VB["버전 보관 설정이 가리킬 저장소"]
그림처럼 env 하나가 저장소 이름과 버전 보관 여부를 함께 정합니다. 버전 보관 설정은 저장소의 id 를 가리키므로 저장소가 먼저 만들어져야 합니다. 테라폼은 이런 참조를 따라 만드는 순서를 정합니다.
목록을 바꿔 새 목록을 만드는 `for` 식도 있습니다. [for n in var.names : upper(n)] 은 이름마다 대문자로 바꾼 목록을 만듭니다. upper(n) 처럼 괄호를 붙이면 함수 호출입니다.
HCL 이 정하는 것과 도구가 정하는 것
위 예의 resource · `variable` 은 HCL 의 낱말이 아닙니다. 테라폼이 정한 블록 타입입니다. HCL 은 「블록은 타입과 라벨과 본문으로 이루어진다」까지만 정합니다.
어떤 블록 타입을 쓸 수 있는지, 라벨이 몇 개인지, 본문에 어떤 속성이 와야 하는지는 HCL 을 쓰는 도구가 정합니다. 이 약속을 도구의 스키마라고 부릅니다. 스키마는 데이터에 어떤 칸이 있고 칸마다 무엇이 드는지를 적은 설계도입니다.
함수도 같습니다. HCL 은 이름(인자) 라는 호출 모양만 정합니다. upper 같은 함수를 내주는 것은 도구입니다. 식 안의 var 같은 이름이 무엇을 가리키는지도 도구가 정합니다.
flowchart TD
H["HCL · 블록 · 속성 · 식의 문법"]
H --> T["테라폼 어휘 · resource · variable · output · module"]
H --> P["Packer 어휘 · source · build"]
H --> N["Nomad 어휘 · job · group · task"]
그래서 해시코프의 여러 도구가 같은 HCL 을 쓰면서도 파일에 적는 블록은 다릅니다. Packer 는 서버를 띄울 때 쓰는 머신 이미지를 만드는 도구입니다. Nomad 는 여러 서버에 작업을 나눠 돌리는 도구입니다. 둘 다 HCL 로 설정을 적지만 블록 타입은 제각각입니다.
이 때문에 「HCL 을 안다」와 「테라폼 파일을 읽는다」는 다른 일입니다. 문법은 한 번 익히면 도구를 건너가도 같습니다. 블록 타입과 그 안의 속성은 도구마다 새로 익혀야 합니다.
JSON 으로 적는 HCL
HCL 은 같은 내용을 JSON 으로도 적을 수 있게 되어 있습니다. 블록은 JSON 객체가 됩니다. 라벨은 객체 안의 한 겹이 됩니다. 앞의 저장소 설정은 이렇게 됩니다.
{
"resource": {
"aws_s3_bucket": {
"logs": {
"bucket": "app-logs"
}
}
}
}
사람이 쓰기에는 앞의 모양이 편합니다. JSON 판은 다른 프로그램이 설정을 만들어 낼 때 씁니다. JSON 을 뽑는 기능은 어느 언어에나 있으니 설정을 코드로 찍어 내기 쉽습니다. 테라폼은 이런 파일을 확장자 .tf.json 으로 읽습니다.
이 언어가 못 하는 일
HCL 의 식은 값을 계산할 뿐 순서대로 일을 시키지 못합니다. while 같은 반복문이 없습니다. 사용자가 함수를 새로 만드는 문법도 없습니다. 반복은 for 식처럼 목록에서 새 목록을 만드는 모양으로만 적습니다.
일부러 이렇게 좁혔습니다. HCL 파일은 「무엇이 있어야 하나」를 적는 선언적 설정입니다. 끝 모습만 적습니다. 거기까지 가는 단계는 도구에 맡깁니다.
식도 같은 까닭으로 좁혔습니다. 부수 효과는 값을 내는 일 말고 바깥에 남기는 흔적입니다. 파일을 쓰거나 서버를 만드는 일이 그렇습니다. HCL 의 식은 부수 효과 없이 값만 냅니다. 그래서 파일을 몇 번 읽어도 같은 결과가 나옵니다.
대신 복잡한 논리는 적기 어렵습니다. 조건이 여러 겹 겹치면 조건식이 한 줄에 길게 이어집니다. 그래서 설정을 Python · TypeScript 같은 프로그래밍 언어로 적는 도구도 따로 있습니다. Pulumi 와 AWS CDK(Cloud Development Kit, 클라우드 개발 키트)가 그런 쪽입니다.
다른 설정 포맷과 견주기
세 포맷은 담을 수 있는 데이터가 비슷합니다. 다른 것은 사람이 쓰고 고치는 편의입니다.
| JSON | YAML | HCL | |
|---|---|---|---|
| 주석 | ✗ | ✓ | ✓ |
| 구조를 나타내는 법 | 중괄호 · 대괄호 | 들여쓰기 | 중괄호 · 대괄호 |
| 값 계산 | ✗ | ✗ | ✓ 식 |
| 흔히 쓰는 곳 | 웹 응답 · 프로그램이 만드는 설정 | Kubernetes 매니페스트 · 앤서블 | 해시코프 도구의 설정 |
HCL 은 주로 해시코프 도구와 그 둘레에서 쓰입니다. 파서가 Go 언어 라이브러리로 나와 있어서 Go 로 짠 도구가 가져다 쓰기는 쉽습니다. 다른 언어에서 HCL 파일을 읽으려면 따로 만든 파서를 찾아야 합니다.
테라폼은 terraform fmt 명령으로 HCL 파일의 들여쓰기와 = 줄맞춤을 한 모양으로 고쳐 줍니다. 여럿이 같은 파일을 고쳐도 모양 차이로 변경 내역이 어지러워지지 않습니다.
관련 항목
HCL 파일을 이루는 구성 요소
블록 (HCL) · 속성 (HCL) · 라벨 (HCL) · 표현식 · 보간 · 조건식 · for 식 · 주석
HCL 로 설정을 적는 도구
테라폼 · OpenTofu · Packer · Nomad · Vault · Consul · HashiCorp
HCL 위에 테라폼이 얹는 블록 타입
리소스 (테라폼) · 변수 (테라폼) · 출력 (테라폼) · 모듈 (테라폼) · 데이터 소스 (테라폼) · 프로바이더 (테라폼)
HCL 대신 설정을 적는 다른 포맷과 언어
JSON · YAML · TOML · Jsonnet · CUE · 템플릿 엔진
HCL 대신 인프라를 코드로 적는 도구
Pulumi · AWS CDK · CloudFormation · 앤서블
HCL 이 속하는 상위 분류
다른 이름: HashiCorp Configuration Language · 해시코프 설정 언어