본문 바로가기

프로젝트/팀 개발일지

[개발일지/Quostomize] 24.12.04 - 카드혜택 변경 및 예약 API 연결 이슈 해결

✏️12.4(수) 한 일

1. 카드혜택 변경 및 예약 API 연결 이슈

Error 발생 이미지

📌 문제 인식

  • 카드혜택 변경 기능에서 일부 혜택만 선택했을 때 데이터가 제대로 insert 되지 않는 문제가 있다고 전달 받았고, 이를 해결하는 것이 목표였습니다.
    • 이는 다른 팀원이 벌써 일주일 넘게 진행한 이슈였습니다.
    • 프론트엔드에서 null 예외 처리를 하고 있음에도 불구하고, 하위 카테고리가 null이 되는 것 때문에 문제라고 전달 받았습니다.
  • 해당 문제를 해결하기 위해 해당 백엔드 API를 개발한 경험을 바탕으로, 제가 직접 프론트엔드 API 연결 이슈를 처리하기로 했습니다.

 

📌 문제 분석

  • JavaScript 난독증 발생...!
    • 조금이라도 복잡하거나 이해가 안 되는 로직이 있을 경우 이게 대체 무슨... 동작을 하는 코드인지, 왜 이렇게 짠 건지 이해하기가 어려웠습니다.
    • 백엔드 API는 정상적으로 작동하는 것 같은데, 어떤 문제 때문에 프론트단에서 데이터 insert가 안 되는 것인지 테스트하기 어려웠습니다.
  • 해결 방안 설정
    • 우선 선택하지 않은 혜택의 BenefitRate를 0으로 처리하고, 상위 혜택만 선택할 수 있도록 로직을 수정하는 방향으로 문제를 해결하려 했습니다.
    • null 값 처리와 관련된 문제는 어떻게 해결할지 고민하며 로직을 구상했습니다.
  • 문제 해결 로직
    • BenefitRate가 0인 경우에도 유효한 값으로 처리
    • 상위 혜택만 선택하는 경우, 상위 혜택만 처리하고 하위 혜택은 null로 처리

 

📌 해결 과정

✅ 일부 혜택만 선택 가능하게 수정

  • BenefitRate 처리 방식 수정
    • 선택하지 않은 혜택이 있을 때도 우선 BenefitRate가 0이 되도록 처리하였습니다.
  • 하위 카테고리 null 처리
    • 분명 null 처리 코드가 분명히 있다고 했으나, 막상 동작하는 것을 살펴보니 없었습니다...!!!!
    • DB에서도 null값을 받는 것도 문제가 없었습니다.
    • 따라서 프론트엔드에서 하위 카테고리가 null일 때 이를 처리할 수 있도록, || null을 추가했습니다.
  • AS-IS
const requestBody = selectedCategories.map((upperCategoryId, index) => ({
  benefitEffectiveDate: formattedDate,
  benefitRate: categoryValues[index] - 1,
  isActive: true,
  cardSequenceId,
  upperCategoryId: upperCategoryId,
  lowerCategoryId: selectedOptions[index],
  secondaryAuthCode: authCode,
}));
  • TO-BE
const requestBody = selectedCategories
    .map((upperCategoryId, index) => {
      const benefitRate = Math.max(0, categoryValues[index] - 1);
      if (upperCategoryId === null) return null;
      return {
        benefitEffectiveDate: formattedDate,
        benefitRate,
        isActive: true,
        cardSequenceId,
        upperCategoryId,
        lowerCategoryId: selectedOptions[index] || null,
        secondaryAuthCode: authCode,
      };
    })
    .filter(Boolean);

 

 

✅ 불필요한 예외 처리 삭제

  • 카드혜택 변경 기능에서 HTTP 204(no Content)로 반환되므로 response.text() 호출 및 응답 본문에 대한 예외 처리가 불필요했습니다.
  • page.js에서 response.text에 대한 예외 처리가 불필요하게 되어 있었기 때문에 이를 제거하고, 불필요한 코드들을 간소화했습니다.
  • 삭제한 예외 처리 코드
const responseText = await response.text();
if (!responseText) {
  throw new Error("Empty response body");
}

const result = JSON.parse(responseText);
console.log(result);

setAuthSuccess(result.status === 400 ? "400" : "200");

 

 

✅ 렌더링 위치 변경

  • React 컴포넌트 내부의 Hooks 사용 규칙에 맞추기 위해 렌더링 위치를 변경했습니다.
  • React에서는 컴포넌트가 렌더링되는 과정에서 return 문을 조건부로 처리할 때, useState, useEffect와 같은 Hook들이 상단에 위치해야 합니다.
  • 이를 위반하면 Hook이 예상대로 동작하지 않거나, 렌더링 문제를 일으킬 수 있습니다.
  • 핵심 변경 사항
    • 렌더링 위치 변경
      : if (!isOpen) return null; 을 컴포넌트 반환 이전에 조건문으로 처리하던 방식에서, Hooks 호출 이후에 return <></>;로 수정하여 Hooks가 정상적으로 자동하도록 했습니다.
    • 조건문 내에서 Hook 호출 방지
      : React 컴포넌트 내에서는 Hooks (useState, useEffect)가 컴포넌트의 최상단에서 호출되어야 하므로, 조건문 내에서 렌더링을 막는 부분을 변경하여 Hooks 호출이 컴포넌트 바깥에서 이루어지게 하여 React의 Hook 사용 규칙을 준수했습니다.
  • AS-IS
const SecondAuthModal = ({ isOpen, onClose, onComplete, isConfirm }) => {
    if (!isOpen) return null;  // isOpen이 false일 때 컴포넌트를 렌더링하지 않음
    const [authCode, setAuthCode] = useState("");  // Hook들이 조건문 뒤에 있음
    ...
    };
  • TO-BE
const SecondAuthModal = ({ isOpen, onClose, onComplete, isConfirm }) => {
    const [authCode, setAuthCode] = useState("");
    const [shuffledNumbers, setShuffledNumbers] = useState([]);
    ...
    if (!isOpen) return <></>;
    };

 


✏️12.4(수) 발생한 이슈

1. React 컴포넌트 내부 Hooks 사용 규칙

📌 훅은 항상 동일한 순서로 호출되어야 한다

  • 위 내용을 대부분의 팀원이 잘 모르고 있었고(저 포함), 이를 준수하지 않아서 에러가 발생하는 경우가 더러 있었습니다.
  • 따라서 에러를 해결하면서 아래 내용을 공부할 수 있었습니다.

 

✅ React 18

  • 이때부터 정적 렌더링 기능이 도입되었습니다.
    • Hooks 호출 순서 유지
      : Hooks는 항상 동일한 순서로 호출되어야 합니다. 이는 React의 내부 상태 관리 메커니즘과 직접적으로 연관되어 있습니다.
    • 최상위 레벨에서만 Hooks 호출
      : Hooks는 React 함수 컴포넌트의 최상위 레벨에서만 호출해야 합니다. 조건문, 루프, 중첩 함수 내부에서 Hooks를 호출하면 안 됩니다.
    • 조건부 렌더링 시 주의사항
      : React 18의 정적 렌더링 기능과 관련된 내용은 매우 중요합니다. 조건부 early return을 사용할 때는 모든 Hooks 호출 이후에 배치해야 합니다.
  • 이러한 규칙을 위반하면 'Rendered fewer hooks than expected'와 같은 내부 React 오류가 발생할 수 있습니다. (React 내부 최적화 기능이 제대로 작동하지 않고, 예기치 않은 오류가 발생할 수 있음)
  • 따라서, 렌더링을 조건문으로 결정할 때는 훅 호출 이후에 조건문을 배치해야 합니다.

 

✅ 정리해 보면...

 

  • React 훅 규칙: 훅은 컴포넌트 렌더링 중 항상 호출되어야 하며 호출 순서가 변하면 안 됩니다.
  • AS-IS 문제: 조건문에 의해 훅 호출이 생략되어 순서가 변합니다.
  • TO-BE 해결: 조건문을 훅 호출 이후로 이동하여 순서 보장됩니다.

 


⏰ 내일 목표

✅ 프론트엔드 코드 리뷰하기


👀 프로젝트가 궁금하다면

반응형