인계받은 프로젝트를 확인하던 중 재밌는 상황을 발견했습니다.
로그인 후 홈 화면에서 뒤로가기 버튼을 누르면, 다시 로그인 페이지로 돌아가는 문제였습니다. 상단의 GNB(글로벌 네비게이션 바)를 보면, 로그인 상태임이 분명한데, 콘텐츠 영역에서 로그인 페이지가 표시되는 부자연스러운 상황이었습니다.
생각해보면, 로그인 상태에서는 뒤로가기를 눌러도 로그인 페이지가 표시되지 않는 것이 자연스러운 흐름일텐데, 이 상황은 UX와 보안 측면에서 정상적인 서비스 플로우는 아니라고 판단하여 사용자가 뒤로가기를 눌러도 로그인 페이지로 돌아갈 수 없도록 로그인 페이지 접근 제한을 구현하기로 했습니다.
해결 방법
처음에는 토큰 유무를 확인해 리다이렉트 처리하는 방법으로 접근했습니다.
이전 프로젝트에서도 이런 방식으로 권한 별 접근 제한을 구현했었기에, 익숙한 방식으로 생각했던 것이죠.
그런데 이런 복잡한 작업 없이 단 한 줄로 해결 가능한 방법이 있었습니다.
바로 router.push 대신 router.replace를 사용하는 것이었습니다.
//기존 코드
router.push('/home');
//수정 후 코드
router.replace('/home');
코드 한 줄만 바꿨을 뿐인데, 이제 홈 화면에서 뒤로가기를 눌러도 로그인 페이지로 돌아갈 수 없게 되었습니다.
router.push와 router.replace의 차이
Next.js에서 router.push와 router.replace는 클라이언트 사이드 네비게이션을 위한 메서드입니다. 하지만 브라우저 히스토리 스택과 관련해 중요한 차이가 있습니다.
히스토리 스택은 브라우저가 페이지 탐색 기록을 관리하는 구조입니다. 사용자가 방문한 페이지를 쌓아두며, 뒤로가기/앞으로가기 등으로 이전 기록으로 돌아갈 수 있게 합니다.
router.push
router.push는 현재 히스토리 스택에 새로운 항목을 추가합니다. 따라서 뒤로가기를 누르면 이전 페이지로 접근 할 수 있습니다. 일반적인 페이지 이동에 적합한 네비게이션 방식입니다.
router.replace
router.replace는 현재 히스토리 스택을 덮어씁니다. push처럼 스택을 추가하는게 아니어서 뒤로가기를 눌러도 이전 페이지로 돌아가지 않고, 이전 페이지의 이전 페이지로 접근하게 됩니다. 보안 또는 UX 관점에서 이전 페이지로 돌아가면 안 되는 상황에 적합합니다.
직접 히스토리 스택을 비교해보면서 확인해봅시다. 히스토리 스택 길이는 window.history로 확인 가능합니다.
"use client";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default function Home() {
const router = useRouter();
useEffect(() => {
console.log(`히스토리 스택: ${window.history.length}`);
}, []);
return (
<div>
<h1>로그인 페이지</h1>
<div>router.push 히스토리 스택</div>
<button
className="bg-red-400"
type="button"
onClick={() => router.push("/main")}>
로그인
</button>
</div>
);
}
router.push를 사용하는 로그인 페이지와 히스토리 스택 출력 코드를 작성해보았습니다. 위 코드를 실행하고, 히스토리 스택을 확인해보면 다음과 같습니다.
1. router.push 테스트
경로: 브라우저 새 탭 -> localhost:3000 -> localhost:3000/main-> [뒤로가기] -> localhost:3000
- 새 탭을 띄우고, localhost:3000으로 접근하면 히스토리 스택은 2입니다.
- 로그인 버튼을 클릭하여 /main으로 이동하면 히스토리 스택이 3으로 증가합니다.
- 뒤로가기를 누르면 다시 localhost:3000으로 돌아가며, 히스토리 스택은 3을 유지합니다.
다음으로는 router.replace의 히스토리 스택입니다. 위의 코드에서 router.push를 router.replace로 변경하고 실행해봅시다.
2. router.replace 테스트
경로: 브라우저 새 탭 -> localhost:3000 -> localhost:3000/main-> [뒤로가기] -> 새 탭
- 로그인 버튼을 눌러도 히스토리 스택이 증가하지 않고 2 유지
- 뒤로가기를 눌러도 로그인 페이지로 돌아가지 않고 탭 초기 상태로 이동
replace는 페이지를 이동하는 것이 아니라, 현재 페이지를 바꿔주는 개념(히스토리를 덮어씀)으로 이해하면 될 것 같습니다.
router.replace를 통해 로그인 페이지로 돌아가는 문제를 간단하게 해결할 수 있었습니다.
이번 경험을 통해 작은 코드 수정 하나가 서비스 흐름을 얼마나 크게 개선할 수 있는지 경험했습니다.. 항상 습관적으로 router.push부터 치고 봤는데, 앞으로는 페이지 흐름을 매끄럽게 만드는 디테일을 고민하여 코드를 작성해야겠습니다.