개발공부 임고미

[Next.js] state로 컴포넌트 보여줄때, 뒤로가기 히스토리에 쌓기 - useRouter이용해 뒤로가기 구현 본문

카테고리 없음

[Next.js] state로 컴포넌트 보여줄때, 뒤로가기 히스토리에 쌓기 - useRouter이용해 뒤로가기 구현

임고미 2022. 6. 28. 16:47
728x90
300x250

결론, path를 바꾸는방법으로history를 쌓을수도 있지만 쿼리를 바꿔줌으로써 스텍을 쌓을 수 있다.


일반적으로 경로를 바꿔가며 페이지가 전환되는경우에는 

push나 replace를 이용해서 히스토리를 쌓을지 말지를 결정할 수가 있습니다. 

push history stack o ( = 뒤로가기했을때 해당페이지를 보여줄 수 있게함)
replace history stack x ( = 뒤로가기 했을때 replace로 설정해줬던 페이지는 건너뛰게됨)

그런데, 경로를 바꾸지 않고 sate를 이용해서 컴포넌트를 렌더링하고 있을경우에 브라우저 이벤트인 뒤로가기를 할 경우에는 어떻게 컨트롤해야할까요 ?

문제 상황

홈 > 약관동의 페이지 > 약관상세 모달 > 브라우저 뒤로가기 이벤트 

텍스트만 보고 유추하기로는, 약관동의 페이지에서 상세모달로갔으니까, 약관 상세모달에서 뒤로가기 하면 약관동의 페이지가 보이지않을까? 싶겠지만 해당 모달은 isOpen과 같은 state로 보여줄지 말지를 컨트롤 하고 있었기에 history와는 아무 상관이 없어  실제 작동은 약관동의 페이지에서 뒤로가기를 한것처럼 작동되고 있었습니다.

정리하자면

문제 상황 홈 > 약관동의 페이지 > 약관상세 모달 > 브라우저 뒤로가기 이벤트가 발생했을때
추측 약관 동의 페이지로 이동
실제 홈으로 이동

 

해결 방법

history를 쌓는 방법은 path를 바꾸는 방법도 있겠지만, 쿼리를 세팅해줌으로도 쌓을 수 있습니다. 모달을 오픈할때 쿼리를 세팅해줌으로써 모달에서 뒤로가기 했을떄 리스트를 보여줄 수 있었습니다.

기본

import React, {useState} from 'react';
import { useRouter } from 'next/router';

const RouterTest = () => {
    const router = useRouter();
    const [isOpen, setIsOpen] = useState<boolean>(fasle);
    
    const handleOpenMadal = () => {
    	setIsOpen(true)
    }
    
      const handleCloseMadal = () => {
    	setIsOpen(true)
    }
    
    return (
    	<button onClick={handleOpenMadal}> 약관상세 보기 </button>
        {isOpen && 
        	<div> 
            	<p> 약관상세 모달입니다. </p>
            	<button onClick={handleCloseMadal}> 닫기 </button>
            </div> 
         }

    )

}
export default RouterTest;

클릭으로 모달 토글이 구현되어있는 코드입니다.

이제 뒤로가기 스텍을 쌓을 수 있는 코드를 추가해보겠습니다.

history stack 쌓기

import React, {useState} from 'react';
import { useRouter } from 'next/router';

const RouterTest = () => {
    const router = useRouter();
    const [isOpen, setIsOpen] = useState<boolean>(fasle);
    
    const handleOpenMadal = (termId : string) => {
    	setIsOpen(true)
        //새로추가
        router.push({ query: { termId: termId } });
    }
    
      const handleCloseMadal = () => {
    	setIsOpen(true)
    }
    
    return (
    	<button onClick={()=>handleOpenMadal(termId)}> 약관상세 보기 </button>
        {isOpen && 
        	<div> 
            	<p> 약관상세 모달입니다. </p>
            	<button onClick={handleCloseMadal}> 닫기 </button>
            </div> 
         }

    )

}
export default RouterTest;

이제 뒤로가기는 구현이 완료되었습니다. 쿼리 내용은 정해주기 나름이지만, url에 표시되는 내용인만큼 약관의 고유아이디를 넣어줬습니다.

그런데 한 가지 문제가 발생합니다.

뒤로가기는 되었지만, 다시 앞으로 돌아왔을떄 모달이 떠있습니다.

저희가 isOpen을 바꿔준적이 없기때문입니다.

모달 닫힐 수 있도록 세팅

import React, {useState} from 'react';
import { useRouter } from 'next/router';

const RouterTest = () => {
    const router = useRouter();
    const [isOpen, setIsOpen] = useState<boolean>(fasle);
    
    const handleOpenMadal = (termId : string) => {
    	setIsOpen(true)
        router.push({ query: { termId } });
    }
    
      const handleCloseMadal = () => {
    	setIsOpen(true)
    }
    
    // 새로추가
   const historyBackHandler = function () {
    	if (router.query?.termId) {
      		setIsOpen(false);
    	}
   };
  
  useEffect(() => {
    router.events.on('routeChangeStart', historyBackHandler);
    return () => {
      router.events.off('routeChangeStart', historyBackHandler);
    };
  }, [router.query]);
  
    return (
    	<button onClick={()=>handleOpenMadal(termId)}> 약관상세 보기 </button>
        {isOpen && 
        	<div> 
            	<p> 약관상세 모달입니다. </p>
            	<button onClick={handleCloseMadal}> 닫기 </button>
            </div> 
         }

    )

}
export default RouterTest;
routeChangeStart는 브라우저의 경로가 변경되기 시작할 때 발생하는 이벤트입니다.

 

바뀌기 시작했을때 historyBackHandler함수를 실행시켜주는 내용으로 
historyBackHandler 함수는 쿼리에 termsId가 있으면 모달을 관리해주는 state를 false로 바꿔서 모달을 닫아줍니다.

 

이제 거의다 끝났습니다! 이렇게 되면 이제 뒤로가기했을때의 이벤트는 완전히 구현되었습니다.

그러나 모달내 '닫기' 버튼을 클릭했을때에는 아직 버그가 남아있습니다. 모달만 닫히고 라우트가 변하지 않아서 뒤로가기를 2번해야 페이지를 벗어날 수 있게 됩니다.

예를 들어보겠습니다

보여지는 페이지 이벤트 path
약관동의 페이지   /terms
  약관 상세 클릭   
약관 상세 모달   /terms?termsId=1 
  약관 상세 모달내 닫기버튼  
약관동의 페이지   /terms?termsId=1 
  브라우저 이벤트 뒤로가기  
약관동의 페이지   /terms
  브라우저 이벤트 뒤로가기  
  /

이유는, 모달은 닫혔지만 path가 그대로이기 떄문입니다.

추가! tip! 
🙋🏻‍♀️ 뒤의 쿼리가 맘에들지 않으시나요 ? 방법이 있습니다

 next/router의 push는 다음과 같은 파라미터를 받습니다
push(url: Url, as?: Url, options?: TransitionOptions): Promise<boolean>; 

여기서 as 를 살펴보게되면,
(parameter) as: Url | undefined masks url for the browser 라는 문구를 볼 수 있습니다.

즉 

 router.push({ query: { termId } , 'term'}); 으로 해주면, 쿼리대신 /term 이라는 url로 보여집니다

이를 해결하기 위해선, 닫기버튼을 눌렀을때 route도 바꿔주면 됩니다.

import React, {useState} from 'react';
import { useRouter } from 'next/router';

const RouterTest = () => {
    const router = useRouter();
    const [isOpen, setIsOpen] = useState<boolean>(fasle);
    
    const handleOpenMadal = (termId : string) => {
    	setIsOpen(true)
        router.push({ query: { termId } });
    }
    
      const handleCloseMadal = () => {
    	setIsOpen(true)
        //새로 추가 (하나 전으로 돌아가기)
        router.back();
    }
    
   const historyBackHandler = function () {
    	if (router.query?.termId) {
      		setIsOpen(false);
    	}
   };
  
  useEffect(() => {
    router.events.on('routeChangeStart', historyBackHandler);
    return () => {
      router.events.off('routeChangeStart', historyBackHandler);
    };
  }, [router.query]);
  
    return (
    	<button onClick={()=>handleOpenMadal(termId)}> 약관상세 보기 </button>
        {isOpen && 
        	<div> 
            	<p> 약관상세 모달입니다. </p>
            	<button onClick={handleCloseMadal}> 닫기 </button>
            </div> 
         }

    )

}
export default RouterTest;

이것으로 state로 컴포넌트를 관리할때 뒤로가기 구현 완료입니다!

🙇🏻‍♀️

728x90
300x250
Comments