150시간의 비효율을 0시간으로: 프론트엔드 개발자의 첫 인프라 구축기

삼성 TV Plus AI 더빙 납품의 반복 패키징 작업을 자동화하며 프론트엔드, 백엔드, 인프라에 대해 고민한 경험을 공유합니다.

#automation#nodejs#ffmpeg#infrastructure

안녕하세요. 프론트엔드 개발자 김승진입니다.

저희 팀은 최근 삼성 TV Plus에 AI 더빙을 납품하는 대규모 프로젝트를 진행하고 있습니다. AI 기술로 멋진 결과물을 만들어내는 과정 이면에는, 상상 이상의 고된 '수작업'이 숨어있었습니다.

이번 글에서는 동료들의 150시간짜리 단순 반복 업무를 0시간으로 만드는 것을 목표로 진행 중인 자동화 파이프라인의 첫 인프라 구축 단계와, 프론트엔드 개발자가 백엔드 및 인프라의 세계에 처음 부딪히며 배운 생생한 러닝 포인트를 공유하고자 합니다.

1. 수 시간이 투입되는 수작업, 그 당연함에 질문을 던지다

이번 납품 분량은 20시간, 총 결과물 용량은 1.7TB에 달했습니다. 문제는 이 거대한 데이터를 일정한 규격에 맞춰 패키징하는 과정에 있었습니다.

당시 기술부의 여러 실무진이 이 모든 과정을 나누어 전담하고 계셨습니다. 각기 다른 담당자들이 챗, 이메일, NAS 등 파편화된 채널로 파일을 전달해 오면, 일일이 다운로드해 폴더를 쪼개고 오디오와 자막 포맷을 맞춰 영상과 합성하는 작업을 수동으로 진행하셨죠. 고작 20시간 분량의 단일 프로젝트를 패키징하는 데에만 여러 명의 인력이 투입되어, 2주간 주말과 야근을 포함해 약 150시간의 순수 수작업 리소스가 매몰되고 있었습니다.

동고동락하는 동료들이 겪는 이 거대한 병목을 곁에서 지켜보며, 저는 "왜 이 비효율적인 반복 작업을 당연하게 감수해야 할까?"라는 강한 의문을 품었습니다. 마침 제가 담당하던 미디어 에디터 프로젝트도 안정화 단계에 접어들었기에, 누군가 시키지 않았음에도 이 문제를 제 손으로 직접 타파해 보기로 다짐했습니다.

부장님과 대표님께 자동화 파이프라인 구축에 대한 의견을 말씀드리니, 아주 반가워하시며 전폭적인 서포트를 약속해 주셨습니다. 비록 프론트엔드 개발자로 일해왔기에 백엔드나 인프라 영역은 낯설었지만, 모르는 것은 적극적으로 묻고 부딪히며 하나씩 해결해 보기로 했습니다.

2. 단일 진실 공급원(SSOT)과 자동화 설계

기존에도 패키징을 돕는 프로그램은 있었지만, '인간의 개입'이 너무 많이 필요했습니다. 저의 목표는 이 과정을 사용자가 파일만 올리면 신경 쓰지 않아도 되는 완전 자동화 환경으로 탈바꿈시키는 것이었습니다.

2-1. 규칙의 SSOT: 파편화된 레거시 문서 일원화

가장 먼저 한 일은 복잡하게 얽힌 AI 생성 문서들과 불필요한 입력 소스들을 정리하는 것이었습니다. AI 도구들을 활용해 개발을 진행하면서 생성된 문서들이 시간이 지나며 오래전 내용을 담고(Stale Docs)있는 경우가 많았고, 이런 문서들이 개발에 혼선을 주고있었습니다. 이를 개발자와 실무자 모두가 명확히 이해할 수 있도록 spec 문서를 기준으로 단일 진실 공급원(SSOT) 구조로 정리했고, AI가 정해둔 알아보기 어려운 폴더명도 직관적인 한글로 통일했습니다.

2-2. 저장소의 SSOT: 중복 업로드를 없앤 파이프라인 일원화 (USB vs Cloud)

문서를 정리한 후, 거대한 파일을 어디에 모으고 처리할지 저장소 아키텍처를 전면 재검토하며 첫 번째 고민을 마주했습니다.

  • Trade-off: 1.7TB의 대용량을 다룰 때, 전송 속도가 압도적으로 빠른 물리적 매체(로컬 SSD/USB)를 쓸 것인가, 아니면 속도를 다소 희생하더라도 원격 접근과 자동화가 가능한 클라우드(구글 드라이브)를 쓸 것인가의 문제였습니다. 물리적 매체는 수정 사항이 생길 때마다 사람이 수동으로 다시 복사해 날라야 하므로 SSOT(단일 진실 공급원)가 깨지고 파일 버전의 혼선이 발생합니다.
  • 결정과 이유: 속도를 조금 타협하더라도 모든 결과물 관리를 '구글 드라이브'로 일원화하기로 결정했습니다. 전체 파이프라인은 [실무진의 NAS 업로드 → 자동화 서버 프로세스 → 결과물 구글 드라이브 다이렉트 업로드] 형태로 직선화했습니다.
    • 여기서 실무진의 초기 업로드 창구로 NAS를 선택한 이유는, 이미 사내에 NAS 인프라가 구축되어 있어 모든 작업자의 PC에서 별도 설정 없이 즉각적이고 편하게 접근할 수 있었기 때문입니다.
    • 결과적으로 QC를 위해 다른 드라이브에 모았다가 다시 납품용으로 옮기는 불필요한 '중복 업로드' 공수를 완전히 없애고, 최종 납품할 구글 드라이브 자체를 확정본의 유일한 SSOT로 삼아 검수와 납품이 한 곳에서 모두 끝나도록 구조를 개편했습니다.

2-3. 실행 환경 아키텍처: CLI vs Web App, 그리고 풀스택의 권능(?)

마지막으로 이 파이프라인을 실행할 환경에 대한 두 번째 고민을 마주했습니다. "이 무거운 프로세스를 도대체 어디에서 돌릴 것인가?"

  • Trade-off: 개발 공수만 생각한다면 단순한 백엔드 스크립트(CLI)를 짜서 돌리는 것이 가장 빠릅니다. 하지만 터미널 환경은 비개발자인 실무진이 작업 진행률을 실시간으로 확인하거나 세부 설정을 조작하기에 진입 장벽이 너무 높았습니다.
  • 결정과 이유: 비개발자도 직관적으로 시스템을 다룰 수 있도록, 프론트엔드 웹뷰와 Node.js 백엔드 API 리포지토리를 완전히 분리한 풀스택 환경을 구축하기로 결정했습니다.

감사하게도 이 프로젝트를 위해 회사에서 남는 PC를 하나 지원해 주셨습니다. 텅 빈 깡통 PC에 Ubuntu를 직접 깔아 서버로 띄우고, 제 업무용 맥북에서 원격으로 접속해 세팅하는 모든 과정이 제게는 완전히 새로운 도전이었습니다.

항상 만들어진 API를 가져다 쓰던 프론트엔드 개발자가 직접 물리 서버를 띄우고, 인프라부터 백엔드, 프론트엔드까지 A to Z를 전부 내 손으로 통제하며 설계하는 경험. 솔직히 말씀드리면 이 과정에서 일종의 '권능(?)'마저 느껴질 정도로 가슴이 뛰었습니다.

그렇게 설레는 마음으로 도출해 낸 최종 자동화 아키텍처 구조는 아래와 같습니다.

3. 프론트엔드 우물 밖으로: 모르면 물어봐라

아키텍처 설계까지는 그럴듯했지만, 막상 A to Z를 직접 구축하려니 프론트엔드 생태계에서 그동안 '당연하게' 쓰던 기술들의 민낯을 마주하게 되었습니다. 현재 제가 백엔드와 인프라 영역에서 무엇을 모르는지 명확하고 객관적으로 인지하게 된 순간이었습니다.

저는 혼자 끙끙대는 대신, 부장님과 백엔드 동료분들께 제가 모르는 점을 용기 있게 드러냈습니다. '가장 좋은 학습 수단은 훌륭한 동료'라는 생각으로 적극적으로 다가가 질문하고 피드백을 구하며 도메인 지식을 스펀지처럼 흡수했습니다. 프론트엔드 우물 밖으로 나와, 비어있던 지식들을 하나씩 메꿔나간 트러블슈팅 과정들을 가볍게 정리해 봅니다.

  • Q1. 서버는 200 OK를 주는데, 왜 브라우저에서 CORS 에러가 날까?
    • A. CORS 에러는 서버가 요청을 거부한 게 아니라, 브라우저의 보안 정책(SOP)이 프론트로 데이터를 넘겨주지 않고 막은 것이었습니다. 로컬 개발 환경에서는 빌드 툴의 Dev Proxy를 활용해 도메인을 우회하고, 운영 환경에서는 Nginx 리버스 프록시를 앞단에 두어 근본적인 제약을 차단했습니다.
  • Q2. 수십 기가 영상의 렌더링 진행률을 어떻게 실시간으로 추적할까?
    • A. HTTP GET은 일회성이고, Polling은 네트워크 오버헤드가 크며, WebSocket은 단방향 진행률만 필요한 상황에서 오버스펙이었습니다. 결과적으로 한 번의 요청 이후 서버가 지속해서 데이터를 밀어주는 단방향 스트리밍 기술인 SSE(Server-Sent Events)를 채택해 브라우저에 지연 없이 렌더링했습니다.
  • Q3. 내가 터미널을 끄면 백그라운드 서버도 같이 죽을까?
    • A. 터미널 종료 시 프로세스가 닫히는 Node.js 특성상 처음엔 PM2 같은 데몬 매니저를 도입하려 했습니다. 하지만 서버를 Docker로 띄우면서 개념이 달라졌습니다. 컨테이너 자체에 restart: always 정책이 있고 백그라운드 실행(d)을 지원하기 때문에, 도커 생태계 안에서는 굳이 PM2를 쓸 이유가 없다는 것을 실무 레벨에서 체감했습니다.
  • Q4. 도메인이 다르면 왜 쿠키가 안 보내질까?
    • A. 프론트(웹뷰)와 백엔드(API) 도메인이 분리된 환경에서는 설정이 양방향으로 맞아야만 브라우저가 쿠키를 허용합니다. 프론트는 브라우저에 쿠키 전송을 지시하는 withCredentials: true를 켜고, 백엔드는 크로스 도메인을 허용하는 SameSite=None; Secure 보안 정책을 쿠키에 함께 담아 내려주도록 세팅하여 해결했습니다.

4. 나의 한계가 비즈니스의 병목이 되지 않도록

누군가 시켜서 시작한 일은 아니었습니다. 단지 "우리는 왜 이 비효율을 당연하게 감수하고 있을까?"라는 근본적인 질문에서 출발했죠. 애초에 목표는 단순히 몇 시간을 더 야근해 일정을 맞추거나, 작업 시간을 조금 줄이는 것에 타협하는 것이 아니었습니다. 수작업이라는 비효율 자체를 '구조적으로 소거'하여, '개입 시간 0시간, 완전 자동화’라는 더 높은 기준을 세우고 파이프라인 고도화에 몰입했습니다.

프론트엔드 개발자로서 항상 화면 너머의 사용자 경험(UX)을 고민해 왔지만, 이번 프로젝트의 사용자는 '동고동락하는 동료'들이었습니다. 동료들을 위해 시작한 인프라와 백엔드 첫 구축 경험은, 결과적으로 웹 생태계 전체를 조망할 수 있는 넓은 시야를 제게 선물해 주었습니다.

물론 혼자만의 힘으로 이룬 것은 아닙니다. 제 영역 밖의 지식이 부족하다는 사실을 투명하게 인정하고, 부장님과 백엔드 동료분들께 주저 없이 다가가 조언을 구했습니다. 모르는 것을 용기 있게 드러내고 치열하게 학습했던 이 과정 자체가, 제게는 직무의 벽을 넘는 가장 강력한 성장의 동력이 되었습니다.

이번 경험을 통해 하나의 확고한 기준이 생겼습니다. '나의 직무 경계나 지식의 한계가 비즈니스의 병목이 되게 두지 않겠다'는 것입니다. 앞으로도 관성에 안주하지 않고 모든 것에 질문을 던지며, 팀과 프로덕트의 진짜 문제를 타파하기 위해 한계 없이 배우며 성장해나가는 개발자가 되겠습니다. 감사합니다.

    댓글 0

    닉네임과 댓글은 공개됩니다.

    0/1000

    댓글을 불러오는 중입니다…