프로젝트/리뷰집

하루종일 괴롭게 하는 한글 인코딩 그냥 FilePathField 쓰자

발전생 2021. 2. 9. 17:09

현재 리뷰 프로젝트를 진행하면서 리뷰 파일을 csv로 만들고 데이터베이스에 업로드 시키고 pandas의 read_csv로 읽게 했다. 물론 모든 곳에 encoding='utf-8'

 

cp949 인코딩도 안 되는 것인가?

이 문제는 리뷰에 섞여있는 이모티콘(emoji) 때문에 발생했다고 추측 중이다.

일부 리뷰에는 문제 없이 잘 작동하는 걸로 보아 cp949 인코딩이 해석할 수 없는 문자(이모티콘)가 들어가서 이런 문제가 발생한 듯 하다.

UnicodeEncodeError: 'cp949' codec can't encode character '\U0001f44d' in position 431: illegal multibyte sequence

 

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
if encoding is not specified the encoding used is platform dependent:

파이썬 공식 문서의 open() 함수 설명 일부이다.

 

encoding의 default는 none이어서 따로 지정을 안 해주면 플랫폼 기본 설정(나의 경우 윈도우라 cp949 인코딩)으로 열고 있었다. 

아.....

 

문제 발생점으로 여겨지는 django.core.files.File 의 open() 함수

def open(self, mode=None):
        if not self.closed:
            self.seek(0)
        elif self.name and os.path.exists(self.name):
            self.file = open(self.name, mode or self.mode)
        else:
            raise ValueError("The file cannot be reopened.")
        return self

이 부분에서 open() 함수에 encoding='utf-8'을 추가해봤다.

??? 그래도 안 된다.

 

이모티콘은 단순 인코딩 종류 문제가 아니라 byte 문제인 거 같다.

알아보니 emoji는 4바이트를 사용한다. 일반 utf-8은 2바이트이다.

 

단순히 csv 파일을 작성할 때 이모티콘이 있어도 문제가 없다. (encoding='utf-8' 기준)
확인해 보니 한글도 안 깨지고 이모티콘도 잘 담겨있다.

그러나 django.core.files 의 File()을 사용해서 코드 상에서 직접 파일을 저장하면 문제가 생긴다. 

한글이 깨지고 이모티콘이 살았는 지도 알 수 없다.

 

보통 utf-8은 1바이트에서 4바이트까지 커버할 수 있다.

데이터베이스 종류에 따라 utf-8이 3바이트만 저장 가능할 수도 있다.(Mysql은 3바이트 제한)

django에서는 업로드한 파일이 아닌 코드 상에서 추가된 파일은 조금씩 쪼개서 읽은 뒤 저장한다. 그렇기 때문에 문제가 발생했을 수 있다.

 

더 이상 이 문제로 골치 아프고 싶지 않았다. 그래서 Model을 살짝 바꿔서 FileField 대신 FilePathField를 사용했다.

처음 생성되는 csv 파일에는 한글도 이모티콘도 잘 담겨 있었기 때문에 이 선택을 할 수 있었다.

python의 open 함수로 csv 파일을 만들고 직접 경로에 추가해준 뒤 해당 경로를 데이터베이스에 저장하는 방식이다.

 

이모티콘이 섞여있는 리뷰에서 테스트 결과 아무 문제 없이 Reivew 데이터까지 잘 생성했다.

 

인코딩 문제로 고생 중이라면 어쩌면 데이터베이스의 문제일 수 있다고 말해주고 싶다.