본문 바로가기

Python 업무 자동화

Matplotlib로 그린 트래픽 그래프 글씨가 뭉개지다: DPI 최적화 실패로 쓸모없어진 데이터 시각화

[실패기] Matplotlib로 그린 트래픽 그래프 글씨가 뭉개지다: DPI 최적화 실패로 쓸모없어진 데이터 시각화

파이선생의 1인 기업 자동화 생존기

안녕하세요! AI 자동화 연구소의 파이선생입니다.

혹시 파이썬으로 데이터 분석을 하시면서 엑셀(Excel) 데이터나 웹 크롤링으로 수집한 데이터를 예쁘게 시각화해보려고 하신 적 있으신가요?
데이터를 수집하고 전처리하는 과정은 코드가 복잡하고 예외 처리가 많아 고단하지만, 막상 그 데이터가 한눈에 들어오는 아름다운 그래프로 그려질 때의 쾌감은 이루 말할 수 없습니다. 저 역시 이전 편에서 다루었던 Ppedi Market Intelligence 프로젝트의 트래픽 데이터를 파싱하며 인코딩 에러와 싸우느라 진을 뺐지만, "이제 그래프만 그리면 끝이다!"라는 생각에 부푼 마음을 안고 파이썬의 국민 시각화 라이브러리인 Matplotlib을 꺼내 들었습니다.

그런데... 제가 마주한 결과물은 아름다운 인사이트가 아니라, 글씨를 도저히 읽을 수 없는 처참한 '저화질 픽셀 덩어리' 그래프였습니다.




[그림 1] 옵션 없이 저장하여 픽셀이 뭉개지고 여백이 잘린 기본 저화질 그래프



🚨 문제 발생: 픽셀이 박살 난 저화질 그래프와 잘려 나간 X축

트래픽 증감을 보여주는 선 그래프(Line Chart)를 그리기 위해 pandas로 데이터를 깔끔하게 정리하고, plt.plot() 함수를 이용해 x축에는 날짜(dates)를, y축에는 방문자 수(traffic_data)를 할당했습니다.




[코드 1] 무심코 작성했던 초기 Matplotlib 플로팅 코드


주피터 노트북(Jupyter Notebook) 화면상에서는 그래프가 어느 정도 형태를 갖추고 나오는 것을 확인했습니다. 그래서 자신 있게 plt.savefig("traffic_report.png") 코드를 실행하여 이미지 파일로 자동 추출되도록 파이프라인을 짰죠.

하지만 자동 렌더링되어 폴더에 저장된 traffic_report.png 파일을 더블클릭해서 연 순간, 저는 제 눈을 의심했습니다. 화질이 너무 낮아서 이미지를 조금만 확대해도 선명해야 할 그래프 선이 계단처럼 픽셀이 다 깨져 보였습니다. 게다가 X축의 '날짜' 라벨과 Y축의 '방문자 수' 수치들은 형체를 알아볼 수 없을 정도로 뭉개져 있었고, 심지어 X축의 긴 날짜 텍스트는 캔버스 밖으로 잘려 나가 절반은 보이지도 않았습니다.

이 이미지를 그대로 고객용 보고서에 넣거나, 유튜브 영상 자료로 썼다가는 "이 사람 코딩할 줄 모르는 거 아니야?"라는 핀잔을 듣기에 딱 좋은, 도저히 실무에서는 쓸 수 없는 쓰레기 데이터 시각화가 되어버린 것입니다.

🔍 원인 분석: Matplotlib 기본 DPI 100의 치명적 함정

도대체 왜 화면에서 볼 때랑 파일로 저장했을 때의 퀄리티가 다를까요? 원인은 아주 단순하면서도 파이썬 초보자들이 가장 많이 겪는 치명적인 함정에 있었습니다.

바로 Matplotlib의 savefig() 함수가 기본적으로 설정하고 있는 해상도, 즉 DPI(Dots Per Inch) 값이었습니다.
Matplotlib은 코드가 가볍게 돌아가고 화면에 빠르게 결과물을 띄워주는 데 최적화되어 있습니다. 그래서 이미지 파일로 저장할 때 별도의 옵션을 명시하지 않으면 기본 DPI가 100이라는 매우 낮은 수치로 강제 설정되어 렌더링됩니다. (과거 버전에서는 심지어 80 DPI인 적도 있었습니다).

요즘 우리가 사용하는 스마트폰이나 모니터는 대부분 FHD를 넘어 4K, 레티나 디스플레이를 지원하는 엄청난 고해상도 기기들입니다. 이런 고화질 디스플레이에서 고작 100 DPI짜리 이미지를 띄우면 당연히 이미지가 억지로 늘어나면서 픽셀이 깍두기처럼 깨져 보이는 '안티 앨리어싱(Anti-Aliasing)' 붕괴 현상이 일어날 수밖에 없는 것입니다.

또한 X축 글씨가 밖으로 튕겨져 나가 잘린 이유는 Matplotlib이 이미지를 저장할 때 'Figure'라는 기본 캔버스 사이즈만 딱 맞게 저장하고, 캔버스 바깥으로 살짝 삐져나온 텍스트 라벨이나 범례(Legend)의 크기까지는 똑똑하게 계산해서 여백을 늘려주지 않기 때문입니다.

💡 해결 방안: 인쇄 표준 DPI 300과 bbox_inches='tight' 의 마법

문제를 정확히 진단했으니 해결은 명쾌했습니다. 화질을 대폭 끌어올리고, 글씨가 잘리는 것을 막아줄 두 가지 마법의 옵션을 savefig() 함수에 추가했습니다.

첫 번째는 dpi=300 옵션입니다. 출판사나 인쇄소에서 고해상도 브로셔를 출력할 때 요구하는 최소 표준 해상도가 바로 300 DPI입니다. 이 옵션을 주면 이미지의 픽셀 밀도가 3배로 조밀해지면서, 선의 윤곽선과 폰트가 레티나 디스플레이에서도 쨍하게 보이도록 고해상도로 렌더링됩니다.

두 번째는 bbox_inches='tight' 옵션입니다. 이 옵션은 "Bounding Box(경계 상자)를 타이트하게 맞춰라"라는 뜻입니다. 즉, 캔버스 사이즈에 얽매이지 말고, X축이나 Y축 라벨, 혹은 그래프 밖에 위치한 범례(Legend)까지 모두 포함하는 가장 외곽의 크기를 계산해서 이미지가 하나도 잘리지 않도록 여백을 딱 맞게 자동 조절해 주는 핵심 필수 옵션입니다.

더불어, 한글 폰트가 네모 박스 모양(ㅁㅁㅁ)으로 깨지는 고질적인 현상과 마이너스(-) 기호가 깨지는 현상까지 방지하는 세팅을 추가하여 최종 코드를 완성했습니다.




[코드 2] DPI 300 및 tight 옵션을 적용한 최종 최적화 코드


✅ 짜릿한 결과 확인과 E-E-A-T 품질 확보

위의 최적화 코드를 실행하고 두근거리는 마음으로 새로 생성된 traffic_report_high_res.png 파일을 열어보았습니다.
결과는 대성공이었습니다!

그래프의 굵은 선은 매끄러운 곡선을 자랑했고, 각 날짜를 가리키는 X축의 작은 글씨와 한글 라벨들은 제가 사용하는 32인치 4K 모니터에서 아무리 마우스 휠을 굴려 확대해도 전혀 깨지지 않고 선명하게 렌더링 되었습니다. 무엇보다 tight 옵션 덕분에 길게 적혀 있던 X축 날짜 라벨이 하나도 잘려 나가지 않고 캔버스 안에 안전하게 쏙 들어와 있었습니다.




[그림 2] DPI 300과 bbox_inches='tight' 옵션이 적용되어 4K 해상도에서도 선명한 그래프



이 선명한 고화질 이미지를 제 자동 포스팅 파이프라인의 결과물로 첨부하여 리포트로 받아보니 그동안의 체증이 싹 내려가는 기분이었습니다. 데이터 분석의 진정한 완성은 타인이 한눈에 알아보기 쉽도록 명확하고 아름답게 시각화하여 전달하는 것에 있다는 것을 뼈저리게 느낀 순간이었습니다.

🎯 오늘의 교훈 (Takeaways) 및 실무 적용 팁

  1. DPI 300은 선택이 아닌 필수다: 파이썬 시각화 결과물(Matplotlib, Seaborn 등)을 파일로 저장하여 남에게 보여줘야 한다면, 코드의 가장 마지막 줄 savefig에는 무조건 숨도 쉬지 말고 dpi=300을 입력하는 습관을 들이세요.
  2. 글씨 잘림의 구원자, bbox_inches='tight': 기껏 예쁘게 꾸민 그래프인데 X축 글자나 범례가 뚝 잘려서 저장된다면 bbox_inches='tight' 옵션 하나로 모든 렌더링 스트레스를 날려버릴 수 있습니다.
  3. 한글 폰트 설정은 스크립트 최상단에: plt.rcParams['font.family'] 설정은 코드가 실행될 때 가장 먼저 선언해 두어야 이후 그려지는 모든 그래프에 오류 없이 반영됩니다.




고해상도로 변환된 깔끔한 그래프를 보고 안도하는 파이선생의 모습




다음 편 예고
시각화까지 깔끔하게 끝내고 자신만만해진 저는, 내친김에 시장 점유율을 늘려보고자 경쟁 채널의 키워드를 무작정 대량으로 크롤링(Crawling)하는 스크립트를 짜서 돌리기 시작했습니다. 그런데... 트래픽에 욕심을 내어 너무 공격적인 크롤링 루프를 돌린 탓에, 구글 서버 측에 IP 차단(Block)을 당하며 회사 공용 IP 전체가 막혀버렸던 끔찍한 썰이 기다리고 있습니다. 다음 편을 기대해 주세요!