본문 바로가기
React Native

react-native-magic-modal 이중 모달 사용 시 첫번째 모달 닫히지 않는 이슈 / 백핸들러 안되는 이슈 해결

by 어느새벽 2025. 12. 31.
반응형

게시글 제목을 어떻게 써야할지도 모르겠는데 어떻게든 내가 삽질하다가 구현 완료된거를 공유하고 싶었다

 

우선 내가 원했던 플로우는

로그인 화면 -> 회원가입 이동 -> 즉시 약관동의 바텀시트 띄움

1. 약관 필수 동의 모두 체크 -> 완료 -> 회원가입 진행
2. 필수 동의 미체크 -> 완료 -> 필수 체크 안내 알림 모달 띄움 -> 확인
 -> 다시 1번 진행 or 뒤로가기 백핸들러 실행 -> 바텀시트 닫히고 로그인 화면 이동
3. 필수 동의 미체크로 백드랍 터치 시 바텀시트 닫히지 않게 방어
4. 아무것도 동작하지 않고 백핸들러 작동하면 바텀시트 닫히고 로그인 화면 이동

 

그런데 문제 상황은

로그인 화면 -> 회원가입 이동 -> 즉시 약관동의 바텀시트 띄움

1. 약관 필수 동의 모두 체크 -> 완료 -> 회원가입 진행
2. 필수 동의 미체크 -> 완료 -> 필수 체크 안내 알림 모달 띄움 -> 확인
 -> 다시 1번 진행 or ** 뒤로가기 백핸들러 작동 안함 **
3. 필수 동의 미체크로 백드랍 터치 시 바텀시트 닫히지 않게 방어
4. 아무것도 동작하지 않고 백핸들러 작동하면 바텀시트 닫히고 로그인 화면 이동

 

2번에서 필수 체크 안내 모달이 뜬 후에는 백핸들러 버튼을 아무리 눌러도 아무런 반응이 없다는 것이었다.

 

일단 해결법부터 설명하자면

 

//import문 생략
import { magicModal } from "react-native-magic-modal";

export default function SignUpScreen() {
  const router = useRouter();
  const agreeToTermsRef = useRef<AgreeToTermsRef>(null);
  const isAgreedRef = useRef(false);

  const methods = useForm<Signup>({
    resolver: zodResolver(signupSchema),
    mode: "onChange",
    defaultValues: {
      step: "stepOne",
      loginId: "",
      email: "",
      emailConfirm: "",
      password: "",
      passwordConfirm: "",
      nickname: "",
    },
  });
  const { watch } = methods;
  const step = watch("step");

  const showTermsBottomSheet = () => {
    openBottomSheet(
      {
        title: "약관 동의",
        content: () => <AgreeToTermsAndConditionsModal ref={agreeToTermsRef} />,
        buttons: [
          {
            text: "완료",
            onPress: () => {
              const isValid = agreeToTermsRef.current?.validate();

              if (!isValid) {
                openModal({
                  title: "필수 항목 동의가 필요해요",
                  content: "모든 필수 약관 항목을 확인 후 동의해주세요",
                  buttons: [
                    {
                      text: "확인",
                      secondary: true,
                      onPress: () => {
                        magicModal.hideAll();
                        showTermsBottomSheet();
                      },
                    },
                  ],
                });
                return false;
              }

              isAgreedRef.current = true;
            },
          },
        ],
      },
      true,
      {
        onBackButtonPress: () => {
          if (!isAgreedRef.current) {
            openModal({
              title: "약관 동의가 필요해요",
              content: "회원가입을 위해서는 필수 약관에 동의해야 합니다.\n정말 나가시겠어요?",
              buttons: [
                {
                  text: "나가기",
                  secondary: true,
                  onPress: () => {
                    magicModal.hideAll();
                    router.back();
                  },
                },
                { text: "취소" },
              ],
            });
            return true;
          }
          return false;
        },
        onBackdropPress: () => {
          if (!isAgreedRef.current) return true;
          return false;
        },
      },
    );
  };

  useEffect(() => {
    showTermsBottomSheet();

    return () => {
      magicModal.hideAll();
    };
  }, []);

  return (
    <View className="flex-1">
      <FormProvider {...methods}>
        {step === "stepOne" && <SignupForm />}
        {step === "stepTwo" && <NicknameForm />}
        {step === "stepThree" && <CompleteForm />}
      </FormProvider>
    </View>
  );
}

 

약관 동의 체크를 하지 않고 바텀시트의 완료 버튼을 누르면 알림 모달이 뜨고, 다시 확인 버튼을 누른 후 백핸들러를 동작시킬 때

회원가입 진행하지 않고 나가는지 알림 모달을 다시 띄우는 방법이다.

 

분기처리 내용을 다시 자세히 보면

 

const showTermsBottomSheet = () => {
    openBottomSheet(
      {
        title: "약관 동의",
        content: () => <AgreeToTermsAndConditionsModal ref={agreeToTermsRef} />,
        buttons: [
          {
            text: "완료",
            onPress: () => {
              const isValid = agreeToTermsRef.current?.validate();

              if (!isValid) {
                openModal({
                  title: "필수 항목 동의가 필요해요",
                  content: "모든 필수 약관 항목을 확인 후 동의해주세요",
                  buttons: [
                    {
                      text: "확인",
                      secondary: true,
                      onPress: () => {
                      // 현재 떠 있는 모든 모달과 바텀시트를 전부 닫음
                        magicModal.hideAll();
                      // 약관 바텀시트만 다시 즉시 띄움
                        showTermsBottomSheet();
                      },
                    },
                  ],
                });
                // 바텀 시트를 닫지 말라는 신호
                return false;
              }

              isAgreedRef.current = true;
            },
          },
        ],
      },
      // 기본 닫힘(backdrop, back button) 허용
      true,
      {
        onBackButtonPress: () => {
          if (!isAgreedRef.current) {
            openModal({
              title: "약관 동의가 필요해요",
              content: "회원가입을 위해서는 필수 약관에 동의해야 합니다.\n정말 나가시겠어요?",
              buttons: [
                {
                  text: "나가기",
                  secondary: true,
                  onPress: () => {
                  // 모든 모달 닫고
                    magicModal.hideAll();
                  // 이전 화면 이동
                    router.back();
                  },
                },
                { text: "취소" },
              ],
            });
            // 백버튼으로 바텀시트 닫는 걸 막음
            return true;
          }
          // 약관 동의 완료 상태면 바텀시트 닫아도 됨
          return false;
        },
        onBackdropPress: () => {
        // 동의 안 했으면 닫힘 차단
          if (!isAgreedRef.current) return true;
        // 동의 했으면 닫힘 허용
          return false;
        },
      },
    );
  };

 

좀 복잡해서 나도 아직 헷갈리긴 한데 계속 해보면서 이해하면 될 것 같다 ! 

반응형