Vue 3 Composition API와 커스텀 API 통신 모듈을 활용하여 구축한 SKALA Stock Market의 핵심 구현 내용 정리
📌 프로젝트 개요
시스템 구성

기술 스택
Frontend: Vue 3 (Composition API), Vite
UI: Bootstrap 5, Bootstrap Icons
상태 관리: Reactive, Ref, SessionStorage
통신: Custom API Wrapper (Axios 기반)
데이터 흐름 요약 (Data Flow)
Vue.js의 반응형 시스템(Reactivity System)을 기반으로 한 전체 데이터 흐름은 다음과 같다.
- 사용자 입력: v-model을 통해 사용자의 입력값이 ref 또는 reactive 변수에 실시간으로 반영된다.
- 자동 감지: 변수가 변경되면 watch가 이를 감지하여 API를 호출하거나, computed가 계산된 값을 즉시 업데이트한다.
- 비동기 통신: apiCall 모듈을 통해 서버로 데이터를 전송하거나 수신한다.
- 상태 업데이트: 서버로부터 받은 응답 데이터(Body)를 다시 반응형 변수에 할당한다.
- 화면 갱신: Vue의 가상 DOM이 변경된 상태를 감지하여 브라우저 화면을 자동으로 재렌더링한다.
핵심 API 엔드포인트 정리
프론트엔드와 백엔드 간의 약속된 인터페이스 명세는 다음과 같다. 모든 요청은 공통 응답 포맷을 준수한다.
| 기능 | Method | URL | 용도 |
| 로그인 | POST | /api/players/login | 사용자 인증 및 세션 생성 |
| 회원가입 | POST | /api/players | 신규 플레이어 계정 생성 |
| 주식 목록 | GET | /api/stocks/list?offset=0&count=10 | 페이징을 적용한 주식 데이터 조회 |
| 주식 추가 | POST | /api/stocks | 관리자에 의한 신규 종목 등록 |
| 플레이어 정보 | GET | /api/players/:playerId | 특정 플레이어의 잔고 및 보유 주식 조회 |
| 주식 구매 | POST | /api/players/buy | 플레이어 자산을 사용한 매수 트랜잭션 |
| 주식 판매 | POST | /api/players/sell | 보유 주식 매도 및 자산 최신화 |
1️⃣ 로그인 프로세스 (StartMain.vue)
사용자 인증 및 세션 관리의 시작점이다.
- 데이터 바인딩: v-model을 통해 사용자가 입력한 playerId, playerPassword를 실시간으로 저장한다.
- 로그인 로직:
- 로그인 버튼 클릭 시 login() 함수를 실행하여 apiCall.post()로 서버에 요청을 보낸다.
- 성공 시: storePlayer()로 사용자 정보를 저장하고, router.push('/stock')을 통해 메인 페이지로 이동한다.
- 실패 시: notifyInfo()로 에러 메시지를 표시하며, 신규 사용자인 경우 회원가입 모드(isNewPlayer = true)로 전환한다.
🔑 주요 기술 구현 포인트
StartMain: 사용자 인증 및 상태 처리
- ref를 활용한 반응형 상태 관리: 단일 데이터 값인 playerId 등은 ref로 선언하여 관리한다. 스크립트 단에서는 .value로 접근하며, 템플릿에서는 자동으로 언랩핑되어 편리하게 사용 가능하다.
const playerId = ref(''); // 단일 값 → ref 사용
// 접근 시: playerId.value
// 템플릿에서는 자동으로 언랩핑됨
- 조건부 렌더링 활용: v-if를 사용하여 로그인과 회원가입 버튼을 교체한다. 이는 v-show와 달리 조건이 맞지 않을 때 DOM에서 요소를 완전히 제거하므로 보안 및 초기 렌더링 최적화에 유리하다.
<button v-if="isNewPlayer">회원가입</button>
<button v-else>로그인</button>
- 표준 에러 핸들링 패턴: apiCall.Response.SUCCESS 상수를 활용해 응답 결과를 판별하며, 성공과 실패 로직을 명확히 분리하여 사용자 피드백(알림창 등)을 제공한다.
if (response.result === apiCall.Response.SUCCESS) {
// 성공 로직
} else {
// 실패 로직 + 사용자 피드백
}
2️⃣ 주식 시장 관리 (StockList.vue)
전체 주식 목록을 조회하고 새로운 종목을 추가하는 대시보드다.
- 자동 데이터 로드: onMounted() 생명주기 훅을 통해 컴포넌트 로딩 시 getStockList()를 자동으로 실행한다.
- 주요 기능:
- 목록 표시: 서버에서 가져온 데이터를 table.items에 저장하고, ItemsTable 컴포넌트에 전달하여 화면에 렌더링한다.
- 주식 추가: InlineInput에 입력된 값을 addStock() 함수를 통해 서버에 POST 요청을 보낸 후, 목록을 새로고침한다.
- 이벤트 핸들링: 특정 행(Row) 클릭 시 handleRowSelected()가 실행되며, 콜백 함수를 통해 선택된 주식 정보를 PlayerStocks 컴포넌트로 전달한다.
- 반응형 페이징: 페이지 네비게이터의 변경 사항을 watch()가 감지하여 자동으로 리스트를 동기화한다.
🔑 주요 기술 구현 포인트
StockList: 목록 조회 및 페이징 전략
- reactive 기반 객체 상태 관리: 테이블 헤더나 아이템 목록처럼 구조화된 데이터는 reactive를 사용하여 객체 단위로 관리한다. ref와 달리 .value 없이 속성에 직접 접근할 수 있어 코드가 간결해진다.
const table = reactive({
headers: [...],
items: []
});
// 직접 접근 가능 (. value 불필요)
table.items = newData;
- watch를 활용한 자동 갱신: 페이지 번호(page.current)나 페이지당 개수(page.count)가 변경될 때마다 watch가 이를 감지하여 getStockList()를 재호출한다. 이는 사용자 액션과 데이터 동기화를 연결하는 강력한 도구다.
// 단일 값 감시
watch(() => page.current, getStockList);
// 복합 처리
watch(() => page.count, () => {
page.current = 1; // 리셋
getStockList(); // 재조회
});
- Offset 기반 페이징: API 규격에 맞춰 현재 페이지 번호에서 1을 뺀 offset 값을 계산하여 전달한다. (예: 1페이지 → offset 0)
// API는 page가 아닌 offset을 받음
queryParams: {
offset: page.current - 1, // 0부터 시작
count: page.count
}
- 콜백 기반 이벤트 전파: getCallback을 통해 등록된 함수가 있는지 확인 후 실행함으로써, 컴포넌트 간 결합도를 낮추면서도 데이터를 안전하게 전달한다.
// 1. 콜백 가져오기
const callback = getCallback('selectStock');
// 2. 존재 여부 확인 후 실행
if (callback) callback(item);
3️⃣ 플레이어 자산 및 거래 (PlayerStocks.vue)
개인 자산을 관리하고 실제 매수/매도 거래가 이루어지는 컴포넌트다.
- 초기 설정: 로드 시 getPlayerInfo()를 호출하여 잔고와 보유 주식을 조회하며, selectStock 콜백을 등록한다.
- 거래 흐름:
- 주식 선택: StockList에서 주식을 클릭하면 selectStock()이 실행되어 해당 주식 ID가 입력란에 자동 바인딩된다.
- 유효성 검사: isFormValid computed 속성이 수량 입력 여부 등을 감지하여 버튼의 활성화 상태를 결정한다.
- 거래 실행: 구매(buyPlayerStock) 또는 판매(sellPlayerStock) 버튼 클릭 시 서버 요청을 보낸다.
- 상태 갱신: 거래 성공 후 getPlayerInfo()를 재호출하여 잔고와 보유 목록을 즉시 최신화하고 입력란을 초기화한다.
🔑 주요 기술 구현 포인트
PlayerStocks: 비즈니스 로직 및 UI 인터랙션
- computed를 활용한 폼 검증: stockId와 stockQuantity의 입력 여부를 computed로 실시간 감시한다. 이를 버튼의 :disabled 속성에 바인딩하여 잘못된 요청이 서버로 전송되는 것을 원천 차단한다.
const isFormValid = computed(() => {
return stockId.value !== '' && stockQuantity.value !== '';
});
- Lifecycle 기반 콜백 등록: onMounted 시점에 콜백을 등록하여 외부 컴포넌트(StockList)로부터 넘어오는 데이터(주식 선택 정보)를 받을 준비를 한다.
onMounted(() => {
setCallback('selectStock', selectStock);
});
- 트랜잭션 후 상태 동기화: 주식 매매 성공 시 서버에서 다시 정보를 불러오는 await getPlayerInfo()를 실행한다. 이는 클라이언트의 데이터와 서버의 DB 상태를 일치시키는 핵심 프로세스다. 이후 입력 폼을 초기화하여 다음 거래를 준비한다.
💡 시스템 핵심 요약
| 구분 | 주요 기술 및 패턴 |
| 상태 관리 | v-model 및 로컬 상태를 활용한 실시간 데이터 동기화 |
| 통신 구조 | Axios 래퍼(apiCall)를 통한 일관된 REST API 처리 |
| 컴포넌트 통신 | 사용자 정의 콜백 패턴을 활용한 컴포넌트 간 데이터 전달 |
| 사용자 경험 | watch, computed를 활용한 반응형 UI 및 자동 갱신 로직 |
'SKALA' 카테고리의 다른 글
| Front-framework: Vue.js (0) | 2026.02.05 |
|---|---|
| HTML, CSS, JavaScript (0) | 2026.02.05 |
| 생성형 AI 기초 및 Prompt Engineering (0) | 2026.02.03 |
| Vector Database (0) | 2026.02.02 |
| LLM 모델 이해 및 활용(2) (0) | 2026.02.02 |

