살아있는 AI 고양이 프로젝트 만들어보기(3) - 배포(.exe)

2025. 12. 8. 23:25·5. [개인] 프로젝트 및 공모전/4-2 바탕화면 AI 펫 프로그램

매번 npm run dev로 개발 서버를 띄워야만 볼 수 있었던 내 캐릭터를, 드디어 설치 파일(.exe)로 만들어 실제 바탕화면 위젯처럼 띄우는 데 성공했다.

 

하지만 끝내 해결되지 않은 '좌우 반전' 문제는... 결국 개발자스러운(?) 방식보다는 디자이너스러운 방식으로 해결하기로 타협했다.


1. 배포의 시작: Electron과 Python 엮기

가장 큰 과제는 React(화면)와 Python(AI 뇌)을 분리된 상태로 유지하면서, 하나의 .exe 파일 안에 모두 담는 것이었습니다.

A. Python 서버 (FastAPI) 패키징

AI 서버 역할을 하는 Python 코드를 pyinstaller로 실행 파일(main.exe)로 만들었습니다. 이때 uvicorn을 CLI 명령어가 아닌 스크립트 내부에서 실행하도록 코드를 수정했습니다.

# server/main.py
if __name__ == "__main__":
    import uvicorn
    # exe 파일에서는 reload 옵션을 제거하고 명시적으로 포트를 지정합니다.
    uvicorn.run(app, host="0.0.0.0", port=8000)

B. Electron Builder 설정

electron-builder를 사용하여 최종 .exe 파일을 생성합니다. 이때, 위에서 만든 Python 실행 파일을 extraResources 옵션을 통해 패키지 내부에 포함시킵니다.

// client/package.json
"scripts": {
  "dist": "vite build && electron-builder"
},
"build": {
  // ...
  "extraResources": [
    { "from": "api", "to": "api" } // Python EXE가 있는 폴더를 통째로 포함
  ]
}

2. 자동 실행과 바탕화면 점령

앱이 켜지는 순간 Python 서버도 자동으로 켜지도록 Electron의 메인 프로세스(electron.cjs)에 child_process.spawn 로직을 구현했습니다.

A. Python 서버 자동 실행 로직

앱이 배포된 상태(app.isPackaged)일 때만 내부의 main.exe를 실행합니다.

// electron.cjs의 startPythonServer 함수
function startPythonServer() {
  if (app.isPackaged) {
    // resourcesPath는 패키징된 앱 내부의 파일 경로를 가리킵니다.
    const apiPath = path.join(process.resourcesPath, "api", "main.exe");
    
    // 서버 프로세스 실행 및 에러 로그 콘솔 출력
    apiProcess = spawn(apiPath);
    apiProcess.stderr.on("data", (data) => {
      console.error(`Python Error: ${data}`);
    });
  } 
}

B. 풀스크린 및 투명 창 설정

캐릭터가 바탕화면 전체를 돌아다니고, 배경은 투명하게, 클릭은 캐릭터 위에 있을 때만 작동하도록 설정했습니다.

// electron.cjs의 createWindow 함수
function createWindow() {
  const primaryDisplay = screen.getPrimaryDisplay();
  const { width, height } = primaryDisplay.workAreaSize; // 모니터 크기 가져오기

  mainWindow = new BrowserWindow({
    x: 0,
    y: 0,
    width: width,      // 화면 전체 너비
    height: height,    // 화면 전체 높이
    transparent: true, // 투명 창 설정
    frame: false,      // 창 테두리 제거
    // ...
  });
  // ... (마우스 관통 IPC 로직)
}

3. 난제 해결: 이미지 로딩 및 좌우 반전

A. 이미지 로딩 문제 해결

배포 환경에서 이미지가 보이지 않는 문제(엑스박스)는 Vite가 빌드할 때 이미지 경로를 인식하지 못했기 때문이었습니다. public 폴더 대신 src/assets에 이미지를 넣고, new URL을 사용해 강제로 경로를 주입하여 해결했습니다.

// App.jsx의 getAssetPath 함수
const getAssetPath = (prefix, frame) => {
  try {
    // 빌드 후 경로가 꼬이지 않게 URL 객체를 사용하여 실제 경로를 얻어냅니다.
    return new URL(`./assets/${prefix}${frame}.png`, import.meta.url).href;
  } catch (e) {
    console.error("Image not found:", prefix, frame, e);
    return "";
  }
};

B. 최종 난제: 좌우 반전과의 타협

가장 많은 시간을 소모한 부분은 캐릭터가 왼쪽으로 움직일 때 이미지를 뒤집는 로직이었습니다. scaleX = -0.5를 적용했음에도 Matter.js 엔진 내부의 복잡한 렌더링 과정 때문에 캐릭터가 좌우 반전을 무시하거나 깜빡거리는 현상이 지속되었습니다.

// App.jsx (계속 실패했던 코드 로직)
// Matter.js는 텍스처가 바뀌는 순간 스케일을 초기화하는 경향이 있습니다.
const baseScale = 0.5;
cat.render.sprite.xScale = state.facingLeft ? -baseScale : baseScale;
// 이 코드를 아무리 강제해도, 렌더링 타이밍 문제로 제대로 적용되지 않았습니다.

현실적인 타협: 이미지 리소스로 해결

결국, 코드 레벨에서 복잡한 렌더링 버그와 싸우는 대신, 리소스 레벨에서 문제를 해결하기로 결정했습니다.

향후 계획:

  1. 현재 오른쪽을 보고 있는 이미지 파일을 포토샵 등 툴로 좌우 반전하여 walk_left_01.png 와 같이 왼쪽 보는 전용 이미지로 저장합니다.
  2. App.jsx에서 scaleX: -0.5 로직을 완전히 제거하고, state.facingLeft 값에 따라 이미지 경로(walk_left 또는 walk_right)만 바꿔주는 방식으로 전환할 예정입니다.

오늘은 코드의 논리적 승리보다 "결과물을 만드는 실용성"을 택한 날이었습니다. 일단 바탕화면에 펫을 띄우는 데 성공했으니, 다음 목표는 이미지 교체 로직 구현입니다!

'5. [개인] 프로젝트 및 공모전 > 4-2 바탕화면 AI 펫 프로그램' 카테고리의 다른 글

AI 바탕화면 펫 프로젝트 개선기 - 좌우 반전부터 실행 최적화까지  (0) 2025.12.15
살아있는 AI 고양이 프로젝트 만들어보기(2)  (0) 2025.12.07
살아있는 AI 고양이 프로젝트 만들어보기(1)  (0) 2025.12.06
'5. [개인] 프로젝트 및 공모전/4-2 바탕화면 AI 펫 프로그램' 카테고리의 다른 글
  • AI 바탕화면 펫 프로젝트 개선기 - 좌우 반전부터 실행 최적화까지
  • 살아있는 AI 고양이 프로젝트 만들어보기(2)
  • 살아있는 AI 고양이 프로젝트 만들어보기(1)
고니3000원
고니3000원
프로젝트의 구현 과정과 기술적 노하우를 담았습니다. AI 모델 연구와 매일의 학습 기록을 차곡차곡 공유하고 있습니다.
  • 고니3000원
    곤이의 공부 블로그
    고니3000원
  • 전체
    오늘
    어제
    • 분류 전체보기 (231)
      • 1. AI 논문 + 모델 분석 (22)
        • AI 논문 분석 (13)
        • AI 모델 분석 (9)
      • 2. 자료구조와 알고리즘 (16)
        • 2-1 자료구조와 알고리즘 (13)
        • 2-2 강화학습 알고리즘 (3)
      • 3. 자습 & 메모(실전, 실습, 프로젝트) (27)
        • 3-1 문제 해석 (4)
        • 3-2 메모(실전, 프로젝트) (14)
        • 3-3 배포 실전 공부 (7)
        • 3-4 최신 기술 분석 (2)
      • 4. [팀] 프로젝트 및 공모전 (31)
        • 4-1 팀 프로젝트(메모, 공부) (1)
        • 4-2 Meat-A-Eye (6)
        • 4-3 RL-Tycoon-Agent (3)
        • 4-4 구조물 안정성 물리 추론 AI 경진대회(D.. (4)
        • 4-5 AgentShield(보안 플랫폼) (17)
      • 5. [개인] 프로젝트 및 공모전 (25)
        • 4-1 귀멸의칼날디펜스(자바스크립트 활용) (5)
        • 4-2 바탕화면 AI 펫 프로그램 (4)
        • 4-3 개인 프로젝트(기타) (3)
        • 4-4 공모전 (9)
        • 4-5 나만의 로컬 LLM 멀티 에이전트 구축 (4)
      • 6. 경진 대회 (kaggle, dacon 등) (12)
        • 6-1 연구 및 개선 기록 (11)
        • 6-2 공유 코드 분석하기 (1)
      • 개념 정리 step1 (32)
        • Python 기초 (7)
        • DBMS (1)
        • HTML | CSS (3)
        • Git | GitHub (1)
        • JavaScript (5)
        • Node.js (5)
        • React (1)
        • 데이터 분석 (6)
        • Python Engineering (3)
      • 개념 정리 step2 (60)
        • Machine | Deep Learning (15)
        • 멀티모달(Multi-modal) (23)
        • 강화 학습 (10)
        • AI Agent (9)
        • 메디컬 이미지 (3)
      • 개인 공부 - 내가 공부하고 싶은 모든 것 (3)
        • 1. 인프라 (2)
        • 2. 자율주행 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Notion-포트폴리오
    • Github
  • 공지사항

    • 나의 핵심 가치
  • 인기 글

  • 태그

    강화학습
    자바스크립트
    공모전
    연구
    자료구조
    데이터분석
    llm
    전처리
    API
    Vision
    Python
    논문 리뷰
    OCR
    구현
    ViT
    프로젝트
    학습
    pandas
    Agent
    transformer
    Lora
    RAG
    github
    Ollama
    파이썬
    인공지능
    Ai
    알고리즘
    paddleocr
    파인튜닝
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고니3000원
살아있는 AI 고양이 프로젝트 만들어보기(3) - 배포(.exe)
상단으로

티스토리툴바