배포 실패 및 크래시 즉시 해결 가이드
업데이트: 2026-10-02
DashCloud에 봇이나 자동화 프로그램을 배포할 때 발생하는 대부분의 문제는 복잡한 인프라 결함이 아니라, **로컬 머신과 클라우드 환경의 사소한 설정 차이(의존성 누락, 경로 불일치, 프로세스 루프 부재)**에서 비롯됩니다.
가장 빈번하게 보고되는 10대 에러의 증상과 원인, 1분 즉시 해결 레시피를 정리했습니다.
1. ModuleNotFoundError: No module named '...'
증상 및 로그
Traceback (most recent call last):
File "/app/main.py", line 3, in <module>
import requests
ModuleNotFoundError: No module named 'requests'원인
로컬 컴퓨터(내 PC)에는 패키지가 전역 환경이나 기존 가상환경에 이미 설치되어 있어 정상 실행되었으나, 클라우드 빌드 환경에 제공된 requirements.txt에 해당 패키지 이름이 적혀있지 않은 경우입니다.
1분 해결법
- 프로젝트 폴더의
requirements.txt파일을 엽니다. - 에러가 발생한 모듈 이름을 추가합니다. (예:
requests>=2.31.0) - 파일을 저장하고 대시보드에서 재배포 버튼을 누르거나 폴더를 다시 업로드합니다.
2. 배포 직후 프로세스가 꺼짐 (Process exited with code 0)
증상 및 로그
[DashCloud Runner] Container started successfully.
Hello World!
[DashCloud Runner] Main process finished with exit code 0.
[DashCloud Runner] Service state changed to STOPPED.원인
코드가 끝까지 실행된 후 대기하지 않고 스스로 종료된 종료 코드 0(정상 종료)으로 스크립트가 끝난 상태입니다. 로컬 터미널에서는 1회성 스크립트로 실행하고 끝냈지만, 클라우드 서버는 24시간 켜져 있어야 하므로 이벤트 대기 루프가 필요합니다.
1분 해결법
텔레그램 봇 애플리케이션의 롱폴링 대기 함수(run_polling())를 호출하고 있는지 확인하세요.
# 잘못된 예: 핸들러만 등록하고 대기 루프 없이 스크립트가 끝남
app.add_handler(CommandHandler("start", start))
# 올바른 예: 24/7 폴링 루프 가동
app.run_polling(drop_pending_updates=True)3. KeyError: 'TELEGRAM_BOT_TOKEN' 또는 환경변수 누락
증상 및 로그
Traceback (most recent call last):
File "/app/main.py", line 12, in <module>
token = os.environ["TELEGRAM_BOT_TOKEN"]
File "/usr/local/lib/python3.12/os.py", line 685, in __getitem__
raise KeyError(key) from None
KeyError: 'TELEGRAM_BOT_TOKEN'원인
소스 코드에서는 os.environ["..."]으로 특정 환경변수를 요구하고 있으나, DashCloud 대시보드의 환경변수 설정에 해당 키가 등록되지 않았거나 오타가 있는 경우입니다.
1분 해결법
- 프로젝트 상세 페이지 ➔ 환경 변수 탭으로 이동합니다.
- 누락된 환경변수 이름(
TELEGRAM_BOT_TOKEN)을 오타 및 앞뒤 공백 없이 정확히 입력하고 토큰 값을 넣습니다. - 코드 작성 시
os.environ["KEY"]대신os.environ.get("KEY")를 사용하고 누락 시 명확한 에러 메시지를 출력하도록 방어 코드를 작성하는 것을 권장합니다:
token = os.environ.get("TELEGRAM_BOT_TOKEN")
if not token:
logger.critical("필수 환경변수 TELEGRAM_BOT_TOKEN이 등록되지 않았습니다!")
sys.exit(1)4. telegram.error.Conflict: Terminated by other getUpdates request
증상 및 로그
telegram.error.Conflict: Conflict: terminated by other getUpdates request;
make sure that only one bot instance is running원인
동일한 텔레그램 봇 토큰으로 두 개 이상의 프로세스가 동시에 롱폴링을 요청했을 때 텔레그램 서버가 기존 연결을 끊어버리는 현상입니다.
- 내 로컬 컴퓨터 터미널에서 봇을 켜둔 상태로 DashCloud에도 배포한 경우
- 다른 호스팅이나 이전 테스트 서버에 봇이 여전히 실행 중인 경우
1분 해결법
- 내 로컬 컴퓨터 터미널이나 VS Code에서 실행 중인 파이썬 프로세스를
Ctrl + C로 완전히 종료합니다. - BotFather에서 봇 토큰을 재발급받아 대시보드 환경변수에 새로 입력하면 기존의 모든 유령 연결이 즉시 강제 종료됩니다.
5. sqlite3.OperationalError: database is locked
증상 및 로그
sqlite3.OperationalError: database is locked원인
여러 비동기 코루틴이나 스레드가 단일 SQLite 파일에 동시에 쓰기 트랜잭션을 시도하여 파일 락 충돌이 발생한 경우입니다.
1분 해결법
SQLite 연결을 생성할 때 WAL(Write-Ahead Logging) 저널 모드를 활성화하고 연결 타임아웃을 넉넉히 지정합니다.
import sqlite3
def get_connection():
conn = sqlite3.connect("/app/data/bot.db", timeout=30.0)
# WAL 모드 활성화: 동시 읽기/쓰기 성능 및 충돌 방지 극대화
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
return conn6. FileNotFoundError: [Errno 2] No such file or directory
증상 및 로그
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\admin\\bot\\config.json'원인
코드 내부에 로컬 PC 전용 절대 경로(C:\Users\... 또는 /Users/name/...)가 하드코딩되어 클라우드 리눅스 파일시스템에서 경로를 찾지 못하는 경우입니다.
1분 해결법
pathlib.Path를 사용하여 항상 현재 스크립트 실행 위치 기준의 상대 경로로 참조하세요.
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
CONFIG_PATH = BASE_DIR / "config.json"
DATA_DIR = BASE_DIR / "data"7. telegram.error.RetryAfter: Flood control exceeded (429 에러)
증상 및 로그
telegram.error.RetryAfter: Flood control exceeded. Retry in 18 seconds.원인
텔레그램 봇 API의 초당 발송 한도(그룹 채팅 초당 20건, 개인 초당 30건)를 초과하여 대량의 메시지를 짧은 시간에 쏟아부었을 때 발생하는 레이트 리밋 차단입니다.
1분 해결법
- 메시지를 즉시 전송하지 말고 비동기 큐(
asyncio.Queue)에 담아 초당 20건 이하로 조율하여 순차 발송합니다. RetryAfter예외를 캐치하여 지정된 초만큼 대기한 뒤 재전송하는 재시도 로직을 적용합니다:
from telegram.error import RetryAfter
import asyncio
async def safe_send_message(bot, chat_id, text):
try:
await bot.send_message(chat_id=chat_id, text=text)
except RetryAfter as e:
logger.warning(f"속도 제한 발생. {e.retry_after}초 후 재시도합니다.")
await asyncio.sleep(e.retry_after)
await bot.send_message(chat_id=chat_id, text=text)8. OOM_KILLED (메모리 상한 초과 강제 종료)
증상 및 로그
[DashCloud Monitor] Process exceeded memory limit (256MB).
[DashCloud Monitor] OS Kernel sent SIGKILL.
Service status: OOM_KILLED원인
- Playwright/Selenium 크롤러에서 페이지를 이동할 때 브라우저 컨텍스트(
context.close())를 닫지 않아 Chromium DOM 힙이 누적된 경우 - 대용량 파일(CSV, 엑셀, 비디오)을 청크 단위 스트리밍이 아니라 한 번에 통째로 메모리에 로드(
read())한 경우
1분 해결법
- 크롤러의 경우 각 수집 작업마다
new_context()를 생성하고 반드시finally: context.close()를 호출하세요. - 대시보드의 메트릭 탭에서 메모리 사용량 그래프의 우상향 기울기를 확인하고 메모리 누수 지점을 진단하세요.
- 헤드리스 Chromium 메모리 최적화 가이드를 참고하여 이미지/폰트 네트워크 차단을 적용하세요.
9. OSError: [Errno 98] Address already in use
증상 및 로그
OSError: [Errno 98] Address already in use: ('0.0.0.0', 8080)원인
텔레그램 롱폴링 봇이나 크롤러에 불필요하게 app.run(port=8080) 같은 웹서버 코드가 포함되어 포트 바인딩 충돌을 일으킨 경우입니다.
1분 해결법
롱폴링 봇과 크롤러는 웹 서버 포트를 열 필요가 없습니다. Flask/FastAPI 서버 기동 코드를 제거하고 순수 백그라운드 봇 스크립트로 실행하세요. 외부에서 웹훅을 받아야 하는 경우에만 포트를 바인딩합니다.
10. 스크린샷 캡처 시 한글 폰트가 깨짐 (뛟뛟, 사각형 기호)
증상
Playwright로 한국어 웹페이지를 캡처했을 때 한글 텍스트가 전부 네모 박스(□□□)로 깨져 나오는 현상입니다.
원인
리눅스 서버 환경에 기본 한글 폰트 패키지가 설치되어 있지 않은 경우입니다.
1분 해결법
DashCloud의 Chromium + Python (crawler-chromium) 런타임 프리셋을 선택하세요. Noto Sans CJK 한글 글꼴이 OS 레벨에 기본 탑재되어 있어 별도 설정 없이 완벽한 한글 렌더링을 지원합니다.