1. 문제 발생❓
- be, fe, ai 세 개의 페이지 설명회 모달 작업 중
- 세 페이지 모두 remoteConfig 또는 orientation 값이 null일 경우 모달이 표시되지 않도록 조건부 렌더링을 설정했습니다.
- be, fe 페이지는 필요한 데이터(remoteConfig, orientation)를 정상적으로 받아와 설명회 모달이 표시됩니다.
- ai 페이지는 현재 설명회가 없어 관련 데이터가 전달되지 않으므로, 모달이 표시되지 않는 것이 의도한 동작입니다.
- ai 페이지에서는 예상대로 모달이 표시되지 않았지만, ai → be 또는 ai → fe로 페이지 전환 시 모달이 정상적인 위치에서 나타나지 않고 즉시 표시되는 현상이 발생했습니다.
- 이 과정에서 세 페이지의 팝업 상태를 하나의 아톰(atom)으로 관리하고 있다는 구조적 문제도 발견했습니다. + 다음 블로그에서 작성!
2. 원인 추론 🔎
- AI 페이지에서는 remoteData와 orientationData가 null이기 때문에 모달이 표시되지 않는 것이 정상적인 동작!
- 그래서 콘솔로 isAlreadyPopup 상태값을 확인해보니 AI 페이지에서도 이 값이 true로 변경되는 현상을 발견했습니다.
- isAlreadyModal은 모달이 사용자에게 한 번 표시되었는지(true) 또는 **아직 표시되지 않았는지(false)**를 관리하는 상태값입니다.
- AI 페이지에서는 실제로 모달이 화면에 표시되지 않았음에도 불구하고 isAlreadyModal 값이 true로 변경되고 있었습니다.
- 이로 인해 AI 페이지에서 BE/FE 페이지로 이동했을 때, 시스템은 이미 모달이 표시되었다고 판단하여 정상적인 모달 표시 로직이 실행되지 않는 문제가 발생했습니다.
- ai 페이지에서는 모달이 뜨지 않으니 isAlreadyModal이 true 값으로 변경되면 절대 안됨 => 이것을 해결해야 함!
const { remoteData, orientationData } = useOrientationBanner(
COURSE_DETAIL[course].koreanName,
);
const [modal, setModal] = useAtom(orientationModalAtom);
const scrollY = useWindowScrollY();
const [isAlreadyModal, setIsAlreadyModal] = useAtom(isAlreadyModalAtom);
useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
});
if (isNil(remoteData) || isNil(orientationData)) {
return null;
}
return // 해당 모달 디브
3. 해결 과정 📋
1. 호출 순서를 바꿔봄
: 초기에 데이터가 없으면 return null을 해 커스텀 훅이 실행되지 않게 해봄
javascript
if (isNil(remoteData) || isNil(orientationData)) {
console.log('remoteData 또는 orientationData이 null이므로 컴포넌트 반환하지 않음');
return null;
}
useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
});
- React Hooks 규칙 위반: 조건부(if문 이후)로 훅을 호출하고 있음
- ESLint 에러: React Hook "useScrollToTriggerModal" is called conditionally. React Hooks must be called in the exact same order in every component render.
2. useScrollToTriggerModal 훅 분석
: 이 함수를 호출하는 부분을 살펴보면 커스텀 훅 밑에 if 조건문이 있으니 데이터가 없어도 커스텀 훅을 실행될 것입니다.
useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
});
if (isNil(remoteData) || isNil(orientationData)) {
return null;
그래서 커스텀 훅을 살펴보았습니다:
- showModal 파라미터가 기본값 true로 설정되어 있었습니다.
- 이로 인해 첫 번째 조건에서 안걸리기 때문에 로직이 실행되었습니다.
- 로직 실행 결과 => setIsAlreadyModal이 true값으로 바뀜
javascript
function useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
showModal = true,
}: UseScrollToTriggerModalProps) {
useEffect(() => {
const innerHeight = window.innerHeight;
if (!reachingDiv || isAlreadyModal || !showModal) {
return;
}
if (!isAlreadyModal && scrollY + innerHeight >= reachingDiv) {
setModal(true);
setIsAlreadyModal(true);
}
}, [scrollY, reachingDiv]);
}
그래서 이 ai 페이지에서는 로직이 실행되면 안되니깐
- 커스텀 함수 호출 부분에서 showModal에 조건문을 걸어주었습니다.
useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
showModal: !isNil(remoteData) && !isNil(orientationData)),// 핵심 수정
});
if (isNil(remoteData) || isNil(orientationData)) {
return null;
}
초기에 showModal 파라미터를 활용하여 데이터가 없을 때 로직이 실행되지 않도록 설정
- 데이터가 없을 시 false값으로 내려와 첫 번째 조건에서 걸리게 되어 isAlreadyPopup이 true값으로 변경되지 않을 것입니다!!
- 이로 인해 AI 페이지에서는 isAlreadyModal 상태가 변경되지 않아, 페이지 전환 시 모달이 예기치 않게 표시되는 문제를 해결했습니다.
function useScrollToTriggerModal({
scrollY,
reachingDiv,
isAlreadyModal,
setModal,
setIsAlreadyModal,
showModal = true,
}: UseScrollToTriggerModalProps) {
useEffect(() => {
const innerHeight = window.innerHeight;
if (!reachingDiv || isAlreadyModal || !showModal) {
return;
}
if (!isAlreadyModal && scrollY + innerHeight >= reachingDiv) {
setModal(true);
setIsAlreadyModal(true);
}
}, [scrollY, reachingDiv]);
}
4. 결론 💬
- React Hook는 항상 조건문 이전에 호출해야 합니다.
- 디버깅 시 콘솔로그를 전략적으로 배치하여 데이터 상태 변화를 추적하는 것이 중요합니다!
'인턴' 카테고리의 다른 글
[ Troubleshooting🛠️ ] 여러개의 Atom을 하나로 통합 (0) | 2025.04.30 |
---|---|
[ Troubleshooting🛠️ ] 모바일 Swiper에서 absolute 요소가 깜박이는 현상 해결 (0) | 2025.04.28 |
[ Troubleshooting🛠️ ] sticky 포지션과 overflow: hidden 충돌 해결하기 (0) | 2025.04.21 |
Zustand 완전 정복 가이드 (0) | 2025.04.15 |
[성능 최적화⚙️] Set 활용해보기 (1) | 2025.04.14 |