유튜브 쇼츠, 릴스, 틱톡 등 숏폼 비디오의 수요는 폭발적으로 증가했지만, 하나의 영상을 제작하는 데에는 여전히 많은 시간과 노력이 소요됩니다. 스크립트 작성, 나레이션 녹음, 적절한 영상 클립 탐색, 그리고 최종 편집까지의 과정은 매우 반복적이고 노동 집약적입니다.
이번 가이드에서는 이 모든 과정을 자동화하는 시스템을 Python으로 구축합니다. 사용자가 ‘주제’ 하나만 던져주면, AI가 시나리오 작가, 성우, 그리고 비디오 편집자의 역할을 모두 수행하여 완성된 영상을 출력하는 파이프라인을 만듭니다. 우리는 다음의 AI 기술들을 조합하여 이 목표를 달성할 것입니다.
- 스크립트 생성: Google Gemini API
- 음성 합성(TTS): ElevenLabs API
- 영상 소스 수집: Pexels API
- 영상 합성/편집: MoviePy 라이브러리
이 가이드를 완료하면, 당신은 콘텐츠 생산성을 극적으로 향상시키는 강력한 자동화 엔진을 소유하게 될 것입니다.
사전 준비 사항
본격적인 구축에 앞서 다음 항목들이 반드시 준비되어야 합니다.
- Python 3.8 이상 버전: 로컬 컴퓨터에 Python이 설치되어 있어야 합니다.
- API 키: 아래 서비스들에 가입하고 무료 API 키를 발급받아야 합니다.
- Google AI (Gemini): Google AI Studio에서 API 키를 생성합니다.
- ElevenLabs: ElevenLabs 웹사이트에서 가입 후, Profile > API Key에서 키를 복사합니다. (무료 플랜으로도 충분히 테스트 가능합니다.)
- Pexels: Pexels 웹사이트에서 가입 후 API 키를 요청합니다.
- FFmpeg: MoviePy 라이브러리의 핵심 의존성 프로그램입니다. 영상과 오디오를 처리하는 데 반드시 필요합니다.
- 설치 안내: 공식 FFmpeg 웹사이트의 가이드에 따라 당신의 운영체제(Windows, macOS, Linux)에 맞게 설치해야 합니다. 이 과정 없이는 스크립트가 작동하지 않습니다.
1단계: 프로젝트 환경 설정 및 라이브러리 설치
목표: 프로젝트 폴더를 구성하고, 필요한 모든 Python 라이브러리를 설치하며, API 키를 안전하게 관리할 환경을 설정합니다.
이유: 프로젝트별로 독립된 가상 환경을 사용하는 것은 의존성 충돌을 방지하는 가장 좋은 방법입니다. 또한, API 키를 코드에 직접 하드코딩하는 대신 .env 파일을 사용하면 보안을 강화하고 키 관리를 용이하게 할 수 있습니다.
- 프로젝트를 진행할 폴더를 생성하고, 터미널에서 해당 폴더로 이동합니다. code Bashdownloadcontent_copyexpand_less
mkdir ai-video-generator cd ai-video-generator - 가상 환경을 생성하고 활성화합니다. code Bashdownloadcontent_copyexpand_lessIGNORE_WHEN_COPYING_STARTIGNORE_WHEN_COPYING_END
# 가상 환경 생성 python -m venv venv # Windows .\venv\Scripts\activate # macOS / Linux source venv/bin/activate - 필요한 라이브러리를 설치합니다. code Bashdownloadcontent_copyexpand_lessIGNORE_WHEN_COPYING_STARTIGNORE_WHEN_COPYING_END
pip install moviepy google-generativeai pexels-api elevenlabs python-dotenv - 프로젝트 폴더 최상단에 .env 파일을 생성하고, 발급받은 API 키들을 다음과 같이 저장합니다. code .envdownloadcontent_copyexpand_lessIGNORE_WHEN_COPYING_STARTIGNORE_WHEN_COPYING_END
GOOGLE_API_KEY="여기에_구글_API_키를_입력하세요" PEXELS_API_KEY="여기에_Pexels_API_키를_입력하세요" ELEVENLABS_API_KEY="여기에_ElevenLabs_API_키를_입력하세요"
검증: pip list 명령어를 실행했을 때 설치한 라이브러리 목록이 보이고, 프로젝트 폴더에 .env 파일이 생성되었으면 이 단계는 성공입니다.
2단계: AI 스크립트 및 영상 검색어 생성
목표: 주제를 입력받아 Gemini AI를 통해 영상 시나리오와 각 장면에 어울리는 영상 소스 검색어를 동시에 생성하는 함수를 구현합니다.
이유: 자동화를 위해서는 AI가 예측 가능한 ‘구조화된 데이터’를 반환하도록 만드는 것이 핵심입니다. 단순히 줄글 스크립트만 받는 것이 아니라, 각 문장에 맞는 영상 검색어(예: ‘flying eagle’)를 함께 생성하도록 프롬프트를 설계해야 다음 단계가 순조롭게 진행됩니다.
main.py 파일을 생성하고 다음 코드를 추가합니다. code Pythondownloadcontent_copyexpand_less
IGNORE_WHEN_COPYING_START
IGNORE_WHEN_COPYING_END
import os
import google.generativeai as genai
from dotenv import load_dotenv
# .env 파일에서 환경 변수 로드
load_dotenv()
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
def generate_script_and_visuals(topic):
"""주제를 기반으로 스크립트와 영상 검색어를 생성합니다."""
# 구조화된 응답을 유도하는 프롬프트
prompt = f"""
당신은 숏폼 비디오 작가입니다. "{topic}"이라는 주제로 5개의 장면으로 구성된 짧은 비디오 스크립트를 작성해주세요.
각 장면은 나레이션 텍스트와 그 장면에 어울리는 영상 소스를 찾기 위한 영어 검색 키워드, 단 2~3단어로 구성되어야 합니다.
반드시 다음 형식을 엄격하게 지켜서 응답해주세요:
Scene 1: [나레이션 텍스트] | Visual: [영상 검색 키워드]
Scene 2: [나레이션 텍스트] | Visual: [영상 검색 키워드]
Scene 3: [나레이션 텍스트] | Visual: [영상 검색 키워드]
Scene 4: [나레이션 텍스트] | Visual: [영상 검색 키워드]
Scene 5: [나레이션 텍스트] | Visual: [영상 검색 키워드]
"""
try:
model = genai.GenerativeModel('gemini-pro')
response = model.generate_content(prompt)
# 응답 파싱
scenes = []
for line in response.text.strip().split('\n'):
if 'Scene' in line and '|' in line:
parts = line.split('|')
narration = parts[0].split(':')[1].strip()
visual_keyword = parts[1].split(':')[1].strip()
scenes.append({"narration": narration, "visual": visual_keyword})
if not scenes:
raise ValueError("AI가 유효한 형식의 스크립트를 생성하지 못했습니다.")
print("✅ 스크립트 및 영상 키워드 생성 완료")
return scenes
except Exception as e:
print(f"❌ 스크립트 생성 중 오류 발생: {e}")
return None
# --- 테스트 코드 ---
if __name__ == "__main__":
test_topic = "우주 탐사의 놀라운 순간들"
script_data = generate_script_and_visuals(test_topic)
if script_data:
for i, scene in enumerate(script_data, 1):
print(f"--- Scene {i} ---")
print(f" 나레이션: {scene['narration']}")
print(f" 영상 키워드: {scene['visual']}")
검증: 터미널에서 python main.py를 실행했을 때, “✅ 스크립트 및 영상 키워드 생성 완료” 메시지와 함께 각 장면의 나레이션과 영상 키워드가 깔끔하게 출력되면 성공입니다.
3단계: 나레이션 음성 파일 생성
목표: 2단계에서 생성된 전체 스크립트 텍스트를 하나로 합쳐, ElevenLabs API를 통해 자연스러운 목소리의 음성 파일(.mp3)로 변환합니다.
이유: 고품질의 TTS는 영상의 몰입감을 결정하는 중요한 요소입니다. ElevenLabs는 현존하는 가장 자연스러운 목소리를 제공하는 서비스 중 하나로, 영상의 완성도를 높여줍니다.
main.py에 다음 함수를 추가합니다. code Pythondownloadcontent_copyexpand_less
IGNORE_WHEN_COPYING_START
IGNORE_WHEN_COPYING_END
from elevenlabs.client import ElevenLabs
def create_voiceover(script_text, filename="narration.mp3"):
"""주어진 텍스트로 음성 파일을 생성합니다."""
try:
client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
response = client.text_to_speech.convert(
voice_id="pNInz6obpgU5sV7F_ynA", # 예시 목소리 ID (Rachel)
text=script_text,
model_id="eleven_multilingual_v2" # 다국어 지원 모델
)
with open(filename, "wb") as f:
for chunk in response:
f.write(chunk)
print(f"✅ 음성 파일 '{filename}' 생성 완료")
return filename
except Exception as e:
print(f"❌ 음성 파일 생성 중 오류 발생: {e}")
return None
4단계: 영상 소스 다운로드
목표: 스크립트 각 장면에 지정된 ‘영상 키워드’를 Pexels API에 전달하여 관련 영상을 검색하고, 가장 적합한 영상을 다운로드합니다.
이유: 영상의 내용과 시각 자료가 일치할 때 메시지 전달력이 극대화됩니다. Pexels는 고품질의 무료 스톡 영상과 사진을 API를 통해 제공하므로, 자동화 시스템에 통합하기 매우 용이합니다.
main.py에 다음 함수를 추가합니다. code Pythondownloadcontent_copyexpand_less
IGNORE_WHEN_COPYING_START
IGNORE_WHEN_COPYING_END
from pexels_api import API
import requests
def get_visuals(scenes):
"""각 장면에 맞는 영상을 Pexels에서 다운로드합니다."""
try:
api = API(os.getenv("PEXELS_API_KEY"))
video_paths = []
# 영상 저장을 위한 폴더 생성
if not os.path.exists("videos"):
os.makedirs("videos")
for i, scene in enumerate(scenes):
keyword = scene['visual']
print(f"🔍 '{keyword}' 키워드로 영상 검색 중...")
api.search_videos(keyword, page=1, results_per_page=1)
videos = api.get_videos()
if not videos:
print(f" ⚠️ '{keyword}'에 대한 영상을 찾을 수 없습니다. 건너뜁니다.")
continue
# 가장 적합한 화질의 비디오 링크 찾기
video_url = None
for video_file in videos[0].video_files:
# 1080p 영상을 우선적으로 찾음
if video_file.height == 1080:
video_url = video_file.link
break
# 1080p가 없으면 가장 큰 화질의 영상을 사용
if not video_url:
video_url = videos[0].video_files[0].link
# 영상 다운로드
response = requests.get(video_url, stream=True)
response.raise_for_status() # 오류 발생 시 예외 처리
video_path = f"videos/scene_{i+1}.mp4"
with open(video_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
video_paths.append(video_path)
print(f" ✅ 영상 다운로드 완료: {video_path}")
print("✅ 모든 영상 소스 준비 완료")
return video_paths
except Exception as e:
print(f"❌ 영상 다운로드 중 오류 발생: {e}")
return []
5단계: 모든 요소 결합 및 최종 영상 렌더링
목표: 지금까지 준비된 나레이션 음성 파일과 다운로드한 영상 클립들을 MoviePy를 사용하여 하나의 완성된 비디오로 합칩니다.
이유: MoviePy는 Python에서 프로그래밍 방식으로 영상을 자르고, 붙이고, 효과를 추가하는 등 복잡한 영상 편집 작업을 수행할 수 있게 해주는 강력한 라이브러리입니다. 자동화 파이프라인의 최종 조립 단계에 해당합니다.
main.py에 마지막 함수와 실행 로직을 추가합니다. code Pythondownloadcontent_copyexpand_less
IGNORE_WHEN_COPYING_START
IGNORE_WHEN_COPYING_END
from moviepy.editor import VideoFileClip, AudioFileClip, concatenate_videoclips
def assemble_video(video_paths, audio_path, output_filename="final_video.mp4"):
"""영상 클립들과 오디오를 합쳐 최종 영상을 만듭니다."""
try:
print("🎬 최종 영상 조립 시작...")
clips = [VideoFileClip(path) for path in video_paths]
# 각 클립의 길이를 오디오 길이에 맞춰 분배
audio_clip = AudioFileClip(audio_path)
total_duration = audio_clip.duration
avg_clip_duration = total_duration / len(clips)
# 각 클립을 평균 길이에 맞게 자르고 리사이즈 (9:16 비율, 1080x1920)
processed_clips = []
for clip in clips:
# 숏폼 영상 비율(9:16)에 맞게 중앙을 크롭
(w, h) = clip.size
target_ratio = 9.0 / 16.0
current_ratio = w / h
if current_ratio > target_ratio: # 가로가 더 넓으면 좌우를 자름
new_width = int(h * target_ratio)
crop_x = (w - new_width) / 2
cropped_clip = clip.crop(x1=crop_x, width=new_width)
else: # 세로가 더 길면 위아래를 자름
new_height = int(w / target_ratio)
crop_y = (h - new_height) / 2
cropped_clip = clip.crop(y1=crop_y, height=new_height)
# 최종 사이즈로 리사이즈 및 길이 조절
resized_clip = cropped_clip.resize(height=1920)
final_subclip = resized_clip.set_duration(avg_clip_duration)
processed_clips.append(final_subclip)
# 모든 클립을 하나로 합침
final_clip = concatenate_videoclips(processed_clips, method="compose")
# 오디오 추가
final_clip = final_clip.set_audio(audio_clip)
# 최종 파일로 렌더링
final_clip.write_videofile(
output_filename,
codec='libx264',
audio_codec='aac',
temp_audiofile='temp-audio.m4a',
remove_temp=True
)
print(f"🎉 최종 영상 '{output_filename}' 렌더링 완료!")
except Exception as e:
print(f"❌ 영상 조립 중 오류 발생: {e}")
# --- 메인 실행 로직 ---
if __name__ == "__main__":
topic = "지구 온난화가 북극에 미치는 영향"
# 1. 스크립트 생성
scenes = generate_script_and_visuals(topic)
if not scenes:
exit() # 스크립트 생성 실패 시 종료
# 2. 음성 파일 생성
full_narration = " ".join([s['narration'] for s in scenes])
audio_file = create_voiceover(full_narration)
if not audio_file:
exit()
# 3. 영상 소스 다운로드
video_files = get_visuals(scenes)
if not video_files:
exit()
# 4. 최종 영상 조립
assemble_video(video_files, audio_file)
결론 및 다음 단계
축하합니다! 이제 당신은 터미널에서 python main.py 명령어 하나만으로 특정 주제에 대한 숏폼 영상을 자동으로 생성하는 완벽한 시스템을 갖추게 되었습니다. 이 파이프라인은 콘텐츠 생산의 병목 현상을 해결하고, 당신이 더 창의적인 아이디어 구상에 집중할 수 있도록 도와줄 것입니다.
다음 단계로 시도해볼 만한 개선 아이디어:
- 자동 자막 추가: 생성된 스크립트 텍스트를 이용해 MoviePy의 TextClip으로 영상에 자막을 입힐 수 있습니다.
- 배경 음악 추가: 저작권 없는 음악 라이브러리에서 무드에 맞는 음악을 자동으로 다운로드하여 작은 볼륨으로 추가할 수 있습니다.
- AI 이미지 생성 연동: Pexels 대신 DALL-E 3나 Stable Diffusion API를 연동하여 세상에 없는 독창적인 시각 자료를 생성할 수 있습니다.
- 다양한 영상 길이 지원: 스크립트 생성 프롬프트를 수정하여 원하는 영상 길이나 장면 수를 동적으로 조절할 수 있습니다.
- 오류 처리 및 로깅 강화: 각 단계에서 실패 시 재시도 로직을 추가하거나, 진행 상황을 로그 파일에 기록하여 안정성을 높일 수 있습니다.