만들어 보고 싶었던 코인 사이트 토이 프로젝트를 완성했다.
개발하면서 느낀점과 무엇이 부족했는지에 대하여 4L(Liked, Learned, Lacked, Longed for) 형식에 맞춰 후기를 작성해보려한다.
강의 없이 직접 만들어보고 고뇌하며 코딩에 익숙해지는 느낌이 너무좋았다.
예를 들어 이론으로만 접했던 CORS, 성능최적화, WebSocket API 등을 실제로 구현해보고 프로젝트에 녹였다는 점이다.
토이 프로젝트지만 강의없이 기획부터 배포까지 나만의 웹 사이트를 보며 보람을 많이 느꼈다.
이번 프로젝트를 개발하며 평소에 눈여겨 보던 라이브러리들을 사용하고 정답은 아닐 수 도 있지만
내가 생각한대로 커스터마이징 해본 경험이 소중했다.
클론코딩에 대한 강의는 너무 많다.
강의하나만 있으면 누구나 그럴듯한 결과물을 만들어낼 수 있다.
스스로 생각하며 어떤 라이브러리를 사용할지, 상태관리를 어떻게 관리할지, 재활용 가능한 부분이 있는지 등
이외에도 여러 부분이 있지만, 개발하면서 이건 정리해야겠다고 생각이든 부분만 블로그에 남겨보고자한다.
여기서 대부분의 시간을 쏟아부었다.
처음엔 WebSocket API 라는 개념이 없었고, REST API 를 시간 텀을 주고 계속 요청하여 리렌더링 하는 형태로 개발했었다.
하지만 완성하고나니 내가 원하는 결과물과 너무 달랐고 많은 검색과 예시를 보며 WebSocket API 로 리팩토링 하였다.
아래에 이전의 코드가 어떻게 바뀌었는지 간략하게 코드를 첨부한다.
// REST API 를 사용한 데이터 Fetching 함수들
export function getMarketCoins() {
return fetch('https://api.upbit.com/v1/market/all').then((res) => res.json());
}
export function getBtcAccPrice() {
return fetch('https://api.upbit.com/v1/ticker?markets=KRW-BTC').then((res) => res.json());
}
export function getDetailCoin(coin: string) {
if (coin) {
return fetch(`https://api.upbit.com/v1/ticker?markets=${coin}`).then((res) => res.json());
} else return null;
}
너무 안일하게 생각했던 코드였고 간단히 데이터를 받아 실시간 처럼 보이게 하려는 속임수를 사용했다.
하지만 Upbit 에서 제공하는 WebSocket API 가 있었다.
아래의 코드는 리팩토링된 부분이다.
useEffect(() => {
try {
if (!ws.current) {
ws.current = new WebSocket(UPBIT_WS_URL);
ws.current.binaryType = BINARY_TYPE;
// open
const openHandler = () => {
const sendField = [
{ ticket: "coinzero" },
{
type: "ticker",
codes: searchCoin.map((code: any) => code.market),
},
];
ws.current.send(JSON.stringify(sendField));
};
// close
const closeHandler = () => {
setBufferData([]);
setWsData([]);
bf.current = [];
};
// msg
const msgHandler = (e: any) => {
const wsData = dataEncoder(e.data);
if (wsData) bf.current.push(wsData);
throttled();
};
ws.current.onopen = openHandler;
ws.current.onclose = closeHandler;
ws.current.onmessage = msgHandler;
}
return () => {
if (ws.current) {
ws.current.close();
ws.current = null;
}
};
} catch (error) {
console.error(error);
}
}, [searchCoin]);
기본적으로 Recoil 에서 관리되는 searchCoin 을 바라보고있고 사용자가 코인을 검색할 때
searchCoin 의 값을 변경하여 체결량, 시세, 차트 등의 데이터가 반환되는 값으로 나타난다.
1초에 몇백개의 데이터가 들어오다보니 사이트가 멈추거나 수많은 리렌더링이 일어났다.
버퍼를 사용하여 들어온 데이터를 버퍼에 쌓아두고 데이터를 내보내는 형태로 개발됐다.
제일 힘들었던 부분이 이부분이었다. 검색되는 해결방안이 거의 없었고
Open Library 를 통하여 직접 코드를 분석하여 참고했다.
데이터가 fetching 중일 때, Loading bar 를 보통 사용해서 시각적으로 사용자에게 편의를 제공한다.
나는 처음에 Spinner 형태로 로딩 진행 상황을 표시했지만, 유튜브와 당근마켓 어플을 사용하다가 좋은 예시를 적용해봤다.
Youtube-shorts 의 skelton UI
쇼츠를 보다 이런 화면을 많이보았고 자주 사용하던 당근마켓 앱도 이와 비슷한 UI 로 로딩을 관리하였다.
훨씬 더 세련하고 어느 위치에 데이터가 표현될지 예측할 수 있는 장점이 있는것 같아서 내 프로젝트에 녹여보기로했다.
비교적 최신 라이브러리이지만 공식문서를 통해 그래프의 styling 까지 적용시켰다.
거래소 클론코딩글을 찾아보면 실제 거래소에서 구현되어있는 미니차트들은 구현되어있지않았다.
나는 이부분까지 구현하기위해 chart.js 를 사용하여 미니차트를 구현했다.
처음엔 코인 정보, 차트, 체결정보 등 각각의 컴포넌트에서 불러오는 형태로 개발을 했다.
로컬 환경에서 Loading Spinner 를 통해 대기화면을 만들고 해당 작업을 하니 어느정도 참아줄만한 시간이었다.
하지만 컴포넌트들이 늘어나며 대기시간이 비약적으로 길어져 잘못됐음을 깨닳았다.
서비스 사용자들에게 제공되는 코인 정보, 체결량, 차트 등의 컴포넌트들을 감싸는 부모 컴포넌트를 생성한다.
부모 컴포넌트에서 data fetching 이 이루어지고 받은 데이터를 자식 컴포넌트에게 넘겨주는 형태로 개발했다.
// 코인, 차트, 실시간 체결량, 코인목록
<CoinListsFrame>
{coinNames && lineData && renderTimer ? (
<>
<CoinSummary coinNames={coinNames} lineData={lineData} />
<CoinChart wsCoin={searchCoin[0].market} />
<TradingVolume daysData={lineData.slice(0, 49)} coinName={searchCoin[0].market} />
<SimpleSearch marketCodes={marketCodes} />
</>
) : (
<Skeleton />
)}
</CoinListsFrame>
이전의 개발 형태보다 덜 무거워졌지만 하나의 문제가 생겼다.
나는 지금 Upbit API 를 사용하고 있기 때문에 여러번의 API 를 호출할 수 밖에 없었다.
한번에 코인들의 정보가 넘어오는게 아닌, 기존 코인들의 market 데이터를 한번 수신해야한다.
// result
{
market: "KRW-BTC",
korean_name: "비트코인",
english_name: "Bitcoin",
}
result 의 market 값으로 다시 요청해야한다.
이처럼 두 번의 요청을 최소화 하기위해 market 의 정보는 recoil 을 사용하여 전역으로 상태관리한다.
아래의 구조처럼 컴포넌트를 각각의 styled, util, hook 으로 관리하였다.
프로젝트를 마칠때마다 드는 생각이있다.
코딩을 좀더 잘하고싶다 라는 생각인데, 공부할게 너무 많은것같다.
쌓인 강의, 책을 처리해야하는데 쉽지않다.
이번 프로젝트를 하면서 새로운 라이브러리를 접하거나 WebSocket API 를 사용한게 큰 도움이 되는것 같다.
언제가 이 프로젝트도 다시 리팩토링 하고싶다는 생각이 든다.
한번에 일취월장하는 기적을 바라지않고 꾸준히 계속 하는 코딩습관을 길러야겠다.
수많은 구글링을 하며 많은 도움을 받았고 그들 처럼 도움이 되는 개발자가 되고싶다.
Coinzero 라는 프로젝트는 사실 엄청 오래전에 틀만잡아두고 최근에 끝낸 프로젝트이다.
그사이 퇴사도하고 부트캠프도하고 어쩌면 완성하지못할 프로젝트였는데 완성하게되서 기분이좋다.
별거아닌 프로젝트지만 다음엔 더욱더 완벽한 프로젝트 후기를 쓸 수 있도록 노력해야겠다.
나만의 기록용으로 남기는 글이지만 혹시나 정독해주신 분이 있다면 너무 감사하다고 말하고싶다 !
잘봤습니다. 혹시 깃허브주소 알려주실수있나요?!