2scent / kimpsoom

실시간 업비트 - 바이비트 김치 프리미엄 조회

Home Page:https://kimpsoom.vercel.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Kimpsoom

  • 암호화폐(이하 코인)는 우리나라 거래소와 해외 거래소의 시세 차익이 존재하는데 그것을 김치 프리미엄, 일반적으로 줄여서 김프라고 부른다.
  • 나는 우리나라의 업비트 거래소와 해외의 바이비트 USDT 선물 거래소를 주로 이용하는데, 기존의 김프 사이트들은 이 거래소들 간의 김프 조회를 지원하지 않아서 직접 개발하게 되었다.

데모

demo

주요 기능

  • 실시간 원 달러 환율 조회
  • 실시간 업비트 거래소 가격 조회
  • 실시간 바이비트 USDT 선물 거래소 가격 조회
  • 실시간 업비트 거래소 - 바이비트 USDT 선물 거래소 시세 차익 조회

사용 기술

  • JavaScript, TypeScript, React, React Query, Redux, Next.js, WebSocket

기술적 이슈

TDD와 테스트 커버리지 100%

test-coverage

  • 프로젝트를 시작할 때는 모든 기능을 TDD로 개발하고, 테스트 커버리지도 100%를 유지하려고 했었다. 하지만 막상 개발을 하다 보니 구현에 쫓겨 모든 기능을 TDD로 개발하는 데 어려움이 있었다.
  • 결국 구현이 거의 끝난 시점에 부족한 테스트를 추가하게 되었는데, 어느새 테스트하기 어려운 코드, 즉 유지보수가 어려운 코드가 되어 있었다. 그래서 기존의 코드를 테스트하기 쉬운 코드로 리팩토링하면서 테스트를 추가했고, 테스트 커버리지 100%를 달성할 수 있었다.
  • 이런 과정을 통해 처음부터 테스트하기 쉬운 코드가 강제되는 TDD의 유용성을 다시 한번 느낄 수 있었다.

선언적인 비동기 처리

  • 비동기 상태나 에러 처리를 위한 가장 단순한 방법은 if문이나 try ~ catch문을 사용하는 것이다. 하지만 이는 명령적인 방법으로 데이터 로딩과 UI 렌더링이라는 다른 목표가 하나의 컴포넌트 안에 커플링 되어 코드의 가독성이 떨어지고, 상위 컴포넌트의 비동기 작업이 끝나야 하위 컴포넌트의 비동기 작업이 시작되는 waterfall 현상이 발생할 수 있다.
  • 이를 해결하기 위해 React 18에서 정식적인 기능으로 채택된 SuspenseErrorBoundary를 이용해 비동기 및 에러 처리를 선언적으로 관리할 수 있게 했다.
  • SuspenseErrorBoundary에 대한 테스트를 작성하면서 한 가지 이슈가 있었다. 이 프로젝트에서 대부분의 비동기 작업은 React Query의 useQuery를 이용해 외부 데이터를 가져오는 것이었다. 이를 테스트하기 위해서는 useQuery가 비동기 작업 중이거나 에러가 발생했다는 것을 테스트 환경에서 재현해야 했는데 이 부분에 어려움이 있었다. 많은 시행착오 끝에 useQuery를 mocking 해서 Promise 또는 Errorthrow 하는 것으로 해결할 수 있었다.

상태 관리

  • 서버로부터 정기적으로 데이터를 받아와서 업데이트해야 하는 업비트 티커(e.g., BTC, ETH, …)들의 정보와 환율 정보는 React Query로, 소켓 통신을 통해 실시간으로 업데이트해야 하는 각 코인의 가격 정보는 Redux로 관리하도록 했다.
  • 특히 React Query는 선언적인 비동기 처리(Suspense, ErrorBoundary) 및 Next.js 대한 지원이 잘 돼 있어서 보다 수월하게 가독성 좋은 코드를 작성할 수 있었다.

폴더 구조

  • 지역성의 원칙을 고려해 폴더를 기능별로 나누었다. 지역성이란 시간적 지역성과 공간적 지역성으로 나눌 수 있다.
    • 시간적 지역성: 특정 기능에 속한 코드를 개발한 프로그래머는 조만간 같은 기능에 속한 다른 기능을 개발할 가능성이 높다.
    • 공간적 지역성: 특정 기능에 속한 코드를 개발한 프로그래머는 그 기능 주변의 (상대적으로 연관된) 다른 기능에 속한 코드를 개발할 가능성이 높다.
  • React 프로젝트에서 가장 단순하게 폴더를 나누는 방법은 최상위 폴더에 components, utils, hooks, api 같은 폴더를 두는 방식일 것이다. 아주 간단한 프로젝트일 때는 별문제가 없지만, 조금만 프로젝트가 커져도 기능 하나를 수정하기 위해 여러 폴더를 광범위하게 건드려야 하는 문제가 발생할 수 있다.
  • 처음부터 기능별로 폴더를 나누면 특정 기능과 관련된 파일들을 모아서 관리할 수 있고, 그 자체로서 모듈화가 되어 필요한 기능만 export 할 수 있다는 장점이 있다.

Throttle을 이용한 성능 최적화

  • 평소에는 괜찮다가 코인의 가격 변동이 커져서 거래가 활발하게 일어나는 경우에 사이트가 버벅대는 문제가 있었다.
  • 어떤 코인의 가격이 바뀔 때마다 코인들의 정렬을 위해 계산 및 리렌더링이 발생하는데 가격 변동이 심해지면 그만큼 계산과 리렌더링이 발생하는 게 주된 원인이었다.
  • Throttle을 적용하여 각 코인 별로 거래소마다 최대 0.5초에 한 번씩만 가격 정보를 업데이트할 수 있게 하여 문제를 해결하였다.

기술적 시도

TypeScript 마이그레이션

  • 프로젝트 대부분을 JavaScript로 개발했다가 TypeScript를 공부하면서 마이그레이션을 진행했다.
  • 마이그레이션을 진행하면서 babel, eslint, jest 등 개발 환경과 관련된 문제도 있었는데 이를 해결하면서 TypeScript뿐만 아니라 개발 환경에 대한 이해도 높일 수 있었다.
    • 한 번에 모든 파일을 TypeScript로 바꾼 게 아니라 조금씩 점진적인 마이그레이션을 했는데 jest가 정상적으로 동작하지 않는 문제가 발생했다. JavaScript 파일과 TypeScript 파일을 동시에 테스트하는 게 원인이었다. 이를 해결하기 위해 jest의 설정 파일에 대해 공부했고, preset 설정을 ts-jest/presets/js-with-ts로 수정함으로써 해결할 수 있었다.
    • JavaScript 파일에서 문제없이 사용하던 given을 TypeScript에서 인식하지 못하는 문제가 있었다. JavaScript에서는 jest 설정 파일만 잘 구성해 놓으면 됐지만, TypeScript에서는 선언돼 있지 않은 타입은 인식할 수 없어서 발생한 문제였다. global 네임스페이스에 given에 대한 타입을 선언함으로써 이 문제를 해결할 수 있었다.
  • TypeScript를 적용하면서 직접 느낀 TypeScript의 가장 큰 장점은 역시 타입 시스템이다. 개발 단계에서 타입과 관련해서 생길 수 있는 에러를 예방할 수 있기 때문에 보다 안전한 프로그램 개발이 가능하다.

Next.js 적용

  • Next.js를 적용하면서 업비트 티커(e.g., BTC, ETH, …)들과 환율 정보에 대해 SSG를 적용했다.
  • 사실 이 프로젝트에서 Next.js를 도입함으로써 얻는 이점은 크지 않다. 가장 핵심 기능이라 할 수 있는 코인의 가격 정보는 소켓 통신을 통해 실시간으로 업데이트해줘야 하기 때문이다.
  • 하지만 간단한 기능이라도 Next.js를 적용하기 위해 Next.js 및 SSR, SSG와 관련된 공부를 했고, 어떤 경우에 Next.js를 적용하면 좋을지 판단할 수 있는 좋은 경험이 되었다.

참고

About

실시간 업비트 - 바이비트 김치 프리미엄 조회

https://kimpsoom.vercel.app


Languages

Language:TypeScript 96.5%Language:JavaScript 3.5%