import os import logging from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request from googleapiclient.discovery import build from googleapiclient.http import MediaFileUpload import pickle SCOPES = ['https://www.googleapis.com/auth/drive.file'] logger = logging.getLogger(__name__) if not logger.handlers: handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s - %(message)s') handler.setFormatter(formatter) logger.addHandler(handler) logger.setLevel(logging.INFO) class GoogleDriveUploader: def __init__(self, credentials_file, token_file): self.credentials_file = credentials_file self.token_file = token_file self.service = None self._authenticate() def _authenticate(self): """구글 드라이브 인증""" creds = None logger.info("구글 드라이브 인증 시작 | credentials=%s token=%s", self.credentials_file, self.token_file) # 기존 토큰 파일이 있으면 로드 if os.path.exists(self.token_file): with open(self.token_file, 'rb') as token: creds = pickle.load(token) logger.info("기존 토큰 파일 로드 성공") # 유효한 인증 정보가 없으면 새로 인증 if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: logger.info("토큰 만료, refresh 시도") creds.refresh(Request()) logger.info("토큰 refresh 성공") else: if not os.path.exists(self.credentials_file): raise FileNotFoundError( f"구글 드라이브 인증 파일을 찾을 수 없습니다: {self.credentials_file}\n" "Google Cloud Console에서 OAuth 2.0 클라이언트 ID를 다운로드하여 " "credentials.json으로 저장해주세요." ) flow = InstalledAppFlow.from_client_secrets_file( self.credentials_file, SCOPES) # 서버 환경에서는 브라우저가 없을 수 있으므로 예외 처리 try: logger.info("로컬 서버 플로우로 인증 시도") creds = flow.run_local_server(port=0) logger.info("로컬 서버 플로우 인증 성공") except Exception as e: # 브라우저를 열 수 없는 경우 (서버 환경 등) # 토큰 파일이 이미 있다면 재시도하지 않고 예외 발생 raise Exception( "구글 드라이브 인증을 완료할 수 없습니다. " "로컬 환경에서 먼저 인증을 완료하여 token.json 파일을 생성해주세요." ) from e # 토큰 저장 with open(self.token_file, 'wb') as token: pickle.dump(creds, token) logger.info("토큰 파일 저장 완료: %s", self.token_file) self.service = build('drive', 'v3', credentials=creds) logger.info("구글 드라이브 서비스 생성 완료") def upload_file(self, file_path, file_name, folder_id=None): """파일을 구글 드라이브에 업로드""" if not self.service: raise Exception("구글 드라이브 서비스가 초기화되지 않았습니다.") file_metadata = {'name': file_name} if folder_id: file_metadata['parents'] = [folder_id] logger.info("구글 드라이브 업로드 준비 | file=%s, name=%s, folder=%s", file_path, file_name, folder_id) media = MediaFileUpload(file_path, mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document') file = self.service.files().create( body=file_metadata, media_body=media, fields='id' ).execute() file_id = file.get('id') logger.info("구글 드라이브 업로드 성공 | file_id=%s", file_id) return file_id