AI는 같은 얼굴을 두 번 그리지 못한다 — 캐릭터 일관성을 위해 남긴 사람의 자리
2편에서는 제작의 중심을 Claude Code와 스크립트로 옮긴 이유를 다뤘다. 이번에는 그 과정에서 부딪힌 캐릭터 일관성 문제와 사람의 역할을 살펴본다. 제목은 당시 프롬프트만으로 인물을 반복 생성하며 겪은 어려움을 표현한 것이다. 모든 이미지 모델이 같은 얼굴을 재현할 수 없다는 뜻은 아니다.
문제 — 컷마다 달라지는 캐릭터의 얼굴
나는 하나의 텍스트 원천을 장 단위로 나누어 영상 시리즈를 만들었다. 대본 분할, 음성 생성, 이미지 생성, 자막 배치와 업로드까지 여러 단계를 도구와 스크립트로 연결했다. 반복 작업을 줄여도 생성 결과가 이야기와 맞는지 확인하는 일은 남았다.
특히 어려웠던 것은 캐릭터 일관성이었다. 같은 인물이 여러 장에 걸쳐 등장하는데, 동일한 외모 묘사를 넣어도 다른 얼굴처럼 보이는 컷이 나왔다. 인물의 외형이 크게 바뀌면 같은 이야기의 등장인물임을 전달하기 어려워진다. 그래서 얼굴과 복장의 기준을 정해 두고 재사용할 방법이 필요했다.
대본이나 음성도 오류 없이 생성되는 것은 아니다. 이 프로젝트에서는 이미지의 인물 외형이 특히 두드러진 문제였고, 편집 단계에서도 자막과 음성의 타이밍을 확인해야 했다. 텍스트·음성은 안전하게 자동화할 수 있고 이미지만 불확실하다는 구분으로는 이 작업을 설명할 수 없다.
접근 — 결과를 확인하며 다음 단계로 넘어간다
나는 레퍼런스를 활용하되 중요한 선택과 결과 확인에 사람이 참여하는 방식을 택했다. 이것이 HITL(Human-in-the-loop), 즉 자동화 과정에 사람의 판단을 포함하는 설계다. 어떤 인물의 외형을 고정할지, 생성된 컷을 채택할지, 최종 영상의 자막과 전환이 적절한지는 내가 확인했다.
1편에서 직접 만들 범위를 줄였다면, 여기서는 도구에 맡길 작업과 확인할 결과를 나눴다. 캐릭터 등록과 최종 편집이 이 글의 중심이지만 사람의 개입이 이 두 곳에만 있는 것은 아니다. 제작 스킬에는 생성된 대본과 장면 분할을 사용자에게 확인받는 단계도 있다.
구현 — 레퍼런스는 재사용하고, 등록 여부는 사람이 정한다
아래는 이미지 기반 영상 제작의 주요 흐름이다. 장 선택과 대본 분할은 한 상자로 묶었으며, 등록 분기는 별도 도표로 풀었다. DB 저장 같은 보조 단계는 생략했다.
파란색은 이 글에서 자세히 다루는 캐릭터 확인과 최종 편집이다. 대본 확인에도 사람이 참여한다. 캐릭터 외형을 맞추는 구현은 다음 세 부분으로 나뉜다.
첫째, 캐릭터 레지스트리. 채택한 얼굴 이미지를 파일로 저장하고 이름과 파일명을 JSON으로 연결한다. 다음은 인물 이름을 일반화한 예시다.
{
"주인공": "protagonist.png",
"조력자": "mentor.png"
}

등록한 레퍼런스 이미지. 이 인물이 등장하는 컷에 전달할 외형의 기준이다.
둘째, 레퍼런스 전달. 스크립트는 등록된 파일을 업로드해 URL을 얻고, 각 컷의 캐릭터 목록과 이름이 일치하는 레퍼런스를 생성 요청에 넣는다. 아래는 이 부분을 단순화한 예시다. char_refs는 인물 이름과 업로드된 URL의 매핑이며, cut_characters는 현재 컷의 인물 목록이다.
elements = []
for name in cut_characters:
ref_url = char_refs.get(name)
if ref_url:
elements.append({
"frontal_image_url": ref_url,
"reference_image_urls": [ref_url],
})
image = client.generate_image(prompt, elements=elements or None)
프롬프트는 외모와 장면을 설명하고, 레퍼런스는 재사용할 외형을 이미지로 제공한다. 이 구현이 사용하는 API의 입력 명세에도 정면 이미지와 참조 이미지를 담는 elements가 있다. 다만 입력으로 전달했다는 사실이 결과의 동일성까지 보장하지는 않는다. 모델이 기준 이미지를 얼마나 반영했는지는 생성된 컷에서 확인해야 한다.
레퍼런스의 첫 이미지는 범용 이미지 생성 모델로 만들었다. 운영 중 그 모델을 바꿔도 파일을 등록하고 생성 요청에 전달하는 흐름은 유지했다. 교체할 수 있었던 것은 레퍼런스를 만드는 도구이며, 어떤 이미지든 같은 품질을 낸다는 뜻은 아니다.
셋째, 배경 컷의 인물 억제. 현재 컷의 캐릭터 목록이 비어 있으면 인물 관련 표현을 네거티브 프롬프트, 즉 생성에서 피할 내용을 나타내는 입력에 넣는다.
has_characters = bool(cut_characters)
negative = "" if has_characters else "people, characters, human, face, portrait"
실제 호출에서는 이 값을 negative_prompt 인자로 전달한다. 인물 출현을 줄이려는 지시이며, 인물이 없는 결과를 보장하는 검사는 아니다. 이 분기는 캐릭터 목록에 의존하므로 목록이 빠졌거나 잘못 작성됐다면 의도와 다른 요청이 만들어질 수 있다.
자동 처리 앞뒤에는 두 가지 목적의 수동 작업을 남겼다.
(A) 외형을 정하는 판단 — 캐릭터 등록. 제작 스킬은 등장인물과 등록 상태를 보여 주고 레퍼런스를 추가할지 묻는다. 반복 등장하는 인물은 외형을 고정할 필요가 큰 반면, 일회성 인물은 프롬프트만으로 진행할 수도 있다. 추가한다면 이미지 파일과 이름 매핑을 등록한 뒤 다음 단계로 넘어간다.
여기서 확인 절차의 구현 위치가 중요하다. “사용자 응답 없이 다음 단계로 넘어가지 않는다”는 규칙은 Claude Code가 읽는 제작 스킬의 지침이다. 이미지 생성 스크립트 자체가 승인 여부를 검사하는 것은 아니다. 스크립트를 직접 실행하면 미등록 캐릭터를 경고한 뒤 레퍼런스 없이도 생성하므로, 이를 코드가 강제하는 차단 장치로 볼 수는 없다.
(B) 결과의 품질을 맞추는 작업 — 최종 편집. 처음에는 FFmpeg로 영상 편집까지 자동화했으나 자막과 음성의 타이밍, 장면 전환이 내가 원하는 수준에 미치지 못했다. 이후에는 스크립트가 조립한 편집 프로젝트를 CapCut에서 열고, 필요한 부분을 조정한 뒤 내보냈다. 자동 조립은 유지하면서 최종 확인과 수정을 사람이 맡은 것이다.
등록 판단은 외형의 기준을 정하는 일이고 편집은 생성된 결과를 다듬는 일이다. 둘 다 사람의 판단이 들어가지만 목적과 입력이 다르다. 어느 쪽을 더 자동화할지는 도구의 성능과 검수 기준에 따라 다시 정할 수 있으며, 지금의 분담을 영구적인 경계로 볼 이유는 없다.
결과
레퍼런스를 적용한 뒤 서로 다른 영상에서 주인공의 외형을 비슷하게 유지한 컷을 얻었다. 아래 세 장은 그 예시다. 도입 전후의 전체 생성 결과나 실패 비율을 비교한 자료는 아니므로, 이 사례만으로 일관성 향상률이나 제작 속도 개선을 계산할 수는 없다.
서로 다른 세 편에서 고른 컷. 주인공의 머리 덮개, 긴 수염과 갈색 계열 옷은 유사하지만 얼굴과 복장의 세부 표현은 다르다.
레퍼런스를 넣어도 표정과 각도, 그림의 질감은 달라졌다. 여러 인물이 등장하는 컷에는 배경 인물이 주인공과 비슷해 보이는 부분도 있다. 내게 필요한 것은 픽셀 단위의 복제가 아니라 같은 인물로 이어 보이는 외형이었고, 레퍼런스와 결과 확인을 함께 사용했다.
학습
입력의 기준과 결과의 검수를 함께 둔다. 이름과 레퍼런스를 고정하면 매번 외형을 새로 설명하는 부담을 줄일 수 있다. 그 기준이 실제 컷에 반영됐는지는 별도로 확인해야 한다.
확인 지침과 실행 차단은 다르다. 이 작업의 캐릭터 확인은 제작 스킬에 둔 운영 규칙이다. 스크립트만 실행해도 승인을 반드시 거치게 하려면 승인 상태를 검사하는 처리가 추가로 필요하다.
사람을 남긴 이유에 따라 자동화 범위를 재검토한다. 캐릭터 등록에서는 외형의 기준을 정하고, 최종 편집에서는 타이밍과 전환을 다듬었다. 지금 사람이 맡는다는 이유만으로 영원히 수동일 필요는 없고, 자동화할 수 있다는 이유만으로 결과 확인을 생략할 수도 없다.