트러블슈팅

PyMuPDF 버전 업데이트로 인한 PDF 스트림 파싱 오류 해결하기

gudaeng 2025. 6. 20. 22:56

PDF 문서 파싱 작업 중 갑자기 발생한 에러를 해결하면서 배운 내용을 공유한다. 같은 코드인데 배포 환경에서만 에러가 발생하는 상황이었는데, 원인을 찾아보니 라이브러리 버전 차이로 인한 문제였다.

문제 상황

Azure Blob Storage에서 PDF 파일을 다운로드해서 PyMuPDF로 파싱하는 작업을 진행하던 중 다음과 같은 에러가 발생했다:

doc = pymupdf.open(stream=file_path, filetype="pdf")

이 코드 실행 시 Document('None', <memory, doc# 1>)처럼 None이 반환되면서, 후속 처리에서 다음과 같은 에러가 발생:

TypeError: expected str, bytes or os.PathLike object, not NoneType

특이한 점은 로컬 환경에서는 정상 동작하는데 배포 환경에서만 이 에러가 발생한다는 것이었다.

원인 분석 과정

1. 기본적인 확인 작업

  • file_path에 들어오는 데이터 타입 확인 → BytesIO 객체로 정상 확인
  • Azure Blob에서 데이터 로드 상태 확인 → 정상 로드 확인
  • 동일한 코드를 사용하는 다른 배포 환경 확인 → 정상 동작 확인

2. 환경 차이 분석

로컬과 배포 환경의 차이점을 찾기 위해 각 환경에서 pip list를 실행해서 패키지 버전을 비교했다.

# 정상 동작하는 환경
pymupdf==1.24.x
pymupdf4llm==0.0.x

# 에러 발생하는 환경  
pymupdf==1.26.x
pymupdf4llm==0.0.x

pymupdf4llm은 동일한 버전이지만 pymupdf 자체 버전이 달랐다!

해결 과정

1. 버전 변경 이력 확인

  • 8일 전 배포와 현재 배포 환경의 패키지 버전 비교
  • pymupdf 1.24.x → 1.26.x로 업데이트되면서 내부 동작 방식이 변경됨

2. 버전별 차이점 분석

pymupdf 1.24.14:

  • 파라미터 타입 체크가 상대적으로 느슨함
  • BytesIO 객체를 stream 파라미터로 전달해도 내부에서 적절히 처리

pymupdf 1.26.x:

  • 파라미터 타입 체크가 훨씬 엄격해짐
  • stream 파라미터로 전달되는 객체의 타입을 더 까다롭게 검증

3. 해결 방법

두 가지 해결 방법이 있었다:

방법 1: 버전 고정

pip install pymupdf==1.24.14

방법 2: 코드 수정

# 기존 코드
doc = pymupdf.open(stream=file_path, filetype="pdf")

# 수정된 코드
if isinstance(file_path, BytesIO):
    file_path.seek(0)  # 스트림 포지션을 처음으로 이동
    doc = pymupdf.open(stream=file_path.read(), filetype="pdf")
else:
    doc = pymupdf.open(stream=file_path, filetype="pdf")

배운 점

1. 의존성 관리의 중요성

  • 라이브러리 업데이트 시 호환성 문제가 발생할 수 있음
  • requirements.txt에 정확한 버전을 명시하는 것이 중요
  • 배포 전에 의존성 버전 변경사항을 반드시 확인해야 함

Poetry 사용 시 추가 고려사항

Poetry를 사용하는 경우에도 비슷한 문제가 발생할 수 있다:

  • 간접 의존성 자동 업데이트: poetry install 또는 poetry update 시 직접 명시하지 않은 하위 의존성들이 자동으로 최신 버전으로 업데이트될 수 있음
  • Lock 파일의 중요성: poetry.lock 파일을 버전 관리에 포함시키지 않으면 환경마다 다른 버전의 패키지가 설치될 수 있음
# pyproject.toml - 직접 의존성만 명시
[tool.poetry.dependencies]
python = "^3.8"
pymupdf4llm = "^0.0.16"  # 이 패키지가 pymupdf를 간접 의존성으로 가짐

# poetry.lock에는 실제 설치된 모든 패키지의 정확한 버전이 기록됨

Poetry 환경에서의 해결 방법:

  1. Lock 파일 관리: poetry.lock 파일을 반드시 git에 포함
  2. 명시적 버전 고정: 문제가 되는 간접 의존성도 직접 명시
    [tool.poetry.dependencies]
    pymupdf4llm = "^0.0.16"
    pymupdf = "1.24.14"  # 간접 의존성이지만 명시적으로 버전 고정
  3. 업데이트 전략: poetry update보다는 poetry install을 기본으로 사용하고, 업데이트는 신중하게 진행

2. 환경별 차이 디버깅

  • 로컬과 배포 환경에서 다른 결과가 나올 때는 환경 차이부터 확인
  • pip list로 패키지 버전 비교하는 습관 필요
  • 같은 코드라도 의존성 버전에 따라 동작이 달라질 수 있음

3. 라이브러리 업데이트 대응

  • 메이저 버전 업데이트 시에는 변경사항을 꼼꼼히 확인
  • 특히 타입 체크나 파라미터 검증이 엄격해지는 경우가 많음
  • 업데이트 전에 테스트 환경에서 충분한 검증 필요

마무리

단순해 보이는 에러였지만 원인을 찾는 데 시간이 걸렸던 이유는 "같은 코드인데 왜 환경에 따라 다르게 동작하지?"라는 관점에서 접근하지 못했기 때문이다.

앞으로는 환경별로 다른 결과가 나올 때 의존성 버전부터 확인하는 습관을 가져야겠다. 특히 CI/CD 파이프라인에서 패키지 버전이 자동으로 업데이트되는 경우가 있으니, requirements.txt에 정확한 버전을 명시하는 것이 중요하다는 것을 다시 한번 깨달았다.

'트러블슈팅' 카테고리의 다른 글

문제 발생/해결 - 생각해야 할 것  (0) 2025.03.03
저장이 필수인 데이터 누락  (0) 2025.03.03
서비스 - 목록 API 오류 발생  (0) 2025.03.03
Push 알림 발송 실패 문제  (0) 2025.03.03
Airflow 설정 관련  (0) 2025.03.03