BSN (Breaking Stock News) 개발기

본 프로젝트는 다음 링크를통해 확인하실수 있습니다.

Homepage : http://www.stocknews.me
Github : https://github.com/taes-k/stock_analysis


Contents List

  1. 프로젝트 요약
  2. 사용된 스택
  3. 1.프로젝트 시작
  4. 2.프로젝트 기획
  5. 3. 개발 시작
  6. 4. 어쩌다보니 머신러닝?
  7. 5.프론트엔드 & 마무리

프로젝트 요약

뉴스 기사 크롤링을 통해 수집한 뉴스데이터를 머신러닝으로 상장기업들의 연관성 및 호재/악재를 분석하는 웹 서비스 프로젝트 입니다.


사용된 스택

  • Python
  • Scikit-learn
  • Django
  • Elasticsearch
  • Nori-analyzer
  • React
  • Webpack
  • Babel
  • Redux
  • React-router

1.프로젝트 시작

이 프로젝트는 몇년 전 부터 개인적으로 만들어보고싶어 아이디어 노트에 기록해두었던 서비스 였습니다. 제가 직접 주식투자를 하면서 떠오른 아이디어는 아니고 개인적으로 사회적으로 이슈가 되는 사건 사고들이 실제로 우리주변에 어떤영향으로 나타나는지 분석하는것을 좋아해서 뉴스를 보면 관련 상장주들을 찾아보고 주가변화를 통해 실질적으로 어떤 변화가 일어나는지 보다가 이 프로젝트를 아이디어로써 스케치 해 두었습니다.

그렇게 생각만 해두었던 아이디어를 묵혀두다가 올해초 퇴사후에 신선한 자극이 필요해 공부도 할 겸 가볍게 작업을 진행하기 시작했습니다.

사실 처음 기획할때는 [ 1. 뉴스 크롤링 > 2. 상장기업 추출 알고리즘 > 3. 데이터 저장 > 4. 보여주기 ] 단순히 이정도의 로직으로만 생각해 전혀 어려울것이 없을것으로 판단해 2-3주의 작업기간을 산정 했으나 알고리즘의 정확성문제와 리액트를 척음 사용하면서 러닝커브로 인하여 결국 2-3주가 아닌 2-3달의 작업시간이 소요되었습니다.

저는 작업해온 이프로젝트에 대해서 시작하면서부터 생각했던 내용들, 작업했던 과정들,또 그 중에 드러났던 문제점과 수정 보완 사항들을 작업 과정에 따라 정리해서 BSN의 프로젝트 진행기를 남겨 보려합니다.


2.프로젝트 기획

이 프로젝트를 완성시키기위해 저는 다음과 같은 요구사항들을 정리했습니다.

1. 뉴스기사들을 실시간으로 크롤링 해야한다.
2. 크롤링한 뉴스기사들을 분석해서 호의적인 기사인지 부정적인 기사인지 판단할수 있어야 한다.
3. 크롤링한 뉴스기사와 관련된 상장기업을 찾아야한다. (다수)
4. 기업별로 호재/악재가 상이 해야한다.
5. 실시간으로 뉴스가 업데이트되며 보여 줄 수 있어야 한다.

위에서 정리한 요구사항에 따라 이제 다음과같은 기술 스택 및 알고리즘을 만들어 보았습니다.

1. 뉴스기사들을 실시간으로 크롤링 해야한다.

인터넷 뉴스를 크롤링 하기위해 찾아보니 대부분의 인터넷 뉴스들은 카테고리별로 나누어져 최신 뉴스들을 확인하는데 많은 오버헤드가 들어갔습니다. 그러다가 네이버뉴스 속보 에서는 종합적인 뉴스들이 1분에 5-10개의 뉴스들이 실시간으로 업데이트되고 있음을 확인하여 해당 뉴스탭을 사용하기로 정했습니다.

2. 크롤링한 뉴스기사들을 분석해서 호의적인 기사인지 부정적인 기사인지 판단할수 있어야 한다.

이 요구조건을 만족시키는 알고리즘을 만드는것이 가장 핵심이라고 생각했습니다. 하지만 꽤나 단순하게 생각하여 다음과같은 알고리즘을 구성했습니다.

1. 뉴스 제목을 형태소 분석을 통해 쪼개기
2. 형태소별 Positive 점수를 매긴 샘플 Positive 딕셔너리 제작
3. 결과로써 나온 점수를 반영해 Positive 딕셔너리 반영 (학습)

3. 크롤링한 뉴스기사와 관련된 상장기업을 찾아야한다. (다수)

가장먼저, 상장기업 테이블이 필요했습니다. 이 데이터는 KRX 주식공시시스템 사이트 에서 얻을수 있었습니다.

이제 뉴스기사와 상장기업간의 연관성을 찾기위해 가장먼저 뉴스를 키워드화 시키는것이 필요하다고 생각했습니다.

1. 뉴스 제목, 본문을 형태소 분석을 통해 쪼개기
2. 제목에서 나오는 형태소와 본문에서 나오는 형태소의 가점을 다르게해 제목에서 키워드 추출 확률을 높임

다음과 같은 조건을 통해 뉴스별로 상위 5개의 키워드를 추출한 후 해당 키워드 내에서 상장기업명이 있을시 해당 키워드 리스트를 상장기업명과 매칭시켜 데이터 딕셔너리로 저장시켜주는 알고리즘을 구성했습니다.

1. 뉴스별 상위 5개 키워드 추출후 상장기업명 포함시 데이터 딕셔너리로 저장 (학습)
2. 키워드 별 상장기업과 연관성의 크기가 다를수 있으므로 점수를 따로 저장
3. 해당 데이터 딕셔너리의 경우 과거 뉴스데이터들을 학습시켜 미리 초기 사전을 구축해 두도록 함

위의 사전 과정이 완료되었으면 이제 뉴스별로 연관 상장기업들 데이터를 추출할수 있습니다.

1. 뉴스별 상위 5개 키워드 추출
2. 키워드별 데이터 딕셔너리를 통해 회사 연관 점수 계산 3. 일정 점수 이상인 상장기업 데이터 추출

4. 기업별로 호재/악재가 상이 해야한다.

이 내용의 경우 하나의 키워드가 회사마다 관련성의 정도와 방향이 다르다는 요구조건 이었습니다. 이 요구조건을 해결하기위해 (3)요구조건에서 키워드와 상장기업별로 -1 ~ +1 의 실수형 점수로써 구분을 주었습니다.

5. 실시간으로 뉴스가 업데이트되며 보여 줄 수 있어야 한다.

뷰단에서 모듈형으로 잦은 업데이트에 적합한 리액트로 구성하기로 했습니다.


결정된 스택

위의 요구조건들을 만족시키기위해 다음과 같은 기술스택들로 개발을 하기로 결정했습니다.

파이썬을 선택한 이유는 크롤링과 형태소분석을 위한 라이브러리들이 잘 구성되어있기 때문입니다. 또한 Django를 통해 간단하게 API 웹서버를 만들어 React front-end와의 연동이 가능하다고 판단 되었기 때문입니다.

자 이제 다음은 개발기로 이어집니다!


3. 개발 시작


Crawler

가장먼저 뉴스데이터 수집을 위해 크롤링 개발을 진행했습니다. 크롤러는 파이썬 request와 BeautifulSoup 라이브러리를 통해 데이터들을 간단하게 가져올수 있습니다.

하지만 속보 탭에는 정치,사회,과학 뉴스들 부터 스포츠,해외 뉴스들이 모두 포함되어 사실상 주가변동과는 크게 관련없는 신문사들을 선정해 블랙리스트로 걸러 데이터 수집을 진행했습니다.


형태소 분석기

이제 크롤링해온 뉴스데이터 분석을 위해 형태소 분석기를 사용할 차례입니다. Python에는 한국어 정보처리를 위한 형태소분석기 패키지인 KoNLPy 오픈소스가 있습니다. 이 패키지 내에는 Kkma, Twitter, Mecab등의 여러 형태소 분석기가 포함되어 있어 사용 목적에 따라 분석기를 선택해 사용이 가능합니다. KoNLPy 참고자료

어떤 분석기를 사용할까 고민중에 마침 Elasticsearch에서도 기본 한글형태소분석기(nori-analyzer)를 제공한다는 소식을 확인하고 데이터 저장소도 Elasticsearch로 쓸겸 nori를 사용하기로 결정했습니다.


Positive 알고리즘

이제 뉴스기사의 호의도를 분석하기 위해 세워두웠던 알고리즘을 실행시킬수 있게되었습니다.

1. 뉴스 제목을 형태소 분석을 통해 쪼개기
2. 형태소별 Positive 점수를 매긴 샘플 Positive 딕셔너리 제작
3. 결과로써 나온 점수를 반영해 Positive 딕셔너리 반영 (학습)

하지만 아직 샘플 Positive 딕셔너리가 없기때문에 1000개 정도의 뉴스기사를 하나씩 크롤링하면서 호의도를 직접 판단하여 Positive 딕셔너리를 학습시켜 나갔습니다. 해당 딕셔너리는 다음과 같이 저장되었습니다.

keyword, positive
날씨NNG, 0
투자NNG, 1
개발NNG, 1
기초NNG, -1
조사NNG, -1
법원NNG, -1
기초NNG, 1
단계NNG, 1
투자NNG, 1
투자NNG, -1
실패NNG, -1

어느정도 완성된 샘플 Positive 딕셔너리를 이용해 직접 뉴스기사들을 Positive 점수를 계산해보니 굉장히 신뢰도가 낮은 결과를 얻었습니다. 사실, 위의 딕셔너리를 보면 알수있다시피 앞,뒤 조사 하나 만으로도 문맥이 완전히 달라 질 수 있기때문에 하나의 형태소로써 점수를 매겨 분석을 한다는것 자체가 거의 불가능하다는 것을 깨달았습니다.

첫번째 알고리즘부터 난관이 생겨서 구글링을 하던도중 서울대학교에서 저와 비슷한 개념을 통해 형태소별 감성 정도를 분석해둔 딕셔너리가 있었습니다. 참고
속으로 유레카를 외치며 해당 딕셔너리를 참고하려 하였으나 해당 딕셔너리의 경우 다른 분석기를 사용하여 nori와는 형태소의 차이가 있었으며 이 또한 정적인 데이터분석으로 신뢰도가 그렇게 높지는 않을것이라는 판단이 들었습니다. 다만 해당 딕셔너리에서는 형태소 낱개단위로 점수를 매긴것이 아닌 형태소의 집합단위로 딕셔너리를 구성해 둔것을 보고 좋은 힌트를 얻을 수 있었습니다.


4. 어쩌다보니 머신러닝?


Positive 알고리즘2

‘감성형태소사전’에서 힌트를 얻은 저는 구글링을 더 해보다가 ‘의사결정트리‘모델을 적용시켜볼수 있겠다는 생각이 들었습니다. 충분한 샘플 데이터만 있다고 가정하면 형태소로 이루어진 문장들을 형태소별로 끊어 점수를 매긴다면 더 높은 신뢰도를 얻을수 있을것 같았습니다. 다만, 특정 조건들이 아닌 ‘형태소’로써 의사결정 트리를 사용하기에는 너무나도 많은 학습 데이터가 필요할것 같아 조금더 보완한 랜덤포레스트를 사용해 작업을 진행해보기로 결정했습니다.

랜덤포레스트를 사용하기 위해서는 학습용 샘플 데이터가 필요하기때문에 이번에도 마찬가지로 2,000개 정도의 뉴스를 직접 크롤링해 하나씩 호의도를 판단하여 샘플데이터를 저장시켰습니다. 이를통해 얻어진 학습 데이터는 다음과 같습니다.

test, positive
실시간NNG 전국NNG 날씨NNG 오전NNG 현재MAG 대체로MAG 흐림VA ,0
플랫폼NNG 투자NNG 유치NNG ,1
피해NNG 투자NNG 사기NNG 사내NNG 이사NNG 징역NNG ,-1
디스플레이NNG 개발자NNG 원NNG 팀NNG 뭉쳐VV 걸릴VV 일NNG 성과NNG ,1
졸속NNG 환경NNG 파괴NNG 난VV 개발NNG ,-1

위와 같이 이제 단순히 형태소 하나에 점수가 아닌 형태소 집합들에 점수가 매겨져 투자NNG 유치NNG투자NNG 사기NNG를 구별 할 수 있게 되었습니다. 특히나 랜덤포레스트를 통해 Bi-gram, Tri-gram으로 형태소들을 분석하여 훨씬 더 신뢰도 높은 결과값을 얻을 수 있었습니다.


키워드 추출

이제 뉴스와 관련된 상장기업들을 찾아야 했습니다. 가장먼저 키워드 분석시 상장기업 리스트들을 하나의 형태소 단위로 인식을 해야하기 때문에 엘라스틱서치 nori 분석기 unser-dictionary에 상장기업명들을 등록시켜주었습니다. (이 작업을 진행하지 않는다면 호텔신라호텔 신라가 되는 참사가 일어날수 있습니다…)

형태소분석기에 사전 작업이 끝났다면 이제 뉴스의 키워드를 뽑아낼수 있습니다. 단, 키워드를 뽑아낼때 모든 형태소를 대상으로 하지 않았습니다. 모든 형태소가 된다면 -ㄴ다/EFN와같은 종결어미나 접두사, 관형사 등이 키워드가 될 수 있기때문에 애초에 키워드가 될 수 있는 품사의 화이트리스트 [‘NNG’(일반명사) ,’NNP’(고유명사) ,’NP’(대명사) ,’SL’(외국어)]로 관리하여 키워드 형태소 후보로 잡았습니다.
또한 기사 제목에 나오는 형태소들에 키워드가 될 수 있는 가점을 주기위해 기사 제목에 나오는 형태소에는 +3, 기사 본문에 나오는 형태소에는 +1 점으로 더하여 최종 뉴스별 최종 키워드 5개 까지 선별해줍니다.


연관 상장기업 추출

아직은 키워드별 상장기업과의 연관관계가 없기때문에 이 또한 학습데이터가 필요합니다. 이때는 뉴스별로 탑 5로 선정된 키워드들 중 상장기업명이 들어갈시 다른 키워드들을 해당 상장기업과의 연관키워드 데이터로써 저장시키는 방식을 사용했습니다. 이렇게 저장된 데이터는 아래와 같이 저장되어집니다.

keyword,company,score
레모나,경남제약,0.3
스크린골프,골프존,0.3
BTS,넷마블,0.3
삼성전자,삼성물산,0.5
헬스케어,유비케어,0.3

위의 예시를 보시면 아시겠지만 키워드들 중에서도 연관성의 차이를 주기위해 ‘연관점수’를 두어 일반적인 키워드는 0.3점, 다른 상장기업명 키워드는 0.5점으로 연관 회사를 좀더 묶어줄수 있도록 했습니다.

현재는 키워드 점수들의 합이 0.8 이상일때 해당 뉴스와 상장기업의 연관성이 있다고 정량적으로 판단하고 있습니다. 이후에 키워드 데이터가 많이생긴다면 해당 정량을 증가시켜 테스트 할 예정입니다.

위와같은 방식으로 근 3년내의 뉴스기사 데이터를 모두 매칭해 5,000개 정도의 키워드 데이터를 수집하였고 실시간으로 크롤링하면서도 매칭되는 데이터들을 계속 학습시켜나가고 있습니다.


뉴스 데이터 저장

이제 크롤러와 모든 알고리즘의 구현이 완료되었습니다. 이전 단계에서 구한 모든 알고리즘 들은 크롤러에서 뉴스기사를 하나씩 불러올때마다 실행하여 해당 뉴스를 판단하게 됩니다. 그 중, 다음과 같은 조건에 부합하면 뉴스데이터를 저장시킵니다.

1. 해당 뉴스의 Positive 점수가 1 혹은 -1 인가?
2. 해당 뉴스와 연관된 상장기업이 하나라도 있는가?

위의 조건들이 부합시 엘라스틱서치에 뉴스데이터를 저장시켜 줍니다.


Django rest 구축

API는 저장된 뉴스를 조회할수 있도록 만들어주면 됩니다. 이를위해 Django-rest-framework 를 사용하여 간단하게 뉴스를 조회하는 api 를 만들어 주었습니다. 데이터를 불러올때는 초기 세팅, 실시간 최신 데이터, 이전 데이터 조회가 모두 필요하므로 크롤링 데이터를 기준으로 데이터를 조회 할 수 있도록 해주었습니다.

이렇게 백엔드 개발기는 완료되었습니다.


5.프론트엔드 & 마무리


React

앞서서도 말씀드렸다시피 뉴스속보를 보여주는 서비스의 특성상 리액트 가상돔으로써 뉴스를 업데이트 시키는 것이 서비스와 굉장히 잘 맞을것같아 리액트를 사용해보진 않았지만 프로젝트를 시작할때부터 프론트엔드는 리액트로 작업 할 것을 염두를 하고 시작했습니다.

사실 이전까지 프론트엔드 작업을 진행하면 HTML/CSS/JS/Jquery로 모든작업을 진행해 오면서 ‘프론트엔드=노가다’, ‘타이핑으로 그림을 그리는 작업’이라는 생각을 가지고 있었는데 리액트를 써보고나서는 신세계를 깨우치고 왜 진작 써보지 않았을까 하는 생각이 들었습니다…

리액트의 큰장점으로는 앞서말씀드린 옵저버 패턴에 기반한 state의 변화에 따른 가상돔의 리렌더링, 또 제가 가장 마음에 들었던 프론트엔드의 구조적인 모듈화가 가능하다는 것이었습니다.

쓰다보니 리액트 찬양이 되어버렸는데… 우선 메인 화면을 만들기전에 고려했던 사항은 단 두가지 였습니다.

1. 한눈에 뉴스별로 Positive/Negative를 구분 할 수 있어야한다.
2. 연관 상장기업들의 정보가 뉴스의 영향으로 변동되는점이 잘 보여야 한다.

이자리를 빌어 바쁜시간내서 도와준 디자이너 여자친구분께 감사의 말씀을 드립니다… :)

디자인이 나온후부터는 작업이 일사천리로 해결되어 나갔습니다. 리액트를 사용하면서 가장 마음에 들었던, 기능별 뷰를 각각 모듈화 해서 다음과같이 나누어 작업을 진행했습니다.

// Index

- Header
- NewsBody
  ㄴ HeadLine
  ㄴ NewsLine
- Footer
// Search

- Header
  ㄴ Search
- NewsBody
  ㄴ Company
  ㄴ NewsLine
- Footer

위와같이 모듈로서 분할하여 프론트작업을 진행하고, Redux를 통해 뉴스와 회사정보들을 통합 store시켜 모든 모듈에서 편하게 데이터를 연동 할 수 있도록 했습니다.

위와같은 과정으로 리액트가 어느정도 익숙해지고 나서는 프론트작업은 무탈하게 완료될 수 있었습니다.


마무리

2-3주로 기획했던 프로젝트가 마무리 하고나니 (사실 아직도 조금씩 손보고 있긴 합니다…) 2-3달이 되어있었습니다. 물론 다른작업들과 병행하여 진도가 더뎠던 이유도 있지만 이미 Python과 Elasticsearch를 새로게 경험 하고 있는 와중에 머신러닝과 React를 또 추가로 새롭게 접하느라 속도가 느려졌던것 같습니다.

아직은 처음에 기대했던것 처럼 하나의 뉴스에대하여 여러 상장사들의 종합적인 분석이 나오는 만큼은 아니지만 생각보다도 데이터가 잘 분석되고 프론트 서비스도 최적화되어 잘 적용되어 굉장히 뿌듯하고 자랑스러운 결과물이 나온 것 같습니다.

사실 이전 회사에서 창립멤버로 있으면서 ‘내 개인프로젝트 진행할 시간에 우리 회사 작업을 더 진행하겠다’라는 생각을 가지고 스스로에게 개인프로젝트를 진행할 여유를 주지를 않았습니다. 이 프로젝트를 시작할수 있었던것도 사실 퇴사를 하면서 시간적 여유가 생겨서 시작할수 있었지만, 앞으로는 틈틈히 개인프로젝트를 진행하면서 개발적 여유를 가지고 환기를 시키며 자기계발도 해나갈수 있는 개발자가 되도록 하겠습니다.

이프로젝트를 진행하면서 느꼇던점들을 최대한 담으려고 노력했는데 잘 전달되었는지 모르겠습니다. 두서없이 쓴 글들을 잘 읽어주셔서 감사드립니다. 문의사항은 이메일 혹은 댓글로 남겨주시면 답변드리도록 하겠습니다.

감사합니다!