개요
어쩌다 보니 이미지 확장 기능을 만들고 있다. 원본 이미지의 경계 너머로 캔버스를 넓히고, 그 빈 공간을 원본과 자연스럽게 이어지도록 채우는 기능이다.

상업적으로 사용 가능한 모델을 골라 PoC를 진행했다. 처음 시도한 20B 규모 모델은 RTX 4090에서 4bit 양자화를 해야 겨우 돌았고, 큰 이미지는 실패했다. 최종적으로는 1/5 크기인 FLUX.2 Klein 4B에 배경 캡션 → 배경 생성 → 경계 희석으로 이어지는 2-pass 파이프라인을 얹어 품질을 확보했다.
이 글은 모델 선정부터 프롬프팅, 경계의 자연스러움을 위한 전처리 튜닝(feathering·denoise), 그리고 ComfyUI PoC를 Python 서버로 재구현하기까지의 시행착오를 정리한 기록이다.
결과부터
핵심은 모델의 절대 크기가 아니라 작은 모델의 한계를 파이프라인 설계로 메우는 것이었다.
| 항목 | Qwen-Image + ControlNet (초기 시도) | FLUX.2 Klein 4B (최종) |
|---|---|---|
| 파라미터 | 20B | 4B (약 1/5) |
| VRAM | 22GB (4bit NF4 양자화, peak 22.6GB) | ~13GB |
| RTX 4090(24GB) 여유 | 거의 없음 | 여유 있음 |
| 큰 이미지 | 실패 | 처리 가능 |
| 양자화 | 필요 | 불필요 |
| 라이선스 | Apache 2.0 | Apache 2.0 |
측정 환경: RTX 4090 24GB, 일반 사진 이미지 기준. VRAM은 추론 시 실측 peak 값이다.
실제 결과 품질을 초기 시도와 비교하면 다음과 같다.
| 초기 시도 (단일 pass) | 최종 (배경 캡션 + 2-pass) |
|---|---|
![]() | ![]() |
딥다이브 1 : 인페인팅 vs 아웃페인팅
이미지 확장 PoC를 시작하며 두 가지 방식을 후보로 두었다. 인페인팅과 아웃페인팅이다.
| 구분 | 인페인팅 | 아웃페인팅 |
|---|---|---|
| 작업 위치 | 이미지 내부 | 이미지 외부(경계 너머) |
| 캔버스 크기 | 변화 없음 | 확장됨 |
| 목적 | 수정, 제거, 복원 | 확장, 상상, 컨텍스트 생성 |
| 참고 데이터 | 주변 픽셀(양방향) | 경계 픽셀(한쪽 방향으로 추론) |
인페인팅을 사용하는 경우, 원본보다 넓은 공간을 할당한 후 마스킹된 영역을 생성하면 아웃페인팅과 결과가 같을까?
위와 같은 고민이 들 수 있다. (나도 그랬다.) 차이는 참고하는 데이터가 양방향 추론인지, 단방향 추론인지에 있다.
인페인팅은 양방향으로 참고해야 하는데, 경계 너머는 한쪽 데이터만 존재한다. 참고할 픽셀이 한쪽밖에 없으니 결과가 더욱 상상에 가까워진다. 대표적으로 아래와 같은 상황에 놓이게 된다.

딥다이브 2 : 모델 리서치와 첫 실패
이미지 확장 기능을 출시해보기 위해 이것저것 시도해봤다. 상업 사용 가능 여부를 기준으로 후보 모델을 비교했다.
| 모델 | 파라미터 | 특징 |
|---|---|---|
| FLUX.2 [klein] 4B | 4B | 최신 세대, 가볍고 빠름, Apache 2.0 |
| Qwen-Image + InstantX ControlNet | 20B | 최신·고품질, 무거움, Apache 2.0 |
| SDXL Inpainting (+ ControlNet) | ~3.3B | diffusers 공식 아웃페인팅 가이드의 기준 경로, 자료·생태계 큼 (그러나 OpenRAIL 계열) |
SDXL은 자료와 생태계가 크지만 라이선스가 OpenRAIL 계열이라 상업 사용 관점에서 걸림돌이 있었다.
먼저 Qwen-Image + InstantX ControlNet을 사용해봤다. 합계 메모리가 54GB 필요했다. 4bit NF4로 양자화해 22GB까지 낮췄다. RTX 4090은 24GB이므로 돌아는 갔다. 다만 이미지가 큰 경우엔 실패했다. 일반 이미지 처리 시 실측 peak는 ~22.6GB까지 올라갔다.
확장된 이미지가 원본과 무관하다

확장된 이미지가 원본 이미지와 연관이 없어 보였다. 프롬프팅을 수정해 원본과 유사하게 유도했다.
a seamless natural continuation of the scene, high quality, highly detailed, sharp focus, photorealistic, consistent lighting and perspective
이미지 처리 프롬프팅에는 유의점이 있었다.
- DALL·E 3 논문(Improving Image Generation with Better Captions)에서는 이미지를 상세하게 묘사한 캡션을 사용할수록 생성 결과가 프롬프트를 더 잘 따라간다고 강조한다.
- 그러나 다대일(사용자 다수) 기능에 반영하려면 특정 이미지에 종속되지 않는 제네럴한 프롬프팅이 필요하다고 판단했다.
연관성은 개선됐지만 화질이 떨어진다
프롬프팅 수정 후 확장된 이미지와 원본의 연관성이 어느 정도 개선됐다. 대신 확장된 이미지의 화질이 떨어졌다. 품질이 낮은 이유는 VAE 타일링 여부 차이였다. 해당 옵션을 끄면 매끄러운 결과가 나올 예정이었다. 그러나 사용 중인 RTX 4090에서는 이 옵션을 사용하면 GPU 메모리 부족으로 구동이 불가능했다.

이미지 모델을 낮춰서 화질이 떨어지는 문제를 개선할 필요가 있다고 생각했다.
최적화 과정 : ComfyUI로 전처리 튜닝
어도비는 이미지 확장을 참 잘 만들었다. 쟤네는 잘되는데 우리가 안 될 수는 없었다. 못 하면 잠 못 잘 것 같아서 마저 진행했다.

그래서 ComfyUI(desktop)를 사용해 전처리를 튜닝했다. 정성적 비교를 빠르게 반복하며 파라미터를 조정하는 방식이었다.
| 예시 1 | 예시 2 |
|---|---|
![]() | ![]() |
경계를 자연스럽게 잇기까지 네 번의 시도를 거쳤다.
| 시도 1 | 시도 2 | 시도 3 | 시도 4 |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| 확장된 외부 영역이 원본과 전혀 매치되지 않았다. 이미지를 묘사하는 프롬프팅이 필요하다고 판단해, image caption으로 원본을 묘사하도록 했다. | image caption을 붙이자 확장 영역이 원본과 제법 잘 어울리기 시작했다. 다만 경계 부근이 아직 부자연스러워, feathering을 20px에서 80px로 넓혔다. | 경계 톤은 유사해졌지만 이음새는 여전히 어색했다. denoise를 조절해 모델을 한 번 더 돌려 경계를 다듬었다. | 백그라운드와 원본 간 경계가 자연스럽게 연결됐다. |
1단계 : 생성 영역과의 맥락 불일치
image caption을 활용해 원본을 묘사하도록 했다. Florence2Model을 활용했다.

결과적으로 기존 대비 유사한 맥락의 이미지를 생성하게 됐다. 그러나 기존 영역과 생성된 영역 간 경계가 발생했다.
| AS-IS | TO-BE |
|---|---|
![]() | ![]() |
2단계 : 경계선 블렌딩 (feathering + denoise)
feathering 조절
feathering은 원본과 새 영역의 경계를 부드럽게 섞는 폭이다. 아웃페인팅을 위한 이미지 패딩 노드의 가장자리 흐림 처리에 해당한다. 40 → 80으로 설정했다. 어느 정도 연결되는 부분이 생겼다.
| AS-IS | TO-BE |
|---|---|
![]() | ![]() |
denoise 조절
완성된 이미지 전체를 아주 약한 강도로 한 번 더 돌려서 톤을 통일한다. denoise를 0.2~0.3으로 낮게 설정하면 그림은 그대로 두고 경계 톤만 매끈하게 블렌딩한다.
denoise(노이즈 제거량)는 원본을 얼마나 지우고 새로 그릴지를 정하는 값이다. Stable Diffusion 계열은 노이즈에서 시작해 점점 그림을 복원하는 방식인데, denoise는 “시작할 때 원본에 노이즈를 얼마나 끼얹을지”를 정한다.
| denoise | 결과 | 언제 씀 |
|---|---|---|
| 1.0 | 완전히 새로 그림 (원본 무시) | txt2img, 아웃페인팅 새 영역 |
| 0.6~0.8 | 원본 구도만 참고, 많이 변형 | 스타일 크게 바꾸기 |
| 0.3~0.5 | 원본 유지하며 다듬기 | 이음새 통합, 디테일 보정 |
| 0.1~0.2 | 원본 거의 그대로, 살짝만 | 색·톤 미세 조정 |
즉, 위에서 만들어진 결과에 denoise 0.2~0.3을 세팅하고 돌리면 경계 블렌딩이 가능하다.

결과는 다음과 같다.
| AS-IS | TO-BE |
|---|---|
![]() | ![]() |
두 번째 결과도 만족스럽다. (오른쪽을 자세히 보면 기둥을 만들어서 소파를 숨김 처리해버렸다… 신기하다.)
| 마스크 생성 | 백그라운드 이미지 생성 | 백그라운드↔원본 경계 희석 |
|---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
3단계 : 원본 보존과 피사체 혼입 문제
문제 1 : 원본 이미지가 수정되는 이슈
원본 이미지가 수정되는 이슈가 있었다. 경계값을 넓게 포함해 마스크 영역을 지정하고, 외부 영역만 자연스럽게 개선하는 방향이 좋아 보였다.
AS-IS
| 마스크 생성 | 백그라운드 이미지 생성 | 백그라운드↔원본 경계 희석 |
|---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
TO-BE
| 마스크 생성 | 백그라운드 이미지 생성 | 백그라운드↔원본 경계 희석 |
|---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
문제 2 : 캡션에 피사체가 섞이는 문제
이미지 캡션 시 설명에 피사체가 묘사되면, 백그라운드에 피사체가 포함되는 것이 부자연스러움의 원인이 되는 듯했다. 다음 세 가지를 시도했다.
- 배경 설명을 받도록 수정. 기존 이미지 캡션에서 LLM을 바탕으로 배경을 묘사하도록 변경.
- 마스크 크기 조절 시도. → 변화가 발생했으나 미미했다.
- 기존 원본 데이터를 추가로 합성. → 원본 이미지가 유지되긴 했다.
| 마스크 생성 | 백그라운드 이미지 생성 | 백그라운드↔원본 경계 희석 |
|---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
마지막 결과는 일부 개선됐다.
| 마스크 생성 | 백그라운드 이미지 생성 | 백그라운드↔원본 경계 희석 |
|---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
ComfyUI PoC를 Python 서버로 재구현하기
ComfyUI로 파이프라인을 검증했다. 노드 단위로 중간 결과를 확인하며 파라미터를 좁혀갈 수 있었다.

그러나 ComfyUI 그래프는 그대로 사용할 수 없었다. ComfyUI는 검증과 디버깅에는 훌륭했지만, 서비스로 배포하기엔 제약이 존재했다. 그래서 검증이 끝난 그래프를 순수 Python(diffusers) 서버로 재구현했다. 저장소는 geon-chang/hello-image-outpainting에 공개해두었다.
2-pass 오케스트레이션은 ComfyUI 그래프의 실행 순서를 그대로 옮겼다. 전체 실행 흐름을 도식화하면 다음과 같다.

백엔드 측면에서의 고민
- 실시간성 : 파이프라인이 캡션 → 1-pass → 2-pass 여러 단계를 거치므로 응답 시간이 길다. 사용자 기능이 되려면 실시간성 확보가 다음 과제다.
- GPU 자원 직렬화 : GPU는 한정 자원이라 동시 요청을 GPU 락으로 직렬 처리했다. 처리량을 늘리려면 다중 서버로 분산하는 구조가 필요하다.
- 모델 선택이 곧 운영 비용 : FLUX.2 Klein 4B로 VRAM을 13GB 수준까지 낮춘 덕에, 고가의 대용량 GPU 없이도 운영 가능한 선택지가 생겼다. 품질과 운영 비용의 균형점을 찾는 것이 백엔드 관점의 핵심이었다.
마무리하며
- 정성적 비교를 무시할 수 없다. 가장 빠르게 검증 가능한 수단이었기 때문에, 굳이 불필요한 정밀 검증은 스킵했다. 정량적 지표가 필요한 순간과 눈으로 빠르게 판단하는 게 나은 순간을 구분하는 게 중요했다.
- 도구가 곧 디버깅 능력이었다. 이미지 확장을 이렇게 진행해야 한다는 것 자체가 신기했다. ComfyUI가 아니었으면 디버깅할 꿈도 못 꿨을 것 같다. 각 노드 단위로 중간 결과를 눈으로 확인할 수 있었기에 feathering·denoise 같은 파라미터를 하나씩 좁혀갈 수 있었다.
- 협업으로 얻은 도구였다. ComfyUI를 알게 된 것도 AI 리서치 엔지니어에게 이미지 처리 등에 대해 조언을 구하다가 알게 된 것이다. 모르는 영역일수록 그 분야를 아는 사람에게 묻는 게 가장 빠른 지름길이었다.
- 품질은 모델 크기가 아니라 파이프라인 설계에서 나왔다. 20B 모델의 메모리 한계에 막혔지만, 1/5 크기인 FLUX.2 Klein 4B에 배경 캡션·2-pass·feather 보존을 얹어 품질을 확보했다. 큰 모델을 무작정 쓰기보다, 작은 모델의 한계를 설계로 메우는 접근이 운영 비용까지 함께 잡는 길이었다.
































