2025-06-01 9:00 AM
미리캔버스나 Adobe Stock 같은 플랫폼에서 사용할 수 있는 디자인 에셋을 대량으로 생성해서 부가적인 수익을 창출할수 있는 사실 알고계시나요? 하지만 이미지 생성에 사용되는 API 비용은 많은 부담요소입니다. 그래서 저는 부담 없이 작업하고 싶었고, 로컬 GPU 자원을 최대한 활용해보고 싶었습니다. 그래서 여러 이미지 생성 모델을 통합하고, 프롬프트 생성부터 메타데이터 관리까지 전체 파이프라인을 자동화하는 시스템을 만들어보았습니다.
전체 시스템을 다음과 같은 구조로 설계했습니다:
각 기능을 독립적인 함수로 분리하여 유지보수와 확장이 용이하도록 구현했습니다:
# 프롬프트 생성 모듈
def generate_prompt(base_idea=None):
"""키워드 기반 또는 랜덤 프롬프트 생성"""
# 구현 내용...
def generate_dynamic_prompt_with_ollama():
"""Ollama를 활용한 동적 프롬프트 생성"""
# 구현 내용...
# 이미지 생성 모듈
def generate_image_with_local(prompt_data):
"""로컬 SDXL 모델 활용"""
# 구현 내용...
def generate_image_with_flux(prompt_data):
"""로컬 Flux 모델 활용"""
# 구현 내용...
# 후처리 모듈
def process_image(image, prompt_data, format_type):
"""이미지 후처리 및 메타데이터 생성"""
# 구현 내용...
환경 변수와 설정을 통해 다양한 환경에서 동작할 수 있도록 구현했습니다:
# 환경별 설정
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
OLLAMA_API_URL = "http://localhost:11434/api/generate"
OLLAMA_MODEL = "qwen2.5:14b" # 설정 가능한 모델
# 라이브러리 선택적 import로 의존성 문제 해결
try:
from rembg import remove
REMBG_AVAILABLE = True
except ImportError:
print("경고: 배경 제거 기능이 비활성화됩니다.")
REMBG_AVAILABLE = False
기존의 랜덤 조합이나 키워드 기반 프롬프트 생성 방식은 다음과 같은 한계가 있었습니다:
Ollama LLM을 활용하여 구조화된 JSON 형식으로 프롬프트를 생성하도록 구현했습니다:
def generate_dynamic_prompt_with_ollama():
master_prompt = f"""
You are a creative assistant specialized in generating ideas for design assets.
Your task is to generate ONE creative concept for a design asset.
Output the result ONLY in JSON format with the following exact keys:
{{
"korean_description": "<생성된 에셋 아이디어에 대한 간결한 한글 설명>",
"english_image_prompt": "<Detailed English prompt for image generation>",
"tags": ["<List of 5-7 relevant Korean and English tags>"]
}}
Generate a new, creative concept now.
"""
try:
payload = {
"model": OLLAMA_MODEL,
"prompt": master_prompt,
"format": "json",
"stream": False
}
response = requests.post(OLLAMA_API_URL, json=payload, timeout=120)
# JSON 파싱 및 검증 로직
parsed_result = json.loads(response.json().get("response"))
return {
"prompt": parsed_result.get("english_image_prompt"),
"korean_prompt_base": parsed_result.get("korean_description"),
"tags": parsed_result.get("tags", [])
}
except Exception as e:
# 실패 시 기본 프롬프트 생성기로 폴백
return generate_prompt()
입력: "새로운 디자인 에셋 아이디어를 만들어줘"
Ollama 출력:
{
"korean_description": "우주 정거장에서 바라본 지구의 일출 풍경",
"english_image_prompt": "Spectacular sunrise over Earth as seen from space station window, cinematic lighting, detailed planet surface, realistic space environment, high contrast, professional space photography style",
"tags": ["우주", "지구", "일출", "space", "earth", "sunrise", "cinematic"]
}
이처럼 단순한 키워드 조합으로는 만들어내기 어려운 창의적이고 구체적인 프롬프트를 생성할 수 있었습니다.
각 모델이 실패할 경우를 대비해 단계적 폴백 시스템을 구현했습니다:
def test_image_generation(count=1, model="local", user_prompt=None, prompt_mode="random"):
# 모델별 실행 및 폴백 로직
if selected_model == "gemini" and GOOGLE_API_KEY:
image, prompt_data = generate_image_with_gemini(prompt_data)
if image is None:
actual_model_used = "dalle"
elif selected_model == "dalle" and OPENAI_API_KEY:
image, prompt_data = generate_image_with_dalle(prompt_data)
if image is None:
actual_model_used = "random"
elif selected_model == "local":
if DIFFUSERS_AVAILABLE:
image, prompt_data = generate_image_with_local(prompt_data)
else:
# 라이브러리 미설치 시 API 모델로 대체
if OPENAI_API_KEY:
actual_model_used = "dalle"
image, prompt_data = generate_image_with_dalle(prompt_data)
# 최종 실패 시 랜덤 색상 이미지 생성 (완전한 실패 방지)
else:
color = (random.randint(200, 255), random.randint(200, 255), random.randint(200, 255))
image = Image.new('RGB', (1024, 1024), color=color)
로컬 GPU를 사용할 때 메모리 효율성을 위해 다음과 같이 구현했습니다:
def generate_image_with_local(prompt_data):
# 장치 및 데이터 타입 자동 감지
if torch.cuda.is_available():
device = torch.device("cuda")
torch_dtype = torch.float16 # GPU 메모리 절약
print("🚀 CUDA 가속 (float16) 사용")
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
device = torch.device("mps")
torch_dtype = torch.float16 # Mac GPU 지원
print("🚀 MPS 가속 (float16) 사용")
else:
device = torch.device("cpu")
torch_dtype = torch.float32
print("⚠️ CPU 사용 (속도 저하 예상)")
# 메모리 사용량 최적화를 위한 설정
pipe = StableDiffusionXLPipeline.from_pretrained(
base_model_id,
torch_dtype=torch_dtype,
variant="fp16", # 절반 정밀도 모델 사용
use_safetensors=True
)
실제 사용 경험을 바탕으로 각 모델의 특성을 정리했습니다:
장점:
적합한 상황:
장점:
적합한 상황:
항목 | 로컬 SDXL | 로컬 Flux | DALL-E 3 | Google Gemini |
---|---|---|---|---|
생성 속도 | 40-60초 | 15-20초 | 10-15초 | 8-12초 |
품질 | 좋음 | 매우 좋음 | 우수 | 우수 |
비용 | 전기료만 | 전기료만 | $0.04/장 | 무료 (제한적) |
커스터마이징 | 높음 | 중간 | 낮음 | 낮음 |
테스트 환경: RTX 3060 12GB, 16GB RAM
문제: SDXL + Refiner 모델 동시 로딩 시 12GB GPU 메모리 초과
해결책:
# 단계별 메모리 관리
base_model = load_base_model()
latents = base_model.generate()
del base_model # 베이스 모델 메모리 해제
torch.cuda.empty_cache() # GPU 캐시 정리
refiner_model = load_refiner_model()
final_image = refiner_model.refine(latents)
문제: LLM이 때로는 유효하지 않은 JSON을 반환
해결책:
try:
parsed_result = json.loads(generated_json_str)
except json.JSONDecodeError as json_err:
logging.error(f"JSON 파싱 실패: {json_err}")
# 정규식으로 JSON 부분만 추출 시도
json_match = re.search(r'\{.*\}', generated_json_str, re.DOTALL)
if json_match:
parsed_result = json.loads(json_match.group())
else:
# 완전 실패 시 기본 프롬프트 생성기로 폴백
return generate_prompt()
문제: 플랫폼별로 요구하는 최소 해상도가 다름
해결책:
def process_image(image, prompt_data, format_type):
target_min_resolution = 2500 # 미리캔버스 기준
width, height = image.size
if width < target_min_resolution or height < target_min_resolution:
scale = max(target_min_resolution / width, target_min_resolution / height)
new_width = int(width * scale)
new_height = int(height * scale)
# Lanczos 리샘플링으로 품질 유지
image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
여러 이미지를 연속 생성할 때 모델 로딩 시간을 줄이기 위해 싱글톤 패턴을 검토했지만, 메모리 관리의 복잡성 때문에 단순한 방식을 유지했습니다:
# 각 이미지 생성 시마다 모델 로딩 (단순하지만 안전)
def generate_image_with_local(prompt_data):
pipe = StableDiffusionXLPipeline.from_pretrained(...)
image = pipe(prompt_data["prompt"])
return image
API 모델 사용 시 속도 제한을 고려한 대기 시간 추가:
# API 모델 사용 후 대기
if actual_model_used not in ("local", "flux") and i < count - 1:
wait_time = random.randint(2, 5)
print(f"⏱️ {wait_time}초 대기 중...")
time.sleep(wait_time)
속도: 로컬 모델의 생성 속도가 API 모델 대비 느림
품질 일관성: 프롬프트에 따른 품질 편차
에러 처리: 예상치 못한 상황에서의 에러 처리 부족
이 프로젝트를 통해 로컬 GPU와 다양한 AI 모델을 효과적으로 조합하여 실용적인 자동화 시스템을 구축할 수 있었습니다. 완벽하지는 않지만, 실제 사용 가능한 수준의 결과물을 얻을 수 있었고, 무엇보다 비용 부담 없이 다양한 실험을 할 수 있었던 점이 큰 수확이었습니다.
혹시 비슷한 프로젝트를 진행하시는 분들께 작은 도움이 되길 바라며, 더 좋은 아이디어나 개선 방안이 있으시면 언제든 공유해 주시면 감사하겠습니다.