2-1. 워크플로 해부: 노드와 수식을 맞춰 보기
ComfyUI의 기본 워크플로에는 노드가 일곱 개 있다. 그 일곱 개가 1단계에서 배운 부품과 하나씩 대응한다. 이번 수업은 그 대응을 확인한다. 이것만 되면 어떤 복잡한 워크플로도 읽을 수 있다.
왜 ComfyUI인가
이미지 생성 도구는 여럿이다. 이 강의는 ComfyUI를 쓴다. 이유는 하나다. 구조가 눈에 보인다.
다른 도구는 설정 칸만 보여 주고 안에서 무슨 일이 일어나는지 숨긴다. ComfyUI는 부품을 상자(노드)로 늘어놓고 선으로 잇게 한다. 1-8의 전체 그림을 직접 조립하는 셈이다. 새 모델과 기법도 가장 먼저 지원된다.
기본 워크플로
[Load Checkpoint] ─ MODEL ──────────────────────→ [KSampler] ─ LATENT → [VAE Decode] → [Save Image]
│─ CLIP → [CLIP Text Encode] (긍정) ─ CONDITIONING ─→ ↑ ↑
│─ CLIP → [CLIP Text Encode] (부정) ─ CONDITIONING ─→ │ │
│─ VAE ─────────────────────────────────────────────────│──────────────┘
[Empty Latent Image] ─ LATENT ──────────────────────────────────┘
노드 하나씩
Load Checkpoint
모델 파일을 읽어 세 덩어리로 내놓는다. 1-8의 세 상자다.
| 출력 | 실체 | 배운 곳 |
|---|---|---|
| MODEL | 디노이저 (U-Net 또는 DiT) | 1-2, 1-4, 1-8 |
| CLIP | 텍스트 인코더 | 1-5 |
| VAE | 압축기와 복원기 | 1-1 |
출력 이름이 CLIP이라고 되어 있지만 T5나 LLM 계열 인코더도 이 선으로 흐른다. 처음 만들 때 CLIP뿐이어서 붙은 이름이다.
CLIP Text Encode
글을 받아 토큰 벡터 묶음으로 바꾼다. 1-5의 텍스트 인코더를 실제로 돌리는 노드다. 출력 이름 CONDITIONING이 "조건"이다.
두 개를 쓴다. 하나는 긍정, 하나는 부정이다. 노드 자체는 똑같다. KSampler의 어느 구멍에 꽂느냐가 역할을 정한다.
Empty Latent Image
빈 잠재를 만든다. 가로, 세로, 장수를 정한다.
- 1024×1024를 넣으면 실제로는 128×128짜리 잠재가 만들어진다(1-1).
- 장수(batch)를 4로 하면 한 번에 네 장을 만든다. 시드가 하나씩 다른 노이즈 네 개에서 출발한다.
이름은 "빈"이지만 여기에 KSampler가 노이즈를 채운다.
KSampler
핵심 노드다. 1-3의 걷기를 실행한다.
| 입력, 설정 | 하는 일 | 배운 곳 |
|---|---|---|
| model | 방향을 알려 줄 디노이저 | 1-8 |
| positive | CFG의 "조건 있음" | 1-6 |
| negative | CFG의 "조건 없음" 자리 | 1-6 |
| latent_image | 출발할 잠재 | 1-1 |
| seed | 출발 노이즈를 정한다 | 0-1 |
| steps | 걸음 수 | 1-3 |
| cfg | 프롬프트 방향을 몇 배로 과장할지 | 1-6 |
| sampler_name | 걷는 방법 | 1-3 |
| scheduler | 걸음을 어디에 놓을지 | 1-3 |
| denoise | 얼마나 노이즈를 섞고 시작할지. 1이면 순수 노이즈 | 3-1에서 |
안에서 벌어지는 일을 말로 쓰면 이렇다.
시드로 노이즈를 만든다
스케줄러가 정한 걸음마다:
디노이저를 긍정 조건으로 한 번, 부정 조건으로 한 번 부른다
cfg 로 두 결과를 합친다
샘플러의 방법대로 한 걸음 옮긴다
완성된 잠재를 내놓는다
VAE Decode
잠재를 그림으로 푼다. 1-1의 디코더다. 한 번만 돈다.
Save Image
그림을 파일로 저장한다. ComfyUI는 PNG 안에 워크플로 전체를 함께 적어 둔다. 그 그림을 ComfyUI 창에 끌어다 놓으면 만들 때 쓴 워크플로가 그대로 열린다. 남이 만든 그림의 설정을 배울 때 쓸모 있다.
최신 모델의 워크플로는 조금 다르다
Flux 이후의 모델은 세 덩어리를 따로 받는다. 텍스트 인코더가 수 GB에서 십수 GB로 커져서 한 파일에 묶지 않는다.
| 기본 노드 | 나눠 받을 때의 노드 |
|---|---|
| Load Checkpoint | Load Diffusion Model (디노이저) |
| Load CLIP 또는 DualCLIPLoader (텍스트 인코더) | |
| Load VAE |
노드가 몇 개 더 붙기도 한다.
- 시프트를 정하는 노드: 1-4의 shift 값을 넣는다. 플로우 매칭 모델에 있다.
- 가이던스를 넣는 노드: 1-6의 가이던스 증류 모델에 "CFG를 몇으로 준 것처럼"을 알려 준다. 이때 KSampler의 cfg는 1로 둔다.
상자가 늘어나도 뼈대는 같다. 세 덩어리를 읽고, 프롬프트를 조건으로 바꾸고, 잠재를 만들고, 걷고, 푼다.
선의 색과 타입
선은 타입이 맞아야 이어진다. MODEL 출력은 model 입력에만 꽂힌다. 타입이 곧 "무엇이 흐르는가"다.
| 타입 | 흐르는 것 |
|---|---|
| MODEL | 디노이저 |
| CLIP | 텍스트 인코더 |
| VAE | VAE |
| CONDITIONING | 프롬프트의 토큰 벡터 |
| LATENT | 잠재 |
| IMAGE | 픽셀 그림 |
LATENT와 IMAGE를 구분하는 것이 중요하다. 둘 사이를 오가려면 VAE를 거쳐야 한다. VAE Decode는 LATENT를 IMAGE로, VAE Encode는 IMAGE를 LATENT로 바꾼다.
워크플로는 파일이다
워크플로는 JSON 파일로 저장된다. 그리고 ComfyUI는 그 JSON을 받아 실행하는 서버이기도 하다. 화면에서 손으로 누르지 않고 프로그램이 워크플로를 보내 그림을 받아 올 수 있다. 캐릭터 수백 명의 그림을 한꺼번에 뽑는 일이 이렇게 된다. 7단계에서 다룬다.
해 볼 것
시드를 고정하고 하나씩만 바꿔 본다. 한 번에 하나만 바꿔야 그 설정의 효과만 보인다.
- steps를 5, 10, 20, 40으로 바꿔 본다. 몇에서부터 그림이 더 나아지지 않나?
- cfg를 1, 3, 7, 12로 바꿔 본다. 어디서부터 색이 타기 시작하나?
- sampler를 euler와 euler_ancestral로 바꾸고, 각각 steps를 20과 40으로 돌려 본다. 어느 쪽이 구도가 유지되나?
- Empty Latent Image의 크기를 모델의 기본 해상도의 절반과 두 배로 바꿔 본다. 무슨 일이 생기나?
예상 결과
- 모델마다 다르지만 대개 20~30에서 멈춘다. 그 뒤로는 경로를 이미 충분히 정확하게 따라가고 있다(1-3).
- 1에서는 흐리고 프롬프트를 덜 따른다. 값이 커질수록 또렷해지다가, 어느 선을 넘으면 색이 진해지고 윤곽이 딱딱해진다(1-6).
- euler는 스텝을 바꿔도 구도가 유지된다. euler_ancestral은 걸음마다 노이즈를 넣어서 그림이 달라진다(1-3).
- 절반이면 그림이 무너진다. 두 배면 같은 대상이 여러 번 반복되거나 몸이 늘어난다. 모델은 학습한 해상도 근처에서만 제대로 그린다.