📍 간단 후기
🏷️ 최종 프로젝트
이제 최종 프로젝트의 마무리도 1주일 남았다. 사용할 모델들을 전부 선정해서 활용을 마쳤다. 이후 Runpod에 서빙하여 연동하는 테스트도 진행했다. 그에 맞춰 파이프라인 구성까지 완료한 상태다. 이제 각자 맡은 부분을 이어 붙이기만 하면 된다. 그래서 남은 작업은 플랫폼 완성 및 배포이다. 배포를 위한 기본 세팅은 완료한 상태이다. 팀원들이 각자 마지막 힘을 쥐어짜 내야 되는 시기인 것 같다.
마지막주는 문서 작업 마무리, 기능 고도화, 발표 준비 등으로 완료하면 될 것이다. 예상대로 결과가 잘 나오면 좋겠지만, 각자 노력에 비례해 좋은 성과를 얻었으면 하는 바람이다.
🏷️ 코딩 테스트 스터디
코딩 테스트 스터디가 마무리됐다. 우리를 제외한 거의 모든 스터디 그룹은 저번주를 끝으로 마무리했었다. 우리 조도 마무리를 확실히 짓기 위해 코딩 테스트를 끝까지 놓지 않으며 마지막까지 열심히 했다고 생각한다.
이번주는 프로그래머스에서 진행하는 PCCP 모의고사 1회를 풀어봤다. 종합적으로 생각해야 하고, 깊게 생각해야 하는 알고리즘이 대부분이었다. 그래서 그런지 이해하는 데에도 시간이 오래 걸렸다.
나름 오래, 또 열심히 풀어온 만큼 처음보다 문제를 해결하는 능력이 늘었다고 생각한다. 처음에는 대학교 때 배웠던 알고리즘의 개념 정도만 익혔다면, 이제는 응용 단계였다. 어떤 알고리즘을 어떻게 풀어야 훨씬 효율성이 좋을지 생각하며 풀다 보니, 어느덧 마지막이 됐다. 스터디가 끝나고 최종 프로젝트가 마무리되면, 혼자 열심히 해보면서 코딩 테스트를 통과하는 날이 오기만을 기다려야겠다.
📍 좋았던 점
- Runpod 모델 서빙 및 연결

드디어 vLLM과의 연결이 완료됐다. 그토록 공들인 파인튜닝된 모델들을 사용할 수 있게 되었다는 것이다. 먼저, vLLM 연결 과정부터 설명하겠다.
- pod 생성 시 Edit template 들어가서 container disk 100으로, Expose HTTP Ports에 8001 추가
- 터미널에서 pip install vllm
- python -m vllm.entrypoints.openai.api_server \ --model kakaocorp/kanana-1.5-8b-instruct-2505 \ --enable-lora \ --lora-modules kanana-finetuned=ki-student/kanana-finetuned-model-v1 \ --host 0.0.0.0 를 입력한다.
우리는 sLLM 모델 활용 포트 번호를 8001번으로 통일했다. 불과 저번주까지도 파인튜닝을 진행했던 모델인 Kanana이다. 기본 모델을 지정하고, LoRA 파인튜닝한 어뎁터를 허깅페이스에 올려 어뎁터로 사용하였다. 해당 모델은 VRAM을 73GB나 잡아먹었다. 사실 많이 놀랐다. 왜냐하면 같이 쓰는 멀티모달 모델은 36GB 정도면 끝났기 때문이다. 파라미터 수가 8B인 것도 한몫하는 것 같았다. 그래서 해당 pod을 A100 PCle을 사용했다. 처음에는 멀티모달 모델이랑 같은 pod에 띄울 계획이었다. 그러나 말한 것과 같이 VRAM이 한없이 부족했고, 그에 맞출 GPU를 쓴다는 것이 가격 측면에서 훨씬 비쌌다. 결국 pod을 2개를 사용했다. 심지어 이게 더 이득이었다. A100 하나와 A6000을 사용했다. 멀티모달 모델이 A6000에 해당한다.
Runpod에 모델을 서빙하면 Front, 즉 react와의 연결이 필요했다. 이는 엔드포인트를 사용하면 된다. 엔드포인트 주소는 .env에 "https://엔드포인트-8001/proxy.runpod.net" 이렇게 입력해 주었다. 이렇게 활용한 결과가 위 사진에 해당한다.
다음으로는 이미지 생성용 멀티모달 모델 서빙이다. 해당 모델도 Runpod에 띄었다. 멀티모달 모델은 vLLM에서 활용이 불가하다. 때문에, FastAPI를 활용했다. 과정은 다음과 같다.
- pod 생성 시 Edit template 들어가서 container disk 50으로, Expose HTTP Ports에 8003 추가
- run.sh와 Fast API 서빙 코드 업로드
- 터미널에서 pip install fastapi "uvicorn[standard]" boto3 diffusers transformers accelerate safetensors protobuf sentencepiece peft
- huggingface-cli login
- chmod +x run.sh 로 run.sh 접근 권한 수정
- ./run.sh로 모델 서빙 실행
# ────────────────────────────────────────────────
# 💬 Environment Variables (set default values if not provided)
FLUX_PORT=${FLUX_PORT:-8003}
LOG_DIR="./logs"
WAIT_TIMEOUT=3000
# ────────────────────────────────────────────────
# 📁 Create log directory
mkdir -p "$LOG_DIR"
# ────────────────────────────────────────────────
# 🎨 Color codes
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
# ────────────────────────────────────────────────
# 🔍 Function to check if a port is in use
check_port() {
if ss -tuln | grep ":$1" > /dev/null; then
echo -e "${RED}⚠️ Port $1 is already in use. Aborting.${NC}"
exit 1
fi
}
# ────────────────────────────────────────────────
# 🔄 Function to wait for the server to be ready
wait_for_server() {
local port=$1
local name=$2
local url=$3
local timeout=$WAIT_TIMEOUT
local start_time=$(date +%s)
echo -e "${YELLOW}⏳ Waiting for $name to be ready on port $port...${NC}"
while :; do
# Try to connect to the server; if successful, exit the loop
curl -s "$url" > /dev/null
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ $name is running on port $port${NC}"
return 0
fi
local current_time=$(date +%s)
if (( current_time - start_time > timeout )); then
echo -e "${RED}❌ $name failed to start within the timeout.${NC}"
return 1
fi
# Retry every 5 seconds
sleep 5
done
}
# ────────────────────────────────────────────────
# 🖼️ Start FastAPI server (in the background)
check_port "$FLUX_PORT"
echo -e "${YELLOW}🖼️ Starting FLUX-HD FastAPI on port $FLUX_PORT...${NC}"
uvicorn serve_flux:app --host 0.0.0.0 --port "$FLUX_PORT" > "$LOG_DIR/flux.log" 2>&1 &
# Wait for the FastAPI server to be ready
wait_for_server "$FLUX_PORT" "FastAPI (FLUX-HD)" "http://localhost:$FLUX_PORT/docs" || exit 1
# ────────────────────────────────────────────────
# 📝 Show real-time logs
echo -e "${GREEN}✅ FLUX-HD server is running. Showing real-time logs...${NC}"
# Follow the log file in real-time
tail -f "$LOG_DIR/flux.log"
run.sh 코드이다. FastAPI를 사용했고, 8003 포트번호를 사용했다. 참고로, Wait time이 너무 짧으면 모델 서빙 시간이 부족해 오류 나고, 모델 서빙될 동안 VRAM은 계속 사용되니, pod을 다시 생성해야 하는 번거로움이 발생했었다.
import os
from fastapi import FastAPI, Request, HTTPException
from diffusers import FluxKontextPipeline
import torch
from io import BytesIO
import boto3
import time
app = FastAPI()
# -----------------------------------------------------------------------------
# AWS S3 설정
# -----------------------------------------------------------------------------
# .env 파일에서 환경 변수를 읽어옵니다.
S3_BUCKET_NAME = '버킷 이름'
AWS_ACCESS_KEY_ID = 'S3 생성 시 발급받은 Key'
AWS_SECRET_ACCESS_KEY = 'S3 생성 시 발급받은 Secret Key'
AWS_REGION = 'AWS 에서 연결된 지역'
# 필수 환경 변수가 모두 설정되었는지 확인
if not all([S3_BUCKET_NAME, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION]):
print("Error: Missing one or more required AWS environment variables.")
print("Please check your .env file for: AWS_STORAGE_BUCKET_NAME, ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_REGION_NAME")
# 환경 변수가 없으면 S3 클라이언트 생성하지 않음
s3_client = None
else:
# 모든 설정이 존재하면 S3 클라이언트 생성
try:
s3_client = boto3.client(
's3',
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
region_name=AWS_REGION
)
print("S3 client initialized successfully.")
except Exception as e:
print(f"Error initializing S3 client: {e}")
s3_client = None
# -----------------------------------------------------------------------------
# Diffusion Pipeline 로드
# -----------------------------------------------------------------------------
try:
pipe = FluxKontextPipeline.from_pretrained(
"black-forest-labs/FLUX.1-Kontext-dev",
torch_dtype=torch.float16
)
pipe.load_lora_weights("mingyu-oo/FLUX_LoRA_adapter", weight_name='pytorch_lora_weights.safetensors')
pipe.fuse_lora(lora_scale=0.5)
pipe.enable_vae_slicing()
pipe.enable_vae_tiling()
pipe.to("cuda")
except Exception as e:
print(f"Error loading diffusion pipeline: {e}")
pipe = None
# -----------------------------------------------------------------------------
# 이미지 생성 및 S3 업로드 API 엔드포인트
# -----------------------------------------------------------------------------
@app.post("/generate")
async def generate_image(request: Request):
if pipe is None:
raise HTTPException(status_code=500, detail="Diffusion pipeline not loaded.")
if s3_client is None:
raise HTTPException(status_code=500, detail="S3 client not initialized. Check your AWS credentials.")
try:
data = await request.json()
prompt = data.get("prompt")
prompt_2 = data.get("prompt_2", "") # prompt_2 파라미터 추가 (기본값 빈 문자열)
negative_prompt = data.get("negative_prompt", "") # negative_prompt 파라미터 추가 (기본값 빈 문자열)
if not prompt:
raise HTTPException(status_code=400, detail="Prompt not provided.")
# 이미지 생성
image = pipe(
prompt=prompt,
prompt_2=prompt_2, # 전달받은 prompt_2 사용
negative_prompt=negative_prompt, # 전달받은 negative_prompt 사용
num_inference_steps=50,
true_cfg_scale=1.8,
guidance_scale=9.0,
generator=torch.Generator(device="cuda").manual_seed(42) # 고정 시드 적용
).images[0]
# 이미지를 S3에 업로드
# 파일명 중복을 피하기 위해 타임스탬프와 프롬프트 일부를 사용
file_name = f"images/{int(time.time())}_{prompt[:50].replace(' ', '_').replace('/', '')}.png"
img_buffer = BytesIO()
image.save(img_buffer, "PNG")
img_buffer.seek(0)
s3_client.upload_fileobj(
img_buffer,
S3_BUCKET_NAME,
file_name,
ExtraArgs={'ContentType': 'image/png'}
)
s3_url = f"https://{S3_BUCKET_NAME}.s3.{AWS_REGION}.amazonaws.com/{file_name}"
return {"s3_url": s3_url}
except Exception as e:
print(f"Error during image generation or upload: {e}")
raise HTTPException(status_code=500, detail="Internal server error.")
위 코드가 Flux 모델 서빙을 위한 Fast API 코드이다. 확장자는 png를 이용했다. 또 우리는 S3에 연결해서 생성된 이미지를 사용자에게 보여줘야 했다. 사용자와 sLLM이 소통한 다음, vLLM에 Fast API에 전달해 이미지를 생성하여 S3에 저장한다. 이후 사용자에게 보여줌으로써 이미지 생성이 완료된다.
- Airflow

Airflow란, 데이터 수집 / 전처리 / DB 저장 까지 한 번에 자동으로 해주는 플랫폼이라고 생각하면 이해하기 쉬울 것이다. 물론 이 구성은 본인이 직접 설계만 하면 된다. 해당 플랫폼은 설계된 대로 매일 몇 시에 그 작업이 진행되게끔 해준다. 한마디로 데이터 파이프라인 자동 실행 플랫폼인 것이다.
우리 조는 각 차에 맞는 리뷰 데이터를 최신화했어야 했다. 이를 위해 가장 알맞은 플랫폼이 Airflow이다. 해당 Airflow를 사용하기 위해서는 또 하나의 Docker 컨테이너가 필요했다. 마이크로서비스를 추구했던 만큼, 독립적으로 활용하기 위함이다.
리뷰 데이터에는 각 차에 맞는 리뷰 뿐만 아니라 별점도 들어있다. 따라서 Airflow 폴더에 DAG, Dockerfile, 크롤링 코드가 포함되었다. 이 크롤링 코드는 DB에 들어갈 데이터를 최신화시킨다. 최신화 후, 해당 데이터를 DB에 넣어 서버에 적용시키는 것이 주된 목적이다.
직접 Airflow를 사용해 보니, 정말 신기했다. 되게 간단하게 구성했지만, 체계화되어있다는 사실이 신기했다. Airflow를 쓰겠다고 시작했을 때는 거창한 계획이라고 생각했지만, 막상 진행해 보니 나름 뿌듯한 성과를 낼 수 있어서 다행이라고 생각한다.
📍 부족한 점
- 멀티모달 모델 서빙

위 사진이 S3와 연동되어 이미지가 생성된 객체 캡쳐본이다. S3와의 연동은 성공적으로 완료됐다. 반면, 이미지가 정상적으로 생성되지 못했다. 아무것도 없는 검은색 이미지가 생성될 뿐이었다. 만족스럽지 못한 결과가 나와서 아쉬웠다. 버전 호환성 문제일지, 아니면 코드의 문제일지는 고민해 보면서 마무리를 지어야 할 것 같다.
그래도 이 과정 중에 얻은 것은 있다. 비록 테스트용으로 간단하게 구성했지만, 파이프라인이 제대로 작동한다는 점이다. 우리의 기존 파이프라인에 적용시켜보기만 한다면, 완벽한 결과물이 나올 것 같아서 기대된다.
- 마무리 해야 될 작업들
이제 개발 단계를 슬슬 마무리 짓고, 발표 준비에 들어가야 한다. 유종의 미를 거두기 위해서다. 그러기 위해선 해야 될 작업들이 남았다.
- 멀티모달 모델 이미지 정상 생성
- 영상 생성 모델 및 3D 모델 연동한 Comfy UI 연결
- 해당 모델들 파이프라인에 적용
- 배포 및 정상 실행 여부 파악
- 플랫폼 기능 고도화
- 발표 준비 및 문서 작업
일주일 남은 것치곤 많이 남았다. 불가능하다고 생각할 수도 있지만, 최대한 긍정적으로 프로젝트를 진행하고 있다. 얼른 성공적으로 마무리해서 유종의 미를 거두었으면 좋겠다.
📍 성찰 및 마무리
해당 블로그가 월별 블로그의 마지막이다. 6개월이 종료됐다는 소리이다. 그간 지루했던, 힘들었던, 또 허덕였던 순간들 등이 많았다. 아마 블로그에서 성찰 및 마무리 주제에서 시간을 잘 활용해야겠다는 문장이 많이 사용됐던 것 같다. 되돌아보니, 나는 시간을 허투루 보낸 사람은 아니라는 생각이 든다. 나름 열심히 활동했다고 생각한다. 이제 남은 건, 2번째로 많이 말한 취업이다. 취업 자체에도 시간을 소중이 활용해야 한다.
이제 9월이다. 하반기 공채가 많이 뜨기 시작했다. 이에 맞춰 자소서를 작성 중이다. 자소서를 쓰다 보니, 부트캠프를 다니기 전에 비해 쓸 말도 많았고, 경험해 본 일도 많이 표현할 수 있었다. 확실히 자신감이 생긴 지난 6개월이다. 물론 모든 내용을 내 걸로 만들지는 못했으나, 본인이 자신 있는, 또 가장 재밌었던 일이 무엇이었는지 다시 한번 생각해 보면서 취업 시장에 뛰어들어야 한다고 생각한다. 이번에 공고들을 찾아봐도 AI 관련 직무는 많지만, AI 관련 어떤 일을 할 것인지, 어느 업종으로 갈 지에 대해서 깊게 생각해 보는 시간을 가지는 것도 무조건 필요하다. 물론 지금은 급해서 모든 업종에 지원하는 사람도 있겠지만, 언젠가 "여기에 왜 지원했지?" 했을 때 떳떳하게, 자신 있게 말할 수 있는 곳에 지원해야겠다는 생각이 들었다.
최종 프로젝트가 아직 끝이 안났다. 나름 결과물이 보여서 부정이 안 보이는 것일 수도 있다. 중간중간 해야 될 작업이 많아 불안하기도 했다. 그렇지만 프로젝트의 마무리는 봐야 한다. 앞으로 일주일이 남았다. 할 수 있다고 믿는다. 거의 다 왔다. 우리는 할 수 있다.
'[SKN FAMILY AI CAMP] > 월간' 카테고리의 다른 글
| 🐉 SKN FAMILY AI CAMP 13기 AI 부트캠프 수료 후기 (2) | 2025.09.18 |
|---|---|
| 🐉 SKN FAMILY AI CAMP 13기 19주차(4개월차) 후기 (2025.07.28 ~ 2025.08.01) (10) | 2025.08.01 |
| 🐉 SKN FAMILY AI CAMP 13기 15주차 (3개월차) 후기 (2025.06.30 ~ 2025.07.04 (2) | 2025.07.04 |
| 🐉 SKN FAMILY AI CAMP 13기 10주차 (2개월차) 후기 (2025.05.26 ~ 2025.05.30) (2) | 2025.05.30 |
| 🐉 SKN FAMILY AI CAMP 13기 5주차 한달 후기 (2025.04.21 ~ 2025.04.25) (0) | 2025.04.25 |