시작하기

배포 실패 및 크래시 즉시 해결 가이드

ModuleNotFoundError, Exit 0 즉시 종료, 토큰 충돌, 메모리 초과 등 개발자가 가장 자주 마주치는 10대 에러 해결법.

업데이트: 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분 해결법

  1. 프로젝트 폴더의 requirements.txt 파일을 엽니다.
  2. 에러가 발생한 모듈 이름을 추가합니다. (예: requests>=2.31.0)
  3. 파일을 저장하고 대시보드에서 재배포 버튼을 누르거나 폴더를 다시 업로드합니다.

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())를 호출하고 있는지 확인하세요.

main.py
# 잘못된 예: 핸들러만 등록하고 대기 루프 없이 스크립트가 끝남
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분 해결법

  1. 프로젝트 상세 페이지 ➔ 환경 변수 탭으로 이동합니다.
  2. 누락된 환경변수 이름(TELEGRAM_BOT_TOKEN)을 오타 및 앞뒤 공백 없이 정확히 입력하고 토큰 값을 넣습니다.
  3. 코드 작성 시 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분 해결법

  1. 내 로컬 컴퓨터 터미널이나 VS Code에서 실행 중인 파이썬 프로세스를 Ctrl + C로 완전히 종료합니다.
  2. BotFather에서 봇 토큰을 재발급받아 대시보드 환경변수에 새로 입력하면 기존의 모든 유령 연결이 즉시 강제 종료됩니다.

5. sqlite3.OperationalError: database is locked

증상 및 로그

sqlite3.OperationalError: database is locked

원인

여러 비동기 코루틴이나 스레드가 단일 SQLite 파일에 동시에 쓰기 트랜잭션을 시도하여 파일 락 충돌이 발생한 경우입니다.

1분 해결법

SQLite 연결을 생성할 때 WAL(Write-Ahead Logging) 저널 모드를 활성화하고 연결 타임아웃을 넉넉히 지정합니다.

database.py
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 conn

6. 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분 해결법

  1. 메시지를 즉시 전송하지 말고 비동기 큐(asyncio.Queue)에 담아 초당 20건 이하로 조율하여 순차 발송합니다.
  2. 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분 해결법

  1. 크롤러의 경우 각 수집 작업마다 new_context()를 생성하고 반드시 finally: context.close()를 호출하세요.
  2. 대시보드의 메트릭 탭에서 메모리 사용량 그래프의 우상향 기울기를 확인하고 메모리 누수 지점을 진단하세요.
  3. 헤드리스 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 레벨에 기본 탑재되어 있어 별도 설정 없이 완벽한 한글 렌더링을 지원합니다.


다음 추천 문서