소개
동영상 압축은 원래 단순한 일처럼 보입니다.
파일이 크면 FFmpeg로 다시 인코딩하면 되고, CRF 값을 올리면 용량은 줄어듭니다. 그런데 제가 압축하려던 영상은 일반적인 영화나 브이로그가 아니었습니다. 대부분이 교육 영상이었고, 화면 안에는 발표 자료, 코드, 메뉴, 작은 글씨가 많이 들어 있었습니다.
문제는 여기서 시작됐습니다.
용량은 줄었는데 글씨 가 뭉개지면, 그 영상은 더 이상 좋은 압축 결과물이 아니었습니다. 교육 영상에서 글씨는 배경이 아니라 핵심 정보입니다. 그래서 이 프로젝트의 목표는 단순한 “최대 압축”이 아니라 다음 문장으로 정리됐습니다.
글씨가 버티는 선까지만 압축하자.
이 기준을 가지고 로컬 폴더 안의 많은 동영상을 자동으로 압축하고, 결과와 실패 이유까지 로그로 남기는 프로그램을 만들기 시작했습니다.
프로그램 이름은 AVI Compress입니다.
결과물
현재 프로그램은 Python으로 만든 FFmpeg 기반 CLI 도구입니다.
가장 기본적인 사용법은 아래와 같습니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive이 명령을 실행하면 D:\Videos 폴더와 그 하위 폴더의 동영상을 찾아 압축합니다.
현재 기본값은 다음과 같습니다.
profile: textsafe
video encoder: libx264
preset: veryfast
CRF: 22
audio mode: aac-128k
container policy: auto
output folder: compress_new
log folder: compress_new\logs
success 기준: 40% 이상 절감
partial_success 기준: 20% 이상 절감
_skip 파일: 기본으로 다시 처리결과 파일은 대상 폴더 안의 compress_new 폴더에 저장됩니다. 이 방식은 원본과 새 압축본을 확실히 분리하면서, 이전 압축본과도 섞이지 않게 하기 위한 현재 표준 방식입니다.
D:\Videos\compress_new로그는 compress_new\logs 폴더에 CSV로 저장됩니다.
D:\Videos\compress_new\logs\avi_compress_YYYYMMDD_HHMMSS.csv이 로그에는 어떤 파일을 어떤 방식으로 변환했는지, 성공했는지, 실패했는지, 압축 전후 용량이 얼마나 달라졌는지가 기록됩니다.
필요한 구간만 남기고 압축할 수도 있습니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "Z:\lecture\32우리는변호사다\원본\[우리는변호사다]유튜브_이서영변호사.mp4" --end 1:18:40이렇게 실행하면 00:00:00부터 01:18:40까지만 남긴 뒤 표준 방식으로 압축합니다.
명령어 입력이 번거로운 경우를 위해 GUI도 추가했습니다.
python "D:\03Gpters\avi_compress\avi_compress_gui.py"
GUI에서는 파일 또는 폴더를 추가하고, 파일이나 폴더를 창 위에 드래그 앤 드롭으로 넣을 수도 있습니다. 각 파일 줄마다 시작 시간과 종료 시간을 개별 입력할 수 있습니다. 비워 둔 파일은 전체 구간을 압축하고, MP3 체크박스를 켠 파일은 128 kbps MP3 음성 파일도 함께 추출합니다.
처음 세운 개발 계획
처음부터 목표는 명확했습니다.
폴더 안의 동영상을 한 번에 처리한다.
결과 파일은 기본적으로 MP4로 만든다.
MP4에 안전하게 들어가지 않는 오디오나 자막이 있으면 MKV로 자동 전환한다.
오디오는 기본적으로 AAC 128 kbps로 저장한다.
필요하면 원본 오디오 복사, 가청 대역 필터, 무손실 오디오 저장을 선택할 수 있게 한다.
원하면 128 kbps MP3 파일도 따로 추출한다.
압축 효율이 낮으면 원본을 지우지 않고
_skip표시를 남긴다.모든 작업 결과를 CSV 로그로 남긴다.
처음에는 “좋은 압축 프로그램”을 만들고 싶었습니다. 하지만 개발이 진행될수록 목표가 더 구체화됐습니다.
좋은 압축 프로그램이 아니라, 교육 영상에 맞는 압축 프로그램이 필요하다.
이 차이가 전체 설계를 바꿨습니다.
진행 방법
1. FFmpeg를 직접 감싼 Python CLI부터 만들기
가장 먼저 한 일은 FFmpeg 명령을 자동으로 만들어 주는 Python 프로그램을 만드는 것이었습니다.
처음부터 GUI를 만들기보다, 안정적인 명령줄 프로그램을 먼저 만드는 쪽을 선택했습니다. 폴더 전체 처리, 하위 폴더 검색, 로그 기록, 실패 처리 같은 기본 기능이 먼저 필요했기 때문입니다.
핵심 흐름은 다음과 같습니다.
1. 입력 경로 확인
2. 동영상 파일 검색
3. ffprobe로 원본 스트림 분석
4. MP4 또는 MKV 컨테이너 선택
5. FFmpeg 명령 생성
6. 샘플 또는 전체 인코딩 실행
7. 결과 용량 확인
8. 성공, 부분 성공, 스킵, 실패 기록
9. CSV 로그 저장CLI 구조로 만든 덕분에 나중에 옵션을 빠르게 추가할 수 있었습니다.
2. MP4 우선, 필요하면 MKV 자동 전환
사용하기 쉬운 결과 파일 형식은 MP4입니다. 대부분의 플레이어와 기기에서 잘 열리고, 공유하기도 편합니다.
하지만 모든 원본 스트림이 MP4에 안전하게 들어가는 것은 아닙니다. 그래서 컨테이너 정책은 이렇게 정했습니다.
기본은 MP4
단, 원본 오디오나 자막이 MP4에 안전하지 않으면 MKV로 자동 전환이렇게 하면 사용자는 대부분 MP4를 얻고, 호환성 문제가 생길 수 있는 경우에는 프로그램이 알아서 MKV를 선택합니다.
3. 오디오는 기본 AAC 128 kbps로 제한하기
처음에는 음질 보존을 우선해 기본 오디오 정책을 copy로 정했습니다.
하지만 실제 폴더 비교에서 문제가 드러났습니다. 일부 압축본의 오디오가 512 kbps AAC로 들어가면서, 원본보다 파일 크기가 2~5배 커지는 경우가 많았습니다.
그래서 현재 기본 오디오 정책은 aac-128k입니다.
audio mode: aac-128k원본 오디오를 그대로 보존해야 할 때만 copy를 명시적으로 사용합니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --audio-mode copy별도 옵션으로는 가청 대역 필터와 무손실 저장도 넣었습니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --audio-mode audible-lossless이 옵션은 20 Hz 아래와 20 kHz 위를 자르고, MP4에서는 ALAC, MKV에서는 FLAC으로 오디오를 저장합니다.
MP3 추출 옵션도 넣었습니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --extract-mp3이렇게 하면 128 kbps MP3 파일이 audio 폴더에 따로 저장됩니다.
4. 처음에는 H.265 보수 프리셋을 기본으로 생각했다
처음에는 H.265가 더 좋은 선택처럼 보였습니다. 같은 화질에서 H.264보다 용량을 더 줄일 수 있기 때문입니다.
그래서 초기 개발계획서에는 보수적인 H.265, 균형형 H.265, 강한 압축 H.265, AV1 같은 여러 프로필을 구상했습니다.
하지만 실제로 돌려 보니 문제가 있었습니다.
속도가 너무 느렸습니다.
실제 영상에서 H.265는 speed=1x 안팎으로 나왔습니다. 3시간짜리 영상을 처리하는 데 3시간 이상 걸리면, 폴더에 영상이 많을 때는 현실적으로 쓰기 어렵습니다.
그래서 하드웨어 인코더도 확인했습니다.
NVIDIA NVENC: 사용 불가
AMD AMF: 사용 불가
Intel QSV: 동작은 했지만 기대만큼 빠르지 않음
CPU H.265: 느림
CPU H.264 veryfast: 가장 현실적이 과정에서 빠른 H.264 프로필인 fast3x를 만들었습니다.
5. 70% 절감 목표와 fast3x 실험
한때 목표는 “70% 정도 압축”이었습니다. 그래서 fast3x 프로필은 CRF를 매우 공격적으로 올렸습니다.
fast3x CRF 후보: 34,36,38,40,42결과는 양면적이었습니다.
속도는 꽤 좋아졌고, 용량도 크게 줄었습니다. 하지만 실제 결과물을 열어 보니 글씨가 많이 뭉개졌습니다.
이때 프로젝트의 방향이 완전히 바뀌었습니다.
70% 압축에 성공해도 글씨가 안 보이면 실패다.
압축률 기준으로는 성공일 수 있지만, 교육 영상 기준으로는 실패였습니다.
6. 글씨 우선 프리셋 textsafe로 전환
이후 기본 프리셋을 textsafe로 바꿨습니다.
처음에는 CRF 후보를 24,26,28로 제한했습니다. fast3x보다 훨씬 안전했지만, 실제 샘플 비교에서 더 중요한 사실이 보였습니다.
특정 실험 파일에서 CRF 22, 24, 26, 28을 비교했습니다.
실험 파일:
2025-12-09 21.46.43 CMDS Tech-Savvy Series #1_ Apps for Professionals_skip.mp420초 샘플 결과:
원본 샘플: 4.21 MB
CRF 22: 1.87 MB
CRF 24: 1.80 MB
CRF 26: 1.74 MB
CRF 28: 1.68 MB여기서 재미있는 점은 CRF를 22에서 28까지 올려도 전체 용량 차이가 아주 크지 않았다는 것입니다. 당시에는 오디오를 그대로 보존했기 때문에, 오디오 비트레이트가 높은 파일에서는 영상만 줄여도 전체 용량 차이가 제한적이었습니다.
즉, 이 파일에서는 영상을 더 세게 압축해도 전체 파일 크기 절감 효과는 제한적이었습니다. 반면 글씨 품질은 더 쉽게 나빠질 수 있었습니다.
그래서 결론은 명확했습니다.
이 경우에는 CRF 22가 가장 합리적이다.
현재 기본값은 이 실험 결과를 반영해 CRF 22로 고정했습니다.
실제 기능 정리
현재 프로그램에서 사용할 수 있는 기능은 다음과 같습니다.
폴더 전체 압축
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive현재 폴더 압축
python "D:\03Gpters\avi_compress\avi_compress.py" . --recursive파일 하나만 압축
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos\lesson01.mp4"실제 변환 없이 계획만 확인
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --dry-run_skip 파일 제외
현재는 _skip 파일도 기본으로 다시 처리합니다. 예전의 나쁜 설정으로 스킵된 파일을 새 기준으로 다시 시도하기 위해서입니다.
_skip 파일을 빼고 싶을 때만 아래 옵션을 씁니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --exclude-skip-marked더 많이 압축하고 싶을 때
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --crf-ladder 22,24,26 --min-save-percent 50 --accept-save-percent 30다만 이 경우에는 반드시 샘플 결과를 눈으로 확인해야 합니다. CRF 숫자가 올라가면 글씨가 흐려질 수 있습니다.
개발 과정에서의 시행착오
1. 좋은 코덱이 항상 좋은 기본값은 아니었다
H.265와 AV1은 압축 효율 면에서 매력적입니다. 하지만 로컬 PC에서 너무 느리면 실제 워크플로우에 들어가기 어렵습니다.
결국 이 프로그램의 기본값은 최고 효율 코덱이 아니라, 현재 PC에서 실제로 쓸 수 있는 속도와 글씨 품질을 함께 고려한 H.264 veryfast가 됐습니다.
2. 압축률 70%는 목표가 아니라 유혹이었다
70% 절감이라는 숫자는 매력적이었습니다. 하지만 교육 영상에서는 그 숫자가 오히려 판단을 흐릴 수 있었습니다.
특히 이미 낮은 비트레이트로 녹화된 영상은 더 줄일 여지가 크지 않습니다. 이 상태에서 70%를 억지로 맞추면 글씨부터 무너집니다.
그래서 기본 성공 기준을 40%, 부분 성공 기준을 20%로 낮췄습니다.
success: 40% 이상 절감
partial_success: 20% 이상 절감이 기준은 “압축률을 포기했다”는 뜻이 아닙니다. 글씨 품질을 먼저 지키겠다는 뜻입니다.
3. 오디오 정책은 용량 절감의 하한선을 만든다
사용자는 오디오 품질을 중요하게 생각했습니다. 처음에는 그래서 기본적으로 오디오를 복사했습니다.
하지만 오디오 비트레이트가 높은 영상에서는 이 정책이 용량 절감의 하한선을 만듭니다. 예를 들어 오디오가 512 kbps라면, 영상만 아무리 줄여도 오디오 부분은 그대로 남습니다.
더 나아가 일부 결과물은 오디오가 512 kbps로 재인코딩되어 원본보다 훨씬 커졌습니다. 이 점을 반영해 현재 기본값은 AAC 128 kbps로 변경했습니다.
4. _skip 정책도 바뀌었다
초기에는 _skip 파일을 기본으로 제외했습니다. 이미 압축 효율이 낮다고 판단된 파일을 계속 다시 시도하지 않기 위해서였습니다.
하지만 중간에 기본 압축 정책이 크게 바뀌었습니다. fast3x에서 textsafe CRF 22로 바뀌었기 때문에, 예전 기준에서 _skip된 파일도 다시 평가할 필요가 생겼습니다.
그래서 현재는 _skip 파일도 기본으로 다시 처리합니다.
결과와 배운 점
이번 작업에서 가장 크게 배운 점은 세 가지였습니다.
1. 교육 영상 압축은 숫자가 아니라 글씨가 기준이다
압축률만 보면 CRF를 높이면 됩니다. 하지만 글씨가 뭉개지면 교육 영상의 핵심 정보가 사라집니다.
이 프로젝트의 기준은 결국 이렇게 정리됩니다.
글씨가 읽히면 성공이고, 글씨가 무너지면 실패다.
2. 실제 샘플 비교가 가장 강력한 의사결정 도구였다
이론적으로는 H.265가 좋고, AV1이 좋고, CRF를 높이면 더 줄어듭니다.
하지만 실제 결정은 샘플 비교에서 났습니다. 같은 구간을 CRF 22, 24, 26, 28로 만들어 눈으로 본 뒤에야 기본값을 확정할 수 있었습니다.
3. 자동화는 로그가 있어야 믿고 돌릴 수 있다
폴더 안에 영상이 많으면 하나씩 기억할 수 없습니다.
그래서 CSV 로그가 중요했습니다. 어떤 파일이 성공했는지, 어떤 파일이 스킵됐는지, 실제로 얼마나 줄었는지 남아 있어야 다음 판단을 할 수 있습니다.
이 프로그램은 매번 실행 결과를 CSV로 남기기 때문에, 나중에 압축 정책을 바꾸더라도 이전 결과와 비교할 수 있습니다.
현재 사용 흐름
일반적으로는 아래 한 줄이면 됩니다.
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive다른 폴더를 압축하고 싶으면 "D:\Videos" 부분만 바꾸면 됩니다.
예를 들어 Filmora 녹화 폴더라면:
python "D:\03Gpters\avi_compress\avi_compress.py" "C:\Users\TEARESS\Documents\Wondershare\Wondershare Filmora\Recorded" --recursive실제 변환 전에 확인만 하고 싶으면:
python "D:\03Gpters\avi_compress\avi_compress.py" "D:\Videos" --recursive --dry-run초등학생도 따라 할 수 있는 설명서는 별도로 HOWTO.md에 정리했습니다.
앞으로의 계획
앞으로 개선하고 싶은 부분도 있습니다.
압축 전 예상 시간 표시 개선
글씨 영역을 자동으로 감지해 품질을 점검하는 기능
완료 후 원본과 결과 파일을 쉽게 비교하는 기능
여러 파일 처리 중 일시정지와 재개 기능
샘플 비교 자동화는 이후 업데이트에서 --review-samples와 GUI의 샘플 비교 버튼으로 반영했습니다.
실제 동영상 압축률
한국 컴퓨터 화면의 스크린샷
마무리
처음에는 동영상 용량을 줄이는 프로그램을 만들고 싶었습니다.
하지만 개발이 진행될수록 이 프로젝트는 단순한 압축기가 아니라, 교육 영상의 본질을 지키기 위한 도구가 됐습니다.
압축률은 숫자로 바로 보입니다. 하지만 글씨 가독성은 실제로 열어 봐야 압니다. 이 프로젝트의 핵심은 그 차이를 인정하고, “작은 파일”보다 “쓸 수 있는 파일”을 우선한 데 있습니다.
결국 AVI Compress의 결론은 이렇습니다.
교육 영상은 글씨가 살아 있어야 한다. 압축은 그 다음이다.