지금까지 외부 FMP에서 가격 데이터를 가져오는데 성공하였습니다. 매번 외부로 부터 가격을 가져오는 것은 효율적이지 않습니다.
이와 가져 왔으면 자신이 만든 디비에 데이터를 저장하면 자신의 프로젝트에 맞게 가공하고 또한 내부에서 바로 볼수 있기 때문에 무척 빠릅니다.
디비 저장 코드를 구성하기 전에 프로젝트가 조금 커지면 파일관리가 어려워 집니다. 그래서 뛰어난 이전 선배들의 노하우가 들어가 폴더 구조를 적용하는것이 시행착오를 줄이며 오류 또한 줄이는 지름길 입니다.
가장 많이 쓰이는 FastAPI 표준 구조
project_name/ ├── app/ │ ├── main.py # FastAPI 애플리케이션의 시작점 │ │ │ ├── api/ # API 관련 코드 │ │ ├── deps.py # 의존성 주입 및 공통 처리 함수 │ │ └── v1/ # 버전 1 API │ │ ├── endpoints/ # 엔드포인트 정의 │ │ │ ├── user.py # 사용자 관련 API │ │ │ └── auth.py # 인증 관련 API │ │ └── api.py # API 라우터 설정 │ │ │ ├── core/ # 애플리케이션의 설정 및 보안 │ │ ├── config.py # 설정 파일 (환경 변수, DB 설정 등) │ │ └── security.py # 보안 관련 설정 (JWT, OAuth 등) │ │ │ ├── models/ # 데이터베이스 모델 정의 │ │ └── user.py # 사용자 모델 │ │ │ ├── schemas/ # Pydantic 스키마 정의 │ │ └── user.py # 사용자 관련 스키마 │ │ │ ├── crud/ # CRUD 작업 정의 │ │ └── user.py # 사용자 관련 CRUD 작업 │ │ │ ├── db/ # 데이터베이스 관련 코드 │ │ ├── base.py # SQLAlchemy 기반 모델의 베이스 클래스 │ │ └── session.py # 데이터베이스 세션 관리 │ │ │ └── utils/ # 공통 유틸리티 함수 │ └── common.py # 일반적으로 사용되는 함수들 │ ├── tests/ # 테스트 코드 │ └── test_user.py # 사용자 관련 테스트 │ ├── alembic/ # 데이터베이스 마이그레이션 관련 파일 │ ├── requirements.txt # 프로젝트의 의존성 목록 ├── pyproject.toml # 프로젝트 설정 및 의존성 관리 └── README.md # 프로젝트 설명 문서
각 폴더가 왜 필요한지 알아보겠습니다.
core/ (관리)
백엔드 프로그램의 모든 설정을 모아 놓은 폴더입니다. 만약 FMP(API 키)가 바뀌었는데 여기에 영향을 받는 모든 파일을 모두 수정하는 경우가 빈번하게 일어납니다. core의 설정 하나만 고치면 모든 파일에 적용 하기 위함입니다.
models/ (디비 설계도) VS schemas/ (요청)
왜 이 두 폴더가 따로 있어야 하는지 당장 이해가 힘듭니다. models/ (SQLAlchemy)은 데이터베이스의 테이블 그 자체로 DB 저장용으로 사용됩니다.
schemas/ (Pydantic)는 API가 주고받는 데이터의 규격으로 입출력 검증용으로 필요합니다. 사용자가 보낸 JSON 데이터를 검증하거나, DB에서 가져온 데이터 중 필요한 부분만 골라 프론트엔드에 보낼 때 사용합니다.
crud/ (디비 작업)
소규모 프로젝트에는 큰 필요성이 없지만 만약 엔드포인트가 늘어나면 디비에 요청하는 명령을 모두 엔드포인트에서 담당하면 코드가 길어지고 반복작업이 많아 집니다. 디비에 특화된(Create, Read, Update, Delete)명령을 모아놓은 곳이라고 생각하셔도 무방합니다.
프로젝트 적용 폴더 구조
core, models, schemas, crud폴더를 만들고 디비저장 코드를 작성하겠습니다.
project_name/ ├── app/ │ ├── main.py # FastAPI 애플리케이션의 시작점 │ ├── api/ # API 관련 코드 │ │ └── v1/ # 버전 1 API │ │ ├── endpoints/ # 엔드포인트 정의 │ ├── core/ # 애플리케이션의 설정 및 보안 │ │ ├── config.py # 설정 파일 (환경 변수, DB 설정 등) │ ├── models/ # 데이터베이스 모델 정의 │ ├── schemas/ # Pydantic 스키마 정의 │ ├── crud/ # CRUD 작업 정의
수집에서 데이터 저장까지의 흐름은 아래와 같습니다.
| 단계 | 주체 | 동작 (Action) | 상세 내용 (Data/Method) |
|---|---|---|---|
| 1 | Frontend | 요청 (Request) | POST /api/v1/stocks/sync (Ticker: AAPL) |
| 2 | Backend | 외부 호출 | FMPClient를 통해 FMP 서버에 데이터 요청 |
| 3 | FMP | 데이터 전달 | 애플(AAPL)의 최근 100일치 OHLCV 데이터 반환 |
| 4 | Backend | 데이터 변환 | FMP 데이터를 DB 모델(models) 형식에 맞게 가공 |
| 5 | Backend | DB 저장 명령 | crud/stock.py의 함수를 호출하여 db.add() 실행 |
| 6 | DataBase | 최종 저장 | stock_prices 테이블에 데이터 Insert/Commit |
| 7 | Backend | 응답 (Response) | 프론트엔드에 “저장 완료” 메시지 전송 |
🤖 실전! 금융 자료 디비저장(2-5)
🧠 이번 실습은 FMP에서 자산별 데이터를 수집해 로컬 디비에 저장하는 과정으로 각각의 저장 로직에 맞는 파일을 해당폴더에 맞게(core, models, schemas, crud) 작성하는 과정입니다.
또한 이번과정부터 전체 소규모 계획을 만들어 AI로 하여금 ToDo 리스트를 만들게 하여 작성하게 하겠습니다.
✨ 사용자의 의도(Vibe)
아래 폴더 구조로 backend/app/external_apis/implementations의 클라이언트들을 사용해서 로컬디비세 저장하는 ToDo계획을 만들어줘 디비 설정은 db/init.sql를 사용해줘
project_name/ ├── app/ │ ├── main.py # FastAPI 애플리케이션의 시작점 │ ├── api/ # API 관련 코드 │ │ └── v1/ # 버전 1 API │ │ ├── endpoints/ # 엔드포인트 정의 │ ├── core/ # 애플리케이션의 설정 및 보안 │ │ ├── config.py # 설정 파일 (환경 변수, DB 설정 등) │ ├── models/ # 데이터베이스 모델 정의 │ ├── schemas/ # Pydantic 스키마 정의 │ ├── crud/ # CRUD 작업 정의
💻 AI가 생성한 코드 (Syntax) 및 결과
Implementation Plan.md
외부 데이터 DB 저장(Sync) 구현 계획이 문서는 FMP, Binance 등 external_apis에서 가져온 데이터를 PostgreSQL 데이터베이스에 파싱 및 저장하기 위한 작업 시나리오를 설명합니다. 이 작업은 제안해주신 프로젝트 폴더 구조 하에서 진행됩니다.1. 폴더 구조 확장다음은 데이터 동기화 계층을 구체화한 폴더 구조입니다:textproject_name/├── app/│ ├── main.py│ ├── api/│ │ └── v1/│ │ ├── endpoints/│ │ │ ├── assets.py # 자산(Assets) 관련 API│ │ │ ├── stocks.py # 주식 데이터(FMP) 실시간/차트 API│ │ │ ├── coins.py # 암호화폐 데이터(Binance) 실시간/차트 API│ │ │ └── sync.py # [NEW] 외부 데이터 -> DB 적재 트리거 API│ ├── core/│ │ ├── config.py│ │ └── database.py # (기존 main.py와 동일 레벨에 있던 설정 이동 권장)│ ├── models/│ │ ├── asset.py # (기존 models.py 세분화)│ │ └── ohlcv.py│ ├── schemas/│ │ ├── asset.py # Pydantic (응답/요청 스펙)│ │ └── ohlcv.py│ ├── crud/│ │ ├── crud_asset.py # 자산(Asset/AssetType) DB 기록│ │ └── crud_ohlcv.py # 차트(OHLCV) DB 기록│ └── services/ # [NEW] 비즈니스 로직(API 호출 후 CRUD에 포워딩)│ └── data_sync_service.py2. 작업 계획 (To-Do)단계 1. 코어 및 모델 정리 (구조 리팩토링) 1. 기존 최상단에 있던 database.py 파일을 app/core/ (또는 app/db/) 폴더 내로 이동. 2. 기존 models.py를 app/models/ 패키지 안으로 나누어 이동(asset,ohlcv). 3. app/schemas/ 에 Pydantic 스키마 생성 (요청 유효성 검증 및 데이터 전달용).단계 2. DB 저장 계층 개발 (CRUD) 1. app/crud/crud_ohlcv.py: 전달받은 표준화된 데이터 목록(딕셔너리 리스트)을 ohlcv_data 테이블 스키마에 맞게 파싱하여 DB에 INSERT 또는 UPSERT(ON CONFLICT DO UPDATE) 처리하는 로직 추가. - init.sql 기준 제약 조건 대응: (asset_id, data_interval, timestamp_utc) 기준으로 UNIQUE 제약 처리.단계 3. 동기화 서비스 로직 구축 (Services) 1. app/services/data_sync_service.py 생성. 2. sync_historical_data(asset_id, ticker, type) 같은 함수를 생성. - 내부 로직: 외부 클라이언트(FMPClient 또는 BinanceClient) 호출. 응답이 성공이면 crud_ohlcv를 통해 DB에 저장.단계 4. 동기화 트리거 라우터 연결 (API) 1. app/api/v1/endpoints/sync.py 생성. 2. POST /sync/historical/{asset_id} 형태의 엔드포인트를 만들어, 호출 시 지정한 자산의 최근 차트 데이터를 외부로부터 불러와 DB에 적재. 3. main.py에 해당 라우터 등록 및 최종 테스트 진행.3. 사용자 리뷰 및 확인 요망모델 구조 분리: 현재 models.py와 database.py가 개별 파일로 존재하는데, 제시해주신 구조(app/models/, app/core/)에 맞추어 파일들을 쪼개거나 폴더 안으로 모두 이동시켜도 괜찮을까요?동기화 방식(Upsert): 이미 있는 날짜의 차트 데이터를 또 불러오게 될 경우(예: 어제 데이터를 오늘 또 호출) 최신 값으로 덮어쓰기(Upsert) 하도록 구현하는 것이 좋은지, 아니면 무시(Do Nothing)하는 것이 좋은지 알려주세요.
AI가 Implementation Plan을 만들고 응답을 주었습니다. 2개의 물음에 대해 모두 상관없다고 하고 실행하게 합니다.
- “1.2번 문의 사항은 모두 진행해도 괜찮으니까 실행해줘”
아래와 같은 작업 목록을 보여주고 결과도 보여줍니다.

- http://localhost:8000/docs#/ 로 이동 POST가 생성되었습니다.


- 현재 파라미터가(Parameters) asset_id만 입력하게 되어 있습니다. 또한 현재가격을 요청하는 POST또한 없습니다.
- “/api/v1/sync/historical/{asset_id}에 Parameters를 추가하는데 limit=”데이터 포인트”를 추가하고 asset_id와 ticker도 같이 입력할수 있도록 해줘, 현재가격 수집 POST도 추가해줘”


🤖 실전! 금융 자료 디비 저장 프론트엔드 실행 버튼 추가(2-6)
디비에 있는 자산목록중 선택한 목록의 현재 가격과 히스토리가격을 수집하게 하는 명령을 매번 http://localhost:8000/docs#/ 에서 실행하는 것은 웹서비스 취지에 맞지 않습니다. http://localhost:8000/docs#/ 는 단지 백엔드 문서파일로 참고하는 용도나 오류 검증용입니다.
5-3장에서 만든 라인 차트(Line Chart)페이지를 활용해서 가격수집 버튼과 티커목록, 티커별 데이터 개요(데이터 수, 기간) 현재가격을 볼수 있게 하겠습니다.
🧠 이번 실습은 FMP에서 자산별 데이터를 수집해 로컬 디비에 저장하는 실행버튼을 프론트엔드에 추가하는 과정입니다.
✨ 사용자의 의도(Vibe)
“Assets 엔드포인트에 /api/v1/assets/overview를 추가해줘. 이 엔드포인트는 assets 테이블과 ohlcv_data 테이블을 조인해서 자산 번호(asset_id), 이름(name), 티커(ticker)와 함께, 해당 티커의 전체 데이터 개수(total_count), 그리고 데이터 시작일과 종료일(start_date, end_date)을 리턴해야 해. 특정 asset_id나 ticker로 필터링할 수 있는 기능도 포함해줘.”
💻 AI가 생성한 코드 (Syntax) 및 결과

✨ 사용자의 의도(Vibe)
“기존의 frontend/src/app/line-chart/page.tsx 파일을 수정해서 자산 관리 대시보드를 완성해줘. 상세 요구사항은 다음과 같아.”
- 데이터 연동 (API):
- 페이지 로드 시 백엔드의 GET /api/v1/assets/overview 엔드포인트에서 자산 목록을 가져와줘.
- 응답 데이터의 필드명은 asset_id, name, ticker, total_number, range야.
- 상단 자산 목록 테이블:
- 테이블 컬럼: 번호(ID), 이름, 티커, 데이터 수(total_number), 데이터 기간(range).
- 액션 버튼 (우측):
- 수집 버튼: lucide-react의 RefreshCcw 아이콘 사용하고 클릭 시 POST /api/v1/stocks/sync/{ticker}를 호출하고, 완료되면 테이블 목록을 새로고침해줘. (수집 중 로딩 애니메이션 포함)
- 차트보기 버튼: BarChart3 아이콘 사용. 클릭 시 해당 자산을 선택 상태로 만들고 하단의 차트를 갱신해줘.
- 하단 차트 섹션:
- 테이블 아래에 하나의 큰 차트 영역을 배치해줘.
- recharts 라이브러리를 사용하고, 특정 자산의 차트보기 버튼을 클릭하면 GET /api/v1/stocks/{ticker} 데이터를 가져와서 라인 차트(또는 AreaChart)로 보여줘.
- 차트 데이터가 없을 때나 로딩 중일 때의 UI 처리도 포함해줘.
- 디자인 및 UX:
- 전체적으로 Tailwind CSS를 사용해 현대적이고 깔끔한 대시보드 스타일로 디자인해줘.
- 테이블에서 현재 선택된 자산의 행(Row)은 배경색을 다르게 해서 강조해줘.
- 모든 날짜 데이터는 읽기 쉬운 형식으로 포맷팅해줘.
💻 AI가 생성한 코드 (Syntax) 및 결과
http://localhost:3000/line-chart로 접속하면 차트가 표시가 안됩니다.
- “자산 목록과 차트가 표시 안돼 프론트엔드를 리빌드 리스타트해줘”
프론트엔드나 백엔드등의 코드를 수정하면 리빌드나 리스타트 또는 같이(docker compose build frontend && docker compose up -d frontend) 해주는것이 좋습니다. 비유를 하자면 도커 컨테이너 코드를 수정하는 것은 인쇄기의 활판은 수정했는데 결과를 볼려면 책이나 신문을 새로 인쇄해야 되는것과 비슷합니다.
- http://localhost:3000/line-chart에 자산관리와 차트가 표시 됩니다.


- 2가지 문제가 있습니다. 1번수집에 30개의 포인트만 수집됩니다. 즉 일(days)데이터 기준으로 현재에서 과거 30전까지만 수집되고 있습니다. 수집일수(limit)를 1000으로 변경하고 수집이 안되는 금,은(현재 히스토리 데이터를 무료로 제공하는 FMP가 거의 없습니다.)은 삭제하고 각 자산별 대표자산인 엔비디아(NVDA), 애플(AAPL), 마이크로소프트(MSFT), 비트코인, 이더리움, 리플, SPY, QQQ를 추가하도록 하겠습니다.
- “수집일수(limit)를 1000으로 변경해줘”

- “수집이 안되는 Gold, Silver는 삭제하고 각 자산별 대표자산인 엔비디아(NVDA), 애플(AAPL), 마이크로소프트(MSFT), 리플, QQQ를 자산목록에 스크립트를 작성해서 추가해줘”

- 수집이 안된 티커들도 수집하고 차트가 되는지 확인합니다.
- 오류가 몇가지 보입니다. 비트코인 가격이 이상하게 나오고, QQQ수집이 안됩니다. 또한 더미 데이터가 남아 있습니다.
- “비트코인 가격을 리셋하고 Dummy for ohlcv_data 240 와 QQQ를 삭제해줘”
FMP에서 이전에는 QQQ히스토리 데이터를 지원했지만 현재 정책변화롤 지원하지 않는것 같습니다. 고급 과정에서 다른 클라이언트로 시도해 보겠습니다.
- 차트도 오류가 있습니다. 가격 스케일값과 툴팁(차트에 마으스 오버스 나오는 가격) 가격이 다릅니다. 또한 가로축과 툴팁에 표시되는 날짜가 미국 기준입니다.
- “차트 촤측 세로축 가격 스케일값과 툴팁(차트에 마으스 오버시 나오는 가격) 가격이 다르게 나와 수정해줘 또한 가로축과 툴팁에 표시되는 날짜를 YY-MM-DD로 해줘”
