모의투자 서비스와 실시간 알림 프로젝트
Day 1. 브레인스토밍 & 도메인 설계
백엔드 개발자로서 설계 역량을 키우기 위해 혼자 사이드 프로젝트를 시작하기로 했다.
이번 프로젝트의 목표는 단순히 기능을 구현하는 것이 아니라, 실무에서 고민하는 설계 과정을 직접 경험하는 것이다.
1. 주제 선정
무슨 서비스를 만들어볼까 고민하다가, 평소 관심도 많고 공부해보고 싶었던 주식 모의투자 서비스를 선택했다.
아무래도 관심 있는 분야여야 끝까지 욕심을 가지고 개발하게 될 것 같았다. 😊
2. 모의투자 서비스는 어떻게 동작할까?
실제 증권사는 자주 이용했지만, 모의투자 서비스는 한 번도 사용해본 적이 없었다.
내가 알고 있던 건 단순히
"가상의 돈으로 주식을 사고팔며 연습하는 서비스"
정도였다.
하지만 백엔드에서는 "무엇이 저장되고, 어떤 데이터가 언제 변경되는가" 가 훨씬 중요하다.
그래서 먼저 서비스 흐름부터 정리해보기 시작했다.
사용자(Member)
↓
가상 계좌(Account)
↓
주문(Order)
↓
체결(Trade)
↓
보유 종목(Holding) 갱신
그리고 모든 과정에서
주식 정보(Stock)
를 계속 참고하게 된다.
3. 도메인을 보면서 느낀 점
처음 ERD를 그리면서 가장 먼저 들었던 생각은
Stock 도메인이 다른 도메인들과 조금 성격이 다르다는 것이었다.
사용자의 매수·매도 행위 때문에 주식 정보 자체가 변경되는 것은 아니기 때문이다.
오히려 Stock은
- 여러 도메인에게 참조되는 대상
- 비교적 독립적인 성격의 도메인
이라는 느낌이 강했다.
반면,
- Member
- Account
- Order
- Holding
은 서로 굉장히 유기적으로 연결되어 있었다.
사용자가 주문을 하면 계좌 잔액이 변경되고,
체결이 발생하면 보유 종목이 변경되는 구조이기 때문이다.
4. 첫 번째 설계
처음에는 아래처럼 생각했다.
Member ─ Account (1 : 1)
Member ─ Holding (1 : 1)
그런데 곧바로 문제가 보였다.
회원은 삼성전자만 보유하는 것이 아니라
삼성전자
SK하이닉스
NAVER
카카오
처럼 여러 종목을 동시에 보유할 수 있다.
즉,
Member ─ Holding
1 : N
이 맞았다.
그리고 Holding도 단순히
삼성전자
10주
같은 문자열을 저장하는 테이블이 아니라,
특정 종목(Stock)을 참조하면서
해당 회원이 현재 몇 주를 보유하는지 관리하는 엔티티라는 점도 알게 되었다.
5. 그런데 Order와 Holding은 어떤 관계일까?
여기서부터 조금 헷갈리기 시작했다.
처음에는
Order → Holding
처럼 바로 연결하면 될 것 같았다.
하지만 생각해보니
Order와 Holding은 서로를 직접 소유하는 관계가 아니었다.
둘 다 Member와 Account라는 맥락 안에서 발생하는 데이터였다.
그래서
"Order와 Holding 사이에 중간 개념이 하나 필요한 것 아닐까?"
라는 생각이 들었다.
예전에 진행했던 프로젝트에서
팔로우 / 언팔로우 기능을 구현하면서
매핑 엔티티를 만들었던 기억도 떠올랐다.
6. Trade 도메인을 추가하기로 결정
여러 자료를 찾아보고 정리한 결과,
주문(Order)과 보유 상태(Holding) 사이에는
Trade(체결) 라는 개념이 존재하는 것이 맞았다.
Member
↓
Order (주문)
↓
Trade (실제 체결)
↓
Holding 변경
처음에는
주문하면 바로 보유 종목이 변경되는 줄 알았는데,
실제 거래 시스템에서는
주문(Order) 과
체결(Trade) 이 명확하게 구분된다.
특히 하나의 주문이 여러 번 나누어 체결될 수도 있기 때문에
Order 1
↓
Trade 1
Trade 2
Trade 3
처럼
Order : Trade = 1 : N
관계가 되는 것이 자연스럽다.
7. Order와 Trade는 단순 로그일까?
처음에는 이런 생각도 했다.
"어차피 기록용인데 계속 저장하면 낭비 아닌가?"
하지만 거래 시스템에서는
Order와 Trade가 단순 로그가 아니라
거래의 증거(Audit Log) 이자
정산의 근거 데이터라는 사실을 알게 되었다.
즉,
Holding은 현재 상태(Current State)를 나타내고,
Order와 Trade는
그 현재 상태가 만들어진 이유를 설명하는 이력(History) 이다.
따라서 삭제 대상이 아니라,
장기적으로 보관해야 하는 중요한 데이터였다.
8. 데이터가 계속 쌓이면 어떻게 할까?
거래 데이터는 시간이 지날수록 계속 증가한다.
따라서 처음부터 운영 환경도 함께 고려해 보기로 했다.
- Order / Trade는 영구 보관
- 최근 거래는 인덱스와 페이징을 활용하여 조회 성능 확보
- 오래된 데이터는 아카이빙 테이블로 분리
- 자주 조회되는 최근 데이터는 Redis 같은 캐시에 저장
아직 구현 단계는 아니지만,
포트폴리오에서는 단순 CRUD보다
이런 운영 관점의 고민도 함께 담으면 좋을 것 같았다.
9. 최종적으로 정리한 관계
Member : Account = 1 : 1
Member : Order = 1 : N
Order : Trade = 1 : N
Member : Holding = 1 : N
Holding → Stock (N : 1)
그런데 마지막까지 고민했던 것이 하나 있었다.
바로
Trade ↔ Holding
의 관계였다.
업무 흐름으로 보면
Trade 발생
↓
Holding 변경
이 맞다.
하지만 ERD에서는
Trade와 Holding을 직접 FK로 연결해야 하는지는 또 다른 문제였다.
10. 오늘 가장 크게 배운 점
이번에 가장 크게 깨달은 것은
업무 흐름(Process) 과
ERD의 관계(Foreign Key) 는 서로 다른 관점이라는 것이다.
예를 들어,
Trade 발생
↓
Holding 변경
이라는 흐름은 맞지만,
그렇다고 해서
Trade가 Holding을 직접 참조해야 한다는 의미는 아니다.
즉,
업무적으로는 관계가 있어도
ERD에서는 직접 연결하지 않을 수도 있다.
오늘 설계를 하면서
단순히 "몇 대 몇 관계"만 생각하는 것이 아니라,
도메인을
- 현재 상태(Current State)
- 이력(History)
- 이벤트(Event)
로 나누어 바라보기 시작했다.
아직 많이 부족하지만,
이런 관점으로 고민하는 과정 자체가 이번 단계의 가장 큰 수확이었던 것 같다!
++ 클로드로 생성한 테이블 이미지
