느릴 수밖에 없는 API를 만났을 때 🦥

어떰·2024년 1월 30일
4
post-thumbnail

왜 그 API는 느렸을까?

어느날 이런 이야기가 들려옵니다.

🐝: 지수님 이거 NFT 컬렉션 디테일 페이지 가 안떠요
🐝: 아니다. 오래 기다리면 뜨긴...떠요. 아닌가? (새로고침 🔄 중)

들어가서 확인해보니, 정말 느렸습니다. 진짜 느리면 20초가 걸리기도 했어요.

왜 처음에는 이게 느리다는 것을 몰랐을까요?
이 문제의 API는 맨 처음엔 간단하게 DB에 있는 값을 긁어서 응답값을 주는 API 였습니다. 그래서 처음에는 이렇게 느리진 않았습니다.

그러나 거래소의 핵심 기능 중 "N차 거래" 개발이 완료되면서, 해당 응답값에는 이 거래에 관련된 데이터 값이 하나 추가되었습니다. 그건 바로 해당 아이템의 "최고 제안가(highest-offer)" ❗️

용어 사전

  • N차 거래: NFT를 재거래하는 것. 1차 구매 그 이후의 거래들을 N차 거래라고 함. 여기에는 NFT에 대한 판매, 구매, 제안, 제안 수락 4가지의 행위가 있음. (더 쉽고 간단한 설명은 여기 😉 ↗️)
  • 최고 제안가: 특정 아이템에 대해서 온 거래 제안가 중 가장 최고가

왜 이 "최고 제안가" 하나가 추가되었다고 이렇게 느려졌을까요?
이유는 매우 다양한 확인 과정을 거쳐서 완성되는 데이터였기 때문입니다.

최고 제안가는 매번 API 호출한 순간에 계산해서 가져와야 하는 값 입니다. 심지어 그 제안이 유효한 것인지 확인이 필요했기 때문에 Alchemy API 호출을 기다리는 로직도 있습니다.

해당 API가 사용자 요청에 따라 "유효한 최고 제안가"를 가져오는 방법
1. DB에 있는 해당 아이템의 제안을 모두 가져온다. (가격 순으로 정렬해서)
2. 가장 최고 제안가를 제시한 account의 자산을 Alchemy API를 통해 조회한다.
3. 해당 account에 제안가만큼 현재 자산이 있다면 해당 제안이 "유효한 최고 제안가"이다. 아니면 다음 순서를 확인한다. 찾을 때까지 (😇)

전부 유효하지 않다면? 아이고 완전 최악의 시간 복잡도를 보여주는 로직이 되었네요. (😭)
그러니 당연히 API는 느릴 수밖에 없었습니다. 왜냐하면, 이걸 아이템마다 확인해야 하고, 아이템 목록은 한번에 20개씩 호출했으니 이걸 20번 한다면 ❓


(나무늘보 API: 손님 ... 여기... 영차... 주문하신 아이템 목록 가져왔어요. 😉 아 바쁘다 바빠 ... ❗️)

그러나 그 최고 제안가는 화면에 보여줘야 하는 요소라 꼭 필요했습니다. 그 로직이 꼭 필요했어요.
그렇지만, 20초 이상 걸리는 API라니 말도 안되는 일이죠. 당연히 스켈레톤 UI를 20초나 보여줄 수는 없었습니다.

🐝: 화면이 안나와요 ❗️
🤖: 아직 오고 있는 중이에요 (😭)

느린건 알겠어. 그럼 어떻게 할까?

우리는 API를 분리했습니다.
기존의 아이템 목록을 불러오는 API와 "최고 제안가" 목록을 가져오는 API 두가지로요!

  • 기존: 응답이 대략 20초 걸리는 몸집이 큰 API 하나
  • 변경: 아이템 목록 API + 최고 제안가 목록 API

한 아이템의 최고 제안가를 가져오는 API는 있었지만, 목록으로 응답을 주는 것은 없었기 때문에 새로 생성해야 했습니다. 아무래도 아이템 목록을 한번에 20개씩 가져오는데 20번이나 API 호출을 계속 할 수는 없으니까요. 심지어 Infinite Scroll이 적용되어 있었으니, 해당 NFT 컬렉션의 아이템이 1만개라면? 🤯

따라서 최고 제안가 목록 API를 새로 생성했고, 해당 API의 요청과 응답은 다음과 같았습니다.

🤖: 프론트에서는 이렇게 호출할게요

GET /items/offer/highest-offers
data: { 아이템 아이디들: string[] }

🤖: 응답은 이런 형태로 받고 싶어요

{ 
  최고 제안가 목록: {
  	아이템 아이디: string;
  	최고 제안가: 최고 제안 | null
  }[]
}

프론트에서는 기존에 아이템 목록 API만 호출해서 렌더링을 하면 되었지만, 이제 API가 두개로 변경되었으니 로직도 그에 맞춰서 변경해줘야 했습니다.

  1. 아이템 목록 API를 통해 아이템 목록을 가져온다.
  2. 가져온 아이템 목록 데이터를 사용하여 화면에 아이템 카드 목록을 그린다.
  3. 현재 가져온 아이템의 아이디 목록을 가지고 최고 제안가 목록 API를 호출한다.
  4. 최고 제안가 목록은 스토어에 {[itemId: string]: HighestOfferForItem} 형태로 보관한다.
  5. 아이템 목록 페이지에서는 각 아이템의 아이디에 해당하는 최고 제안가 아이템이 있는지 스토어 조회를 통해 화면을 업데이트한다.

이렇게 변경하고나니 사용자는 이미 2번부터 정상적인 목록 화면을 볼 수 있게 되었습니다! 🎉

최고 제안가 Hook 만들기

최고 제안가는 서비스의 다양한 곳에서 사용되기 때문에 아이템 목록을 가져오는 많은 페이지들에서 해당 로직을 사용해야 했습니다. 그래서 이 최고 제안가를 가져오는 로직은 최종적으로 Custom Hook으로 만들게 되었어요.

(❗️❗️ 위 코드는 실제 코드는 아닙니다)

이런 식으로 Custom Hook을 만들어서, 페이지에서는 최고 제안가 목록을 가져오는 로직은 알 필요없이 간단하게 사용이 가능하도록 변경했습니다.

실제 페이지는 이렇게 되어있지는 않지만, 위의 Custom Hook을 사용해서 이렇게 로직을 숨겨서 페이지를 그리는 컴포넌트는 그 역할에만 충실하도록 만들 수 있습니다. 그와 함께 페이지도 빨라졌으니 너무 쾌적해졌습니다.

그래서 몇 초가 줄었을까요?

아쉽게도 20초 걸리던 때에 찍어둔 충격과 공포의 사진이 없습니다만, (😭)
현재 기준으로 보았을 때 이렇게나 빨라졌습니다.

이제는 호로록(❓) 더이상 오래 기다리지 않고도 NFT 컬렉션의 아이템 목록을 볼 수 있게 되었습니다.

profile
요즘 보기 드문, 자세가 올바른 프론트엔드 개발자

0개의 댓글