요소 그룹핑 (Element Grouping)

템플릿 요소들을 그룹으로 묶어 함께 배치하는 기능입니다. 같은 그룹의 요소들은 항상 같은 컬럼/페이지에 배치됩니다.

개요

요소 그룹핑은 템플릿의 여러 요소를 논리적으로 묶어서, 한 컬럼이나 페이지에 함께 배치되도록 하는 기능입니다.

특징설명
그룹 단위 배치같은 그룹의 요소들은 항상 같은 컬럼/페이지에 배치
자동 이동그룹 내 어떤 요소라도 배치 공간이 부족하면 그룹 전체를 다음 컬럼/페이지로 이동
텍스트 분할 지원splittable 텍스트는 분할 허용 (그룹 이동과 무관)
유효성 검증그룹 높이가 페이지 높이를 초과하면 템플릿 오류 반환

사용 방법

템플릿에 groupName 지정

json
{
  "elements": [
    {
      "element_id": "text-1",
      "type": "text",
      "position": {"x": 40, "y": 20},
      "width": 409,
      "height": 30,
      "text": "$dateStr$",
      "groupName": "group1"
    },
    {
      "element_id": "graphic-1",
      "type": "graphic",
      "imageSource": "pack://siteoforigin:,,,/ebook/images/bar.png",
      "position": {"x": 40, "y": 60},
      "width": 449,
      "height": 2,
      "groupName": "group1"
    },
    {
      "element_id": "text-2",
      "type": "text",
      "position": {"x": 40, "y": 87},
      "width": 409,
      "height": 150,
      "text": "$contents$",
      "isDynamic": true,
      "splittable": true,
      "groupName": "group1"
    },
    {
      "element_id": "photo-1",
      "type": "photo",
      "fileName": "$imageMain$",
      "position": {"x": 40, "y": 250},
      "width": 409,
      "height": 409,
      "groupName": "group2"
    }
  ]
}

그룹화 규칙

같은 groupName이라도 중간에 다른 그룹 요소가 있으면 별도 그룹으로 처리됩니다. 요소들은 Y 좌표 순으로 정렬되어 처리되므로, Y 좌표 순서를 올바르게 유지해야 합니다.
json
// 올바른 예시: group1 요소들이 연속됨 → 하나의 그룹
[
  {"element_id": "text-1",    "y": 20,  "groupName": "group1"},
  {"element_id": "graphic-1", "y": 60,  "groupName": "group1"},
  {"element_id": "text-2",    "y": 87,  "groupName": "group1"}
]

// 잘못된 예시: group1 사이에 group2가 있음 → 2개의 별도 그룹
[
  {"element_id": "text-1",    "y": 20,  "groupName": "group1"},  // group1-1
  {"element_id": "graphic-1", "y": 60,  "groupName": "group1"},  // group1-1
  {"element_id": "photo-1",   "y": 150, "groupName": "group2"},  // group2
  {"element_id": "text-2",    "y": 300, "groupName": "group1"}   // group1-2 (별도 그룹!)
]

배치 동작

케이스 1: 그룹 전체가 현재 컬럼에 배치 가능

그룹 전체 높이 < 남은 공간 → 모든 요소를 현재 컬럼에 배치합니다.

케이스 2: 그룹 전체가 현재 컬럼에 배치 불가능

그룹 전체 높이 > 남은 공간 → 그룹 전체를 다음 컬럼/페이지로 이동합니다.

케이스 3: splittable 텍스트 분할

그룹 내 splittable 텍스트가 있고 최소 한 줄은 배치 가능 → 텍스트를 분할하여 현재 컬럼과 다음 컬럼에 나누어 배치합니다. 그룹의 다른 요소는 현재 위치 유지.

케이스 4: splittable 텍스트 한 줄도 배치 불가능

그룹 내 splittable 텍스트가 있지만 한 줄도 배치할 공간 없음 → 그룹 전체를 다음 컬럼/페이지로 이동합니다.

오류 처리

그룹 높이가 페이지 높이 초과

조건: splittable 텍스트가 없는 그룹의 전체 높이가 페이지 높이를 초과할 때.

json
{
  "success": false,
  "message": "Template error: Group 'group1' height (1200.5px) exceeds page height (1000.8px). Cannot place group."
}

해결 방법: 그룹 내 요소 개수 줄이기, 요소 높이 줄이기, 텍스트 요소에 splittable: true 추가.

주의사항

배치 결과에서 제거되는 속성

groupName은 템플릿 작성 시에만 사용되며, 최종 페이지 JSON에는 포함되지 않습니다.splittable, isDynamic, originalHeight도 배치 결과에서 제거됩니다.

X-lane 분리 그룹화

layoutRules.lanes가 정의된 경우, 같은 Y 범위에 있더라도 서로 다른 X-lane에 속하는 요소들은 별도 그룹으로 처리됩니다. 각 레인의 요소들은 독립적인 Y 흐름으로 배치됩니다.

SubGroups (서브그룹)

groupName으로 묶인 그룹 내에 splittable: true 요소가 포함된 경우, non-splittable 요소들은 Y-overlap 기준으로 SubGroup으로 분리됩니다.

json
// groupName: "dayEntry" 그룹 내 요소들
[
  { "element_id": "teacher-icon",    "y": 63,  "h": 22 },  // SubGroup A
  { "element_id": "teacher-label",   "y": 63,  "h": 25 },  // SubGroup A (Y 겹침)
  { "element_id": "teacher-comment", "y": 90,  "isDynamic": true, "splittable": true }, // 분리
  { "element_id": "gallery-photos",  "y": 280, "isDynamic": true }  // SubGroup B
]
// SubGroup A: teacher-icon + teacher-label (Y 범위 겹침 → 함께 배치)
// 분리: teacher-comment (splittable → 개별 배치, 텍스트 분할 가능)
// SubGroup B: gallery-photos (독립 배치)

TemplateBaseY 앵커와 그룹 배치

text
최종 Y = max(anchoredY, sequentialY)
anchoredY  = TemplateBaseY + OriginalY + DynamicDelta
sequentialY = CurrentSlotMaxY + spacing

DynamicDelta에 대한 자세한 내용은 동적 레이아웃 시스템 문서를 참조하세요.

요약

항목설명
목적관련 요소들을 함께 배치
사용법템플릿 요소에 groupName 속성 추가
그룹화 규칙Y 좌표 순으로 연속된 같은 groupName 요소들
X-lane 분리lanes 정의 시 다른 레인의 요소는 별도 그룹
SubGroupssplittable 포함 그룹에서 non-splittable Y-overlap 서브그룹 분리
배치 원칙그룹 전체가 들어갈 수 없으면 전체 이동
텍스트 분할splittable 텍스트는 분할 허용 (단, 한 줄도 못 들어가면 그룹 전체 이동)
오류 조건splittable 없는 그룹의 높이 > 페이지 높이
배치 결과groupName, splittable, isDynamic, originalHeight 제거됨