[Front-end] 개발자 공부

팀 프로젝트 | 혼란했던 '기술' 문제 해결

MOLLY_ 2025. 3. 9. 08:00
728x90

< 목차 >
0. 들어가며
1. API와 Hook 관리 구조 혼란 → apis 폴더와 hooks 폴더의 역할 분리로 해결

2. Next.js App Router에서 default export 에러 발생 → 올바른 props 처리로 해결

3. ToastPopup 적용 후 무한 리렌더링 발생 → Token 만료 처리 수정으로 해결

 


 

0. 들어가며

오늘 작성할 문제보다 더 많은 문제가 있었는데, 작성하기도 뭐 하다 싶을 정도로 자잘한 문제이거나 작성하기 애매한 문제는 과감히 제하였다. 또, 기술 트러블 슈팅은 프로젝트 중간중간에도 올렸기 때문에 오늘은 3가지만 가져와 봤다.

 

근데 협업보다 기술 트러블 슈팅을 더 작성하기 어려워야 하는데 왜 협업 내용이 더 쓰기 어려웠단 느낌이 드는 거지?.. 혼란하다 혼란해

 

첫 기술 문제는 API와 Hook 관리 구조의 혼란을 겪었던 문제다. 바로 들어가 보자.

 


 

1. API와 Hook 관리 구조 혼란 → apis 폴더와 hooks 폴더의 역할 분리로 해결

 

프로젝트에서 API 호출과 관련된 코드를 정리하는 과정에서, apis 폴더 하위에 Hook까지 관리하는 구조가 적절한지 고민이 있었다. 또한, hooks 폴더도 별도로 존재하고 있어 구조적으로 중복되거나 혼란을 줄 수 있는 문제가 있었다.

 

문제를 좀 더 구체적으로 설명하자면 다음과 같았다.

 

1. apis 폴더에서 API 요청을 관리하는데, 일부 파일에서는 TanStack Query를 활용한 Hook(useQuery, useMutation)까지 포함하고 있음

2. Hook을 분리해 hooks 폴더로 이동하면 파일이 과도하게 증가할 가능성이 있음

3. apis 폴더에 Hook을 계속 포함하면, Hook을 사용하지 않는 API도 있어 네이밍이 어색해질 수 있음

 

기존 구조에서는 useToggleFollow와 같은 로직이 deleteFollow, postFollow API와 같은 파일에 포함되어 있었다. 기존 방식은 API 호출과 상태 관리를 한 곳에서 처리할 수 있어 직관적이지만, API 로직과 비즈니스 로직이 섞이는 단점이 있었다.

 

 

그래서 Front-end 팀원과 해결 방안을 논의했다.

 

1) 기존 방식 유지

: useToggleFollow와 같은 Hook을 관련 API 파일(deleteFollow, postFollow)에 함께 둠

 

- 장점: API 호출과 상태 관리가 한 파일에 있어 가독성이 좋음

- 단점: API 요청과 비즈니스 로직이 혼재됨

 

 

2) 모든 API를 Hook으로 관리

: TanStack Query를 사용하는 API 요청을 전부 Hook으로 변환

 

이렇게 하면 API 요청이 여러 개 포함된 Hook도 자연스러워진다.

하지만, Hook을 사용하지 않는 API도 존재할 수 있어 apis라는 네이밍이 적절하지 않다.

 

 

3) 파일을 명확하게 분리 (선택한 해결책)

- apis: 순수하게 서버 통신만 담당하는 모듈

- hooks: 상태 관리 및 비즈니스 로직을 담당하는 Custom Hook 관리

   - server: TanStack Query를 이용한 서버 상태 관리 (e.g. useQuery, useMutation)

   - client: React 상태 관리 및 비즈니스 로직을 포함한 Custom Hook

 

 

결정된 구조

/src
 ├── 📂apis               # API 요청 관련 코드 모음
 │   ├── deleteArtwork.ts
 │   └── ...
 ├── 📂hooks              # TanStack Query를 활용한 서버 상태 관리 및 Custom Hook 담당
 │   ├── 📂client         # 클라이언트 상태 관리용 Hook (useState, useReducer 기반)
 │   │   ├── useClickOutside.ts
 │   │   └── ...
 │   ├── 📂server         # 서버 데이터 패칭 및 캐싱을 위한 Hook (useQuery, useMutation 기반)
 │   │   ├── useDeleteArtwork.ts
 │   │   └── ...
 └── ...

 

이렇게 명확히 분리함으로써 유지보수성과 가독성을 높일 수 있었고,

코드의 역할이 더욱 명확해져서 협업 시의 혼동을 줄일 수 있었다.

 

 

이전에는 apis 폴더에서 API와 관련된 Hook까지 관리했지만, 장기적인 유지보수를 고려해 apis와 hooks를 분리하는 방식으로 결정했다.

 

apis 폴더는 순수하게 API 요청만 담당하고, hooks 폴더는 TanStack Query와 같은 서버 상태 관리 및 클라이언트 상태 관리를 담당하도록 구조를 재정비했다.

 

결정 과정 중 일부 소통 내용

 


 

2. Next.js App Router에서 default export 에러 발생 → 올바른 props 처리로 해결

 

Next.js의 app 디렉토리 구조에서 page.tsx 파일의 default export에 반드시 React 컴포넌트를 반환해야 한다. 그런데, 빌드 과정에서 default export 관련 에러가 발생했다. 이는 Next.js의 요구사항과 호환되지 않는 타입이 포함된 게 문제였다.

 

ArtworkUploadProps 타입이 default export된 컴포넌트와 호환되지 않은 게 원인이었다. App Router에서는 props를 직접 전달하는 방식이 아니라, Next.js가 제공하는 특정 방법을 활용해야 한다.

 

 

기존 코드의 문제점은 다음과 같다.

 

- ArtworkUploadProps를 props로 직접 받는 방식 사용

- App Router에서는 page.tsx에서 직접 props를 받을 수 없는 구조

- postId를 props로 전달받는 것이 아닌, usePathname 혹은 useSearchParams를 통해 가져와야 함

- 기존 데이터도 props로 전달받지 않고 백엔드 API 요청을 통해 받아와야 함

 

 

다음의 4가지를 순서대로 작업하여 해결했다.

 

1. props 받는 코드 제거

2. postId는 useSearchParams를 활용하여 가져오기

3. 기존 데이터는 백엔드에서 요청하여 받아오는 방식으로 수정

 

 

수정 후 구조

'use client';

import { useSearchParams } from 'next/navigation';

import useGetArtworkPost from '@/hooks/server/useGetArtworkPost';

const ArtworkUpload = () => {
  const searchParams = useSearchParams();
  const postId = searchParams.get('postId');
  const parsedPostId = postId ? parseInt(postId, 10) : null;

  const { existingArtwork, isError } = useGetArtworkPost(parsedPostId);

  ...
}

 

이제 ArtworkUpload 페이지는 postId를 useSearchParams을 통해 가져오고, 기존 데이터는 백엔드 API를 호출하여 받아오도록 변경되었다. 이러한 방식으로 수정하면 Next.js의 App Router 구조를 준수하면서도 올바르게 데이터를 처리할 수 있다.

 

 

[Reference] Stack Overflow: 페이지 컴포넌트에서의 기본 내보내기 관련 에러

- 페이지 컴포넌트에서 기본 내보내기와 관련된 에러 사례와 해결 방법이 논의돼 있음

- Link: https://stackoverflow.com/questions/59873698/the-default-export-is-not-a-react-component-in-page-nextjs

 


 

3. ToastPopup 적용 후 무한 리렌더링 발생 → Token 만료 처리 수정으로 해결

 

눈 건강을 위해 빠르게 보고 스크롤 내리시길 추천드림

 

ToastPopup UI를 프로젝트에 한꺼번에 적용하면서 60개의 파일을 수정했다. 일부만 적용했을 땐 문제가 없었지만, 전체 적용 후 무한 리렌더링이 발생하는 이슈가 생겼다.

 

그때 당시의 상황은 다음과 같았다.

 

- 에러 메시지는 없었고, Build 후에도 정상적으로 실행됨

- 개발 서버에서만 무한 리렌더링 발생

- 다른 팀원에게 확인해 보니 사이트가 정상적으로 뜬다고 함

 

 

ToastPopup 컴포넌트가 사용되는 곳이 엄청 많아서 그걸 다 수정했는데, 갑자기 이 문제가 발생해서 무척 당황스러웠다.

 

이미 60개 이상의 파일을 수정했는데 Error Message도 안 뜨고, 내가 수정한 코드를 아무리 봐도 문제될 게 없는데 저렇게 무한 리렌더링이 돼버리니 귀신이 곡할 노릇이었다. 이 문제 때문에 2일은 날린 것 같다.

 

게다가 계속 새로고침 되니 Console도 볼 수가 없었다. 이것저것 다 해봤는데 도저히 문제를 모르겠어서 정말 아닐 것 같긴 하지만 최후의 수단으로 변경했던 모든 파일을 롤백하였다.

 

하지만 역시 정답이 아니었다.

잠깐 저 깜빡이는 화면을 계속 주시하면서 원인을 분석하였다.

 

 

모바일 비율로 보고 있던 개발 환경 사이트를 데스크톱 화면 비율로 맞춰 살펴보니, Navbar에서 프로필 사진이 떴다가 로그인/회원가입 버튼이 보였다가 사라지는 현상이 반복됐다. Navbar에서 토큰 만료 시의 처리가 잘못된 것 같았다.

 

바로 위 사진처럼 개발자 도구 > Application > Local Storage의 accessToken을 제거하니 무한 리렌더링이 멈췄다. 답을 찾았다.

 

 

해결 과정을 정리하면 다음과 같다.

 

1. Navbar 컴포넌트 코드 수정 → 로그인/회원가입 버튼이 정상적으로 바뀜

2. 개발자 도구(Application > Storage > Local Storage)에서 accessToken 제거 → 무한 리렌더링이 멈춤

3. 토큰 만료 처리 로직에서 문제 발생했음을 확인

 

 

최근 Back-end 팀에서 Refresh Token 버그 수정을 했으나, 이에 맞춰 Front-end 코드 수정이 되지 않았던 것이 원인

 

인증/인가를 구현하였던 Front-end 팀원이 아직 Token 만료 시의 처리 로직을 수정하지 않아서 만료된 accessToken이 남아 있을 때 Navbar가 계속해서 리렌더링되었던 것이다.

 

토큰 만료 시의 올바른 처리가 없었기 때문에 생긴 문제라 Token 만료 시의 처리 로직 수정하면 해결된다.

 

이를 통해서 Back-end API 변경 사항과 Front-end 코드의 동기화를 유지하는 것이 중요함을 다시 확인하고 깨닫게 되었다. 앞으로는 API 변경 사항이 있을 때 협업 프로세스를 강화하여, 이런 문제가 재발하지 않도록 해야겠다.

 

 

728x90