v2.0 에서 만든 게스트 모드가 어느 화면에도 안 붙어 있던 사고 — POP-SPOT v2.5 게스트 재배선
v2.0 이후 v2.1~v2.4 거치며 게스트 기능이 어느 화면에도 안 붙어있었던 사건. 인트로 D-N 필 + 메인 만료 게이트 + signup 배너 세 곳을 다시 연결.
v2.0 에서 만들어둔 7 일 게스트 기능이, v2.1·v2.2·v2.3 의 인트로 재시도·롤백 과정에서 양쪽 화면 모두 연결이 끊겼다. 구현은 있는데 어디에서도 안 보이는 상태였음.
이 글에서 다루는 것
모르는 단어 한 줄로
| 용어 | 한 줄 설명 |
|---|---|
| 재배선 | 기능은 이미 있는데 화면별 호출을 다시 연결해주는 작업. 끝난 케이블을 다시 꽂아주는 이미지 |
| D-N pill | "D-7" 처럼 작은 알약 형 표시 라벨 |
| 만료 게이트 | "7일 지났으멈 이제부터는 가입해야" 를 강제하는 관문 |
무엇이 바뀌었나
| 항목 | v2.4 | v2.5 |
|---|---|---|
| 인트로 우상단 | 로그인/Skip 버튼만 | 라임 그린 라벨로 "D-7"~"D-1" 표시 (시계 아이콘) |
| 비로그인 메인 진입 | 무제한 둘러볼 수 있음 | 7일 지났으면 자동으로 회원가입 페이지로 |
| 회원가입 페이지 | 빈 폼만 | "7일 무료 체험이 끝났어요" 배너 + "30초면 끝" 카피 |
왜 이렇게 했음
사고의 원인 — v2.0 은 인트로 첫 섹션에 있던 "게스트로 둘러보기" 버튼으로 게스트 세션을 시작했는데, v2.1·v2.2 가 인트로를 계속 재설계하면서 그 버튼이 사라졌고, v2.3 로 롤백할 때도 이전판으로만 돌아가고 게스트 버튼을 다시 연결하는 게 빠졌다. 동시에 v2.0 은 "비로그인 메인 진입 = 자동 게스트 시작" 이라 메인에 게스트 진입 버튼 자체가 필요 없었는데, v2.4 에서 읽는 소개 흐름이 바뀌면서 메인 진입의 구현이 끊어졌음.
메인에서 D-N 구현 — 인트로에만 표시하면 매단 다시 인트로를 거쳐야 남은 일수를 확인할 수 있어 불편함. 메인 상단에 항상 따라다니는 필을 둡다. 남은 일수가 1·2 일이면 조금 진한 색으로 넘겨서 주의를 끌어주는 게 광고 같지 않아서 아주 조용하게 둔다.
signup 배너 — "그냥 가입하세요" 는 흐름이 없다. "7일 무료 체험이 끝났어요" 는 일종의 완료 연속성. 그 아래 "30초면 끝" 은 소요 시간의 구체적 제시.
코드로는 어떻게 (필요한 부분만)
파일: popspot-frontend/src/app/intro/page.tsx 의 우상단 상태 바
// v2.5 — intro/page.tsx 의 우상단 D-N 알약 표시
const { active, daysLeft } = useGuestMode();
// 위: v2.0 의 커스텀 훅 — active(남은 일수 있음) 와 daysLeft(남은 일 수) 구조분해
// 이제서야 쓰이기 시작 — v2.0에서 만들어 놓고 아무 화면에서도 호출 안 되고 있던 그 훅
{active && (
// 위: 논리·AND 명제 — active=true 일 때만 표시. 로그인 사용자·게스트 만료 때는 숨김
<span className="px-3 py-1 text-xs rounded-full
bg-lime-400/20 text-lime-200 flex items-center gap-1">
{/* 위: Tailwind 클래스 조합 — pill 형 알약 배지 */}
{/* px-3 py-1 — 좌우 12px / 상하 4px 안여백 */}
{/* text-xs — 작은 글자 (0.75rem) */}
{/* rounded-full — border-radius:9999px — 알약 형태 (pill) */}
{/* bg-lime-400/20 — 라임 그린 20% 불투명 배경. 은은한 색 */}
{/* text-lime-200 — 그린 계열 연한 글자. 배경 대비용 */}
{/* flex items-center gap-1 — 아이콘·글자 가로 정렬 + 4px 간격 */}
<ClockIcon className="w-3 h-3" />
{/* 위: 12px 시계 아이콘. "남은 시간" 시각적 힌트 */}
D-{daysLeft}
{/* 위: D-7 ~ D-1 레이블 (텍스트). daysLeft 에 숫자 그대로 매핑 */}
</span>
)}파일: popspot-frontend/src/app/page.tsx (메인 페이지 최상단)
// v2.5 — app/page.tsx 최상단: 만료 게이트 (7일 지난 비로그인 차단)
const { active } = useGuestMode();
// 위: 게스트 활성 여부만 필요 — daysLeft 는 이 화면에서 표시 안 함
const { user } = useAuth();
// 위: 로그인 사용자 정보. user 가 truthy 이면 로그인 완료 상태
useEffect(() => {
if (!user && !active) {
// 위: 로그인 안 했고 (!user) AND 게스트도 만료됨 (!active)
// → 둘러볼 권한 없음 → 강제 회원가입
router.replace('/signup?reason=guestExpired');
// 위: replace 는 브라우저 히스토리에 현재 페이지 남기지 않음 (뒤로가기로 돌아올 수 없음)
// reason=guestExpired — signup 페이지가 이 이유를 읽고 해당 배너를 띄우도록 신호 전달
}
}, [user, active]);
// 위: 의존성 배열 [user, active] — 이 둘 중 하나라도 변하면 재실행
// 예: 게스트 자연 만료(이 흐름에서는 발생 안 함) / 로그아웃 후 재돌입 시파일: popspot-frontend/src/app/signup/page.tsx 상단 배너 부분
// v2.5 — signup/page.tsx 조건부 배너
const reason = useSearchParams().get('reason');
// 위: Next.js 훅으로 현재 URL 의 쿼리스트리링 읽기
// reason 파라미터 값을 가져옴 (예: 'guestExpired')
// 쿼리가 없으면 null
{reason === 'guestExpired' && (
// 위: 일치할 때만 배너 렌더링
<div className="mb-6 p-4 rounded-xl bg-amber-50 dark:bg-amber-900/20">
{/* 위: 테일윈드 클래스 조합 */}
{/* mb-6 — 아래에 24px 마진 (폼과 간격) */}
{/* p-4 — 안여백 16px */}
{/* rounded-xl — 모서리 둥글게 (반경 12px) */}
{/* bg-amber-50 — 라이트 모드: 아주 연한 엠버 (부드러운 느낌) */}
{/* dark:bg-amber-900/20 — 다크 모드: 진한 엠버 20% 불투명 */}
7일 무료 체험이 끝났어요. 30초면 끝나요.
{/* 위: 완료·소요 연속성을 담은 카피. 장황한 설명 없이 단축 완료 유도 */}
</div>
)}핵심 파일: popspot-frontend/src/app/intro/page.tsx, popspot-frontend/src/app/page.tsx, popspot-frontend/src/app/signup/page.tsx — 기존 guestMode.ts + useGuestMode 훅을 그대로 재활용.
교훈
기능을 만들고 UI 연결만 적어둔 방식은 "다음 달에도 같은 화면이 존재한다" 는 전제를 깔고 있다. 큰 리디자인 이후에는 "기능·버튼 연결 체크리스트"를 다시 점검해야 함.