로컬 자동화 프로그램을 Render 웹앱으로 확장하기
반복적으로 수행하는 업무 가운데 Google Drive의 기존 PDF 파일 뒤에 새로운 PDF를 이어붙이는 작업과 Google Docs 문서 마지막에 공지를 추가하는 작업이 있었다.
작업 자체는 복잡하지 않았지만 반복 횟수가 많았고, 매번 같은 과정을 수행해야 했다.
프로젝트는 이러한 반복 작업을 자동화하는 Python 프로그램을 만드는 것에서 시작했다.
초기에는 로컬에서 실행하는 프로그램으로 개발했지만, 이후 브라우저만 있으면 어디서나 사용할 수 있도록 Render에 배포 가능한 웹앱으로 확장했다.
이 글에서는 프로젝트가 로컬 프로그램에서 웹 서비스로 발전하기까지의 개발 과정을 정리한다.
개발 목표
프로젝트의 목표는 크게 두 가지였다.
첫 번째는 Google Drive에 저장된 기존 PDF 파일 뒤에 새로운 PDF를 자동으로 이어붙이는 기능이다.
두 번째는 Google Docs 문서 마지막에 날짜와 공지 문장을 자동으로 추가하는 기능이다.
추가로 브라우저만 있으면 사용할 수 있도록 웹 환경에서 동작하는 구조를 목표로 했다.
로컬 프로그램으로 시작
프로젝트는 Python으로 작성한 로컬 프로그램으로 시작했다.
Google Drive API와 Google Docs API를 이용하여 각각의 기능을 구현했고, 기능별로 모듈을 분리하여 관리하도록 설계했다.
주요 구성은 다음과 같다.
- web_app.py
- drive_append_pdf.py
- docs_append_notice.py
- export_render_env.py
초기 단계에서는 기능 구현과 API 연동에 집중했고, 배포 환경은 이후 단계에서 검토하기로 했다.
웹앱으로 구조 변경
로컬 프로그램이 안정적으로 동작한 이후에는 브라우저에서 사용할 수 있는 웹앱으로 구조를 변경했다.
웹 환경으로 전환하면서 다음 기능을 추가했다.
- Basic Authentication
- 브라우저 기반 사용자 인터페이스
- Preview 기능
- Health Check(/health)
- Render 환경변수 지원
기능 자체보다 로컬 환경과 서버 환경의 차이를 고려하는 작업이 더 많은 시간을 차지했다.
OAuth 인증 구조 변경
개발 과정에서 가장 많은 시간이 소요된 부분은 Google API 자체보다 OAuth 인증이었다.
Google Cloud에서는 다음과 같은 작업을 진행했다.
- 프로젝트 생성
- Google Drive API 활성화
- Google Docs API 활성화
- OAuth 동의 화면 구성
- 테스트 사용자 등록
- Desktop OAuth Client 생성
로컬 환경에서는 문제가 없었지만, Render에 배포한 이후 새로운 문제가 발생했다.
Render에서는 브라우저를 사용할 수 없다
배포 이후 PDF 기능이 정상적으로 동작하지 않았다.
원인은 Render 환경에서는 Google 로그인 창을 실행할 수 없다는 점이었다.
초기 구조에서는 OAuth 인증이 필요할 때 브라우저를 통해 로그인을 수행하도록 구현되어 있었다.
하지만 Render 서버에서는 이러한 방식이 동작하지 않았다.
결국 인증 구조를 다음과 같이 변경했다.
- 로컬 PC에서 OAuth 인증 수행
- token.json 생성
- token_docs.json 생성
- export_render_env.py를 이용하여 환경변수 변환
- Render 환경변수에 인증 정보 등록
이후에는 서버에서 별도의 로그인 과정 없이 Google API를 사용할 수 있도록 변경했다.
Render 배포
웹앱은 Render Web Service를 이용하여 배포했다.
배포를 위해 다음 파일을 추가했다.
- requirements_web_app.txt
- Procfile
- runtime.txt
- render.yaml
Build Command는 패키지 설치만 수행하도록 구성했고,
Start Command는 web_app.py를 실행하도록 설정했다.
또한 Health Check를 위해 /health 엔드포인트를 구현했다.

개발 과정에서 발생한 문제
프로젝트를 진행하면서 여러 가지 문제를 만났다.
Python 3.13 호환성
초기 배포 과정에서 cgi 모듈이 제거된 것을 확인했다.
Python 3.13부터는 기존 코드가 동작하지 않았기 때문에 런타임을 Python 3.12로 변경하여 문제를 해결했다.
OAuth 인증 실패
OAuth 동의 화면에서 테스트 사용자를 등록하지 않은 상태에서는 로그인이 거부되었다.
테스트 사용자 등록 이후 정상적으로 인증을 진행할 수 있었다.
Render 502 Bad Gateway
배포 이후 가장 많이 확인했던 오류였다.
원인은 대부분 다음 가운데 하나였다.
- 환경변수 누락
- OAuth 토큰 누락
- 앱 재시작
- 메모리 부족
문제 발생 시 Render Logs를 확인하면서 원인을 하나씩 수정했다.
파일명 검증
PDF를 날짜 순으로 정렬하기 위해 업로드 파일명이 YYMMDD 형식으로 시작하도록 규칙을 추가했다.
예를 들어
- 260701_공지.pdf
- 260628_약제팀회람.pdf
처럼 업로드하면 날짜 기준으로 자동 정렬된다.
규칙에 맞지 않는 파일은 업로드 단계에서 검증하도록 구현했다.
보안 구조 정리
웹 서비스로 배포하면서 보안 구조도 함께 수정했다.
OAuth 관련 파일은 GitHub에 포함하지 않도록 .gitignore에 추가했다.
- credentials.json
- token.json
- token_docs.json
- render_env_values.txt
또한 OAuth 정보는 코드에 포함하지 않고 Render 환경변수를 이용하도록 변경했다.
외부 접근을 제한하기 위해 Basic Authentication도 함께 적용했다.
최종 구조
최종적으로 프로젝트는 다음과 같은 기능을 제공한다.
- Google Drive PDF 자동 병합
- Google Docs 공지 자동 추가
- 날짜 자동 변환
- Preview 기능
- Basic Authentication
- Render 배포
- OAuth 환경변수 관리
프로젝트는 단순한 로컬 자동화 프로그램으로 시작했지만, 최종적으로는 브라우저만 있으면 사용할 수 있는 웹 서비스 형태로 확장되었다.
Google Drive API와 Google Docs API 연동뿐 아니라 OAuth 인증, Render 배포, 환경변수 관리, 그리고 서비스 운영을 위한 보안 구조까지 함께 설계하면서 프로젝트를 완성했다.

마무리
이번 프로젝트는 반복 업무를 자동화하는 것에서 출발했지만, 결과적으로는 로컬 프로그램을 웹 서비스로 전환하는 과정을 경험한 프로젝트가 되었다.
기능 구현 자체보다 인증 구조, 배포 환경, 운영 방식처럼 실제 서비스에서 필요한 요소를 함께 설계해야 했다는 점이 가장 큰 특징이었다.
앞으로는 기능을 추가하는 것보다 유지보수와 관리 편의성을 중심으로 프로젝트를 계속 개선해 나갈 계획이다.
💬 댓글 (0)