본문 바로가기

AI 콘텐츠 자동화

파이썬 MoviePy 영상 자동 렌더링 시도와 메모리 누수 극복기

파이썬 MoviePy 영상 자동 렌더링 시도와 메모리 누수 극복기

사업을 운영하다 보면 필연적으로 '콘텐츠 생산의 늪'에 빠지는 순간이 옵니다. 특히 최근 마케팅 트렌드에서는 고객의 이름이나 특정 정보만 살짝 바꾼 '맞춤형 숏폼 영상'이 대량으로 필요했습니다.

처음에는 영상 편집 프로그램을 켜서 텍스트 레이어를 일일이 수정하고 렌더링 버튼을 누르는 수작업을 반복했습니다. 하지만 작업량이 10개, 20개를 넘어가자 확신이 들었습니다. "이건 인간이 할 짓이 아니다."

결국 저는 이 지루한 반복 작업을 코드로 자동화하기로 결심했습니다. 파이썬(Python) 생태계에서 영상 처리로 유명한 MoviePy 라이브러리를 무기 삼아, 나만의 영상 생성 파이프라인 구축에 나섰습니다.


1. 코드 몇 줄의 마법, 그리고 섣부른 기대

MoviePy의 공식 문서와 예제 코드는 생각보다 훨씬 직관적이었습니다. 원본 영상을 불러와 그 위에 텍스트 클립을 얹고 시간대만 맞추면, 순식간에 새로운 영상이 뚝딱 만들어졌습니다.

자신감을 얻은 저는 곧바로 엑셀 파일과 연동했습니다. 고객 이름 리스트를 반복문(Loop)으로 돌려 수백 개의 맞춤형 영상을 자동으로 찍어내는 코드를 완성했죠.

"이제 스크립트만 실행해 두고 퇴근하면, 내일 아침 컴퓨터가 모든 영상을 완성해 놓겠지?"

부푼 기대를 안고 첫 대량 렌더링 스크립트를 실행한 뒤 가벼운 발걸음으로 퇴근했습니다. 진정한 '자동화의 맛'을 깨달았다고 자부하면서 말입니다.


2. 멈춰버린 서버, 처참히 깨진 자동화의 꿈

다음 날 아침, 기대감에 차 모니터를 켠 저는 경악할 수밖에 없었습니다. 밤새 돌아갔어야 할 스크립트는 고작 15개의 영상만 남긴 채 처참하게 뻗어 있었습니다.

터미널 창에는 MemoryError라는 섬뜩한 붉은 글씨, 혹은 운영체제가 프로세스를 강제 종료했다는 Killed 메시지만 덩그러니 남아있었죠.

문제는 그뿐mäß뿐이었습니까? 렌더링된 15개의 영상마저도 1분짜리 결과물을 뽑아내는 데 무려 5~6분이나 소요되었습니다. 게다가 오디오 싱크마저 미세하게 어긋나 있었습니다. 수백 개의 영상을 만들어야 하는 실무 환경에서는 도저히 써먹을 수 없는 속도와 안정성이었습니다.


원인을 찾지 못해 답답하던 다음날 원인을 다시 찾아보았다. 며칠 밤을 새워 해외 개발자 커뮤니티와 MoviePy GitHub을 뒤진 끝에 코드에서 메모리 누수와 단일 스레드라는 두 가지 실수를 발견했다.

영상 자동화 오류를 분석하는 모습

영상 자동화 오류를 분석하는 모습

위 분석에서 아래 두 가지의 해결합이 나왔다. 첫째, 미디어 객체를 닫지 않는 코드 구조의 누수 문제. 둘째, write_videofilethreads=4 옵션 미적용으로 인한 단일 스레드 병목. 이 두 가지를 수정하는 것이 해결 트리거였다.

3. "장비가 문제일까?" 헛다리 짚었던 시간들

처음에는 제가 사용하는 컴퓨터와 클라우드 서버의 낮은 사양을 탓했습니다. 영상 렌더링은 원래 CPU와 RAM 자원을 극한으로 쥐어짜는 작업이니까요.

"결국 돈을 쏟아부어 우수급 서버를 빌려야만 해결되는 건가?" "아니면 파이썬 자체가 C++ 같은 언어보다 느려서 생기는 태생적 한계인가?"

더 나은 하드웨어를 알아보고, 아예 다른 프로그래밍 언어로 갈아타야 하나 진지하게 고민하며 아까운 시간을 흘려보냈습니다.


4. 범인은 '열려있는 객체'와 '단일 스레드'였다

답답한 마음에 며칠 밤낮으로 해외 개발자 커뮤니티와 MoviePy 깃허브(GitHub) 이슈 트래커를 뒤졌습니다. 그리고 마침내, 문제의 진짜 원인이 하드웨어가 아니라 제 '코드'에 있다는 사실을 뼈저리게 깨달았습니다.

첫 번째 원인: 방치된 메모리 누수 (Memory Leak) 저는 VideoFileClip이나 TextClip 같은 미디어 객체를 쓴 뒤, 파이썬의 가비지 컬렉터(Garbage Collector)가 알아서 청소해 줄 것이라 맹신했습니다. 하지만 미디어 파일은 내부적으로 매우 무거운 리소스를 점유합니다. 반복문 안에서 명시적으로 .close()를 호출해 닫아주지 않으면, 메모리 사용량이 눈덩이처럼 불어나 결국 시스템이 터져버리는 구조였습니다.

두 번째 원인: 답답한 렌더링 설정 (Single Thread) MoviePy는 영상을 출력할 때 내부적으로 FFmpeg를 사용합니다. 그런데 제가 write_videofile 함수를 아무 옵션 없이 기본값으로만 쓰다 보니, 멀티 코어 자원이 빵빵한데도 불구하고 단일 스레드(Single Thread)로만 렌더링을 꾸역꾸역 진행하고 있었던 것입니다. 8차선 고속도로를 놔두고 1차선 국도로만 달리고 있던 셈이죠.


5. 최적화의 마법: 메모리를 잡고 속도를 부스트하다

해결책은 두 가지 코드 수정으로 완성되었다. 첫째, 반복문 내부에서 미디어 객체에 .close()를 명시적으로 호출해 메모리를 즐각즉각 해제했다. 둘째, write_videofile() 호출 시 threads=4 옵션을 추가해 FFmpeg가 멀티코어를 활용하도록 했다. 이 두 가지 변경 후 150개 영상 렌더링이 메모리 오류 없이 완주되었고 속도도 영상 1번당 1분 이내로 단축되었다. 하드웨어 업그레이드 없이 코드 두 줄로 해결한 지점이 기억에 남는다.