Skip to content

Rails + Inertia.js + Svelte

Source: integrations/inertia-svelte.md · Live: https://docs.1pass.dev/integrations/inertia-svelte LLM-sanitized: internal links absolutized, VitePress containers → admonitions, line numbers in the Jump-to Index reference this rendered file (1-indexed).

📍 Jump-to Index

  • L34-L51: ## ① 로그인 시작 — 클라이언트 라우터를 우회하라 (가장 중요)
  • L52-L84: ## ② 로그인 상태 — inertia_share 로 current_user 전달
  • L85-L106: ## ③ Svelte 측 — page 는 store 가 아니라 rune (page.props)
  • L107-L113: ## 체크리스트
  • L114-L119: ## 더 보기

Rails + Inertia.js + Svelte

ℹ️ Note: 먼저 읽어주세요 이 페이지는 Rails 8 가이드의 보완편입니다. 컨트롤러 · PKCE · 토큰 교환 · JWT 검증 등 서버 측 로직은 모두 Rails 가이드와 동일합니다. 여기서는 Inertia.js + Svelte 프론트엔드에서만 달라지는 3가지만 다룹니다.

⚠️ Warning: 📋 Confidential client 등록이 필요합니다 서버(Rails)에서 client_secret/oauth/token 에 전달하므로 RP 등록 시 Client type: Confidential 을 선택하세요. 프론트엔드(Svelte)에는 secret 을 절대 노출하지 않습니다.

Inertia 앱은 페이지 전환을 클라이언트 라우터(@inertiajs/svelterouter, use:inertia, <Link>)가 가로채는 SPA 입니다. 1pass 로그인은 cross-origin 302 리다이렉트로 시작하는데, 클라이언트 라우터는 이 302 를 XHR 로 따라가려다 조용히 실패합니다 (Turbo Drive 와 동일한 함정). 그래서 세 군데에서 처리가 달라집니다.

① 로그인 시작 — 클라이언트 라우터를 우회하라 (가장 중요)

"1pass 로 로그인" 버튼은 반드시 전체 페이지 네비게이션이어야 합니다. use:inertia / <Link> / router.visit 를 쓰면 안 됩니다.

svelte
<a href="/auth/logi/start" data-no-inertia>1pass 로 로그인</a>

/auth/logi/start 라우트는 RP 가 자체 결정합니다 (로그인 버튼 참고). 서버 컨트롤러의 redirect_to(..., allow_other_host: true)Rails 가이드와 동일합니다.

💡 Tip: 콜백은 신경 쓸 게 없습니다 /auth/logi/callback 은 서버 컨트롤러가 받아 redirect_to root_path 로 끝내므로 Inertia 가 자연스럽게 다음 페이지를 렌더합니다. 시작(②아님 ①)만 우회하면 됩니다.

② 로그인 상태 — inertia_share 로 current_user 전달

별도 API 왕복 없이 모든 Inertia 페이지에 로그인 사용자를 내려주려면 ApplicationController#inertia_share 를 씁니다.

ruby
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  inertia_share do
    {
      auth: {
        user: current_user && {
          id: current_user.id,
          email: current_user.email,
          displayName: current_user.display_name
        }
      },
      flash: { notice: flash[:notice], alert: flash[:alert] }
    }
  end

  private

  def current_user
    @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
  end
end

⚠️ Warning: front-channel 데이터로 User 를 만들지 마세요 current_user 는 서버 세션(session[:user_id])에서만 옵니다. 사용자 생성은 서버 측 userinfo 호출 결과로만 하세요 — Rails 가이드: 첫 로그인.

③ Svelte 측 — page 는 store 가 아니라 rune (page.props)

🚨 Danger: @inertiajs/svelte v3 함정 v2 의 $page store 패턴은 v3 에서 깨집니다. v3 의 page$state rune 객체라서 $page 로 구독하면 store_invalid_shape 로 hydration 이 실패합니다. page.props직접 읽으세요.

svelte
<header>
  {#if user}
    <span title={user.email}>👤 {user.displayName}</span>
    <button type="button" onclick={logout}>로그아웃</button>
  {:else}
    
    <a href="/auth/logi/start" data-no-inertia>1pass 로 로그인</a>
  {/if}
</header>

로그아웃은 router.delete 로 안전합니다 (same-origin DELETE — cross-origin 302 가 아니므로 클라이언트 라우터로 처리해도 됨).

체크리스트

  • [ ] 로그인 시작 버튼이 plain <a href> (use:inertia/<Link> 아님)
  • [ ] inertia_shareauth.user 내려줌, current_user 는 세션 기반
  • [ ] Svelte 에서 $page 가 아니라 page.props 직접 접근
  • [ ] RP 등록 = Confidential, secret 은 서버 env 에만

더 보기

  • Rails 8 — 컨트롤러 · PKCE · 토큰 교환 · JWT 검증 (서버 측 전부)
  • SPA + 서버리스 — 백엔드 없는 순수 SPA (Public client)
  • 로그인 버튼 — 버튼 디자인 · 시작 라우트 컨벤션

Identity가 제품의 신뢰를 만듭니다.