[아웃소싱 프로젝트] - 불필요한 API 통신 줄이기

2025. 3. 4. 22:32·TroubleShooting

문제 상황

💻 checkBookmarkState 코드

useEffect(() => {
  const checkBookmarkState = async () => {
    const isMarked = await checkJobBookMarks(userId, jobId);
    setIsBookmarked(isMarked);
  };
  checkBookmarkState();
}, [userId, jobId]);

 

💻 checkJobBookmarks 코드

export const checkJobBookMarks = async (userId, jobId) => {
  try {
    const { data } = await supabase
      .from(QUERY_KEY.BOOKMARKS)
      .select('*')
      .eq('user_id', userId)
      .eq('job_id', jobId);

    return data && data.length > 0;
  } catch (error) {
    console.error('북마크 조회 오류', error);
    return false;
}

 

기존에는 `checkBookmarkState`를 통해 채용 정보 카드 내에서 사용자가 해당 카드를 북마크를 했는지 확인하고 있었다.

🚨 채용 정보 카드가 62개 정도 있었는데, 62번의 API 통신이 발생하는 문제가 있었다.

API 통신도 모두 비용이기 때문에 62번의 통신이 일어나지 않도록 코드를 수정하고 싶었다.

 

 

문제 원인

코드를 저렇게 짰던 이유는 마이페이지와 채용 정보 리스트에서 JobItem을 공통 컴포넌트로 사용하고 있었기 때문이다.

사용자가 북마크한 채용 정보만 보여주는 마이페이지에서는 62번을 통신하지 않아도 됐었는데,
채용 정보를 모두 보여주는 채용 정보 페이지에서는 62번의 통신을 하지 않으면 사용자가 해당 채용 정보를 북마크 했는지 확인할 수가 없었다.

 

그런데..!!!

62번의 API 통신을 하지 않고도 사용자가 해당 채용 정보를 북마크했는지 확인하는 방법이 있었다!

 

 

해결 과정

✅ 채용 정보 리스트에서 jobs 테이블에 대한 데이터를 가져올 때, bookmarks 테이블에 대한 정보도 가져오면 된다.

이때 table들이 FK(Foreign Key)로 연결되어 있어야 가능하다.

job_id로 jobs 테이블과 bookmarks 테이블이 서로 연결되어 있었다.

bookmarks 테이블

 

💻 fetchJobsData 코드

export const fetchJobsData = async (table1) => {
  try {
    const { data } = await supabase
      .from(table1)
      .select('*, resumes(*), bookmarks(*)'); // 여기에 추가

    return data || [];
  } catch (error) {
    console.error('fetching error', error);
  }
};

jobs 테이블에 대한 데이터를 가져올 때 bookmarks 테이블을 저렇게 연결하면 된다.

그러면 각각의 채용 정보에 북마크가 되었는지 확인할 수 있다.

 

✅ JobItem에서는 로그인한 userId와 bookmarks 테이블에 들어있는 userId가 일치할 때, 북마크 표시를 해주면 된다!

// JobItem에서 북마크됐는지 확인하는 코드
const isBookmarked = job.bookmarks.some((bookmark) => bookmark.user_id === userId);

 

 

이렇게 코드를 작성하게 되니 마이페이지에서 보여주는 북마크 리스트에 대한 코드를 전반적으로 수정했어야 됐다...

JobItem을 공통으로 사용하다보니 Jobitem의 부모 컴포넌트인 BookmarkList에서 코드를 수정하려고 했다.

어찌어찌한 과정을 거치다 보니 BookmarkList에서도 jobs 테이블 데이터를 가져와서 사용하게 됐다.

기존의 코드는 다음과 같았다.

 

💻 기존의 BookmarkList 코드

import { useBookMarksQuery } from '../../../hooks/bookmarks/useBookMarksQuery';
import JobItem from '../../common/JobItem';
import LoadingPage from '../../common/LoadingPage';

const BookMarkList = () => {
  // bookmarks 테이블에서 사용자가 북마크한 채용 정보만 가져옴
  const { data: bookmarkList = [], isPending, isError } = useBookMarksQuery();

  /** UI */
  if (isPending) return <LoadingPage state="load" />;
  if (isError) return <LoadingPage state="error" />;

  return (
    <div className="flex w-full flex-col items-center gap-5">
      <h1 className="text-2xl">북마크</h1>
      {bookmarkList.length === 0 ? (
        <span>채용 정보를 북마크해주세요!</span>
      ) : (
        bookmarkList.map((data) => {
          const { jobs, resumes } = data;

          return (
            <JobItem key={data.jobs.id} job={{ ...jobs, resumes: [resumes] }} />
          );
        })
      )}
    </div>
  );
};

export default BookMarkList;

원래는 bookmarks 테이블에서 사용자가 북마크한 정보만 가져와서 map을 돌리고 있었다.

여러 가지 시행착오를 겪다가.. 모든 채용 정보 리스트를 보여주는 페이지와 동일하게 jobs 테이블의 데이터를 사용하는 게 어떨까라는 생각이 들었다. `filter`을 통해 사용자가 북마크한 정보만 걸러낼 수 있지 않을까라는 생각이 들었다.

 

그래서 적용해본 결과!!

💻 수정된 BookmarkList 코드

import { useJobsQuery } from '../../../hooks/useJobsQuery';
import useAuthStore from '../../../zustand/useAuthStore';
import JobItem from '../../common/JobItem';
import LoadingPage from '../../common/LoadingPage';

const BookMarkList = () => {
  // 로그인한 사용자 ID 가져오기
  const userId = useAuthStore((state) => state.user.user_id);
  // jobs 테이블에서 데이터 가져오기
  const { data: jobData, isPending, isError } = useJobsQuery();

  // 사용자가 북마크한 채용 정보만 필터링하기
  const bookmarkedJobs = jobData?.filter(
    (job) =>
      job.bookmarks.length !== 0 &&
      job.bookmarks.some((bookmark) => bookmark.user_id === userId),
  );

  /** UI */
  if (isPending) return <LoadingPage state="load" />;
  if (isError) return <LoadingPage state="error" />;

  return (
    <div className="flex w-full flex-col items-center gap-5">
      <h1 className="text-2xl">북마크</h1>
      {bookmarkedJobs.length === 0 ? (
        <span>채용 정보를 북마크해주세요!</span>
      ) : (
        bookmarkedJobs.map((data) => {
          return <JobItem job={data} key={data.id} />;
        })
      )}
    </div>
  );
};

export default BookMarkList;

무사히 잘 동작하였다!

 

 

결과

62번의 API 통신을 하지 않게 되는 코드로 수정할 수 있어서 굉장히 뿌듯하였다.

이 과정에서 팀원분들이 많이 도와주셔서 너무 감사했다.

 

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'TroubleShooting' 카테고리의 다른 글

[Riot API 이용 프로젝트] Router Handler에서 무료 챔피언 리스트에 대한 로직을 짤 수 있을까?  (1) 2025.03.18
[Riot API 이용 프로젝트] - 다크모드 버튼을 누른 뒤 새로고침을 하면 에러가 발생하는 문제 (feat.next-themes)  (0) 2025.03.14
[아웃소싱 프로젝트] - 프로필 수정은 되는데 새로고침만 하면 원래대로 돌아 온다면  (1) 2025.03.03
[아웃소싱 프로젝트] - tailwind에 동적 스타일링이 적용되지 않을 때  (0) 2025.03.03
[MBTI 테스트] - 닉네임을 변경했는데 왜 변경사항이 없다고 하죠?  (0) 2025.02.23
'TroubleShooting' 카테고리의 다른 글
  • [Riot API 이용 프로젝트] Router Handler에서 무료 챔피언 리스트에 대한 로직을 짤 수 있을까?
  • [Riot API 이용 프로젝트] - 다크모드 버튼을 누른 뒤 새로고침을 하면 에러가 발생하는 문제 (feat.next-themes)
  • [아웃소싱 프로젝트] - 프로필 수정은 되는데 새로고침만 하면 원래대로 돌아 온다면
  • [아웃소싱 프로젝트] - tailwind에 동적 스타일링이 적용되지 않을 때
집으로 감자
집으로 감자
Hello World
  • 집으로 감자
    Potato to Home
    집으로 감자
  • 전체
    오늘
    어제
  • 링크

    • Github
    • 분류 전체보기
      • Front-end
        • TIL
        • Javascript
        • Typescript
        • React
        • Next.js
        • CSS
        • 라이브러리
      • Algorithm
        • 프로그래머스: 기초 트레이닝
        • 프로그래머스 LV1
      • TroubleShooting
      • Problem & Solution
      • Project
      • Programing Knowledge
        • 개발 용어
        • CS
        • 운영체제
        • 자료구조
        • 데이터 베이스
  • 태그

    react
    회고
    Til
    tanstackquery
    JavaScript
    개발용어
    실행컨텍스트
    styledcomponent
    반응형웹
    CSS
    프로그래머스
    문제해결
    트러블슈팅
    http
    코딩테스트
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
집으로 감자
[아웃소싱 프로젝트] - 불필요한 API 통신 줄이기
상단으로

티스토리툴바