들어가며
2편에서 증권사 API로 데이터를 수집하는 파이프라인을 만들었다. 그런데 데이터를 수집만 하면 뭐하나? 어딘가에 저장해야 한다.
3,970종목 × 10년치 주가 = 740만 행. 여기에 재무제표, 수급, 배당, 지수까지 합치면 1,000만 행이 넘는다. 이걸 어떤 DB에, 어떤 구조로 넣을지가 이번 편의 주제다.
왜 SQLite인가
AI가 SQLite를 처음부터 추천했다. 확장성을 고려해서 PostgreSQL 사용을 물어봤지만 내 프로젝트 규모는 SQLite도 충분하다고 했다. 개인 프로젝트에서는 SQLite가 압도적으로 편하다고 한다.
| 동작 | SQLite | MySQL / PostgreSQL |
| 설치 | 없음 (파일 1개가 곧 DB) | 서버 설치 + 설정 |
| 백업 | 파일 복사 1번 | 별도 백업 도구 필요 |
| 배포 | 파일 전송이면 끝 | 서버 구성 필요 |
| 동시 접속 | 읽기 OK / 쓰기 1명 | 다중 지원 |
| 적합 규모 | 수천만 행 이하 | 수억 행 이상 |
이 프로젝트는 데이터를 넣는 건 하루에 한 번(장 마감 후), 읽는 건 웹 서버 1대뿐이다. 동시에 여러 명이 쓸 일이 없으니 SQLite로 충분하다.
가장 큰 장점은 파일 하나 = DB 전체라는 점이다. 다른 컴퓨터로 옮기고 싶으면 파일 하나만 복사하면 끝. 백업도 파일 복사 한 번이면 된다. 현재 이 파일 하나가 약 1.8GB다.
어떤 데이터를 어떤 구조로 넣었나
엑셀로 비유하면, 시트(sheet)가 곧 테이블이다. 종류별로 시트를 나눠서 총 7개의 핵심 테이블을 만들었다.
1. 종목 마스터 (stocks)
모든 데이터의 기준이 되는 종목 목록이다. "삼성전자는 005930이고, KOSPI에 상장되어 있고, 반도체 업종이다" 같은 기본 정보가 들어있다.
현재 전체 3,970종목 중 활성 3,878개, 거래정지·상장폐지 92개. 비활성 종목도 지우지 않고 보관한다 — 과거 데이터 분석에 필요하기 때문이다.
2. 일별 주가 (daily_prices)
740만 행으로 DB에서 가장 큰 테이블이다. 2015년 1월부터 매일 수집한 전 종목의 시가, 고가, 저가, 종가, 거래량이 담겨있다.
엑셀로 치면 "삼성전자 탭에 2015년부터 오늘까지 매일 한 줄씩" 있는 셈인데, 이걸 3,970종목 전부 하나의 테이블에 넣는다. 종목코드 + 날짜를 조합하면 어떤 종목의 어떤 날 주가든 바로 찾을 수 있다.
3. 재무제표 (financials)
PER, PBR, ROE, 시가총액, 매출, 영업이익 등 투자 판단에 필요한 재무 데이터다.
PER이나 PBR은 주가에 따라 매일 바뀌지만, 매출이나 영업이익은 분기별로만 발표된다. 그래서 실적 데이터는 새 분기 실적이 나올 때까지 이전 값을 그대로 유지하는 방식을 쓴다.
4. 투자자별 수급 (investor_trading)
외국인, 기관, 연기금 등 11개 투자 주체가 매일 얼마나 사고팔았는지를 기록한다.
"삼성전자를 오늘 외국인이 100만주 샀고, 개인이 80만주 팔았다" 같은 데이터다. 수급 흐름을 추적하면 큰 손들이 어디에 돈을 넣고 있는지 감을 잡을 수 있다.
5~7. 나머지
| 테이블 | 내용 |
| 시장 지수 (index_daily) | KOSPI, KOSDAQ, KOSPI200 지수의 일별 등락 |
| 배당 (dividends) | 종목별 연간 배당금, 배당수익률 |
| 증권사 추천 (broker_consensus) | 증권사 목표가, 투자의견 |
왜 이렇게 나눴을까
주가, 재무, 수급을 하나의 거대한 테이블에 전부 넣을 수도 있다. 그런데 그러면 문제가 생긴다.
- 주가는 매일 업데이트되지만 재무는 분기에 한 번 → 업데이트 주기가 다르다
- 주가만 보고 싶은데 재무 22개 컬럼까지 같이 읽어야 한다 → 느리다
- 수급 데이터 소스(키움)가 다운되면 주가 수집까지 영향받는다 → 독립성이 없다
테이블을 나누면 각각 독립적으로 수집, 조회, 관리할 수 있다. 대신 "삼성전자의 주가 + PER + 외국인 수급을 동시에 보여줘" 같은 요청이 오면 여러 테이블을 합쳐서 보여주는 작업이 필요한데, 이건 백엔드(다음 편)에서 해결한다.
740만 행인데 느리지 않나?
이게 핵심 질문이다. 740만 행을 매번 처음부터 끝까지 뒤지면 당연히 느리다. 그래서 인덱스라는 걸 만든다.
책으로 비유하면 인덱스는 색인이다. 700페이지 책에서 "삼성전자"를 찾으려면 처음부터 넘기면 한참 걸리지만, 뒤쪽 색인을 보면 "삼성전자 → 234페이지"라고 바로 알 수 있다.
DB에서도 마찬가지다. 종목코드와 날짜에 인덱스를 걸어두면:
- "삼성전자 1년치 주가 보여줘" → 종목코드 인덱스로 바로 찾기
- "오늘 전 종목 주가 보여줘" → 날짜 인덱스로 바로 찾기
인덱스 없으면 수 초, 있으면 수십 밀리초. 체감상 즉시 응답이다.
데이터 수집 중에도 사이트가 작동하나?
SQLite는 기본적으로 누군가 데이터를 쓰는 동안에는 다른 사람이 읽지 못한다. 매일 장 마감 후 4,000종목 데이터를 넣는 동안 웹 사이트가 멈추면 곤란하다.
이걸 해결하는 게 WAL 모드라는 설정이다. 쓰기 내용을 별도 파일에 먼저 기록하고, 읽기는 원본 파일에서 독립적으로 진행한다. 덕분에 수집 스크립트가 돌아가는 동안에도 웹 사이트가 정상 작동한다.
"오늘" 데이터가 없을 수도 있다
주식 데이터에서 의외로 까다로운 부분이 날짜 처리다.
토요일에 사이트를 열면 "오늘 데이터"가 없다 — 주말에는 장이 안 열리니까. 공휴일도 마찬가지. 그래서 "오늘"이 아니라 "가장 최근 거래일"을 찾는 로직이 필요하다.
삼성전자 같은 대형주는 거래정지될 일이 거의 없으니, 삼성전자 데이터가 있는 가장 최근 날짜 = 마지막 거래일로 판단한다.
수급 데이터는 한 단계 더 꼼꼼하게 본다. 가끔 수집이 불완전하게 끝나는 경우가 있는데(네트워크 문제 등), 3,000종목 이상 수집된 날만 "정상 수집일"로 인정한다. 이런 안전장치가 없으면 대시보드에서 외국인 순매수 1위가 엉뚱한 종목으로 나온다.
테이블 구조가 바뀌면?
처음에는 주가와 PER/PBR만 저장했다. 그런데 나중에 매출, 영업이익도 필요해졌다. 이미 8만 행이 들어있는 테이블인데 구조를 바꿔야 한다.
다행히 기존 데이터를 건드리지 않고 새 컬럼만 추가할 수 있다. 프로그램이 실행될 때마다 "이 컬럼이 있나?" 자동으로 확인하고, 없으면 추가한다. 기존 데이터의 새 컬럼은 비어있는 상태(NULL)로 시작하고, 이후 수집에서 채워진다.
DB 현황
테이블데이터 규모수집 기간내용
| 테이블 데이터 | 규모 | 수집 기간 | 내용 |
| 종목 마스터 | 3,970종목 | - | 종목코드, 종목명, 시장, 업종 |
| 일별 주가 | 740만 행 | 2015.01~ | 시가/고가/저가/종가/거래량 |
| 재무지표 | 8.6만 행 | 2015.12~ | PER/PBR/ROE/매출/영업이익 |
| 투자자 수급 | - | 2015.01~ | 외국인/기관/연기금 등 11개 주체 |
| 시장 지수 | 9천 행 | 2013.12~ | KOSPI/KOSDAQ/KOSPI200 |
| 배당 | - | - | 연간 배당금/수익률 |
| 증권사 추천 | - | - | 목표가/투자의견 |
전체 파일 크기 1.8GB, 테이블 7개. 이 파일 하나에 한국 주식시장 10년치 데이터가 전부 들어있다.
2015년 부터 모은 이유는 특별히 없다. 퀀트 전략을 만들고 벡테스팅을 할 때 10년 이상 데이터를 가지고 테스트해보고 싶었다.
삽질 기록
처음부터 SQLite는 아니었다
사실 처음에는 종목별로 CSV 파일을 만들었다. 3,970개 파일. 파일 열고 닫는 것만으로 느려서 곧 한계에 부딪혔다. DB로 전환하니 조회 속도가 수십 배 빨라졌다.
인덱스를 빼먹으면
처음에 인덱스 없이 "오늘 전 종목 주가" 쿼리를 날렸더니 5초가 걸렸다. 인덱스 하나 추가했더니 30ms로 줄었다. 인덱스의 위력을 체감한 순간이었다.
장중 수집 사고 (다시)
2편에서도 언급했지만, 장 마감 전에 수집해서 740만 행 중 3,970개가 오염된 적이 있다. DB에서 잘못된 데이터를 찾아서 교정하는 건 생각보다 까다롭다. 이후로 DB 무결성 점검 스크립트를 매일 자동으로 돌린다.
정리
- SQLite 파일 하나에 10년치 전 종목 데이터를 담을 수 있다 (1.8GB)
- 데이터 종류별로 테이블 7개로 나눠서 독립적으로 관리
- 인덱스만 잘 걸면 740만 행도 순식간에 조회
- WAL 모드로 데이터 수집 중에도 사이트 정상 작동
- 비거래일, 불완전 수집 등 날짜 함정에 대비하는 안전장치 필수
데이터를 수집하고(2편), DB에 넣었으니(3편), 다음은 이 데이터를 웹에서 보여주는 서버를 만들 차례다.
다음 편 예고
- 4편: 데이터를 웹에서 보여주는 백엔드 서버 만들기
'바이브 코딩 > 주식 정보 분석 사이트 만들기' 카테고리의 다른 글
| 6편: 종목 상세 페이지 - 종목의 모든 정보를 쉽게 찾아서 보자 (0) | 2026.03.16 |
|---|---|
| 5편: 모든 정보를 화면에서 보여주는 프론트엔드 만들기 (0) | 2026.03.15 |
| 4편: 데이터를 웹에서 보여주는 백엔드 서버 만들기 (0) | 2026.03.15 |
| 2편: 증권사 API로 3,970종목 데이터 자동 수집하기 (0) | 2026.03.14 |
| 1편: AI 한테 주식 정보 사이트를 만들어 달라고 했다. (0) | 2026.03.13 |