카테고리 없음

뉴스분석 및 요약서비스(서버구축 및 로직 정리)

hearai 2025. 1. 25. 17:01

현재 개발서버에서는 runserver하면 크롤링이 시작되고, 실시간 뉴스랭킹 구축된다.
또한 주요기사 모아보기가 생성되고, 주요 기사 요약은 클릭 시 작동하며 자동으로 DB에 저장된다.

즉, 서버시작 시 자동으로 초기 데이터 생성 1시간마다 자동 업데이트 되며

세 섹션 모두 항상 최신 데이터 유지한다. 사용자는 페이지 로드만으로 모든 정보를 확인할 수 있다.

모델 생성 ▶ 마이그레이션 생성 및 적용 ▶ views.py에 저장 로직 추가 ▶ 저장된 요약을 조회하는 뷰 추가 ▶ URL 패턴 추가 ▶ 저장된 요약을 보여주는 템플릿 생성

 

🥕 분석 모델선정

기사당 앞부분 2000자를 분석하는데 여기에 700토큰 정도가 소요된다. 20개 기사 기준으로 보면 14K 토큰 정도가 소요된다.

1. GPT-3.5-turbo-16k(20개 기사 기준): 입력(14K): $0.014 / 출력(1K): $0.002 / 총 비용: 약 $0.016
2. GPT-4-turbo-preview
(20개 기사 기준): 입력(14K): $0.14 / 출력(1K): $0.03 / 총 비용: 약 $0.17

 

GPT-3.5-turbo-16k가 GPT-4-turbo보다 10배 저렴했지만 요약 품질도 충분히 좋았다. 하지만 토큰 수가 잘리는 현상이 발생했는데, 이는 배치를 3개 기준으로 나누어 처리했다.

🥕 데이터베이스 선정
DB는 소규모 서비스에 적합한 SQLite로 시작해서, 동시 접속자가 20명을 넘어가면 MYSQL로 전환하는 것을 목표로 했다.

SQLite는 읽는 것만 할 경우 35-50명 동시 접속이 가능하고, 읽기와 쓰기를 병행할 경우 10-15명 사이가 권장되었다. 빈번하게 쓰기를 사용할 경우 5명 이하까지 커버할 수 있었다.

서비스 이용자 대부분은 대부분은 뉴스 목록과 캐시로 저장된 요약을 조회하는 분들이 다수이고, 쓰기 기능은 1시간 마다 데이터를 크롤링하고, 키워드를 선택해 분석하는 것이기에 SQLite로 충분하다고 판단했다. 또한 SQLite는 파일 기반 DB로, 별도의 DB서버를 필요로 하지 않아 비용을 절감시킬 수 있었다.

 

🥕 크롤링 방식 

현재 크롤링 방식을 cronjop방식으로 시간을 기준으로 하는게 아니라 캐시를 체크(1시간 유지)해 크롤링하도록 설계했다. 뉴스 데이터는 실시간성이 중요하고, 개발/테스트 환경에서 트래픽이 많지 않은 상황으로 리소스 효율을 중시했기 때문이다. selenium이 웹페이지에서 데이터를 가져오고, BeautifulSoup는 HTML소스에서 데이터를 추출하여 models를 통해 db와 캐시에 저장한 후 리소스를 정리한다. webdriver는 chromedriver를 설치하고 관리하며 time, datetime으로 대기 시간 및 타이밍을 제어했다.

 이 경우 사용자 요청이 있을 때만 크롤링이 실행되고, 캐시가 만료되면 첫 사용자가 크롤링 대기 시간을 겪게 되지만 나머지 유저는 캐시에 저장된 데이터를 기다림의 시간 없이 볼 수 있다. 이를 통해 서버 리소스 낭비를 최소화하고, 불필요한 API호출을 방지했다. 크롤링이 실패하더라도 이전 캐시 데이터가 제공되며 새벽 시간대 불필요한 크롤링을 방지할 수 있었다. 다만 첫 요청 시 크롤링 대기 시간이 발생하고, 여러 사용자가 동시에 만료된 캐시에 접근하면 중복 크롤링의 가능성이 있는 부분을 개선할 필요가 있다.

selenium ▶ BeautifulSoup ▶ webdriver_manager ▶ time, datetime ▶ django.core.cache와 데이터 저장 

 

🥕 사용 기술정리

현재까지 사용된 기술은 다음과 같다. django를 웹 프레임워크로 beautifulsoup4는 정적 크롤링을, selenium는 동적 크롤링을 수행한다. openai의 GPT API를 사용하고, langchain은 LLM을 통합하며 faiss는 벡터기반 유사도를 검색한다. 수집된 데이터를 redis는 캐싱처리하는데, 1시간 후 사용자의 요청이 있으면 다시 크롤링된다.

# 웹 프레임워크:Django==4.2 / djangorestframework==3.15.2
# 크롤링:
beautifulsoup4==4.12.3 / selenium==4.27.1
# 환경변수 및 캐시: python-dotenv==1.0.1 / redis==5.2.1 / django-redis==5.4.0
# 데이터 처리: konlpy==0.6.0 / numpy==2.2.1 / pandas==2.2.3

 

🥕 서버 배포
처음 AWS를 AWS Elastic Beanstalk CLI(awsebcli)를 사용하여 배포하고자 했다. 명령줄에서 쉽게 배포할 수 있고, 환경설정이 간편하다고 알려졌기 때문인데 곧 패키지 의존성이 충돌하는 문제가 발생했다. 구체적으로는 pyopenssl과 cryptography 간의 버전 충돌이었는데, pyopenssl은 cryptography의 버전이 38.0.0 이상 42.0.0 미만일 것을 요구했고, auth0-python 4.7.1은 cryptography의 버전이 42.0.4이상이면서 43.0.0 미만이어야 한다고 요구했다. 결국  cryptography의 버전이 pyopenssl와  auth0-python 양쪽 모두를 만족시킬 수 없는 상태였다.

Python버전도 3.10+으로 C 확장 모듈을 포함한 cryptography를 지원했고, Visual C++ Build Tools를 설치했지만 특정 버전의 cryptography가 다른 패키지와 충돌하였다. 그래서 모든 패키지를 제거(pip uninstall -y awsebcli auth0-python urllib3 cryptography pyopenssl)하고, cryptograph를 다양한 버전으로 재설치 해봤지만 의존성 문제를 해결하지 못했다. 그래서 로컬 환경의 의존성 문제를 피하고자, 배포 방법을 GitHub Actions을 기반으로 바꿔보았다. 

 나의 경우 1년 간 단일 인스턴스(프리 티어)를 사용할 수 있었는데, 제공하는 기능은 다음과 같았다. 현재 개발/테스트 환경이고, 트래픽이 많지 않으며 SQLite를 사용해 리소스 부담이 적다고 생각해 선택하게 되었다. 향후 트래픽이 증가하면 더 높은 구성안으로 업그레이드 할 계획이다.

EC2: t2.micro 인스턴스 (월 750시간) / EBS: 30GB / 로드밸런서: 미포함 / 데이터 전송: 월 15GB

 

이후 Mobaxtern에서 ubuntu서버를 만들고, git clone하여 필요한 가상환경을 설치한 후 gunicorn하였지만 우분투 서버에서 파이썬 셀레니움을 사용하는데도 많은 어려움이 있었다. 기본적으로 ubuntu서버에는 그래픽 사용자 인터페이스(gui)가 없었기에 헤드리스 모드를 실행해야 했고, selenium에는 브라우저와 해당 드라이버가 필요했기에, 필요한 라이브러리도 함께 서버에 설치해야 했다. 그 버전도 일치해야 했는데 selenium라이브러리와 브라우저, 드라이버의 호환성도 중요했다.

 이방식은 코드를 개선하며 다시 어려움을 마주할 수 있다고 생각했고, 현재 비교적 구현 난이도가 낮은 ngrok으로 실행 중인 앱을 외부에서 접근 가능하도록 만들어 유저테스트를 진행했다. 하지만 이 대응도 단편적이기에 장기적이고 범용적으로 대응하기 위해서는 Docker로 app환경을 패키징해서 의존성을 관리해야 할 것이다.