졸프관련 최종 글!
졸프는 2023/10-12 기획 단계, 2024/01-06 겨울방학부터 본격적으로 기능 구현에 들어갔다.
우선 졸업프로젝트 기획은 전 포스팅에 썼던 것처럼 뉴스 링크를 넣으면 뉴스의 신뢰도, 정치성향, 경제 개체명 인식과 감정(경제기사일때) 결과가 나온다. 사실 이 프로젝트는 사람들이 편의성을 고려하고 마케팅에 중심을 두는 것보다는 프로젝트를 하는 것에 더 의미를 두었다. 처음 써보는 프레임워크, 모델 연결, 배포까지 배운다는 생각으로 기능에 더 의미를 맞췄던 것 같다. 서비스 흐름은 다음과 같다.
사용자의 flowchart 이다
간단해 보이지만, 구글 로그인 또는 이메일 회원가입을 거치면 링크 내용에 대한 것을 크롤링을 해야 하고 (사실 스크래핑이 정확한 용어) 그 후, 내용을 정제한 것을 기반으로 신뢰도 알고리즘, 정치 모델, 경제 모델에 넣어주고 output을 내놓는 과정을 거쳐야 한다.
단계별로 설명을 해봅시다
Web Scraping
우선 가장 기본이 되는 크롤링. 이건 필수이다. 사용자가 제목, 내용을 일일이 붙여 넣는다면 너무 불편할 것이다. 링크를 넣으면 스크래핑이 되어서 output으로 결과가 나오는 데 그걸 위해서 input으로 요소들을 넣어야 한다. 그러기 위해 필요한 내용만 추출하는 작업이다. 코드를 짤 때 어떤 html구조를 기반으로 스크래핑을 할지 고민이 많았다. 한겨레, 연합뉴스, 조선 일보 등등.. 뉴스 언론사마다 html구조가 다르기 때문에 크롤링 코드를 일일이 다르게 해야 했다. 사실 조금의 수고를 들여서 언론사마다 카테고리를 나누어서 코드를 작성하면 됐지만, 그것은 추가 기능으로 남겨두고 주요 기능을 구현하고자 했기에 그냥 포털 사이트 하나를 잡고 크롤링하기로 했다. 그것은 바로 네이버다. 선정 이유는 간단했다. 한국에서 가장 많이 사용하는 포털이 네이버였기 때문.
제목, 이미지, 내용, 기자이름, 언론사, 카테고리를 정제하는 코드를 가장 먼저 작성했다.
자세한 내용은 아래를 참고 🧚
신뢰도 알고리즘
그리고 신뢰도 알고리즘을 만들었다. 이건 뉴스트러스트위원회가 제공하는 코드를 응용했는데, 아무래도 몇 년 지난 코드이고 환경설정의 문제가 있어서 사용하는데 코드들을 이해하고 수정해야 했다. 단, 텍스트만으로 신뢰도를 어떻게 평가해? 할 수 있지만 주관적 생각이 드러나는 것이 아닌 객관적 텍스트 분석으로 10가지 가치를 각각 평가하기 때문에 사용할 가치가 있다고 생각하였다.
자세한 내용은 아래에 포스팅해놨다 🧚
알고리즘의 파일 구조는 다음과 같다.
📦nt-worker
┣ 📂Analysis
┃ ┣ 📜AnalysisContents.py
┃ ┣ 📜AnalysisScore.py
┃ ┣ 📜AnalysisTitle.py
┃ ┣ 📜ContentSpellCheck.py
┃ ┣ 📜GetAnalysisResult.py
┃ ┗ 📜__init__.py
┣ 📂myMecabUtils
┃ ┣ 📜__init__.py
┃ ┗ 📜mecabUtils.py
┣ 📂newGetByline
┃ ┣ 📜__init__.py
┃ ┣ 📜byLineClass.py
┃ ┗ 📜byLineParserSelector.py
┣ 📂news_aggr
┃ ┣ 📜journal_weight.json
┃ ┣ 📜new_news_aggr.json
┃ ┣ 📜ntrust2_news_aggr.json
┃ ┗ 📜vanilla_weight.json
┣ 📜test.py
┗ 📜test_3_news.json
- Analysis에 AnalysisScore.py가 최종 점수를 출력하는 파일이다.
- myMecabUtils폴더는 형태소 분석
- newGetByline 폴더는 제목과 기자명 분석
- news_aggr 폴더는 각 가치에 가중치가 포함된 json파일들이 있다.
오픈소스라고 해도 이것을 어떻게 이용할지, 구조를 파악하고 공부를 하는 게 먼저였고, 내 식대로 코드를 바꾸어야 했다. 무엇보다 byLineParserSelector.py는 언론사 별로 가중치를 구는 파일인데 모든 언론사가 들어가 있는 것이 아닌 사용한 언론사가 한정되어 있기에 여기에 언론사를 더 추가해야 했다.
최종으로 이런 코드가 나온다.
newsBuffer = Feature(link)
title = newsBuffer[0]['title']
content = newsBuffer[0]['content']
at = AnalysisTitle(title)
ac = AnalysisContent(content, newsBuffer[0]['provider'], newsBuffer[0]['fix_category'])
analysis_score = AnalysisScore(at, ac)
Journal = analysis_score.Journal #신뢰도 점수
Vanilla = analysis_score.Vanilla #알고리즘 점수
analysis_result_json = json.dumps(Journal) #최종 신뢰도 점수
Feature에서 필요한 부분만 정제(스크래핑) 한 다음 AnalysusTiTle에서 제목 점수를, AnalysisContent에서 내용 점수를 추출한 다음 최종으로 AnalysisScore에 넣어 최종 점수를 출력한다. 실제 점수는 아래와 같이 나오도록 구현했다.
이렇게 먼저 크롤링과 신뢰도 알고리즘을 구축하고 이제 모델 2개를 만들 차례였다.
Model 구현
자세한 건 아래 포스팅 해놨다.
백엔드 구축
프레임워크는 FastAPI 사용하였다. 사용한 이유는 아래와 같다.
1. 익숙했다. 장고, DRF 프로젝트 경험은 있기에 파이썬 기반 개발에 익숙했다. 거기다 빠른 속도와 비동기적 처리도 되기에 써보고 싶었다.
2. 어차피 백엔드 상에서 고기능은 크게 없다. 모델 서빙 연결, 그리고 로그인 밖에 없기에 스프링을 혼자 공부하고 쓰는 건 시간적으로 부족했기에 선택했다.
3. docs를 지원한다. 제일 큰 이유!! DRF를 사용했을 때 postman이랑 swagger를 사용하는 게 시간이 걸렸는데 FastAPI는 자동 문서화를 지원한다!!. API 테스트, 문서화가 되니 진-짜 편했다.
4. 공식 문서가 잘 되어있다. 강의나 책을 따로 사지 않아도 자료가 충분하다. 공식 문서가 이렇게 잘 정리되어 있는 건 첨 본다. 그만큼 로그인 방법부터 배포까지 잘 나와있다.
DB 구축
PostgreSQL를 사용했다. 사용한 이유는 아래와 같다.
1. json 형식을 지원한다. 신뢰도 점수 가치가 10가지라 json으로 저장되는 게 편한데 mysql은 이 형식을 지원하지 않는다. 복잡한 데이터를 효율적으로 저장하고 처리할 수 있기에 사용했다.
2. mysql은 rdbms인데 postgresql은 ordbms로 객체 관계 데이터베이스이다! 데이터가 객체로 저장된 것처럼 작동하기에 데이터를 가져다 쓰기에는 편하다. 테이블이 유연해진다.
연동한건 아래 포스팅 했었다.
데이터 모델링
개발 전, ERD부터 작성했다. refresh token 테이블은 사실 처음부터 생각한 건 아니고 나중에 추가했다.
이것도 고민이 많았다. 사용자가 뉴스 링크를 넣으면 결과가 나온다. 그 결과를 한 테이블에 저장할지, 아니면 나눠서 저장할지, 그리고 스크래핑을 한 제목 내용 다 저장을 해야 할지.. 결론은 그냥 결과를 한 테이블 news_question에 다 저장했다. 신뢰도 결과 10가지를 json으로 한꺼번에 저장했다. 따로 저장하면 칼럼이 너무 많아지기에.
이건 정답은 아니니 더 좋은 방법이 생각난다면 댓글 부탁드립니다 ㅎ.ㅎ
프론트엔드 구축
react를 사용했다.
이건 뭐.. 물러설 곳이 없었다. 이유를 쓰자면 컴포넌트 기반이라고 해두자, 리액트 파일구조나 상태관리. 이건 어떻게 할지 모르긴 했다. 프런트를 어떻게 할지 첨부터 고민이 많았는데 주변 프엔친구가 고맙게 도와주었다
배포
배포는 도커로 했다.
시스템 아키텍처는 다음과 같다
도커는 환경 설정 문제없이 각 이미지를 띄워놓으면 컨테이너로 묶기에 하나의 서버만 띄우면 된다.
써보니 편리했다. 백엔드와 프런트엔드 통신만 잘 구축해 놓고 각각의 도커파일만 작성하면 되었다.
핵심 코드는 아래와 같다. 도커 안에 핵심이 /app이라서 /app에 환경을 구축하는 과정이다.
project/backend/Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
WORKDIR /app/projects/bubblow
EXPOSE 8000
#Command to run the FastAPI app with Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
project/frontend/Dockerfile
FROM node:14 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
EXPOSE 443
CMD ["nginx", "-g", "daemon off;"]
project/docker-compose.yml
version: '3.8'
services:
db:
image: postgres:13
container_name: db
environment:
POSTGRES_USER: ######
POSTGRES_PASSWORD: ######
POSTGRES_DB: ######
ports:
- "5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
backend:
build: ./backend
container_name: backend
volumes:
- ./backend:/app
ports:
- "8000:8000"
depends_on:
- db
environment:
DATABASE_URL: ######
frontend:
build: ./frontend
container_name: frontend
ports:
- "80:80"
- "443:443"
volumes:
- ./frontend/nginx/default.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt
depends_on:
- backend
volumes:
db_data:
총평
아쉬운 점
- 디자인 - flow를 혼자 생각하고 구현했기에 어떤 기능을 어디에 넣으면 좋을지, 색은 무엇으로 할지, 로고는 무엇으로 할지 등.. 소소한 부분부터 신경 써야 한다는 것을 깨달았다. 본인은 디자인을 감각이 없다고 생각하는 편이라.. 디자인이 아쉬웠다.
최종 렌딩 페이지는 아래와 같다.
- 기능 - 네이버뉴스만 넣어야 하는 것이 아쉽다. 시간이 남으면 확장 기능으로 넣으려고 했지만, 막판에 시간이 좀 부족했다. 그래서 코드는 네이버뉴스 html에만 맞추었기에 모든 언론사는 가능했지만, 모든 포털 사이트의 기사가 불가능한 것이 아쉬웠다.
- 예외 처리 - 회원가입할 때 비밀번호 8 자 이상, 연속불가능, 등등 예외처리를 처음에 신경 썼지만 막판으로 갈수록 시간이 부족해서 예외처리를 못했다. 예를 들어 구글 로그인했을 때 비밀번호 변경이 불가능하게 한다던지.. 등
- 버전 관리 - 신뢰도 알고리즘을 사용할 때는 mecap과 py-hanspell를 사용한다. 각 형태소 분석과 파이썬 한글 맞춤법 라이브 버리인데 설치하는 방법이 어렵다. 특히 mac os를 쓴다면 오류가 어마(?)하게 나는데 이걸 설치하는 과정에서 window os를 사용하는 팀원과 환경이 일치하지 않았어서 애를 먹었다. 초반에 이 문제를 해결하기 위해서 시간을 쏟은 게 아쉽긴 하다. 환경 설정은 언제나 시간이 오래 걸리고, 답답한 부분 중 하나다.
개선점
- 속도 - 메인 화면에서 최신 분석된 뉴스 기사를 5개 가져오는데 모두 크롤링으로 가져오기에 속도가 느려졌다. DB에는 뉴스 링크만 저장해 놓는다. 제목 내용을 모두 저장하면 DB양이 많아지기에 링크만 저장해서 GET방식이 올 때마다 링크에서 내용을 크롤링하는 방식인데 5개를 동시에 크롤링하다 보니 속도가 매우 느려진다. 지금 생각해 보면 어차피 제목, 내용은 첫 줄만 가져오니까 그냥 DB에 저장해 놓고 쓰면 속도는 개선할 수 있겠다는 생각도 든다.
- 사용자 편의성 - 유일한 프로젝트 테스트 상대는 아빠인데, 서비스를 사용하려면 왜 로그인을 해야 하냐고 했다. 누구든 사용할 수 있게 로그인을 필수로 하지 말라고 했지만, 나는 기왕 만든 구글 로그인과 이메일 인증 그리고 마이페이지 기능을 놓치고 싶지 않았다. 하긴, 아빠 입장에서는 그냥 분석 기능만 쓰고 싶은데 이런 것들이 거슬릴듯싶긴 하다. 처음 기획부터 flowchart를 짤 때까지 많은 아이디어들의 필요성을 느꼈다. 역시 혼자서 프로젝트는 아이디어가 별로 안 나온다 ㅠ
- 확장성 - 흔히 mz들은 유튜브 영상으로 뉴스를 많이 접할 것이다. 유튜브 영상 링크를 넣으면 분석이 가능하게 된다면 더 특별한 서비스가 되지 않았을까 싶다. 영상에서 텍스트 추출을 한 다음에 그걸로 분석하는 것도 괜찮을 것 같다!! 텍스트 분석의 기능을 잘 만든다면 말이다.
- 맞춤법검사 라이브러리 - 신뢰도 점수 알고리즘에서 py-haspell을 사용하는데 쓰다 보면 result error가 뜬다. 이건 봇이나 자동화된 요청을 방지하기 위해 passportkey를 바꾸는데 이걸 업데이트해 주는 게 귀찮은 일이다.... 물! 론 이것도 크롤링해서 바꾸면 되지만 네이버는 바보가 아니다. 이걸 막아놨기에 항상 코드에서 key값을 바꾸는 게 번거로웠다.
- 환경설정 - 처음엔 도커를 사용하지 않았다. 신뢰도 알고리즘에서는 haspell과 형태소 분석기 mecab을 사용했는데 mac에서는 설치가 되어서 팀원들 환경을 위해 README에 초기 세팅 방법을 적어두었지만..
윈도우와와 맥 os가 이때 다르다는 걸 체감했다.. 해도 해도 환경 설정이 안 된다. venv를 삭제하고 다시 해도 되지 않았는데 아직도 그 이유를 모르겠다. 팀원 노트북이라 내가 하루종일 붙들고 있기가 불가능해서 팀원에게 백엔드를 연결하지 않아도 되는 부분만 맡겼다. 그렇게 환경을 해결하느라 일주일 넘게 고생을 했는데 처음부터 도커를 썼다면.. 시간 절감이 되지 않았을까 싶다.
어쨌든 이 모습이 최종 완성이다,,
머신러닝, 딥러닝 데이터 수집, 훈련, 서빙부터 풀스택까지 어떻게 완성은 했는데 아직 아쉬운 부분이 많다.
다음 프로젝트를 언제할지는 모르겠지만 개인적으로 많이 성장한 듯 하다! !
'졸업프로젝트' 카테고리의 다른 글
[MLOps PipLine] model serving 하는 방법 (feat. FastAPI, docker) (3) | 2024.06.29 |
---|---|
[졸업 프로젝트] FastAPI와 PostgreSQL 연동하기 (feat. GCP로 배포까지) (2) | 2024.05.06 |
[졸업 프로젝트 Bubblow] 네이버뉴스 크롤링 하기 (4) | 2024.04.07 |
[졸업 프로젝트] Bubblow 소개 (7) | 2024.03.29 |