- Published on
ComfyUI로 SDXL LoRA 멀티캐릭터 일관성 잡기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서로 다른 캐릭터 LoRA를 한 장면에 동시에 등장시키면 SDXL이 가장 먼저 무너뜨리는 건 정체성입니다. 얼굴형은 섞이고, 헤어 컬러는 교차 오염되고, 의상 디테일은 한 캐릭터에서 다른 캐릭터로 점프합니다. 특히 ComfyUI에서는 노드 연결이 자유로운 만큼, "한 번에 한 장"으로 끝내려 하면 디버깅 포인트가 사라져 재현성이 급격히 떨어집니다.
이 글은 SDXL + ComfyUI에서 멀티캐릭터 일관성을 잡기 위한 실전 워크플로를 단계 분리 관점으로 정리합니다. 핵심은 다음 4가지입니다.
- 캐릭터별 조건을
프롬프트/LoRA/마스크로 분리한다 - 베이스 구도는 한 번에 잡되, 정체성은
영역별 재샘플링으로 고정한다 - 시드, 해상도, 디노이즈, 스텝을
변수로 관리해 재현성을 만든다 - 실패 원인을 로그처럼 추적 가능한 형태로 남긴다
중간중간 설정을 고치다 보면 "왜 이 장면만 유독 깨지지" 같은 디버깅이 필요해지는데, 인프라에서 장애 원인을 좁혀가듯 접근하면 빨리 안정화됩니다. 비슷한 문제해결 사고방식은 Cloudflare 520·521, Nginx·ALB 로그로 30분 진단 같은 글의 흐름과도 닮아 있습니다.
멀티캐릭터가 무너지는 대표 원인 6가지
1) LoRA 토큰 충돌과 의미 중첩
두 LoRA가 모두 blue hair, school uniform, sharp jawline 같은 특징을 강하게 밀면, SDXL은 이를 "한 인물의 특징"으로 합성해버립니다. 결과는 혼종 얼굴입니다.
대응:
- 캐릭터별 고유 토큰을 프롬프트에 명시하고, 공통 속성은 씬 프롬프트로 분리
- LoRA 강도를 동시에 올리기보다
한 번에 하나씩고정 후 합성
2) 한 프롬프트에 두 인물을 동시에 정의
1girl, 1boy 수준으로는 분리가 안 됩니다. SDXL은 장면 전체를 한 번에 최적화하므로, 인물 A와 B의 특징이 서로 끌어당깁니다.
대응:
- 베이스 구도는 전체 프롬프트로 만들고, 인물 정체성은
영역별 인페인트로 확정
3) 디노이즈가 너무 높음
인페인트에서 denoise가 높으면 원본 구도까지 다시 그리며 다른 인물 특징이 섞입니다.
대응:
- 정체성 고정 단계에서는
denoise를 낮게 시작하고 점진적으로 올림
4) 해상도와 리사이즈로 인한 얼굴 디테일 손실
멀티캐릭터는 각 얼굴이 작아지기 쉬워 정체성 유지가 어렵습니다.
대응:
- 처음부터
얼굴 픽셀 크기가 충분하도록 구도를 잡거나 - 업스케일 후 얼굴만 리파인
5) 네거티브 프롬프트가 과도함
bad anatomy, bad face 등을 과하게 넣으면 오히려 모델이 "얼굴을 회피"하며 비슷한 평균 얼굴로 수렴합니다.
대응:
- 네거티브는 최소 세트로 시작하고, 문제 발생 시 항목을 하나씩 추가
6) 샘플러/스케줄러 조합이 불안정
조합에 따라 작은 변화가 큰 변형으로 이어집니다.
대응:
- 베이스 생성은 안정적인 조합으로 고정
- 이후 단계에서만 변수를 바꿔 영향 범위를 제한
추천 워크플로 개요: 구도와 정체성을 분리
ComfyUI에서 가장 재현성 높은 접근은 아래 3단계 파이프라인입니다.
Base Compose: 전체 장면, 포즈, 카메라, 조명 확정Identity Lock: 캐릭터 A 영역 인페인트로 확정, 다음 캐릭터 B 확정Detail Pass: 의상 패턴, 얼굴 디테일, 손 등만 낮은 디노이즈로 보정
이때 중요한 규칙은 한 단계에서 바꾸는 요소는 하나입니다. 인프라에서 설정 한 번에 여러 개 바꾸면 원인 추적이 어려워지는 것과 같습니다. Git에서도 마찬가지로, 변경을 잘게 쪼개야 되돌리기가 쉽습니다. 비슷한 맥락의 팁은 Git rebase 후 강제푸시 막고 안전하게 되돌리기 글의 사고방식과도 통합니다.
ComfyUI 노드 구성 예시
아래는 텍스트로 표현한 기본 연결입니다. 실제 UI에서는 동일한 흐름으로 배치하면 됩니다.
Load Checkpoint: SDXL baseCLIP Text Encode: positive, negativeLoad LoRA: 캐릭터 A, 캐릭터 BKSampler: 베이스 생성VAE Decode: 이미지 출력Inpaint세트 : 마스크 + KSampler로 캐릭터별 정체성 고정
베이스 생성용 파라미터 가이드
- Steps:
25전후 - CFG:
5.0전후 - Sampler:
DPM++ 2M계열 - Scheduler:
Karras계열 - 해상도: 가급적
1024기준에서 시작
여기서 가장 중요한 건 seed를 고정하고, 이후 단계에서 seed를 바꾸지 않는 것입니다.
프롬프트 설계: 씬과 캐릭터를 분리하는 템플릿
멀티캐릭터는 프롬프트를 다음처럼 쪼개면 안정적입니다.
- 씬 프롬프트: 배경, 카메라, 조명, 분위기
- 캐릭터 프롬프트: 고유 토큰 + 얼굴/헤어/의상 핵심 특징
- 포즈/상호작용: 서로의 관계는 최소한의 키워드로만
예시:
scene_prompt:
cinematic lighting, shallow depth of field, street at night, neon signs, medium shot
char_a_prompt:
charA_token, short silver hair, red eyes, black leather jacket, confident expression
char_b_prompt:
charB_token, long brown hair, green eyes, white hoodie, smiling
전체 positive는 1차 베이스에서는 이렇게 섞되, 캐릭터 특징은 약하게 두고 구도만 잡는 편이 낫습니다.
LoRA 멀티 적용 전략: 동시에 세게 넣지 말 것
전략 A: 베이스에서는 LoRA 약하게, 인페인트에서 강하게
- 베이스 생성: LoRA A
0.4, LoRA B0.4 - 캐릭터 A 인페인트: LoRA A
0.8, LoRA B0.2 - 캐릭터 B 인페인트: LoRA A
0.2, LoRA B0.8
이렇게 하면 "장면 전체"는 유지하면서 "영역 정체성"만 강화할 수 있습니다.
전략 B: LoRA를 아예 분리한 두 장을 만든 뒤 합성
- 동일 seed, 동일 씬 프롬프트로
- A 버전은 LoRA A만, B 버전은 LoRA B만
- 이후 마스크로 합성하고 경계만 리파인
이 방식은 계산량이 늘지만 가장 강력합니다.
인페인트로 캐릭터 정체성 고정하기
핵심은 마스크 품질과 디노이즈 관리입니다.
마스크 팁
- 얼굴만 따지 말고
머리카락 + 얼굴 + 목 + 상체 일부까지 포함 - 경계는 약간 feather 처리(너무 딱딱하면 경계가 티 남)
- 두 캐릭터 마스크가 겹치지 않게 관리
디노이즈 운영
- 1차 정체성 고정:
0.35전후 - 디테일 보강:
0.45전후 - 완전 재설계가 필요할 때만
0.6이상
0.6 이상은 사실상 다시 그리는 수준이라, 다른 인물 특징이 침투하기 쉽습니다.
ComfyUI API로 재현 가능한 파라미터 관리
GUI에서 튜닝하다 보면 "어제는 됐는데 오늘은 안 됨"이 자주 생깁니다. 재현성을 확보하려면 워크플로 JSON을 버전 관리하거나, 최소한 파라미터를 코드로 기록해두는 게 좋습니다.
아래는 ComfyUI HTTP API 호출 예시입니다. 환경에 따라 엔드포인트와 워크플로 구조가 다를 수 있으니, 개념적으로 seed, steps, cfg, denoise, lora_strength 같은 변수를 분리해 관리하는 데 목적이 있습니다.
curl -X POST http://127.0.0.1:8188/prompt \
-H "Content-Type: application/json" \
-d @workflow.json
workflow.json에서 바꿔야 할 값들을 한 곳에 모아두면, 실험 로그를 남기기 쉬워집니다.
{
"meta": {
"seed": 123456789,
"base_steps": 26,
"base_cfg": 5.0,
"sampler": "dpmpp_2m",
"scheduler": "karras",
"width": 1024,
"height": 1024
},
"lora": {
"charA_strength_base": 0.4,
"charB_strength_base": 0.4,
"charA_strength_inpaint": 0.8,
"charB_strength_inpaint": 0.8
},
"inpaint": {
"denoise_identity": 0.35,
"denoise_detail": 0.45
}
}
이런 식으로 "변수 테이블"을 두면, 문제 발생 시 변경점을 추적하기가 훨씬 쉽습니다. 타입 안정성 관점에서는 런타임에서 터지는 값을 줄이는 게 중요한데, 프론트엔드에서도 비슷하게 접근합니다. 관련해서는 TypeScript 5.5 noUncheckedIndexedAccess 오류 해결 글이 참고가 됩니다.
멀티캐릭터 일관성 체크리스트
아래 체크리스트를 위에서 아래 순서로 점검하면, 대부분의 실패를 빠르게 줄일 수 있습니다.
1) 구도 단계
- seed 고정했는가
- 인물 수가 프롬프트에 명확한가
2 people같은 표현 포함 - 얼굴 크기가 충분한가
2) LoRA 단계
- 베이스에서 LoRA 강도를 낮췄는가
- 캐릭터 토큰이 서로 겹치지 않는가
- 공통 속성은 씬 프롬프트로 올렸는가
3) 인페인트 단계
- 한 번에 한 캐릭터만 고정하고 있는가
- 마스크가 너무 작지 않은가
denoise가 과하지 않은가
4) 디테일 단계
- 손, 눈, 의상 패턴 등은 별도 패스로 처리했는가
- 전체를 다시 그리려 하지 않고 "부분 보정"으로 끝내고 있는가
실전 예시: 두 캐릭터를 같은 프레임에 고정하는 순서
- 베이스 생성
- 씬 중심 프롬프트
- LoRA A/B 약하게
- 목표는
구도와조명만 통과
- 캐릭터 A 인페인트
- A 마스크 지정
- LoRA A 강하게, LoRA B 약하게
denoise_identity로 1차 확정
- 캐릭터 B 인페인트
- B 마스크 지정
- LoRA B 강하게, LoRA A 약하게
- 동일 seed 유지
- 디테일 패스
- 눈동자, 입 모양, 의상 로고 같은 "정체성 포인트"만 작은 마스크
denoise_detail로 짧게 여러 번
이 순서대로 하면 "둘 다 닮았는데" 같은 문제를 상당히 줄일 수 있습니다.
마무리: 멀티캐릭터는 한 번에 끝내려 하지 말 것
SDXL LoRA 멀티캐릭터의 본질은 충돌하는 조건을 어떻게 분리해 최적화하느냐입니다. ComfyUI의 강점은 이 분리를 노드 레벨로 강제할 수 있다는 점이고, 가장 안정적인 해법은 베이스 구성과 정체성 고정을 분리한 뒤, 인페인트로 캐릭터를 한 명씩 잠그는 방식입니다.
정리하면:
- 베이스는 구도만, LoRA는 약하게
- 정체성은 마스크 인페인트로 한 명씩
- seed와 파라미터를 변수로 관리해 재현성 확보
이 흐름으로 워크플로를 만들면, "운 좋게 한 번"이 아니라 "언제든 다시" 같은 결과를 얻을 수 있습니다.