Published on

SDXL ControlNet+IP-Adapter로 인물 고정하기

Authors

서로 다른 장면을 생성할 때 인물의 얼굴, 헤어스타일, 의상 디테일이 매번 바뀌는 문제는 SDXL에서 가장 흔한 고질병입니다. 프롬프트를 아무리 정교하게 써도 확률적으로 샘플링이 흔들리기 때문에, "텍스트"만으로 캐릭터를 고정하는 데는 한계가 있습니다.

이 글에서는 SDXL에서 ControlNet(포즈·구도·구조 고정)IP-Adapter(참조 이미지 기반 아이덴티티 고정) 를 함께 써서, 같은 인물을 다양한 배경과 연출로 안정적으로 재현하는 방법을 단계별로 정리합니다. 기준은 ComfyUI 중심으로 설명하지만, 개념은 A1111 계열에도 그대로 적용됩니다.

또한 파이프라인을 운영환경에 붙일 때 자주 겪는 병목(여러 요청을 큐잉하고 캐시하는 방식 등)은 웹앱 최적화와도 결이 비슷합니다. 예를 들어 서버 렌더링에서 불필요한 요청 폭포를 끊는 캐시 전략은 이미지 생성 워크로드에도 유효합니다. 필요하면 Next.js 14 RSC fetch waterfall 끊는 캐시·prefetch 최적화 도 함께 참고해보세요.

왜 ControlNet만으로는 인물이 안 고정될까

ControlNet(OpenPose, Depth, Canny 등)은 기본적으로 구조적 조건(뼈대·윤곽·깊이·에지) 을 강하게 주입합니다. 그래서 다음은 잘 고정됩니다.

  • 포즈(팔·다리 각도)
  • 카메라 구도(정면/측면, 전신/반신)
  • 실루엣, 배경의 큰 형태

하지만 ControlNet은 "이 사람이 누구인지" 를 직접 고정하지 않습니다. 같은 포즈를 주더라도 얼굴형, 눈매, 코, 입술, 피부 질감은 샘플링 과정에서 쉽게 바뀝니다. 즉, ControlNet은 "뼈대", IP-Adapter는 "정체성" 쪽을 담당한다고 이해하면 조합 이유가 명확해집니다.

IP-Adapter가 해결하는 것: 참조 이미지 기반 아이덴티티 주입

IP-Adapter는 CLIP 계열 임베딩을 이용해 참조 이미지의 특징을 diffusion 과정에 주입합니다. LoRA처럼 학습이 필요하지 않고, 한 장 또는 소수의 레퍼런스만으로도 다음을 안정화할 수 있습니다.

  • 얼굴 아이덴티티(눈·코·입 비율, 인상)
  • 헤어스타일/색
  • 의상 스타일의 큰 특징(완전한 복제는 아님)

다만 IP-Adapter 단독으로는 포즈나 구도가 흔들릴 수 있어, ControlNet과 결합했을 때 가장 강력합니다.

권장 조합: SDXL + ControlNet(OpenPose) + IP-Adapter Face

실전에서 가장 많이 쓰는 조합은 아래입니다.

  • 베이스 모델: SDXL 1.0 base + (필요시) refiner
  • ControlNet: OpenPose(포즈 고정) 또는 Depth(구도/입체감)
  • IP-Adapter: Face 계열(얼굴 고정) 또는 Plus 계열(스타일+인물 균형)

"인물 고정"이 목표라면 우선순위는 다음이 안전합니다.

  1. IP-Adapter Face로 아이덴티티 먼저 고정
  2. OpenPose로 포즈를 고정
  3. 필요하면 Depth나 Canny를 추가해 구도/배경 구조를 보조

ComfyUI 워크플로: 노드 구성 개요

ComfyUI 기준으로 최소 구성은 아래 흐름입니다.

  • Load Checkpoint(SDXL)
  • Positive/Negative Prompt
  • IP-Adapter Loader + Apply
  • ControlNet Loader(OpenPose) + Apply
  • KSampler
  • VAE Decode

여기서 중요한 포인트는 적용 순서가 아니라 가중치 밸런스입니다. IP-Adapter가 너무 강하면 배경/조명이 레퍼런스에 끌려가고, ControlNet이 너무 강하면 얼굴이 딱딱해지거나 "가면"처럼 보일 수 있습니다.

세팅 가이드: 가중치와 파라미터(실전 범위)

아래 값은 "잘 되는 구간"을 빠르게 잡기 위한 범위입니다. 모델/레퍼런스에 따라 미세 조정이 필요합니다.

IP-Adapter weight

  • 권장 시작: 0.6
  • 얼굴이 자꾸 바뀜: 0.7 ~ 0.9
  • 레퍼런스 스타일(조명/배경)까지 끌려옴: 0.4 ~ 0.6

ControlNet strength(OpenPose)

  • 권장 시작: 0.7
  • 포즈가 자꾸 무너짐: 0.8 ~ 1.0
  • 결과가 경직/부자연: 0.5 ~ 0.7

Denoise(이미지 투 이미지 또는 리파인)

  • 고정된 인물에 장면만 바꾸고 싶다면 denoise를 낮추는 전략이 유효합니다.
  • 예: 0.35 ~ 0.55 구간에서 "아이덴티티 유지"가 쉬운 편

CFG

  • SDXL에서 CFG를 과하게 올리면 얼굴 디테일이 깨지거나 과장될 수 있습니다.
  • 권장 시작: 5.0 ~ 7.0

프롬프트 전략: "고정"은 짧게, "연출"은 길게

인물을 고정하려고 프롬프트에 얼굴 특징을 과도하게 박아 넣으면 오히려 흔들립니다. 이유는 텍스트 조건이 IP-Adapter 조건과 충돌하기 때문입니다.

권장 전략은 다음과 같습니다.

  • Positive는 연출 중심: 장소, 조명, 렌즈, 분위기
  • 인물 특징은 최소한의 키워드만: same person, consistent face, 헤어색 정도
  • Negative로 붕괴 방지: deformed, bad eyes, asymmetry, extra fingers

예시(인물은 IP-Adapter가 책임지고, 프롬프트는 연출에 집중):

Positive:
cinematic portrait photo, 85mm lens, soft rim light, night street background, shallow depth of field, high detail skin

Negative:
deformed, bad anatomy, asymmetrical eyes, crossed eyes, extra fingers, lowres, blurry

레퍼런스 이미지 준비 팁(성공률을 크게 좌우)

IP-Adapter는 레퍼런스 품질에 민감합니다. 아래 조건을 만족하면 일관성이 확 올라갑니다.

  • 얼굴이 충분히 큼: 최소 반신, 가능하면 클로즈업 1장 포함
  • 조명이 과하게 깨지지 않음: 하이라이트가 날아간 사진은 피함
  • 각도 다양화: 정면 1장 + 3/4 각도 1장 조합이 안정적
  • 가림 최소화: 선글라스, 마스크는 가능하면 피함

"한 장"로도 되지만, 가능하면 2장 레퍼런스를 쓰는 편이 훨씬 튼튼합니다.

ControlNet 입력 만들기: OpenPose 추출과 정리

OpenPose는 포즈를 고정하는 데 효과적이지만, 포즈 추출이 지저분하면 결과도 지저분해집니다.

  • 손가락 포즈가 중요하면 hand keypoint가 잘 잡힌 입력을 사용
  • 포즈가 단순할수록(전신 정면, 팔 내린 자세) 일관성이 좋음
  • 과장된 포즈는 얼굴이 무너질 확률이 올라가므로 IP-Adapter weight를 조금 올리거나, denoise를 낮춰 대응

흔한 실패 패턴과 해결 체크리스트

1) 얼굴은 비슷한데 "다른 사람"처럼 느껴짐

원인:

  • IP-Adapter weight가 약함
  • 레퍼런스가 너무 멀거나 얼굴이 작음

해결:

  • IP-Adapter weight를 +0.1씩 올리기
  • 얼굴 클로즈업 레퍼런스 추가
  • 프롬프트에서 얼굴 특징을 과하게 강제하지 않기

2) 포즈는 맞는데 얼굴이 뭉개짐(가면 느낌)

원인:

  • ControlNet strength가 과함
  • CFG가 과함

해결:

  • ControlNet strength를 0.1 낮추기
  • CFG를 1 정도 낮추기
  • 샘플러를 바꾸거나 steps를 소폭 증가

3) 레퍼런스의 조명/배경이 계속 따라옴

원인:

  • IP-Adapter가 스타일 정보까지 강하게 주입

해결:

  • IP-Adapter weight를 낮추기
  • Face 전용 어댑터를 우선 사용
  • 연출 프롬프트를 더 구체화(예: daylight, studio softbox, backlight)

4) 여러 장 생성하면 어느 순간부터 점점 닮지 않음

원인:

  • 워크플로가 "이전 결과"를 다시 레퍼런스로 쓰는 누적 드리프트

해결:

  • 레퍼런스는 항상 원본으로 고정
  • 배치 생성 시 seed를 관리하고, 좋은 결과 seed를 북마크

ComfyUI API로 자동화하기(예: 인물 고정 배치 생성)

운영 관점에서 자주 하는 작업은 "같은 인물로 장면만 바꿔 N장 생성"입니다. ComfyUI는 HTTP API를 통해 워크플로를 템플릿화할 수 있습니다.

아래는 개념용 예시로, 워크플로 JSON에서 프롬프트와 seed만 바꿔 큐에 넣는 형태입니다.

import json
import random
import requests

COMFY_URL = "http://127.0.0.1:8188/prompt"

with open("workflow.json", "r", encoding="utf-8") as f:
    wf = json.load(f)

def enqueue(prompt_text: str, seed: int):
    # 예시: 노드 id는 워크플로에 맞게 수정
    wf["6"]["inputs"]["text"] = prompt_text          # Positive prompt node
    wf["7"]["inputs"]["seed"] = seed                 # KSampler node

    payload = {"prompt": wf}
    r = requests.post(COMFY_URL, json=payload, timeout=60)
    r.raise_for_status()
    return r.json()

scenes = [
    "cinematic portrait photo, rainy night street, neon signs, 85mm",
    "golden hour outdoor portrait, park background, soft sunlight, 85mm",
    "indoor studio portrait, gray backdrop, softbox lighting, 85mm",
]

for s in scenes:
    seed = random.randint(1, 2**31 - 1)
    print(enqueue(s, seed))

생성 요청이 폭주하면 큐가 밀리거나 응답이 느려지는데, 이때는 "요청 폭포"를 줄이듯 캐시와 프리페치 전략을 세우는 편이 효과적입니다. UI에서 썸네일만 먼저 보여주고 원본은 지연 로딩하는 방식은 웹 성능 최적화와 동일한 사고방식입니다. 관련해서는 Next.js 14 캐시 때문에 ISR 갱신 안 될 때 디버깅 도 같이 보면 운영 감각을 잡는 데 도움이 됩니다.

고급 팁: "의상까지" 고정하고 싶을 때

얼굴은 IP-Adapter Face로 잘 고정되지만, 의상은 장면/포즈/프롬프트 영향으로 바뀌기 쉽습니다. 의상까지 고정하려면 다음 중 하나를 추가합니다.

  • 레퍼런스에 의상이 잘 보이는 반신/전신 이미지를 포함
  • IP-Adapter를 Face가 아니라 Plus 계열로 바꾸고 weight를 낮게 시작
  • ControlNet에 추가로 Canny를 걸어 의상 윤곽을 살림(단, 경직될 수 있음)
  • 최후의 수단으로 의상 LoRA를 얹되, LoRA weight는 낮게(과적합 방지)

의상 고정은 "아이덴티티"보다 조건이 복잡해서, 보통은 얼굴 고정 성공 후에 단계적으로 확장하는 것을 권합니다.

정리: 가장 재현성 좋은 최소 레시피

처음부터 모든 조건을 다 넣으면 튜닝이 어려워집니다. 아래 최소 레시피로 시작해 안정화한 뒤 확장하세요.

  1. SDXL base
  2. IP-Adapter Face, weight 0.6 시작
  3. ControlNet OpenPose, strength 0.7 시작
  4. CFG 6, steps는 중간값
  5. 레퍼런스는 얼굴 클로즈업 1장 + 3/4 각도 1장

이 조합만으로도 "같은 인물"이라는 인상을 유지한 채 다양한 장면을 뽑는 성공률이 크게 올라갑니다. 이후에 Depth, Canny, 스타일 LoRA, refiner를 순서대로 추가하며 어느 조건이 결과를 흔드는지 관찰하면, 본인 워크플로에 맞는 안정 구간을 빠르게 찾을 수 있습니다.

추가로 생성 파이프라인을 서비스에 붙여 동시 요청을 처리한다면, 실패 재시도나 백오프 같은 운영 설계도 중요해집니다. 이미지 생성도 결국 외부 리소스(GPU) 호출이므로, 과부하 시에는 재시도 전략이 품질과 비용에 직결됩니다. 이 관점은 OpenAI 429 RateLimitError 재시도·백오프 설계 의 패턴을 그대로 응용할 수 있습니다.