페이지를 따로 그려도 덱의 디자인은 함께 정해야 했다

슬라이드 이미지를 모델에 직접 요청하면 정해 둔 HTML 레이아웃 밖의 표현을 만들 수 있다. 대신 페이지마다 다른 배치와 시각 요소가 나오므로, 한 강의의 공통 디자인을 유지할 입력이 필요해진다. 같은 색상을 적는 것과 각 페이지에서 그 색상을 어떻게 사용할지 정하는 일도 다르다.

AI Commons는 Gemini 이미지 생성 서비스를 추가했다. 초기 서비스는 제목·본문·스타일을 프롬프트에 넣고 이미지를 요청했다. 이후 이미지 호출 앞에 시각 지침을 만들고 저장하는 단계를 더했다. 페이지의 표현을 이미지 바이트와 분리해 다룰 수 있게 한 변경이다.

페이지별 visualInstruction을 생성 흐름에 연결하고 페이지와 이미지 버전에 저장했다. 이후 덱 공통 지침을 추가했고, 이미지 생성기의 입력을 공통 지침·페이지 지침·사용자 디자인 지시로 정리했다.

전체의 시각 규칙과 페이지의 설명을 다른 값으로 남긴다

공통 지침은 덱에 적용할 디자인 방향을 담고, 페이지 지침은 해당 페이지의 내용을 시각적으로 표현하는 방법을 담는다. 둘을 나누면 페이지마다 같은 디자인 설명을 독립적으로 만들게 하는 대신 공통 입력을 공유할 수 있다. 페이지의 표현 지침도 저장된 값으로 검토하고 수정할 수 있다.

입력과 저장 단위
구분역할보관 범위
공통 디자인 지침덱의 색상·타이포그래피·시각 규칙슬라이드 덱
페이지별 시각 지침한 페이지의 배치와 내용 표현페이지와 이미지 버전
사용자 디자인 지시이번 이미지 요청에 추가할 지시이미지 생성기의 입력

초기 구현은 공통 지침을 만든 뒤 페이지마다 별도 호출을 이어 가는 구조가 아니었다. 공통 지침과 페이지 지침 배열을 한 번의 OpenAI 구조화 응답으로 받았다. 디자인과 페이지 표현, 이미지 생성이라는 역할을 나눴다는 설명을 세 번의 순차 모델 호출로 읽어서는 안 된다.

응답은 요청한 페이지와 다시 대응시켜야 한다. 당시 생성기는 기대한 index 집합과 결과의 index 집합을 비교해 누락되거나 예상 밖인 페이지를 확인했다. 형식에 맞는 배열이 돌아와도 다른 페이지에 지침을 연결하면 잘못된 이미지를 만들 수 있기 때문이다.

이 검사는 페이지 대응을 확인한다. 공통 지침과 페이지 지침의 내용이 서로 모순되는지, 생성될 글자가 읽기 좋은지까지 평가하지는 않는다. 구조화 응답은 이미지 품질을 확정하는 단계가 아니다.

이미지 생성기에서 내용을 다시 설계하지 않는다

변경은 별도 이미지 프롬프트 템플릿을 걷어내고 세 입력 문자열을 이어 Gemini에 전달하도록 했다. 앞에서 만든 지침을 이미지 생성기가 다른 긴 템플릿으로 다시 해석하는 구간을 줄인 것이다. 핵심 조립은 다음과 같다.

const promptParts = [
  options.commonsVisualInstruction,
  options.visualInstruction,
  options.userDesignInstructions,
].filter((part): part is string =>
  !!part && part.trim().length > 0
);

if (promptParts.length === 0) {
  throw new Error("SLIDE_IMAGE_PROMPT_EMPTY");
}
const prompt = promptParts.join("\n\n");

공백뿐인 입력은 제외하고, 남은 원래 문자열 사이에 빈 줄을 넣는다. 공통 지침·페이지 지침·사용자 지시가 모두 존재해야만 통과하는 조건은 아니다. 하나라도 비어 있지 않으면 호출할 수 있다. 따라서 이 방어가 모든 요청에 공통 디자인과 페이지 내용이 빠짐없이 들어갔음을 보장하지는 않는다.

  1. 지침 생성

    공통 디자인과 페이지별 시각 표현을 구조화된 응답으로 받는다.

  2. 페이지 대응과 저장

    요청한 index와 대조하고 덱·페이지에 각각 지침을 보관한다.

  3. 이미지 요청

    해당 페이지의 지침과 공통 지침, 사용자 지시를 이어 Gemini로 보낸다.

  4. 이미지 응답 확인

    이미지가 없으면 오류로 처리하고, 있으면 첫 이미지와 MIME 타입을 반환한다.

모델이 텍스트만 돌려줄 수 있기 때문에 정상 API 응답과 이미지 생성 성공을 같게 취급하지 않았다. 이미지 배열이 비면 SLIDE_IMAGE_GENERATION_NO_IMAGE를 던진다. 이 조건은 산출물의 존재를 확인하며 글자 정확도나 색상 일관성을 검사하지는 않는다.

문자열을 나누고 순서를 정해 보내도 모델이 지침의 우선순위를 반드시 지키는 것은 아니다. 공통 지침과 사용자 지시가 충돌하거나 페이지 지침이 잘못 만들어지면 최종 이미지도 영향을 받는다. 입력의 책임을 명확히 하는 것과 결과를 결정적으로 만드는 것은 다른 문제다.

저장한 지침에는 다시 만들 조건도 필요하다

중간 지침을 저장하면 이미지 요청 전에 내용을 살펴볼 수 있지만, 디자인이 바뀐 뒤에도 예전 공통 지침이 남을 수 있다. 후속 변경은 공통 디자인 변경을 표시하는 dirty 필드와 공통 지침 재생성 경로를 추가했다. 저장된 지침을 재사용하는 편의와 변경된 입력을 반영할 책임이 함께 생긴다.

후속 구현에서는 공통 지침의 최초 생성을 독립 기능과 템플릿으로 분리하고, 페이지 지침을 각각의 요청으로 병렬 생성하도록 바꿨다. 생성기와 개요 워커의 테스트도 이 변경에 추가됐다. 초기 구현의 한 구조화 요청과 후속 구현의 분리된 요청 흐름은 같은 호출 구성으로 설명할 수 없다.

지침 생성이 늘면 모델 호출을 기다리고 중간 값을 저장하는 비용이 추가된다. 그 지침 자체도 모델 출력이어서 부적절한 표현을 한 단계 더 만들어 낼 수 있다. 저장과 페이지 대응을 코드로 확인할 수 있어도, HTML 템플릿 방식보다 시각 품질이 얼마나 좋아졌는지나 유지 시간이 얼마나 줄었는지는 이 변경의 비교 자료로 확인되지 않았다.

이후 최종 전송 프롬프트를 추적하는 계약과 원문 보존과 가독성의 비교는 다른 문제를 다뤘다. 선택에서 확인할 수 있는 변화는 AI Commons가 덱의 디자인과 페이지의 표현을 저장 가능한 중간 입력으로 나누고, 최종 이미지 호출에 그 값을 연결했다는 점이다.