Skip to content

인증 게이트 (AuthGate) ​

로그인 여부에 따라 화면을 세 갈래로 가르는 껍데기 컴포넌트입니다. 눈에 보이는 것이 없어 Playground가 없고, 대신 지금 앱들이 이걸 안 쓰고 무엇을 쓰는지가 이 페이지의 핵심입니다.

Alpine 컴포넌트 · Web · ⚠️ 현재 쓰이는 곳 없음 · 진행 중(WIP)

등록돼 있지만 어느 앱도 쓰지 않습니다

authGate는 플러그인이 등록하고 packages/components가 내보내지만, 레포 전체 .html 에서 x-data="authGate" 가 0건입니다(빌드 산출물 포함). 아홉 앱은 전부 라우터 기반 뷰 전환으로 같은 일을 합니다. 새 화면을 만들 때 이 컴포넌트를 고르기 전에 아래를 먼저 읽으세요.

규칙 (강제) · 코딩 에이전트용

AuthGate 강제 규칙. 상세는 아래 본문.

⚠️ 트랩 ① x-html 로 폼을 주입하면 안 됩니다 이 컴포넌트의 주석이 굵게 적어 둔 유일한 제약입니다. Alpine은 x-html로 넣은 HTML 안의 디렉티브를 처리하지 않습니다. 로그인·가입 폼 HTML은 반드시 마크업에 직접 인라인해야 합니다. 넣으면 에러 없이 폼이 통째로 죽습니다.

⚠️ 트랩 ② 모드 전환은 스코프 체인으로 부릅니다 안쪽 loginForm·registerForm에서 @click="switchToRegister()"처럼 그냥 부릅니다 — Alpine의 스코프 체인이 바깥 authGate의 메서드까지 올라가 찾습니다. $parent 같은 것을 쓰지 마세요.

⚠️ 트랩 ③ 라우터 기반 앱에 섞어 쓰지 마세요 아홉 앱은 $store.router.path === '/signin' 같은 조건으로 뷰를 가릅니다. 거기에 authGate를 얹으면 인증 상태를 두 곳이 따로 판단하게 되고, 라우터의 handleAuthRedirect()와 어긋나면 화면이 깜빡이거나 빈 채로 남습니다.

무엇을 하나 ​

로그인 상태를 읽어 세 갈래로 가릅니다. 그리는 것은 없고, 무엇을 그릴지 고르는 조건만 줍니다.

ready · authenticated 둘 다 스토어를 그대로 읽는 getter이고, 자기 상태는 mode 하나뿐입니다.

지금 상태 ​

어느 앱도 쓰지 않습니다. 아홉 앱은 라우터 기반 뷰 전환으로 같은 일을 합니다.

html
<!-- 실제 앱이 쓰는 방식 — 경로로 뷰를 가른다 -->
<template x-if="!$store.hereby.isLoading && $store.router.path === '/signin'">
  <!-- 로그인 화면 -->
</template>

두 방식의 차이는 이렇습니다.

AuthGate라우터 기반 (실제)
로그인/가입 전환mode 상태경로(/signin ↔ /signup)
주소안 바뀜바뀜 (뒤로 가기·북마크·새로고침 됨)
강제 비밀번호 변경다루지 않음handleAuthRedirect()가 /change-password로 보냄
비밀번호 찾기자리가 없음별도 경로

라우터 쪽이 다루는 것이 더 많습니다 — 특히 강제 비밀번호 변경과 비밀번호 찾기는 AuthGate의 세 갈래에 자리가 없습니다. 인증 화면이 넷(로그인 · 가입 · 찾기 · 변경)으로 늘어나면서 두 갈래(login/register)로는 부족해진 것으로 보입니다.

두 방식을 섞지 마세요

라우터 기반 앱에 authGate를 얹으면 인증 상태를 두 곳이 따로 판단합니다. 라우터의 handleAuthRedirect()와 어긋나면 화면이 깜빡이거나 빈 채로 남습니다.

새 앱이라면 ​

주소가 바뀌지 않아도 되는 작은 단일 화면(예: 임베드 위젯)에는 AuthGate가 더 단순합니다. 그 밖에는 라우터 기반을 따르세요 — 여덟 앱의 router.ts가 이미 같은 모양이라 복사할 곳이 많고, 강제 비밀번호 변경까지 함께 딸려 옵니다.

호출 패턴 ​

html
<div x-data="authGate">
  <template x-if="!ready">
    <!-- 로딩 — 영역 링 스피너 -->
    <div class="flex items-center justify-center min-h-screen">
      <span class="ds-spinner"></span>
    </div>
  </template>

  <template x-if="ready && !authenticated">
    <div>
      <!-- ⚠️ 폼 HTML 을 여기에 직접 인라인한다. x-html 로 넣으면 죽는다 -->
      <div x-show="mode === 'login'" x-data="loginForm({ title: '…', showSignupLink: true })">
        <!-- include "../../packages/components/html/signin-form.html" -->
      </div>
      <div x-show="mode === 'register'" x-data="registerForm">
        <!-- include "../../packages/components/html/signup-form.html" -->
      </div>
    </div>
  </template>

  <template x-if="ready && authenticated">
    <!-- 앱 본문 -->
  </template>
</div>

폼 안에서는 @click="switchToRegister()"처럼 그냥 부르면 됩니다 — 스코프 체인이 바깥 authGate까지 올라가 찾습니다.

x-html 로 폼을 주입하지 마세요

Alpine은 x-html로 넣은 HTML 안의 디렉티브를 처리하지 않습니다. 에러도 경고도 없이 폼이 통째로 죽습니다. 컴포넌트 주석이 두 번 반복해 적어 둔 제약입니다.

로딩 자리에 무엇을 넣나 ​

ready가 false인 동안은 화면 전체가 아직 그릴 게 없는 상태이므로 영역 링 스피너(.ds-spinner)를 씁니다. 버튼 안 아이콘 스피너가 아닙니다 — 스피너 규칙의 두 종류 중 앞쪽입니다.

API ​

authGate Alpine.data factory. 인자를 받지 않습니다.

멤버타입설명
mode'login' | 'register'로그인/가입 중 무엇을 보일지. 초기값 'login'
readyboolean (getter)!store.isLoading — 세션 확인이 끝났는지
authenticatedboolean (getter)store.authenticated 그대로
switchToLogin()mode = 'login'
switchToRegister()mode = 'register'

상태를 스스로 만들지 않습니다 — ready와 authenticated는 스토어를 그대로 읽고, 자기 것은 mode 하나뿐입니다. 그래서 로그아웃이나 세션 만료도 여기서 다루지 않습니다.

사용처 ​

없습니다. 레포 전체 .html 에서 x-data="authGate" 가 0건입니다(빌드 산출물 포함, 2026-09-08 확인).

등록과 내보내기는 살아 있습니다.

파트위치
Alpine 등록Alpine.data("authGate", …) (plugin.ts)
패키지 exportauthGateComponent (index.ts · browser.ts)

단일 출처 ​

파트파일
Alpine 컴포넌트packages/components/src/components/AuthGate.ts
HTML partial없음 — 호스트가 세 갈래를 직접 씁니다

검토 필요 사항 ​

  • 쓰이는 곳이 없습니다. 등록·내보내기·주석은 살아 있지만 호출부가 0건입니다. 셋 중 하나를 정해야 합니다 — (a) 남겨 두고 「임베드용 대안」으로 문서화, (b) 라우터 기반이 정본이므로 제거, (c) 강제 비밀번호 변경·비밀번호 찾기까지 다루도록 확장. 이 문서는 (a) 를 전제로 쓰였습니다 — 현재 상태를 적었을 뿐 방향을 정한 것은 아닙니다.
  • 세 갈래로는 인증 화면 넷을 못 담습니다. 로그인 · 가입까지만 있고 비밀번호 찾기 · 강제 변경은 자리가 없습니다. 라우터 기반으로 옮겨 간 이유로 보이지만, 기록이 남아 있지 않아 추정입니다.

변경 이력 ​

날짜내용
2026-09-08AuthGate 페이지 신규 작성 — 세 갈래 분기도 + 라우터 기반과의 비교. Playground 없음(그리는 것이 없는 컴포넌트)