본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2026. 2. 2. · 3 Views
Jinja2 템플릿 활용 완벽 가이드
Ansible에서 동적 설정 파일을 생성하는 Jinja2 템플릿의 핵심 문법부터 실전 활용까지 다룹니다. 변수 치환, 필터, 조건문, 반복문을 마스터하여 인프라 자동화의 효율을 극대화하세요.
목차
1. Jinja2 템플릿 기본 문법
어느 날 김개발 씨는 서버 10대에 각각 다른 설정 파일을 배포해야 하는 상황에 놓였습니다. 일일이 파일을 수정하려니 벌써부터 야근이 눈앞에 아른거립니다.
"분명 더 좋은 방법이 있을 텐데..." 고민하던 그때, 선배 박시니어 씨가 다가와 말했습니다. "Jinja2 템플릿 써본 적 있어요?"
Jinja2는 Python 기반의 템플릿 엔진으로, Ansible에서 동적 설정 파일을 생성하는 핵심 도구입니다. 마치 편지 양식에 받는 사람 이름만 바꿔 넣는 것처럼, 템플릿 파일에 변수를 삽입하여 다양한 설정 파일을 자동으로 생성할 수 있습니다.
중괄호 두 개로 변수를, 중괄호와 퍼센트로 제어문을 표현하는 간단한 문법 체계를 가지고 있습니다.
다음 코드를 살펴봅시다.
# nginx.conf.j2 - Jinja2 템플릿 기본 예제
# 변수 출력: {{ 변수명 }}
server {
listen {{ http_port }};
server_name {{ server_name }};
# 주석은 {# 내용 #} 형태로 작성합니다
{# 이 줄은 렌더링 시 제거됩니다 #}
# 제어문: {% 구문 %}
{% if ssl_enabled %}
ssl_certificate {{ ssl_cert_path }};
{% endif %}
}
김개발 씨는 입사 6개월 차 DevOps 엔지니어입니다. 회사에서 운영하는 서버가 점점 늘어나면서, 설정 파일 관리가 골칫거리가 되었습니다.
개발 서버, 스테이징 서버, 운영 서버마다 포트 번호도 다르고, 도메인도 다르고, SSL 설정도 제각각이었습니다. 처음에는 각 서버별로 설정 파일을 따로 만들어 관리했습니다.
그런데 서버가 10대, 20대로 늘어나자 문제가 생겼습니다. nginx 설정을 하나 변경하려면 20개의 파일을 일일이 수정해야 했던 것입니다.
박시니어 씨가 제안한 Jinja2 템플릿은 바로 이런 문제를 해결해 줍니다. Jinja2가 무엇인지 쉽게 비유해 볼까요?
청첩장을 생각해 보세요. 청첩장에는 "OOO님을 초대합니다"라는 문구가 있습니다.
여기서 OOO 부분만 바꾸면 수백 장의 청첩장을 만들 수 있습니다. Jinja2 템플릿도 마찬가지입니다.
설정 파일의 뼈대는 그대로 두고, 서버마다 달라지는 부분만 변수로 처리하는 것입니다. Jinja2의 핵심 문법은 세 가지입니다.
첫 번째는 **이중 중괄호 {{ }}**입니다. 이 안에 변수명을 넣으면 실제 값으로 치환됩니다.
예를 들어 {{ http_port }}라고 쓰면, Ansible이 실행될 때 실제 포트 번호인 80이나 8080 같은 값으로 바뀝니다. 두 번째는 {% %} 형태의 제어문입니다.
조건문이나 반복문을 작성할 때 사용합니다. 위 코드에서 {% if ssl_enabled %}처럼 조건에 따라 특정 설정을 포함하거나 제외할 수 있습니다.
세 번째는 {# #} 형태의 주석입니다. 템플릿 파일에 메모를 남기고 싶지만, 최종 결과물에는 포함되지 않아야 할 때 사용합니다.
일반 주석과 달리 렌더링 후에는 완전히 사라집니다. 위 코드를 다시 살펴보겠습니다.
nginx.conf.j2 파일은 확장자에서 알 수 있듯이 Jinja2 템플릿입니다. listen {{ http_port }} 부분은 Ansible 변수 http_port의 값으로 치환됩니다.
개발 서버에서는 8080으로, 운영 서버에서는 80으로 다르게 설정할 수 있는 것입니다. {% if ssl_enabled %} 블록은 ssl_enabled 변수가 참일 때만 SSL 관련 설정을 포함시킵니다.
개발 서버에서는 SSL이 필요 없지만, 운영 서버에서는 필수인 경우에 유용합니다. 김개발 씨는 템플릿 하나로 20대의 서버 설정을 관리할 수 있게 되었습니다.
변경 사항이 생기면 템플릿 파일 하나만 수정하면 됩니다. 야근 걱정은 사라지고, 주말 저녁 약속도 지킬 수 있게 되었습니다.
실전 팁
💡 - 템플릿 파일은 반드시 .j2 확장자를 사용하여 일반 설정 파일과 구분하세요
- {{ 와 변수명 사이에 공백을 넣어 가독성을 높이세요
2. 변수 치환하기
김개발 씨가 Jinja2 기본 문법을 익히고 나니, 이제 실제로 변수를 어떻게 전달하는지 궁금해졌습니다. "템플릿에 {{ server_name }}이라고 썼는데, 이 값은 어디서 오는 거죠?" 박시니어 씨가 웃으며 대답했습니다.
"Ansible의 변수 시스템과 연결되어 있어요. 인벤토리, 플레이북, 심지어 팩트까지 모두 사용할 수 있죠."
Jinja2 템플릿에서 변수 치환은 {{ 변수명 }} 구문으로 이루어집니다. Ansible의 모든 변수 소스를 활용할 수 있으며, 인벤토리 변수, 플레이북 변수, 등록된 변수, 그리고 시스템 팩트까지 템플릿 내에서 자유롭게 사용할 수 있습니다.
중첩된 변수나 딕셔너리 접근도 점 표기법으로 간편하게 처리됩니다.
다음 코드를 살펴봅시다.
# app_config.conf.j2
# 기본 변수 치환
app_name = {{ app_name }}
environment = {{ env_type }}
# 딕셔너리 접근 - 점 표기법
db_host = {{ database.host }}
db_port = {{ database.port }}
# 대괄호 표기법 (키에 특수문자가 있을 때)
api_key = {{ credentials['api-key'] }}
# Ansible 팩트 사용
hostname = {{ ansible_hostname }}
ip_address = {{ ansible_default_ipv4.address }}
memory_mb = {{ ansible_memtotal_mb }}
변수 치환은 Jinja2 템플릿의 가장 기본적이면서도 강력한 기능입니다. 김개발 씨가 처음 접한 의문처럼, 템플릿에 적힌 변수들이 실제 값으로 어떻게 바뀌는지 이해하는 것이 중요합니다.
마트에서 장을 볼 때를 떠올려 보세요. 장바구니 목록에 "우유 1개, 계란 한 판"이라고 적어갑니다.
이때 "우유"와 "계란"은 변수명이고, 실제 마트 진열대에서 집어 담는 제품이 변수의 값입니다. Jinja2도 마찬가지로, 템플릿에 적힌 변수명을 보고 Ansible이 제공하는 값 저장소에서 해당 값을 찾아 치환합니다.
Ansible에서 변수가 올 수 있는 곳은 다양합니다. 인벤토리 파일에서 호스트별로 정의할 수도 있고, 플레이북의 vars 섹션에서 선언할 수도 있습니다.
group_vars나 host_vars 디렉토리를 활용하면 더 체계적인 관리가 가능합니다. 위 코드에서 {{ app_name }}처럼 단순한 변수는 직관적입니다.
플레이북에서 app_name: "MyApp"이라고 정의했다면, 렌더링 결과는 app_name = MyApp이 됩니다. 딕셔너리 형태의 변수도 자주 사용됩니다.
database라는 변수가 host와 port를 가진 딕셔너리라면, {{ database.host }}처럼 점 표기법으로 내부 값에 접근할 수 있습니다. 이 방식은 관련된 설정들을 묶어서 관리할 때 유용합니다.
때로는 변수 키에 하이픈이나 공백 같은 특수문자가 포함되어 있을 수 있습니다. 이런 경우에는 {{ credentials['api-key'] }}처럼 대괄호 표기법을 사용해야 합니다.
점 표기법은 유효한 Python 식별자에만 동작하기 때문입니다. 특히 유용한 것은 Ansible 팩트입니다.
ansible_hostname, ansible_default_ipv4.address 같은 팩트는 Ansible이 대상 호스트에서 자동으로 수집한 시스템 정보입니다. 별도로 변수를 정의하지 않아도 호스트명, IP 주소, 메모리 용량 같은 정보를 템플릿에서 바로 사용할 수 있습니다.
김개발 씨는 이 기능을 활용해 각 서버의 설정 파일에 자동으로 호스트명과 IP 주소를 기록하도록 만들었습니다. 더 이상 서버별로 값을 하드코딩할 필요가 없어진 것입니다.
주의할 점도 있습니다. 정의되지 않은 변수를 사용하면 Ansible은 오류를 발생시킵니다.
따라서 변수가 존재하는지 확신이 없다면, 다음에 배울 필터나 조건문으로 기본값을 설정해 두는 것이 안전합니다.
실전 팁
💡 - ansible -m setup 명령으로 사용 가능한 팩트 목록을 미리 확인하세요
- 변수명은 밑줄로 연결하고, 딕셔너리로 관련 설정을 그룹화하면 관리가 편합니다
3. 필터 사용법
김개발 씨가 템플릿을 작성하다 보니 새로운 고민이 생겼습니다. "변수 값을 대문자로 바꾸고 싶은데, 어떻게 해야 하죠?
그리고 변수가 정의되지 않았을 때 기본값을 쓰고 싶어요." 박시니어 씨가 키보드를 두드리며 말했습니다. "Jinja2 필터를 사용하면 됩니다.
파이프 기호 하나로 변수 값을 원하는 대로 변환할 수 있어요."
필터는 파이프(|) 기호를 사용하여 변수 값을 변환하는 기능입니다. 마치 정수기 필터가 물을 정화하듯, Jinja2 필터는 데이터를 원하는 형태로 가공합니다.
문자열 변환, 기본값 설정, 리스트 조작, JSON 변환 등 다양한 내장 필터가 제공되며, 여러 필터를 연결해서 사용할 수도 있습니다.
다음 코드를 살펴봅시다.
# config.yml.j2 - 필터 활용 예제
# 기본값 설정 (변수가 없을 때)
port: {{ http_port | default(8080) }}
timeout: {{ timeout_sec | default(30) }}
# 문자열 변환
app_name_upper: {{ app_name | upper }}
app_name_lower: {{ app_name | lower }}
description: {{ desc | capitalize }}
# 리스트를 문자열로 변환
allowed_hosts: {{ allowed_ips | join(', ') }}
# JSON 형식으로 출력
config_json: {{ config_dict | to_json }}
config_yaml: {{ config_dict | to_yaml }}
# 필터 체이닝 (여러 필터 연결)
normalized: {{ user_input | trim | lower | replace(' ', '_') }}
필터는 Jinja2에서 가장 실용적인 기능 중 하나입니다. 변수 값을 그대로 출력하는 것이 아니라, 원하는 형태로 가공해서 출력할 수 있게 해줍니다.
커피를 만드는 과정을 생각해 보세요. 원두(변수)가 있고, 이것을 그라인더(필터)에 통과시키면 커피 가루(변환된 값)가 됩니다.
여러 필터를 연결하면 원두를 갈고, 물을 붓고, 거르는 과정을 한 번에 처리하는 것과 같습니다. 가장 많이 사용하는 필터는 default입니다.
{{ http_port | default(8080) }}처럼 사용하면, http_port 변수가 정의되지 않았을 때 8080을 기본값으로 사용합니다. 이 필터 하나로 "변수가 없으면 오류"라는 문제를 우아하게 해결할 수 있습니다.
문자열 변환 필터도 자주 쓰입니다. upper는 대문자로, lower는 소문자로, capitalize는 첫 글자만 대문자로 변환합니다.
환경변수나 상수를 정의할 때 일관된 형식을 유지하는 데 유용합니다. 리스트를 다룰 때는 join 필터가 빛을 발합니다.
['192.168.1.1', '192.168.1.2'] 같은 리스트를 {{ allowed_ips | join(', ') }}로 처리하면 "192.168.1.1, 192.168.1.2"라는 문자열이 됩니다. 설정 파일에서 여러 값을 나열할 때 매우 편리합니다.
Ansible 환경에서는 to_json과 to_yaml 필터도 중요합니다. 복잡한 딕셔너리 구조를 그대로 JSON이나 YAML 형식으로 출력해야 할 때 사용합니다.
애플리케이션 설정 파일을 생성할 때 특히 유용합니다. 필터의 진정한 힘은 체이닝에서 나옵니다.
{{ user_input | trim | lower | replace(' ', '_') }}처럼 여러 필터를 파이프로 연결하면, 왼쪽에서 오른쪽으로 순차적으로 적용됩니다. 이 예제는 입력값의 앞뒤 공백을 제거하고, 소문자로 바꾸고, 공백을 밑줄로 치환합니다.
김개발 씨는 필터를 배우고 나서 템플릿이 훨씬 유연해졌다고 느꼈습니다. 이전에는 변수 값을 미리 정확하게 맞춰 놓아야 했지만, 이제는 템플릿에서 필요한 형태로 변환할 수 있게 되었습니다.
한 가지 주의할 점이 있습니다. 필터를 너무 많이 체이닝하면 가독성이 떨어질 수 있습니다.
복잡한 변환이 필요하다면 Ansible의 set_fact로 미리 가공된 변수를 만드는 것이 좋습니다.
실전 팁
💡 - default 필터는 거의 모든 변수에 적용하는 습관을 들이면 오류를 예방할 수 있습니다
- mandatory 필터는 변수가 반드시 있어야 할 때 사용하여 명시적으로 오류를 발생시킵니다
4. 조건문과 반복문 in 템플릿
서버 환경에 따라 설정이 달라져야 할 때가 있습니다. 김개발 씨는 개발 서버에서는 디버그 모드를 켜고, 운영 서버에서는 끄고 싶었습니다.
또한 여러 개의 데이터베이스 연결 정보를 반복해서 출력해야 하는 상황도 생겼습니다. "조건문과 반복문을 템플릿에서 쓸 수 있나요?" 박시니어 씨가 고개를 끄덕였습니다.
"물론이죠. Jinja2의 제어문을 사용하면 됩니다."
Jinja2에서 조건문은 {% if %}, {% elif %}, {% else %}, {% endif %}로 구현합니다. 반복문은 {% for %}와 {% endfor %}를 사용합니다.
이 제어문들을 활용하면 환경별로 다른 설정을 생성하거나, 리스트의 항목들을 순회하며 반복적인 설정을 작성할 수 있습니다. Python과 유사한 문법이라 익히기 쉽습니다.
다음 코드를 살펴봅시다.
# app.conf.j2 - 조건문과 반복문
# 조건문 - 환경별 설정
{% if env == 'production' %}
debug = false
log_level = WARNING
{% elif env == 'staging' %}
debug = true
log_level = INFO
{% else %}
debug = true
log_level = DEBUG
{% endif %}
# 반복문 - 리스트 순회
{% for db in databases %}
[database_{{ loop.index }}]
host = {{ db.host }}
port = {{ db.port }}
name = {{ db.name }}
{% endfor %}
# 조건부 반복문
{% for user in users if user.active %}
{{ user.name }}={{ user.role }}
{% endfor %}
조건문과 반복문은 템플릿에 논리를 부여하는 핵심 기능입니다. 단순히 값을 치환하는 것을 넘어서, 상황에 따라 다른 내용을 생성할 수 있게 해줍니다.
도로 표지판을 떠올려 보세요. "서울 방면 좌회전, 부산 방면 직진"이라는 안내처럼, 조건문은 특정 조건에 따라 다른 길로 안내합니다.
반복문은 마라톤 코스의 이정표와 같습니다. 1km, 2km, 3km 지점마다 같은 형식의 표지판이 반복되지만, 숫자만 달라지는 것과 비슷합니다.
조건문의 기본 구조는 {% if 조건 %}...{% endif %}입니다. 위 코드에서는 env 변수의 값에 따라 세 가지 다른 설정을 생성합니다.
운영 환경에서는 디버그를 끄고 경고 레벨만 로깅하며, 개발 환경에서는 모든 디버그 정보를 출력합니다. elif와 else를 활용하면 여러 조건을 처리할 수 있습니다.
마치 Python의 조건문과 동일한 구조입니다. 한 가지 중요한 점은 반드시 {% endif %}로 조건문 블록을 닫아야 한다는 것입니다.
이를 잊으면 템플릿 렌더링 오류가 발생합니다. 반복문은 {% for 항목 in 리스트 %}...{% endfor %} 구조를 사용합니다.
위 예제에서 databases가 세 개의 데이터베이스 정보를 담은 리스트라면, for 블록 안의 내용이 세 번 반복됩니다. 각 반복에서 db 변수는 현재 순회 중인 데이터베이스 정보를 가리킵니다.
loop 객체는 반복문 내에서 자동으로 사용할 수 있는 특수 변수입니다. loop.index는 현재 반복 횟수를 1부터 셉니다.
loop.index0은 0부터 셉니다. loop.first와 loop.last는 첫 번째와 마지막 반복인지를 알려줍니다.
이런 변수들을 활용하면 더 정교한 템플릿을 작성할 수 있습니다. 조건부 반복문도 가능합니다.
{% for user in users if user.active %}처럼 for 문에 if 조건을 붙이면, 조건을 만족하는 항목만 순회합니다. 별도의 if 문으로 감싸는 것보다 코드가 간결해집니다.
김개발 씨는 이 기능으로 하나의 템플릿에서 개발/스테이징/운영 환경의 설정 파일을 모두 생성할 수 있게 되었습니다. 환경별로 템플릿을 따로 만들 필요가 없어진 것입니다.
주의할 점이 있습니다. 템플릿 안에 너무 복잡한 로직을 넣으면 유지보수가 어려워집니다.
복잡한 조건이 필요하다면, Ansible 플레이북에서 변수를 미리 계산해서 넘기는 것이 좋습니다.
실전 팁
💡 - loop.index는 1부터, loop.index0은 0부터 시작하니 상황에 맞게 선택하세요
- 빈 줄이 너무 많이 생기면 {%- -%} 구문으로 공백을 제어할 수 있습니다
5. 템플릿 파일 배포
김개발 씨가 훌륭한 템플릿 파일을 완성했습니다. 그런데 이 템플릿을 실제 서버에 어떻게 배포해야 할까요?
"혹시 copy 모듈을 쓰면 되나요?" 박시니어 씨가 손을 저었습니다. "copy 모듈은 파일을 그대로 복사해요.
템플릿을 렌더링해서 배포하려면 template 모듈을 사용해야 합니다. 이 둘의 차이를 정확히 알아야 해요."
Ansible의 template 모듈은 Jinja2 템플릿을 렌더링하여 대상 호스트에 배포합니다. copy 모듈과 달리 변수 치환과 제어문이 처리된 최종 결과물이 서버에 전달됩니다.
파일 소유자, 권한, SELinux 컨텍스트 등을 함께 설정할 수 있으며, backup 옵션으로 기존 파일을 보존할 수도 있습니다. 핸들러와 연계하면 설정 변경 시 서비스를 자동으로 재시작할 수 있습니다.
다음 코드를 살펴봅시다.
# playbook.yml - template 모듈 사용
- name: Deploy nginx configuration
hosts: webservers
vars:
http_port: 80
server_name: example.com
tasks:
- name: Create nginx config from template
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
validate: nginx -t -c %s
notify: Restart nginx
handlers:
- name: Restart nginx
ansible.builtin.service:
name: nginx
state: restarted
템플릿을 작성하는 것과 배포하는 것은 별개의 작업입니다. 아무리 완벽한 템플릿을 만들어도, 이를 서버에 제대로 배포하지 못하면 소용이 없습니다.
레스토랑 주방에서 요리사가 레시피를 보고 음식을 만드는 과정을 생각해 보세요. 레시피(템플릿)에는 "소금 적당량"이라고 적혀 있지만, 실제로 요리할 때는 구체적인 양을 결정해서 넣어야 합니다.
template 모듈은 바로 이 요리 과정을 담당합니다. 레시피의 변수들을 실제 값으로 채워서 완성된 요리(설정 파일)를 손님(서버)에게 전달합니다.
copy 모듈과의 차이를 명확히 알아야 합니다. copy 모듈은 파일을 있는 그대로 복사합니다.
템플릿 파일을 copy로 배포하면 {{ http_port }}라는 문자열이 그대로 서버에 들어갑니다. 반면 template 모듈은 {{ http_port }}를 실제 값인 80으로 치환한 결과를 배포합니다.
위 플레이북을 살펴보겠습니다. src는 로컬의 템플릿 파일 경로입니다.
dest는 대상 호스트에서 파일이 생성될 경로입니다. owner, group, mode로 파일의 소유권과 권한을 설정합니다.
backup: yes 옵션은 매우 유용합니다. 기존 파일이 있다면 타임스탬프가 붙은 백업 파일을 만들어 둡니다.
실수로 잘못된 설정을 배포했을 때 빠르게 복구할 수 있습니다. validate 옵션은 안전장치입니다.
템플릿 렌더링 후, 실제 파일을 배포하기 전에 검증 명령을 실행합니다. nginx -t -c %s는 nginx 설정 문법을 검사합니다.
%s는 임시 파일 경로로 치환됩니다. 검증에 실패하면 파일이 배포되지 않으므로, 잘못된 설정으로 서비스가 중단되는 것을 방지할 수 있습니다.
notify와 handlers의 조합도 중요합니다. 설정 파일이 변경되면 notify가 지정된 핸들러를 호출합니다.
핸들러는 서비스를 재시작하여 변경된 설정을 적용합니다. 파일이 변경되지 않았다면 핸들러는 실행되지 않으므로, 불필요한 서비스 재시작을 피할 수 있습니다.
김개발 씨는 validate 옵션 덕분에 문법 오류가 있는 설정 파일을 배포하는 실수를 사전에 방지할 수 있었습니다. 이전에는 배포 후 서비스가 시작되지 않아 당황한 적이 있었는데, 이제는 그런 걱정이 없어졌습니다.
한 가지 팁을 더 드리자면, 템플릿 파일은 프로젝트의 templates 디렉토리에 모아두는 것이 관례입니다. Ansible은 자동으로 이 디렉토리에서 템플릿을 찾습니다.
실전 팁
💡 - 중요한 설정 파일은 반드시 backup: yes 옵션을 사용하세요
- validate 옵션을 적극 활용하여 잘못된 설정 배포를 방지하세요
6. 실전 설정 파일 생성 예제
이제 김개발 씨는 Jinja2 템플릿의 모든 기본기를 익혔습니다. "그런데 실제 현업에서는 어떻게 활용하나요?" 박시니어 씨가 화면을 가리키며 말했습니다.
"자, 우리 회사의 실제 인프라를 예로 들어볼게요. 웹 서버, 애플리케이션 서버, 데이터베이스 서버가 있고, 각각 개발/스테이징/운영 환경이 있습니다.
이걸 템플릿 하나로 관리하는 방법을 보여드릴게요."
실전에서는 여러 기능을 조합하여 복잡한 설정 파일을 생성합니다. 환경별 변수 분리, 조건부 설정 블록, 반복을 통한 동적 설정 생성, 필터를 통한 데이터 가공을 모두 활용합니다.
인벤토리의 그룹 변수와 호스트 변수를 적절히 조합하면, 하나의 템플릿으로 수십 대의 서버 설정을 효율적으로 관리할 수 있습니다.
다음 코드를 살펴봅시다.
# application.yml.j2 - 실전 애플리케이션 설정
server:
name: {{ app_name | default('myapp') }}
port: {{ app_port | default(8080) }}
host: {{ ansible_default_ipv4.address }}
{% if env == 'production' %}
logging:
level: WARNING
file: /var/log/{{ app_name }}/app.log
max_size: 100MB
backup_count: 10
{% else %}
logging:
level: DEBUG
file: /tmp/{{ app_name }}.log
{% endif %}
database:
{% for db in databases %}
{{ db.name }}:
host: {{ db.host }}
port: {{ db.port | default(5432) }}
pool_size: {{ db.pool_size | default(5) }}
{% if db.ssl_enabled | default(false) %}
ssl: true
ssl_cert: {{ db.ssl_cert_path }}
{% endif %}
{% endfor %}
allowed_origins:
{{ allowed_origins | to_nice_yaml | indent(2) }}
지금까지 배운 모든 내용을 종합하는 시간입니다. 실전에서는 개별 기능을 따로 사용하는 것이 아니라, 상황에 맞게 조합하여 활용합니다.
오케스트라 연주를 생각해 보세요. 바이올린, 첼로, 플루트, 트럼펫이 각자의 소리를 내지만, 지휘자의 지휘 아래 하나의 아름다운 음악이 됩니다.
Jinja2 템플릿도 마찬가지입니다. 변수 치환, 필터, 조건문, 반복문이라는 악기들이 조화롭게 어우러져 완벽한 설정 파일이라는 음악을 만들어냅니다.
위 템플릿을 블록별로 분석해 보겠습니다. server 블록은 기본적인 변수 치환과 필터를 사용합니다.
app_name에 default 필터를 적용하여 변수가 없을 때 'myapp'을 사용합니다. ansible_default_ipv4.address는 팩트를 활용하여 서버의 실제 IP 주소를 자동으로 가져옵니다.
logging 블록은 조건문의 좋은 활용 예입니다. 운영 환경에서는 WARNING 레벨로 로깅하고, 로그 파일도 적절한 위치에 저장하며 로테이션 설정도 추가합니다.
반면 개발 환경에서는 DEBUG 레벨로 모든 정보를 임시 디렉토리에 기록합니다. 하나의 템플릿이 환경에 따라 완전히 다른 결과물을 생성하는 것입니다.
database 블록은 반복문과 조건문, 필터를 모두 조합합니다. databases 리스트를 순회하면서 각 데이터베이스 설정을 출력합니다.
port와 pool_size에는 default 필터로 기본값을 지정했습니다. ssl_enabled가 참인 경우에만 SSL 관련 설정을 추가합니다.
이렇게 하면 SSL이 필요한 데이터베이스와 그렇지 않은 데이터베이스를 하나의 템플릿으로 처리할 수 있습니다. allowed_origins 블록은 필터의 고급 활용을 보여줍니다.
to_nice_yaml 필터는 리스트를 보기 좋은 YAML 형식으로 변환합니다. indent(2) 필터는 들여쓰기를 맞춥니다.
복잡한 데이터 구조도 간단하게 출력할 수 있습니다. 이 템플릿을 실제로 배포하려면, 환경별로 다른 변수 파일을 준비해야 합니다.
group_vars/production.yml에는 운영 환경 변수를, group_vars/development.yml에는 개발 환경 변수를 정의합니다. 플레이북을 실행할 때 어떤 그룹을 대상으로 하느냐에 따라 적절한 변수가 자동으로 적용됩니다.
김개발 씨는 이 패턴을 마스터한 후, 인프라 관리 업무가 훨씬 수월해졌습니다. 새로운 서버를 추가할 때도 인벤토리에 호스트만 등록하면, 템플릿이 알아서 적절한 설정 파일을 생성해 줍니다.
박시니어 씨가 마지막으로 조언했습니다. "템플릿을 처음 만들 때는 단순하게 시작하세요.
필요에 따라 조금씩 기능을 추가하면 됩니다. 처음부터 너무 복잡하게 만들면 나중에 유지보수하기 어려워집니다."
실전 팁
💡 - 환경별 변수는 group_vars 디렉토리로 분리하여 관리하세요
- 템플릿이 복잡해지면 include 문으로 여러 파일로 나누는 것도 방법입니다
- 템플릿 변경 후에는 --check --diff 옵션으로 미리 결과를 확인하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
vLLM 통합 완벽 가이드
대규모 언어 모델 추론을 획기적으로 가속화하는 vLLM의 설치부터 실전 서비스 구축까지 다룹니다. PagedAttention과 연속 배칭 기술로 GPU 메모리를 효율적으로 활용하는 방법을 배웁니다.
Web UI Demo 구축 완벽 가이드
Gradio를 활용하여 머신러닝 모델과 AI 서비스를 위한 웹 인터페이스를 구축하는 방법을 다룹니다. 코드 몇 줄만으로 전문적인 데모 페이지를 만들고 배포하는 과정을 초급자도 쉽게 따라할 수 있도록 설명합니다.
Sandboxing & Execution Control 완벽 가이드
AI 에이전트가 코드를 실행할 때 반드시 필요한 보안 기술인 샌드박싱과 실행 제어에 대해 알아봅니다. 격리된 환경에서 안전하게 코드를 실행하고, 악성 동작을 탐지하는 방법을 단계별로 설명합니다.
Voice Design then Clone 워크플로우 완벽 가이드
AI 음성 합성에서 일관된 캐릭터 음성을 만드는 Voice Design then Clone 워크플로우를 설명합니다. 참조 음성 생성부터 재사용 가능한 캐릭터 구축까지 실무 활용법을 다룹니다.
Tool Use 완벽 가이드 - Shell, Browser, DB 실전 활용
AI 에이전트가 외부 도구를 활용하여 셸 명령어 실행, 브라우저 자동화, 데이터베이스 접근 등을 수행하는 방법을 배웁니다. 실무에서 바로 적용할 수 있는 패턴과 베스트 프랙티스를 담았습니다.