Terraform/OpenTofu 기초 · 표현식과 내장 함수 · 이론
값을 만드는 쪽의 문법
한 줄 요약
내장 함수는 설정 파일에서 값을 계산해서 만들게 해 주고, 그 결과는 적용해 보기 전에 콘솔에서 한 줄로 확인할 수 있다.
왜 함수가 필요했나
선언형 설정의 첫인상은 "값을 그대로 적는다" 다. 그런데 조금만 커지면 그대로 적을 수 없는 것들이 나온다. 이름은 환경과 서비스와 번호를 조합해서 만들고, 태그는 공통 몫과 개별 몫을 합쳐서 만들고, 포트는 환경에 따라 다르다. 이것을 값으로 적으면 환경이 하나 늘 때마다 고칠 자리가 여러 곳으로 흩어진다.
더 나쁜 것은 손으로 이어 붙인 직렬화다. JSON 설정을 문자열 더하기로 만들면 따옴표와 이스케이프를 사람이 책임지게 되고, 값에 큰따옴표가 하나 들어오는 순간 깨진다. 그래서 언어 안에 함수가 들어왔다.
어떻게 동작하나
함수는 크게 몇 갈래로 나뉜다. 문자열을 다루는 것, 컬렉션을 다루는 것, 타입을 바꾸는 것, 실패를 다루는 것, 그리고 템플릿과 직렬화다.
join("-", ["web", "prod", "01"]) -> "web-prod-01"format("%s-%03d", "node", 7) -> "node-007"merge({ a = 1 }, { a = 9, b = 2 }) -> { a = 9, b = 2 } 뒤가 이긴다lookup({ web = 80 }, "api", 0) -> 0 없으면 기본값distinct(["a", "b", "a"]) -> ["a", "b"]formatlist("%s=%d", names, ports) -> 두 목록을 나란히 돌며 서식 적용조건 표현식은 조건 ? 참일때 : 거짓일때 꼴이다. 두 갈래의 타입이 같아야 하고, 다르면 도구가 하나로 맞추려다 엉뚱한 변환이 생긴다.
실패를 다루는 두 함수는 성격이 다르다. try 는 앞 인자의 평가가 실패하면 다음 인자를 내놓고, can 은 성공 여부를 참거짓으로 바꾼다. 중요한 제한이 있다 — 둘 다 실행 중에 생기는 오류만 잡는다. 선언조차 없는 변수나 locals 를 참조하는 것은 정적 오류라 try 로도 감싸지지 않는다.
locals { cfg = try(jsondecode(file("${path.module}/in.json")), {}) # 깨진 JSON 이면 빈 맵 name = try(local.cfg.name, "unknown") # 키가 없으면 기본값}템플릿은 값이 여럿일 때 쓴다. 템플릿 파일 안에서 반복과 조건 지시문을 쓸 수 있고, 지시문 줄이 결과에 빈 줄로 남지 않게 공백 제거 표시를 붙인다.
%{ for p in ports ~} listen ${p};%{ endfor ~}직렬화 함수는 값을 JSON 이나 YAML 표기로 옮긴다. 손으로 따옴표를 붙이지 않아도 되고, 무엇보다 되읽으면 같은 데이터가 나온다. 같은 맵을 두 함수로 내보낸 뒤 각각 되읽어 보면 정확히 같다는 것을 확인할 수 있다.
타입 변환 함수는 조용한 실패가 없어서 좋다. 바꿀 수 없는 값을 만나면 그 자리에서 오류를 낸다.
tonumber("007") -> 7parseint("ff", 16) -> 255tostring(true) -> "true"tonumber("abc") -> 오류try(tonumber("abc"), -1) -> -1그리고 이 모든 것을 적용 없이 확인하는 자리가 콘솔이다. 콘솔은 지금 디렉터리의 변수와 locals 를 읽으므로, 설정을 고치지 않고도 다른 변수 값으로 다른 분기를 평가해 볼 수 있다.
현장에서 만나는 모습
가장 자주 보는 것은 lookup 의 기본값을 빠뜨린 코드다. 맵에 키가 늘 있을 것이라고 믿고 적으면, 새 환경을 추가한 날 그 환경에서만 적용이 멈춘다. 기본값을 주는 습관 하나로 이 부류의 사고가 통째로 사라진다.
두 번째는 try 를 방패로 오해하는 경우다. 값이 이상할 때 조용히 기본값으로 내려앉는 것이 항상 옳지는 않다. 설정이 틀렸는데 기본값으로 적용이 성공하면, 문제는 몇 주 뒤 운영에서 드러난다. 바깥에서 들어오는 데이터에는 쓰되, 우리 저장소 안의 값에는 쓰지 않는 편이 낫다.
세 번째는 템플릿을 쓰지 않고 문자열을 이어 붙인 설정이다. 처음에는 짧아서 괜찮아 보이지만, 조건이 두 개만 들어와도 읽을 수 없게 된다. 여러 줄짜리 결과물은 템플릿 파일로 빼면 그 파일만 보고도 결과를 짐작할 수 있다.
다음 실습에서 할 것
여덟 단계를 돕니다. 문자열 함수 여섯 개를 콘솔에서 확인하고, 맵을 합치고 뽑는 함수들을 실제 출력으로 내보내고, 조건 표현식으로 환경별 값을 고릅니다. 이어서 깨진 JSON 을 흡수하는 설정을 만들고, 템플릿으로 여섯 줄짜리 설정 파일을 찍어 내고, 같은 데이터를 두 형식으로 내보내 되읽었을 때 같은지 확인합니다. 마지막 두 단계에서는 변환 함수와 그 실패를 확인하고, 값을 하나도 직접 적지 않은 설정을 만들어 채점기가 다른 입력으로 적용해도 결과가 따라오는지 확인합니다.