상품 동기화 개선 회고

Dongwon Ahn·2021년 7월 29일
0

회고

목록 보기
1/3

작년 10월에 이커머스 스타트업인 (주)우리동네커머스에 백엔드 개발자로 입사하였습니다.
회사 내 첫번째 개발자 정직원으로 그 전 개발은 외주로 진행하고, 계약의 이슈인지 유지보수가 잘 진행되지 않아, 입사 후 여러 기능들은 만들고, 개선해야 됐었습니다.
해당 시리즈에서는 업무 진행하면서 느꼈던 점들을 정리하는 글입니다!

상품 동기화 기능?

저희는 동네시장 장보기 라는 네이버쇼핑에서 장보기 서비스 중 전통시장 장보기를 운영 중에 있습니다.
즉 저희 자사 쇼핑몰이 있는 것이 아닌 네이버 쇼핑에서 주문을 받고 해당 주문을 상인들에게 공유하는 프로세스를 취하고 있습니다.
그렇기에 저희 DB와 네이버 쇼핑에 올린 상품의 상태가 다른 경우 (상품 추가, 품절 등)가 있습니다.
그 간극을 줄이기 위해 특정 시간마다 스마트스토어 API를 통해 상품을 받아서 저희 DB에 update를 진행하였습니다.

초기에는 상품의 수가 적어 문제가 없던 기능이 상품의 수가 늘어남에 따라 길게는 8분이 걸려 이슈가 발생하기 시작하였습니다.

기존에 구현되어 있던 상품 동기화 기능 로직

저희 DB 모든 상품을 조회하고, 스마트스토어 API를 통해 저희가 등록한 모든 상품을 가져와
저희 모든 상품 중에 찾아서 없으면 신규 등록하고, 있는 경우 update 하는 방식으로 구현되어 있었습니다.
추가적으로 스마트스토어 API는 상품을 받아오는 것이 한번에 100개씩 조회가 가능하였습니다.

해당 기능은 typeorm의 save를 통해 DB에 저장하기 때문에 있으면 update 신규 등록한 상품 같은 경우는 저희 DB에 없기 때문에 insert 되었습니다.

해당 기능에서 제가 생각한 문제점은 아래와 같았습니다.

  • 불필요한 컬럼에도 index 설정이 되어 있는 점
  • 불필요하고 무거운 data를 저장되고 있는 점
  • 변경되지 않은 상품도 update되는 점

개선을 위해 시도했던 것

개선을 위해 시도한 것에 대한 결과는 기존 상품 동기화에 걸렸던 시간과 비교하여 성과가 있었는지 판단하였습니다.
기존에 걸렸던 시간은 상품 약 25000개 기준으로 8분정도 소요가 되엇습니다.

  1. mysql을 사용중에 있었는데, 불필요한 index 제거 및 전체 조회 결과 json을 저장중이던 컬럼에 update 하는 부분 삭제
    • 변경 결과 약 5초 미만의 미미한 개선 효과가 있었습니다.
  1. 자바스크립트에서 제공해주는 find가 아닌 lodash의 find 사용
    • 구글링 결과 lodash find가 성능이 좋다고 나왔는데 시간적인 차이점을 발견하지 못했습니다.
// 과거 es6의 find 사용
const product = products.find((p) =>
      (p.ProductId === prop.ProductId.toString()));
// 변경 lodash 사용
const product = _.find(products, p =>
        p.ProductId === prop.ProductId.toString());
  1. 변경되지 않은 상품은 update X
    • 기존의 상품 정보와 받아온 상품 정보를 비교하는 함수를 따로 작성하여, 비교 후 변경사항이 있는 경우 update 진행하고, 조회가 안된 경우 insert를 진행하였습니다.
    • 해당 작업 결과 8분 -> 4~5분 정도로 시간이 줄어드는 효과가 있었습니다.
  1. 조회 후 상품 찾으면 list에서 제거하여 다음 번 조회에서 표본을 줄이도록 진행
    • 저희 DB의 상품을 받아온 다음 스마트스토어에서 받아온 상품 리스트를 반복문을 통해 찾는 과정을 진행
    • 중복이 없기 때문에 찾은 다음 list에서 제거하면 다음 상품 찾을 때 줄어들 것이라고 예상하였습니다.
    • lodash의 findIndex를 통해 index값으로 찾은 다음에 비교 후 splice를 사용하여, list에서 제거
    • 해당 작업 결과 4~5분에서 약 30초 정도 줄어드는 효과가 있었습니다.
// 상품 index 조회
const productIndex = _.findIndex(products, p =>
  p.ProductId === prop.ProductId.toString());

// 찾은 경우 list에서 제거 
if (productIndex !== -1) {
  products.splice(productIndex, 1);
}
  1. 서비스의 성장에 따라 결국 상품이 40000개가 넘어가게 되어, 상품 받아오는 작업 자체를 promise.all를 활용하여 분리하였습니다.
    • 기존 받아오는 코드가 100개씩 재귀 처리가 되어 있기 때문에 전체 상품 수를 받아 반으로 나눠 promise.all로 각각 받아오게 하였습니다.
    • 그 결과 해당 작업이 약 2분 가량 줄어드는 효과가 있었습니다.
const [] = await promise.all([함수, 함수]);

느낀점

레거시 코드는 당시의 최선이라고 생각을 하긴 합니다. 초기 개발 당시에는 운영중인 시장의 수가 적기 때문에 상품의 수가 적었고 그렇기에 문제가 되지 않았을 것입니다.
하지만 운영중인 전통시장이 늘어남에 따라 서비스의 규모가 커져 당시 최선이었던 코드는 이슈를 발생시켰던 것 같습니다.
신규 기능이나, 운영팀의 편의성을 위한 기능 개발도 중요하지만, 서비스를 운영하는 개발자라면, 과거의 최선이었던 코드를 현재에도 최선이도록 꾸준히 개선을 해야된다는 것을 느꼈습니다.

추가적으로 멘토님(천재 개발자 jun) 그리고 회사에서 연결해주신 멘토님들이 추천해주신 방법으로 더 개선을 할 예정입니다.

profile
Typescript를 통해 풀스택 개발을 진행하고 있습니다.

0개의 댓글