🇺🇸 English version

2편에서는 TBEG(티백)의 기본 사용법을 살펴봤습니다. Excel 템플릿을 만들고, 데이터를 바인딩해서 보고서를 생성하는 전체 흐름을 익혔죠.

이번 편에서는 TBEG 템플릿 문법 전체를 체계적으로 정리합니다. ${...} 마커 하나하나가 어떤 역할을 하는지, 어떻게 조합하면 복잡한 보고서도 깔끔하게 만들 수 있는지 - 레퍼런스 수준으로 다뤄봅니다.


마커의 두 가지 형태

TBEG 템플릿에서 동적으로 처리되는 요소는 모두 마커(Marker)라고 부릅니다. 마커는 크게 두 가지 형태입니다.

변수형 마커

${변수명}
${emp.name}
${emp.department.name}

데이터에서 값을 꺼내 셀에 채워 넣습니다. 점(.) 표기법으로 중첩 접근도 가능합니다.

함수형 마커

${repeat(employees, A3:C3, emp)}
${image(logo, B2, 200:150)}
${merge(emp.dept)}
${bundle(A5:H12)}
${size(employees)}

특별한 동작을 수행하는 함수 형태입니다. repeat, image, merge, bundle, size가 여기에 해당합니다.

수식 형태 (대안 표기)

함수형 마커는 Excel 수식처럼 =TBEG_함수명(...) 형태로도 쓸 수 있습니다.

텍스트 형태수식 형태
${repeat(col, range, var)}=TBEG_REPEAT(col, range, var)
${image(name)}=TBEG_IMAGE(name)
${merge(item.field)}=TBEG_MERGE(item.field)
${bundle(범위)}=TBEG_BUNDLE(범위)

수식 형태로 작성하면 Excel의 셀 참조 기능(클릭, 드래그)을 활용해서 범위를 지정할 수 있어 편리합니다. 두 표기는 완전히 동일하게 동작합니다.

대소문자 규칙: 함수명과 키워드(DOWN, RIGHT, fit 등)는 대소문자를 구분하지 않습니다. 단, 데이터 키(emp.name 등)는 대소문자를 구분합니다.


변수 치환

마커: ${변수명}

단순 변수

템플릿결과
${title}월간 보고서
${date}2026-01-15
${author}황용호

셀에 ${title}이라고 써두면, 렌더링 시 해당 키의 값으로 교체됩니다.

점 표기법

data class, Map, getter - 어떤 형태든 점 표기법으로 중첩 접근이 가능합니다.

${emp.name}              // data class 필드
${emp.department.name}   // 중첩 data class
data class Department(val name: String, val code: String)
data class Employee(val name: String, val department: Department)

val data = mapOf(
    "emp" to Employee("황용호", Department("개발팀", "DEV"))
)
// ${emp.department.name} -> "개발팀"

복합 텍스트

한 셀에 여러 마커를 섞어 쓸 수 있습니다.

셀 내용결과
작성자: ${author} (${department})작성자: 황용호 (개발팀)
${year}년 ${month}월 보고서2026년 3월 보고서

수식 바인딩

바인딩하는 값이 =으로 시작하면 텍스트가 아닌 Excel 수식으로 처리됩니다. 데이터에 따라 수식을 동적으로 결정할 수 있는 강력한 기능입니다.

val data = mapOf("formula" to "=SUM(A1:A10)")
// 템플릿 셀: ${formula}
// 결과: =SUM(A1:A10) (실제 수식)

repeat 내에서의 자동 조정

repeat 안의 수식 바인딩은 행 시프트와 범위 확장이 자동 적용됩니다.

템플릿

ABCD
1${repeat(sales, A3:D3, s)}
2이름매출목표달성률
3${s.name}${s.amount}${s.target}${s.rateFormula}
4합계${totalRevenue}

데이터

mapOf(
    "sales" to listOf(
        mapOf("name" to "A팀", "amount" to 15000, "target" to 20000, "rateFormula" to "=B3/C3"),
        mapOf("name" to "B팀", "amount" to 22000, "target" to 18000, "rateFormula" to "=B3/C3"),
    ),
    "totalRevenue" to "=SUM(B3:B3)"
)

결과 (2개 아이템)

ABCD
1
2이름매출목표달성률
3A팀15,00020,000=B3/C3
4B팀22,00018,000=B4/C4
5합계=SUM(B3:B4)
  • rateFormula(=B3/C3)가 각 행에서 =B3/C3, =B4/C4으로 행 시프트
  • totalRevenue(=SUM(B3:B3))가 =SUM(B3:B4)으로 범위 확장

주의: =으로 시작하는 모든 문자열이 수식으로 처리됩니다. "=A급 인재" 같은 일반 텍스트는 앞에 공백을 추가하세요: " =A급 인재"


반복 처리

마커: ${repeat(컬렉션, 범위, 변수, 방향, 대체범위)}

리스트 데이터를 행 또는 열 방향으로 반복 확장합니다. TBEG에서 가장 핵심적인 문법입니다.

기본 반복 (DOWN)

템플릿

ABC
1${repeat(employees, A3:C3, emp)}
2이름직급연봉
3${emp.name}${emp.position}${emp.salary}

데이터

mapOf(
    "employees" to listOf(
        Employee("황용호", "부장", 8000),
        Employee("한용호", "과장", 6500),
        Employee("홍용호", "대리", 4500)
    )
)

결과

ABC
1
2이름직급연봉
3황용호부장8,000
4한용호과장6,500
5홍용호대리4,500

RIGHT 방향 반복

4번째 파라미터에 RIGHT를 지정하면 열 방향으로 반복합니다.

템플릿

AB
1${repeat(months, B1:B2, m, RIGHT)}${m.month}
2${m.sales}

결과

ABCD
11월2월3월
2100150200

다중 행 반복

범위에 여러 행을 지정하면 하나의 반복 단위가 여러 행이 됩니다.

템플릿

AB
1${repeat(employees, A2:B3, emp)}
2이름: ${emp.name}직급: ${emp.position}
3연봉: ${emp.salary}

A2:B3 (2행)이 하나의 반복 단위 - 직원 한 명당 2행씩 렌더링됩니다.

결과 (2명)

AB
1
2이름: 황용호직급: 부장
3연봉: 8,000
4이름: 한용호직급: 과장
5연봉: 6,500

빈 컬렉션 처리

5번째 파라미터로 대체 범위를 지정하면, 컬렉션이 비어있을 때 해당 범위의 내용이 대신 표시됩니다.

템플릿

ABC
1${repeat(employees, A3:C3, emp, DOWN, A10)}
2이름직급연봉
3${emp.name}${emp.position}${emp.salary}
...
10(데이터 없음)

대체범위를 단일 셀(A10)로 지정하면, 반복 영역(A3:C3) 전체가 자동 병합됩니다.

결과 (빈 컬렉션)

  • A10의 내용과 스타일이 A3 위치에 복사되고, A3:C3이 하나의 셀로 병합됩니다
  • 대체범위가 여러 셀(예: A10:C10)이면 병합 없이 각 셀에 개별 복사됩니다

이미지 삽입

마커: ${image(이름, 위치, 크기)}

마커가 있는 셀(또는 병합 영역)에 이미지를 삽입합니다. 데이터에서 바이트 배열 또는 URL로 제공합니다.

val provider = simpleDataProvider {
    image("logo", logoBytes)                             // ByteArray
    imageUrl("banner", "https://example.com/banner.png") // URL (자동 다운로드)
}

위치와 크기 지정

${image(logo, B2)}            // B2 셀에 삽입
${image(logo, B2, 200:150)}   // 200×150 픽셀
${image(logo, B2, fit)}       // 셀 크기에 맞춤 (기본값)
${image(logo, B2, original)}  // 원본 크기 유지
크기 표기설명
fit 또는 0:0셀/범위에 맞춤 (기본값)
original 또는 -1:-1원본 크기
200:150너비 200px, 높이 150px
200:-1너비 200px, 높이 비율 유지
-1:150높이 150px, 너비 비율 유지

URL 이미지

URL로 지정한 이미지는 Excel 생성 시점에 자동 다운로드하여 파일에 임베딩합니다. 생성된 Excel 파일을 열 때는 네트워크가 필요하지 않습니다. 같은 URL은 한 번만 다운로드되며, 다운로드 실패 시 해당 이미지를 건너뛰고 Excel 생성은 정상 완료됩니다.


자동 셀 병합

마커: ${merge(item.field)}

repeat 확장 시 연속된 같은 값의 셀을 자동으로 병합합니다. 부서별 그룹핑 표를 만들 때 유용합니다.

예시

템플릿

ABCD
1부서이름직급${repeat(employees, A2:C2, emp)}
2${merge(emp.dept)}${emp.name}${emp.rank}

데이터 - 부서별로 정렬된 직원 목록

mapOf(
    "employees" to listOf(
        mapOf("dept" to "영업부", "name" to "황용호", "rank" to "사원"),
        mapOf("dept" to "영업부", "name" to "한용호", "rank" to "대리"),
        mapOf("dept" to "개발부", "name" to "홍용호", "rank" to "과장"),
    )
)

결과 - "영업부"가 2행에 걸쳐 자동 병합됨

A열의 "영업부"가 연속 2행이므로 자동으로 세로 병합됩니다. 다중 컬럼 병합(부서+팀 등)도 각 컬럼에 merge 마커를 개별 지정하면 됩니다.

중요: 병합은 연속된 같은 값만 처리합니다. 의도한 대로 병합되려면 데이터를 미리 정렬해서 전달해야 합니다.


요소 묶음

마커: ${bundle(범위)}

지정된 범위 내의 모든 요소를 하나의 묶음으로 취급하여, repeat 확장 시 묶음 전체가 일체로 이동합니다.

왜 필요한가?

repeat으로 행이 추가될 때, 반복 범위에 속한 열만 아래로 밀리고 범위 밖의 열은 원래 행에 남습니다. 이러면 같은 행에 서로 다른 내용이 섞여 표가 깨집니다.

예시

아래와 같이 A~B열에 부서 repeat이 있고, 그 아래(4~6행)에 A~E열에 걸친 상세 테이블이 있는 상황을 생각해봅시다.

템플릿

ABCDE
1${repeat(depts, A2:B2, dept)}
2${dept.name}${dept.revenue}
3${bundle(A4:E6)}
4이름매출원가이익합계
5황용호10005005002000
6합계=SUM()

bundle 없이 - 반복 열(A~B)만 밀리고, C~E열의 표는 제자리:

ABCDE
2영업부52000
3개발부38000
4기획부28000원가이익합계
55005002000
6이름매출=SUM()
7황용호1000
8합계

A~B열의 테이블(이름, 매출, ...)은 반복에 의해 6행으로 밀렸지만, C~E열의 테이블(원가, 이익, 합계)은 원래 4행에 그대로 남아 있습니다. 같은 테이블의 열들이 서로 다른 행에 흩어져 표가 완전히 깨집니다.

bundle(A4:E6) 적용 - A~E열 전체가 일체로 이동:

ABCDE
2영업부52000
3개발부38000
4기획부28000
5
6이름매출원가이익합계
7황용호10005005002000
8합계=SUM()

모든 열이 같은 행에서 시작하므로 테이블이 정상 유지됩니다.


기타 유용한 문법

컬렉션 크기

마커: ${size(컬렉션)}

총 직원 수: ${size(employees)}명  ->  총 직원 수: 5명

수식 내 변수

Excel 수식 안에서도 TBEG 변수를 사용할 수 있습니다.

=HYPERLINK("${linkUrl}", "${linkText}")
=SUM(B${startRow}:B${endRow})

자동으로 조정되는 것들

repeat으로 행이 추가되면 TBEG이 자동으로 처리해주는 항목들입니다.

항목처리 내용
수식 참조=SUM(B2:B2)=SUM(B2:B4) (범위 확장)
차트데이터 소스 범위 자동 확장
피벗 테이블소스 데이터 범위 자동 확장
병합 셀반복 영역 외부의 병합 셀 위치 조정
조건부 서식적용 범위 자동 확장

절대 참조로 확장 제어

자동 확장을 원하지 않는 참조는 $ 절대 참조를 사용하세요.

=SUM(B3:B3)      → =SUM(B3:B5)     // 상대 참조: 확장됨
=SUM($B$3:$B$3)  → =SUM($B$3:$B$3) // 절대 참조: 그대로 유지
=SUM($B3:$B3)    → =SUM($B3:$B5)   // 열만 절대: 행 방향 확장됨

선택적 필드 노출 (hideable)

마커: ${hideable(value=item.필드, ...)}

상황에 따라 특정 필드(열)를 숨기거나 비활성화할 수 있습니다. 예를 들어 같은 템플릿으로 "급여 포함 보고서"와 "급여 제외 보고서"를 모두 생성할 수 있습니다.

기본 사용법

템플릿에서 숨길 수 있는 필드에 hideable 마커를 배치합니다:

${hideable(value=emp.salary)}

코드에서 숨길 필드를 지정합니다:

val provider = simpleDataProvider {
    items("employees", employeeList)
    hideFields("employees", "salary")  // salary 필드 숨김
}

hideFields를 지정하지 않으면 일반 필드처럼 값이 출력됩니다.

HideMode

모드동작예시
DELETE (기본)열/행을 물리적으로 삭제하고 나머지를 시프트급여 열 자체가 사라짐
DIM값을 제거하고 비활성화 스타일 적용 (열/행 유지)급여 열은 있지만 빈 칸 + 회색 배경
${hideable(value=emp.salary, mode=dim)}

bundle 범위 지정

헤더와 데이터 셀을 함께 숨기려면 bundle 파라미터로 범위를 지정합니다:

${hideable(value=emp.salary, bundle=C1:C3)}

C1(헤더)부터 C3(데이터)까지 한 번에 숨깁니다.

수식 마커

=TBEG_HIDEABLE(value=emp.salary, bundle=C1:C3, mode=DELETE)

주의: hideable 마커는 repeat 영역 내의 아이템 필드에만 사용할 수 있습니다. 단순 변수에는 사용할 수 없습니다.


실전 팁

마커 배치: 함수형 마커는 대상 범위 밖이면 어디에든 배치할 수 있습니다. 별도 시트에 모아두면 템플릿 가독성이 높아집니다.

명시적 파라미터: 긴 파라미터는 이름을 명시하면 가독성이 좋아집니다.

${repeat(collection=employees, range=A3:C3, var=emp, direction=DOWN)}

범위 경계 걸침 금지: repeat, bundle, merge 등의 범위는 서로 부분적으로 겹칠 수 없습니다. 각 요소는 다른 요소 안에 완전히 포함되거나 완전히 밖에 있어야 합니다.


마무리

이번 편에서는 TBEG 템플릿 문법 전체를 훑어봤습니다. 정리하면:

마커용도
${변수명}단순 치환, 점 표기법, 수식 바인딩
${repeat(...)}리스트를 행/열로 확장, 빈 컬렉션 처리
${image(...)}이미지 삽입, URL 자동 다운로드
${merge(...)}같은 값 자동 병합
${bundle(...)}요소 일체 이동
${size(...)}컬렉션 아이템 수
${hideable(...)}선택적 필드 노출 (DELETE/DIM)

각 문법의 전체 파라미터와 엣지 케이스는 github의 템플릿 문법 레퍼런스를 참고하세요.

다음 편(4편)에서는 Spring Boot와의 연동을 다룹니다. Auto-configuration으로 간편하게 설정하고, @RestController에서 Excel 파일을 바로 응답하는 방법까지 살펴볼 예정입니다.

0개의 댓글