OpenCut 완전 가이드: macOS 로컬 테스트 완료! 무료 오픈소스 비디오 에디터로 CapCut 대체하기
OpenCut 소개
OpenCut은 웹, 데스크톱, 모바일에서 사용할 수 있는 무료 오픈소스 비디오 에디터입니다. CapCut의 완전한 대안으로 개발되었으며, 사용자의 프라이버시를 보호하면서도 전문적인 비디오 편집 기능을 제공합니다.
왜 OpenCut을 선택해야 할까요?
🔒 프라이버시 보호
- 모든 비디오 처리가 사용자 기기에서 이루어집니다
- 클라우드 업로드 없이 완전히 로컬에서 작업
- 개인 정보 수집 없음
💰 완전 무료
- CapCut의 기본 기능들이 이제 유료화됨
- OpenCut은 모든 기능을 무료로 제공
- 워터마크나 구독료 없음
🎯 사용 편의성
- CapCut처럼 직관적인 인터페이스
- 초보자도 쉽게 사용할 수 있는 디자인
- 웹 브라우저에서 바로 실행
주요 기능
- 📹 타임라인 기반 편집: 전문적인 비디오 편집 워크플로우
- 🎵 멀티트랙 지원: 비디오, 오디오, 텍스트 트랙 독립 편집
- ⚡ 실시간 미리보기: 편집 중 즉시 결과 확인
- 🎨 다양한 효과: 필터, 전환, 애니메이션 효과
- 📱 반응형 디자인: 데스크톱, 태블릿, 모바일 지원
시스템 요구사항
웹 브라우저 버전
# 지원 브라우저
- Chrome 90+ (권장)
- Firefox 88+
- Safari 14+
- Edge 90+
# 시스템 요구사항
- RAM: 4GB 이상 (8GB 권장)
- 저장공간: 2GB 이상 여유공간
- 인터넷 연결: 초기 로딩용 (편집 중에는 불필요)
개발 환경 요구사항
# 필수 소프트웨어
- Node.js 16.9+ (테스트 환경: v24.1.0)
- Bun 1.2.17 (프로젝트 패키지 매니저)
- Git
- PostgreSQL 14+ (로컬 개발용)
# macOS 환경에서 확인된 버전
- PostgreSQL 14.18 (Homebrew)
- macOS: darwin 25.0.0 (Apple Silicon)
사용자 가이드
웹 버전 빠른 시작
- OpenCut 웹사이트 접속
# 공식 웹사이트 https://opencut.app
- 프로젝트 생성
- “새 프로젝트” 버튼 클릭
- 프로젝트 이름 및 해상도 설정
- 프레임 레이트 선택 (24fps, 30fps, 60fps)
- 미디어 파일 업로드
- 드래그 앤 드롭으로 파일 추가
- 지원 형식: MP4, MOV, AVI, MP3, WAV, JPG, PNG
기본 편집 워크플로우
1. 타임라인에 클립 추가
// 기본 편집 단축키
Ctrl + I: 클립 분할
Ctrl + D: 클립 복제
Ctrl + Z: 실행 취소
Ctrl + Y: 다시 실행
Space: 재생/일시정지
2. 클립 편집 기본 기능
- 트리밍: 클립 끝점을 드래그하여 길이 조정
- 분할: 재생 헤드 위치에서 클립을 두 부분으로 나누기
- 이동: 클립을 드래그하여 타임라인상 위치 변경
- 복제: 클립을 복사하여 다른 위치에 붙여넣기
3. 효과 및 필터 적용
// 주요 효과 카테고리
- 색상 보정: 밝기, 대비, 채도, 색조
- 필터: 빈티지, 흑백, 세피아, 블러
- 전환: 페이드, 슬라이드, 줌, 와이프
- 텍스트: 제목, 자막, 애니메이션 텍스트
4. 오디오 편집
- 볼륨 조절: 오디오 트랙 볼륨 슬라이더
- 페이드 인/아웃: 자연스러운 오디오 전환
- 오디오 분리: 비디오에서 오디오 트랙 분리
- 배경음악: 별도 오디오 트랙에 BGM 추가
로컬 개발 환경 설정
1단계: 저장소 클론
# 프로젝트 클론
git clone https://github.com/OpenCut-app/OpenCut.git
cd OpenCut
# 웹 앱 디렉토리로 이동
cd apps/web
2단계: PostgreSQL 설정
# PostgreSQL 설치 (macOS)
brew install postgresql@14
brew services start postgresql@14
# admin 사용자 생성 (슈퍼유저 권한)
createuser -s admin
# admin 사용자 패스워드 설정
psql -d postgres -c "ALTER USER admin PASSWORD 'admin';"
# opencut 데이터베이스 생성
createdb -O admin opencut
# 연결 테스트
psql -d "postgresql://admin:admin@localhost:5432/opencut" -c "SELECT version();"
3단계: 환경 설정
# 환경 변수 파일 생성
cp .env.example .env.local
# .env.local 파일 내용 (admin/admin 사용자로 설정)
cat > .env.local << 'EOF'
DATABASE_URL="postgresql://admin:admin@localhost:5432/opencut"
BETTER_AUTH_URL=http://localhost:3001
BETTER_AUTH_SECRET=your-secret-key-here-opencut-test-2025
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
NODE_ENV=development
UPSTASH_REDIS_REST_URL=http://localhost:8079
UPSTASH_REDIS_REST_TOKEN=example_token
EOF
4단계: Bun 설치 및 의존성 설치
# Bun 설치 (프로젝트에서 지정된 패키지 매니저)
curl -fsSL https://bun.sh/install | bash
# PATH 업데이트
export PATH="$HOME/.bun/bin:$PATH"
# Corepack 활성화 (필요시)
corepack enable
# 의존성 설치
bun install
5단계: 데이터베이스 스키마 생성
# 데이터베이스 스키마 생성
bun run db:generate
# 로컬 데이터베이스에 스키마 적용
bun run db:push:local
# 생성된 테이블 확인
psql -d "postgresql://admin:admin@localhost:5432/opencut" -c "\dt"
6단계: 개발 서버 실행
# 포트 충돌 방지를 위해 3001 포트 사용 (OrbStack 등이 3000 포트 사용시)
PORT=3001 bun run dev
# 접속 URL
# Local: http://localhost:3001
# Network: http://[your-ip]:3001
✅ 성공 시 확인사항
서버가 정상적으로 실행되면 다음과 같은 출력을 볼 수 있습니다:
▲ Next.js 15.3.4 (Turbopack)
- Local: http://localhost:3001
- Network: http://172.30.1.64:3001
- Environments: .env.local
✓ Starting...
✓ Compiled middleware in 71ms
✓ Ready in 707ms
주요 라우트 확인:
/
- 메인 페이지/editor
- 비디오 에디터 페이지/api/auth/get-session
- 인증 API
데이터베이스 테이블 확인:
-- 생성되는 테이블들
accounts | table | admin
sessions | table | admin
users | table | admin
verifications | table | admin
waitlist | table | admin
고급 기능 활용
사용자 정의 효과 개발
// src/effects/customEffect.ts
export interface CustomEffect {
name: string;
apply: (canvas: HTMLCanvasElement, params: any) => void;
parameters: EffectParameter[];
}
export const VintageEffect: CustomEffect = {
name: 'Vintage',
apply: (canvas, params) => {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// 빈티지 효과 적용 로직
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// 세피아 톤 적용
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
}
ctx.putImageData(imageData, 0, 0);
},
parameters: [
{ name: 'intensity', type: 'range', min: 0, max: 100, default: 50 }
]
};
플러그인 시스템 활용
// src/plugins/textAnimations.ts
export class TextAnimationPlugin {
constructor(private timeline: Timeline) {}
fadeIn(element: TextElement, duration: number = 1000): void {
const keyframes = [
{ opacity: 0, transform: 'translateY(20px)' },
{ opacity: 1, transform: 'translateY(0px)' }
];
this.timeline.addAnimation({
element,
keyframes,
duration,
easing: 'ease-out'
});
}
typewriter(element: TextElement, text: string, speed: number = 50): void {
const characters = text.split('');
let currentText = '';
characters.forEach((char, index) => {
setTimeout(() => {
currentText += char;
element.updateText(currentText);
}, index * speed);
});
}
}
배치 처리 및 자동화
// scripts/batchProcess.ts
import { VideoProcessor } from '../src/lib/videoProcessor';
interface BatchJob {
inputPath: string;
outputPath: string;
preset: string;
effects?: string[];
}
export class BatchProcessor {
private processor: VideoProcessor;
constructor() {
this.processor = new VideoProcessor();
}
async processBatch(jobs: BatchJob[]): Promise<void> {
console.log(`Processing ${jobs.length} jobs...`);
for (const job of jobs) {
try {
console.log(`Processing: ${job.inputPath}`);
await this.processor.loadVideo(job.inputPath);
if (job.effects) {
job.effects.forEach(effect => {
this.processor.applyEffect(effect);
});
}
await this.processor.export({
path: job.outputPath,
preset: job.preset
});
console.log(`Completed: ${job.outputPath}`);
} catch (error) {
console.error(`Error processing ${job.inputPath}:`, error);
}
}
}
}
// 사용 예시
const batchProcessor = new BatchProcessor();
const jobs: BatchJob[] = [
{
inputPath: './videos/raw/video1.mp4',
outputPath: './videos/processed/video1_processed.mp4',
preset: 'web-optimized',
effects: ['stabilization', 'color-correction']
},
{
inputPath: './videos/raw/video2.mp4',
outputPath: './videos/processed/video2_processed.mp4',
preset: 'mobile-optimized',
effects: ['noise-reduction']
}
];
batchProcessor.processBatch(jobs);
실전 프로젝트: 교육 콘텐츠 제작 도구
OpenCut을 활용한 교육용 비디오 자동 제작 시스템을 만들어보겠습니다.
프로젝트 구조
mkdir educational-video-maker
cd educational-video-maker
# 프로젝트 구조 생성
mkdir -p {src,templates,assets,output}
touch src/{main.ts,videoGenerator.ts,templateEngine.ts}
교육 비디오 생성기
// src/videoGenerator.ts
import { OpenCutAPI } from './opencut-api';
export interface LessonConfig {
title: string;
duration: number;
slides: SlideConfig[];
narration: NarrationConfig;
style: StyleConfig;
}
export interface SlideConfig {
type: 'intro' | 'content' | 'example' | 'summary';
text: string;
image?: string;
duration: number;
animation: 'fadeIn' | 'slideLeft' | 'zoomIn';
}
export class EducationalVideoGenerator {
private opencut: OpenCutAPI;
constructor() {
this.opencut = new OpenCutAPI();
}
async generateLesson(config: LessonConfig): Promise<string> {
// 새 프로젝트 생성
const project = await this.opencut.createProject({
name: config.title,
resolution: '1920x1080',
fps: 30,
duration: config.duration
});
// 배경 설정
await this.addBackground(project, config.style);
// 슬라이드별 콘텐츠 생성
let currentTime = 0;
for (const slide of config.slides) {
await this.addSlide(project, slide, currentTime);
currentTime += slide.duration;
}
// 나레이션 추가
if (config.narration) {
await this.addNarration(project, config.narration);
}
// 최종 렌더링
const outputPath = `./output/${config.title.replace(/\s+/g, '_')}.mp4`;
await this.opencut.export(project, {
path: outputPath,
quality: 'high',
format: 'mp4'
});
return outputPath;
}
private async addBackground(project: any, style: StyleConfig): Promise<void> {
const backgroundTrack = await this.opencut.addTrack(project, 'background');
// 그라데이션 배경 생성
const background = await this.opencut.createGradientBackground({
colors: style.backgroundColor || ['#667eea', '#764ba2'],
direction: 'diagonal'
});
await this.opencut.addElement(backgroundTrack, background, {
start: 0,
duration: project.duration
});
}
private async addSlide(project: any, slide: SlideConfig, startTime: number): Promise<void> {
const contentTrack = await this.opencut.getTrack(project, 'content');
// 텍스트 요소 생성
const textElement = await this.opencut.createTextElement({
text: slide.text,
font: 'Arial',
size: 48,
color: '#ffffff',
position: { x: 960, y: 540 },
alignment: 'center'
});
// 애니메이션 적용
await this.opencut.addAnimation(textElement, {
type: slide.animation,
duration: 1000,
easing: 'ease-out'
});
await this.opencut.addElement(contentTrack, textElement, {
start: startTime,
duration: slide.duration
});
// 이미지가 있는 경우 추가
if (slide.image) {
const imageElement = await this.opencut.createImageElement({
src: slide.image,
position: { x: 960, y: 300 },
size: { width: 400, height: 300 }
});
await this.opencut.addElement(contentTrack, imageElement, {
start: startTime + 0.5,
duration: slide.duration - 1
});
}
}
private async addNarration(project: any, narration: NarrationConfig): Promise<void> {
const audioTrack = await this.opencut.addTrack(project, 'narration');
// TTS 또는 오디오 파일 추가
const audioElement = await this.opencut.createAudioElement({
src: narration.audioPath,
volume: 0.8
});
await this.opencut.addElement(audioTrack, audioElement, {
start: 0,
duration: project.duration
});
}
}
템플릿 엔진
// src/templateEngine.ts
export class VideoTemplateEngine {
private templates: Map<string, VideoTemplate> = new Map();
constructor() {
this.loadDefaultTemplates();
}
private loadDefaultTemplates(): void {
// 기본 교육 템플릿
this.templates.set('education-basic', {
name: '기본 교육용',
description: '심플한 교육 비디오 템플릿',
tracks: [
{ name: 'background', type: 'video' },
{ name: 'content', type: 'text' },
{ name: 'narration', type: 'audio' }
],
style: {
backgroundColor: ['#667eea', '#764ba2'],
textColor: '#ffffff',
fontSize: 48,
fontFamily: 'Arial'
},
transitions: ['fadeIn', 'slideLeft'],
duration: 300 // 5분
});
// 프레젠테이션 템플릿
this.templates.set('presentation', {
name: '프레젠테이션',
description: '비즈니스 프레젠테이션용 템플릿',
tracks: [
{ name: 'background', type: 'video' },
{ name: 'title', type: 'text' },
{ name: 'content', type: 'text' },
{ name: 'footer', type: 'text' },
{ name: 'audio', type: 'audio' }
],
style: {
backgroundColor: ['#2c3e50', '#3498db'],
textColor: '#ffffff',
fontSize: 36,
fontFamily: 'Helvetica'
},
transitions: ['zoomIn', 'slideRight'],
duration: 600 // 10분
});
}
getTemplate(name: string): VideoTemplate | undefined {
return this.templates.get(name);
}
async applyTemplate(project: any, templateName: string, content: any): Promise<void> {
const template = this.getTemplate(templateName);
if (!template) {
throw new Error(`Template ${templateName} not found`);
}
// 템플릿에 따라 프로젝트 구성
await this.setupProjectStructure(project, template);
await this.populateContent(project, template, content);
}
private async setupProjectStructure(project: any, template: VideoTemplate): Promise<void> {
// 트랙 생성
for (const track of template.tracks) {
await this.opencut.addTrack(project, track.name, track.type);
}
// 기본 스타일 적용
await this.applyGlobalStyle(project, template.style);
}
private async populateContent(project: any, template: VideoTemplate, content: any): Promise<void> {
// 콘텐츠 데이터를 템플릿에 맞게 배치
if (content.slides) {
await this.addSlides(project, template, content.slides);
}
if (content.audio) {
await this.addAudio(project, template, content.audio);
}
}
}
사용 예시
// src/main.ts
import { EducationalVideoGenerator } from './videoGenerator';
async function createMathLesson() {
const generator = new EducationalVideoGenerator();
const lessonConfig = {
title: '파이썬 기초 - 변수와 데이터 타입',
duration: 300, // 5분
slides: [
{
type: 'intro' as const,
text: '파이썬 기초 강의\n변수와 데이터 타입',
duration: 5,
animation: 'fadeIn' as const
},
{
type: 'content' as const,
text: '변수란 무엇인가?\n\n변수는 데이터를 저장하는 공간입니다.',
duration: 10,
animation: 'slideLeft' as const
},
{
type: 'example' as const,
text: '# 변수 선언 예시\nname = "홍길동"\nage = 25\nheight = 175.5',
duration: 15,
animation: 'zoomIn' as const,
image: './assets/code-example.png'
},
{
type: 'summary' as const,
text: '정리\n\n• 변수는 데이터 저장 공간\n• 문자열, 숫자, 불린 타입\n• 동적 타이핑 지원',
duration: 10,
animation: 'fadeIn' as const
}
],
narration: {
audioPath: './assets/narration.mp3'
},
style: {
backgroundColor: ['#667eea', '#764ba2'],
textColor: '#ffffff'
}
};
try {
const outputPath = await generator.generateLesson(lessonConfig);
console.log(`교육 비디오 생성 완료: ${outputPath}`);
} catch (error) {
console.error('비디오 생성 중 오류:', error);
}
}
createMathLesson();
성능 최적화 및 팁
브라우저 성능 최적화
// 메모리 사용량 최적화
const optimizeMemory = () => {
// 큰 비디오 파일 처리시 청크 단위로 처리
const processInChunks = (videoData, chunkSize = 1024 * 1024) => {
const chunks = [];
for (let i = 0; i < videoData.length; i += chunkSize) {
chunks.push(videoData.slice(i, i + chunkSize));
}
return chunks;
};
// Web Worker 활용
const worker = new Worker('/workers/videoProcessor.js');
worker.postMessage({ type: 'process', data: videoData });
};
// 렌더링 성능 최적화
const optimizeRendering = () => {
// Canvas 이중 버퍼링
const offscreenCanvas = new OffscreenCanvas(1920, 1080);
const offscreenCtx = offscreenCanvas.getContext('2d');
// GPU 가속 활용
const canvas = document.getElementById('preview-canvas');
const ctx = canvas.getContext('2d', {
alpha: false,
willReadFrequently: false
});
};
파일 크기 최적화
# 출력 설정 최적화
# 웹용 최적화
Resolution: 1280x720
Bitrate: 2000 kbps
FPS: 30
Format: MP4 (H.264)
# 모바일용 최적화
Resolution: 720x480
Bitrate: 1000 kbps
FPS: 24
Format: MP4 (H.264)
# 고품질 보관용
Resolution: 1920x1080
Bitrate: 8000 kbps
FPS: 60
Format: MP4 (H.264)
문제 해결
일반적인 문제
# 브라우저 호환성 문제
# 최신 브라우저 사용 권장
Chrome 90+ / Firefox 88+ / Safari 14+
# 메모리 부족 오류
# 브라우저 메모리 증가 방법
chrome --max-old-space-size=8192
# 파일 업로드 실패
# 파일 크기 제한 확인 (기본 100MB)
# 지원 형식: MP4, MOV, AVI, MP3, WAV, JPG, PNG
개발 환경 문제
# 1. 패키지 매니저 오류 (Corepack 관련)
# 오류: "This project's package.json defines packageManager: yarn@bun@1.2.17"
corepack enable
# 또는 직접 Bun 설치
curl -fsSL https://bun.sh/install | bash
# 2. 포트 충돌 문제
# 포트 3000이 사용 중인 경우 (OrbStack, Docker 등)
lsof -i :3000 # 사용 중인 프로세스 확인
PORT=3001 bun run dev # 다른 포트 사용
# 3. 의존성 설치 오류
# 기존 lock 파일 정리 후 재설치
rm -f package-lock.json yarn.lock
bun install
# 4. Node.js 버전 확인
node --version # 16.9+ 필요 (테스트: v24.1.0)
bun --version # 1.2.17 확인
# 5. 데이터베이스 연결 오류
# PostgreSQL 서비스 상태 확인
brew services list | grep postgresql
brew services start postgresql@14
# 6. 데이터베이스 권한 문제
# admin 사용자 재생성
dropuser admin # 기존 사용자 삭제 (있는 경우)
createuser -s admin
psql -d postgres -c "ALTER USER admin PASSWORD 'admin';"
# 7. 스키마 생성 실패
# 데이터베이스 재생성
dropdb opencut
createdb -O admin opencut
bun run db:push:local
macOS 특정 문제
# 1. Apple Silicon (M1/M2) 환경
# Rosetta 없이 네이티브 ARM64 바이너리 사용
arch -arm64 brew install postgresql@14
# 2. Xcode Command Line Tools 필요
xcode-select --install
# 3. 환경 변수 PATH 설정
# ~/.zshrc에 추가
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
# 4. 방화벽/보안 설정
# 시스템 설정 > 보안 및 개인정보보호 > 방화벽에서 포트 허용
서버 실행 확인
# 서버 정상 실행 확인
curl -I http://localhost:3001
# 성공 시 응답
# HTTP/1.1 200 OK
# content-type: text/html; charset=utf-8
# 실패 시 대안
# 1. 포트 변경: PORT=3002 bun run dev
# 2. 캐시 정리: bun install --force
# 3. 프로세스 종료: lsof -ti:3001 | xargs kill -9
커뮤니티 및 기여
오픈소스 기여 방법
- GitHub Issues 확인
- 버그 리포트 작성
- 기능 요청 제안
- 문서 개선 제안
- 코드 기여
# 포크 및 클론 git clone https://github.com/YOUR_USERNAME/OpenCut.git cd OpenCut # 기능 브랜치 생성 git checkout -b feature/new-feature # 변경사항 커밋 git commit -m "Add new feature" # 풀 리퀘스트 생성 git push origin feature/new-feature
- 문서 및 번역
- README 번역
- 사용자 가이드 작성
- API 문서 개선
커뮤니티 참여
- GitHub Discussions: 질문 및 아이디어 공유
- Discord 채널: 실시간 소통
- 블로그 포스트: 사용 경험 공유
마무리
OpenCut은 단순히 CapCut의 대안이 아닌, 프라이버시와 자유를 중시하는 차세대 비디오 에디터입니다. 오픈소스의 힘으로 지속적으로 발전하고 있으며, 누구나 무료로 사용할 수 있습니다.
다음 단계
- OpenCut 체험하기: opencut.app에서 바로 시작
- 로컬 개발 환경 구축: 위의 가이드를 따라 macOS에서 테스트 완료
- 커뮤니티 참여: GitHub에서 다른 개발자들과 소통
- 고급 기능 활용: 플러그인과 API로 확장하기
🎯 macOS 테스트 완료 확인사항
테스트 환경:
- macOS darwin 25.0.0 (Apple Silicon)
- Node.js v24.1.0
- PostgreSQL 14.18 (Homebrew)
- Bun 1.2.17
확인된 기능:
- ✅ 데이터베이스 연결 및 스키마 생성 성공
- ✅ Next.js 15.3.4 (Turbopack) 서버 실행 성공
- ✅ 주요 라우트 (
/
,/editor
,/api/auth/get-session
) 정상 동작 - ✅ Better Auth 인증 시스템 로드 완료
- ✅ 개발 서버 핫 리로드 기능 정상
알려진 제한사항:
- Google OAuth 설정 없음 (선택사항)
- Redis 미설정 (선택사항)
- 포트 3000 충돌 가능 (3001 포트 사용 권장)
OpenCut과 함께 창의적이고 자유로운 비디오 편집의 새로운 시대를 열어보세요!
참고 링크
- GitHub Repository: OpenCut-app/OpenCut
- 공식 웹사이트: opencut.app
- 문서: OpenCut Documentation
- 라이선스: MIT License