안녕하세요, Sean 입니다.
홍익대학교 4학년을 무사히 마치며 졸업 프로젝트도 끝이 났습니다. 졸업 프로젝트 설계와 구현 과정에서 있었던 일들을 이야기하고자 글을 쓰게 되었습니다.
저는 NewsFit 프로젝트에서 인프라 설계 및 구축, 추천 시스템 서비스, 웹 스크래핑 서비스 구현을 담당했습니다.
3-tier architecture
본 백엔드 서버 아키텍처는 3계층 구조를 기반으로 설계되었으며, 확장성, 보안성 및 고가용성을 중점적으로 고려하였습니다. 각 구성 요소의 역할과 기능은 다음과 같습니다.
EC2 인스턴스 및 로드밸런서
백엔드 애플리케이션이 배포된 인스턴스로, 프라이빗 서브넷 내에 위치합니다. 오토 스케일링을 활용하여 트래픽의 증가 및 감소에 따라 인스턴스 수를 자동으로 조정함으로써 시스템의 탄력적, 수평적인 확장성과 고가용성을 보장합니다. 클라이언트 요청은 애플리케이션 로드 밸런서를 통해 EC2 인스턴스로 전달되며, 퍼블릭 서브넷에 배치되어 외부와의 접근 지점을 제공합니다.
NAT Gateway
프라이빗 서브넷 내 EC2 인스턴스가 인터넷에 대한 아웃바운드 접근을 수행할 수 있도록 지원합니다. NAT Gateway는 퍼블릭 서브넷에 배포되며, 인터넷 트래픽을 중계하는 역할을 수행합니다. 이를 통해 외부 리소스에 대한 접근은 가능하지만, 프라이빗 서브넷 내 인스턴스에 대한 직접적인 인바운드 접근은 차단됩니다.
VPC Gateway Endpoint
AWS Lambda와 같은 AWS 서비스에 접근할 때 인터넷 게이트웨이나 NAT Gateway를 거치지 않고, VPC 내부에서 직접 통신할 수 있도록 지원합니다. 또한, 네트워크 비용을 절감하고 통신 성능을 최적화할 수 있습니다.
Web scraping
웹 스크래핑 서비스는 뉴스 기사 API로부터 URL을 가져와 기사를 분류하고, 뉴스핏 백엔드에 데이터를 저장하는 역할을 수행합니다.
AWS Lambda와 EventBridge Scheduler를 기반으로 스크래핑 서비스를 구현하였으며, 데이터 저장 및 중복 처리를 위해 NoSQL 데이터베이스인 DynamoDB를 활용하였습니다. 이 구조는 확장성과 효율성을 고려하여 설계되었으며, 특히 중복 데이터 검사를 통해 성능을 최적화하는 데 중점을 두었습니다.
스크래핑 서비스는 이벤트 기반 아키텍처를 따르며, AWS Lambda는 EventBridge Scheduler에 의해 주기적으로 호출됩니다. 이로 인해 스크래핑 작업이 지정된 주기마다 자동으로 실행되며, 작업의 운영 및 관리가 용이해졌습니다. DynamoDB는 중복 URL 검사를 위한 주요 저장소로 활용되었으며, 빠른 읽기/쓰기 성능과 확장성을 바탕으로 대량의 데이터를 효율적으로 처리할 수 있었습니다.
최종적으로 성공적으로 처리가 완료된 데이터는 뉴스 기사 저장소에 저장되어 이후 뉴스 조회 및 요약에 사용됩니다.
Web scraping: data collecting methods
유효 페이지의 정확성을 기준으로 한 스크래핑
이 방식은 처음 실행 시 데이터 처리 성공률이 높아 초기 수집된 데이터량이 많았습니다. 첫 번째 시도에서 321건의 데이터를 성공적으로 처리했으며, 이는 전체 데이터의 약 40.1%에 해당합니다. 그러나 이 방식은 반복적으로 실행할수록 한계가 드러났습니다. 중복 데이터 비율이 98% 이상으로 높게 나타났으며, 중복 데이터를 제외한 새로운 기사 수집은 거의 이루어지지 않았습니다. 반복 실행 이후 최종적으로 수집된 데이터량은 324건으로, 증가율은 0.3%에 불과했습니다. 결과적으로 이 방식은 반복 수행에 적합하지 않으며, 시간이 지남에 따라 새로운 데이터를 확보하는 데 어려움이 있었습니다.
최신 기사를 기준으로 한 스크래핑
최신 기사를 기준으로 한 스크래핑은 초기 수집된 데이터량이 적었습니다. 첫 번째 실행에서 147건의 데이터를 수집하였고, 이는 전체 데이터의 약 18.4%에 해당합니다. 하지만 이 방식은 반복적으로 실행할수록 데이터 수집량이 꾸준히 증가하는 특징을 보였습니다. 두 번째 실행에서는 202건으로 72.7% 증가했으며, 이후 245건으로 82.4% 추가 증가하며 선형적인 성장 패턴을 보였습니다. 이는 시간이 지남에 따라 최신 기사를 효과적으로 수집할 수 있는 강점을 보여주었습니다.
언론사별 기사 분포 차이
정확성을 기준으로 한 스크래핑은 특정 주요 언론사에 편향된 결과를 보였습니다. 연합뉴스, 뉴시스, 뉴스1과 같은 주요 언론사가 전체 기사 중 42% 이상을 차지했으며, 나머지 언론사들의 기사는 상대적으로 적었습니다. 반면 최신 기사를 기준으로 한 스크래핑은 기사 수가 언론사별로 보다 고르게 분포되는 경향을 보였습니다. 다양한 언론사의 기사를 균형 있게 확보함으로써 특정 언론사에 지나치게 의존하지 않는 결과를 얻을 수 있었습니다.
이와 같은 테스트 결과를 종합했을 때, 최신 기사를 기준으로 한 스크래핑 방식이 장기적인 관점에서 더 적합하다는 결론을 내렸습니다. 이 방식은 시간이 지남에 따라 꾸준한 데이터 수집량 증가를 보장하며, 다양한 언론사로부터 데이터를 확보할 수 있는 균형 잡힌 수집 결과를 제공합니다. 결과적으로 데이터의 양적, 질적 측면에서 최신 기사를 기준으로 한 방식이 더 우수하다고 판단하여 이를 채택하기로 결정하였습니다.
Web scraping: performance optimization
성능 최적화 및 효과
중복 URL 처리 로직은 스크래핑 작업의 효율성을 크게 향상시켰습니다. 기존에는 모든 URL에 대해 반복적인 검사를 수행하였으나, 중복 URL이 감지되면 스크래핑 프로세스를 조기에 종료하도록 구성하여 시스템 자원을 절약할 수 있었습니다. 이를 통해 불필요한 네트워크 호출 및 데이터베이스 접근이 감소하였으며, 스크래핑 속도와 비용 효율성이 동시에 개선되었습니다. 예를 들어, 매주 정기적으로 동일한 뉴스 웹사이트를 대상으로 스크래핑을 수행할 경우, 평균적으로 전체 URL 중 약 70~80%가 중복으로 검출되었습니다. 이러한 중복 URL을 선별적으로 제외함으로써 처리 시간은 약 30% 단축되었으며, DynamoDB의 읽기/쓰기 용량 사용량도 25% 이상 절감되었습니다.
결론 및 장점
AWS Lambda와 EventBridge Scheduler를 기반으로 구축된 이벤트 기반 아키텍처를 통해 확장성과 효율성을 동시에 만족시켰습니다. 특히 DynamoDB를 활용한 중복 URL 검사 기능은 스크래핑 작업의 성능 최적화에 있어 핵심적인 역할을 수행하였습니다. 이를 통해 최신 기사를 효율적으로 수집하고, 중복 데이터로 인한 처리 비용을 줄이는 데 성공했습니다. 결과적으로 본 서비스는 데이터 수집의 정확성과 효율성을 모두 확보할 수 있는 솔루션을 제시합니다.
Recommendation system
Python 기반의 Flask 프레임워크와 SQLite를 사용하여 추천 시스템 전용 서버를 구축하였으며, Item-Item Collaborative Filtering 알고리즘을 활용하여 사용자에게 적합한 항목을 추천하는 모델을 구현하였습니다. 추천의 핵심 요소로는 코사인 유사도를 기반으로 항목 간의 유사도를 계산하였으며, 서버는 경량화된 데이터베이스와의 연동을 통해 효율적으로 작동하도록 설계되었습니다. 추천시스템은 온프레미스 환경에서 구동하며 Ngrok을 통해 호스팅합니다.
본 시스템은 다음과 같은 구조로 설계되었습니다:
- Flask: REST API 서버로 사용자 요청 처리
- SQLite: 사용자 데이터 및 뉴스 기사 데이터 저장
- Pandas: 데이터 전처리 및 필터링 수행
- Item-Item Collaborative Filtering: 뉴스 간 유사도 분석 및 추천 수행
추천 과정
- 사용자-항목 행렬에서 특정 사용자에 대한 항목 선호도 벡터를 가져옵니다.
- 항목 유사도 행렬(Item Similarity Matrix)과 사용자의 선호도 벡터를 곱하여 추천 점수를 계산합니다.
- 사용자가 이미 확인한 뉴스 기사는 제외하고, 유사도가 높은 뉴스 기사를 추천합니다.
추천된 뉴스 외에도 사용자가 구독한 카테고리와 언론사에 속하는 최신 뉴스를 보완적으로 제공하였습니다. 최종적으로 추천 뉴스와 추가 뉴스를 합친 뒤, 추천 점수를 기준으로 정렬하였습니다.
Recommendation system: performance optimization
시스템 성능을 평가하기 위해, 뉴스 추천 기능의 정확도와 효율성을 점검하기 위해 다양한 테스트 시나리오를 설정하고 이를 자동화된 스크립트를 통해 반복적으로 수행하였습니다.
성능 최적화
5000개의 뉴스 데이터와 100명의 유저 데이터를 기반으로 테스트한 결과 서버 성능에 지정이 거의 없었고 빠른 응답속도 (<50ms)를 보이는 것을 확인했습니다. 따라서 서버의 성능을 고려하기 위해 받아오는 데이터량을 조정하였습니다.
다음 수식에 기반하여 데이터베이스에 저장되는 데이터량을 계산할 수 있었습니다. 각각의 기호는 다음을 의미합니다:
- D_total: 데이터베이스에 저장되는 데이터량
- C: 뉴스 카테고리 개수
- N_success: 평균적으로 처리에 성공하는 뉴스 데이터 개수
- T_cycle: 실행 주기 (단위: 시간)
- T_period: 데이터를 저장하는 기간 (단위: 시간)
C는 8으로 고정 값입니다. T_cycle은 EventBridge Scheduler에서 Lambda 함수를 호출하는 주기를 의미하며, T_period는 백엔드 API에서 배치 작업을 통해 데이터를 삭제하는 주기를 의미합니다.
처리 전 뉴스 데이터의 개수가 N_raw이고 성공적으로 처리되는 뉴스 데이터의 개수가 N_success일 때 뉴스가 성공적으로 처리될 확률은 P_success라고 하면, 현재 P_success는 6.25%입니다.
T_cycle을 1, T_period를 48으로 설정하면 다음과 같습니다:
따라서 뉴스 기사를 2일간 저장하고, 최대 5000개의 뉴스 기사를 저장하기 위해선 5000 / 24 = 208.34를 N_raw의 최대값으로 설정해야 합니다.
현재 저희 뉴스핏 서비스는 서비스 안정성을 위해 카테고리 당 N_raw 값을 160 으로 설정하였고, 2 일간 뉴스 데이터를 저장합니다. 따라서 데이터베이스에 약 3840 개의 뉴스 데이터를 저장함으로써 성능 부하를 예방하고 있습니다.
Retrospective
이 프로젝트를 진행하며 올바른 설계란 무엇인지 생각해볼 수 있었습니다. 데이터 ingestion부터 presentation까지 전반의 모든 부분을 설계 및 구현하며, 흥미롭고 재밌었지만 더 잘 설계하는 방법이 분명 있었을 것이라는 생각을 지울 순 없었습니다.
백엔드 API 서버를 서버리스 컴포넌트로 구현하고, 스크래핑 서버를 EC2 서버에 구축했다면 Lambda 함수의 호출 횟수에 관계 없이 계속해서 실시간으로 데이터를 가져오도록 구현할 수 있지 않았을까 싶기도 합니다. 또 스크래핑 서버에 메시지 큐를 넣어서 느슨한 결합을 구현했다면 조금 더 안정적인 서비스를 구축할 수 있었겠죠.
어쨌든 나름 여러 서비스를 함께 돌리면서 프리 티어로 Public IPv4 비용만 청구하며 이런 서비스를 호스팅 할 수 있었다는건 최악의 설계는 아니었다고 말하고 싶습니다 (우리집 전기세 + Ngrok 유료 플랜 비용도 들었군요).
성능 최적화는 재미있는 경험이었습니다. 옛날부터 다들 수치화 해라, 테스트 해라 이런 말들을 들어왔지만 와닿은 적이 없었습니다. 테스트를 해야만 하는 상황에 놓이니 어쩔 수 없이 하게 되었는데, 실제 데이터를 통해 설계하는 일이 굉장히 재미있는 일이기도 했습니다.
내 의사 결정의 타당한 근거는 이러한 데이터 포인트로부터 나오는 것 아닐까 싶습니다.
결론적으로 재미있는 경험이었습니다! 😁
'Server' 카테고리의 다른 글
DayCarat 인프라 구조 설계: 내가 한 달 만에 설계부터 구현까지 성공적으로 마칠 수 있었던 이유 (0) | 2024.04.08 |
---|---|
Github Actions: ssh-aciton IPv6 지원 (1) | 2024.03.17 |
[Terraform] state locking을 위한 s3, ddb 설정하기 (0) | 2024.02.23 |
[서버] Github Actions: 다양한 명령 내리기 (0) | 2023.11.29 |
[Docker, AWS, Spring] docker-compose시 환경변수 전달하고 스프링부트 application.yaml에서 사용하기 (3) | 2023.10.06 |