Nuxt 3 및 Vue 3 애플리케이션에 인증 추가 (Logto)
Source: Dev.to
Nuxt 3와 Vue 3 애플리케이션에 Logto 인증 추가하기
이 글에서는 Logto를 사용해 Nuxt 3와 Vue 3 프로젝트에 인증을 손쉽게 통합하는 방법을 단계별로 살펴봅니다. Logto는 오픈소스 인증 서비스로, OAuth 2.0 및 OpenID Connect 표준을 기반으로 하며, 토큰 관리와 사용자 프로필 조회 등을 간단하게 처리해 줍니다.
목차
프로젝트 초기 설정
# Nuxt 3 프로젝트 생성
npx nuxi init my-nuxt-app
cd my-nuxt-app
npm install
Tip: Node.js ≥ 18 버전을 권장합니다.
Logto SDK 설치
Logto는 @logto/next 패키지를 제공하며, Nuxt 3와도 호환됩니다.
npm i @logto/next
환경 변수 구성
프로젝트 루트에 .env 파일을 만들고 Logto 콘솔에서 발급받은 값을 입력합니다.
LOGTO_ENDPOINT=https://your-logto-instance.logto.io
LOGTO_APP_ID=YOUR_APP_ID
LOGTO_APP_SECRET=YOUR_APP_SECRET # 필요 시
Nuxt는 자동으로 process.env를 읽어 주입합니다.
Nuxt 플러그인 만들기
plugins/logto.client.ts 파일을 생성하고 아래와 같이 설정합니다.
import { defineNuxtPlugin } from '#app'
import { LogtoClient } from '@logto/next'
export default defineNuxtPlugin(() => {
const logto = new LogtoClient({
endpoint: process.env.LOGTO_ENDPOINT!,
appId: process.env.LOGTO_APP_ID!,
// 옵션: redirectUri, postLogoutRedirectUri 등
})
// 전역으로 제공
return {
provide: {
logto,
},
}
})
Note:
.client.ts확장자를 사용하면 클라이언트 사이드에서만 실행됩니다.
인증 상태 사용하기
컴포넌트 혹은 페이지에서 useNuxtApp()을 통해 Logto 인스턴스를 가져올 수 있습니다.
<script setup lang="ts">
const { $logto } = useNuxtApp()
const user = ref(null)
onMounted(async () => {
const isAuthenticated = await $logto.isAuthenticated()
if (isAuthenticated) {
user.value = await $logto.fetchUserInfo()
}
})
</script>
<template>
<div v-if="user">
<p>환영합니다, {{ user.name }}님!</p>
<button @click="$logto.signOut()">로그아웃</button>
</div>
<div v-else>
<button @click="$logto.signIn()">로그인</button>
</div>
</template>
보호된 라우트 만들기
Nuxt 3에서는 라우트 미들웨어를 활용해 인증이 필요한 페이지를 보호할 수 있습니다.
middleware/auth.global.ts
import { defineNuxtRouteMiddleware, navigateTo } from '#app'
export default defineNuxtRouteMiddleware(async (to, from) => {
const { $logto } = useNuxtApp()
const isAuth = await $logto.isAuthenticated()
if (!isAuth && to.path !== '/login') {
return navigateTo('/login')
}
})
위 미들웨어를 nuxt.config.ts에 자동 적용하도록 설정합니다.
export default defineNuxtConfig({
router: {
middleware: ['auth.global'],
},
})
로그아웃 및 토큰 갱신
Logto는 자동 토큰 갱신을 지원합니다. 별도 설정이 필요 없지만, 수동으로 토큰을 갱신하고 싶다면 다음과 같이 호출합니다.
await $logto.refreshAccessToken()
로그아웃은 signOut 메서드로 간단히 처리됩니다.
await $logto.signOut()
마무리
이제 Nuxt 3와 Vue 3 애플리케이션에 Logto 인증이 완전히 통합되었습니다.
- 핵심 포인트: Logto SDK를 플러그인으로 래핑하고, 전역 제공(
provide)을 통해 어디서든 접근 가능하게 만든다. - 보호된 라우트: 라우트 미들웨어를 사용해 인증 여부를 체크하고, 비인증 사용자를 로그인 페이지로 리다이렉트한다.
추가적인 커스터마이징(예: 역할 기반 접근 제어, 소셜 로그인 연동 등)은 Logto 공식 문서를 참고하세요.
Reference: https://logto.io/docs/quick-start/nuxt
Happy coding! 🚀
시작하기 전에: 콘솔에서 Logto 앱 만들기
Logto 콘솔에서 프레임워크에 맞는 애플리케이션을 생성하세요:
| Framework | Logto Application Type |
|---|---|
| Nuxt 3 | 전통 웹 |
| Vue 3 | 싱글 페이지 애플리케이션 (SPA) |
두 설정 모두에 필요한 값은 다음과 같습니다:
- Endpoint – 테넌트 URL (예:
https://your-tenant.logto.app) - App ID
- App Secret – Nuxt 전용
Part 1 — Nuxt 3 (SSR): Add Authentication with @logto/nuxt
Nuxt는 종종 SSR(또는 하이브리드 렌더링)과 함께 배포됩니다. Logto의 Nuxt SDK는 SSR을 위해 설계되었으며, 보안 쿠키 + 서버‑사이드 처리를 사용해 인증을 완료합니다.
1️⃣ Install the Nuxt SDK
npm i @logto/nuxt
# or
pnpm add @logto/nuxt
# or
yarn add @logto/nuxt
2️⃣ Add Environment Variables
프로젝트 루트에 .env 파일을 생성합니다:
NUXT_LOGTO_ENDPOINT="https://your-tenant.logto.app"
NUXT_LOGTO_APP_ID="your-app-id"
NUXT_LOGTO_APP_SECRET="your-app-secret"
NUXT_LOGTO_COOKIE_ENCRYPTION_KEY="a-strong-random-string"
Important:
NUXT_LOGTO_COOKIE_ENCRYPTION_KEY는 암호화된 인증 쿠키를 보호하므로 강력한 무작위 문자열이어야 합니다.
3️⃣ Configure nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@logto/nuxt'],
runtimeConfig: {
logto: {
endpoint: process.env.NUXT_LOGTO_ENDPOINT,
appId: process.env.NUXT_LOGTO_APP_ID,
appSecret: process.env.NUXT_LOGTO_APP_SECRET,
cookieEncryptionKey: process.env.NUXT_LOGTO_COOKIE_ENCRYPTION_KEY,
},
},
})
4️⃣ Configure Redirect URIs in Logto Console
Nuxt 앱이 http://localhost:3000에서 실행된다고 가정합니다:
| Redirect Type | URI |
|---|---|
| Redirect URI | http://localhost:3000/callback |
| Post sign‑out redirect URI | http://localhost:3000/ |
이 값들은 SDK가 사용하는 콜백 및 반환 URL과 일치해야 합니다.
5️⃣ Understand the Built‑In Routes
Nuxt 모듈은 인증 흐름을 위한 내장 라우트를 제공합니다:
/sign-in/sign-out/callback
경로를 커스터마이즈할 수 있습니다:
// nuxt.config.ts
export default defineNuxtConfig({
logto: {
pathnames: {
signIn: '/login',
signOut: '/logout',
callback: '/auth/callback',
},
},
})
콜백 경로를 변경하면 Logto 콘솔에서도 리디렉션 URI를 동일하게 업데이트해야 합니다.
6️⃣ Implement a Simple Sign‑In / Sign‑Out Button
<script setup lang="ts">
const user = useLogtoUser()
</script>
<template>
<button @click="user ? signOut() : signIn()">
Sign {{ user ? 'out' : 'in' }}
</button>
<pre>{{ user }}</pre>
</template>
useLogtoUser()는 반응형이며, 세션이 변경될 때 자동으로 업데이트됩니다.
7️⃣ Request Additional User Claims (Scopes)
기본적으로 기본 OIDC 클레임만 반환됩니다. 이메일, 전화번호 등 추가 클레임을 요청하려면 스코프를 추가합니다:
// nuxt.config.ts
import { UserScope } from '@logto/nuxt'
export default defineNuxtConfig({
logto: {
scopes: [UserScope.Email, UserScope.Phone],
},
})
8️⃣ Use the Logto Client (Server‑Only)
useLogtoClient()는 서버 전용이며, 클라이언트에서는 undefined를 반환합니다. 예시: 서버에서 한 번만 사용자 정보를 가져오기.
// composables/useServerUserInfo.ts
import { useLogtoClient, useState, callOnce } from '#imports'
export const useServerUserInfo = async () => {
const client = useLogtoClient()
const userInfo = useState('logto-user-info', () => null)
await callOnce(async () => {
if (!client) return
if (!(await client.isAuthenticated())) return
userInfo.value = await client.fetchUserInfo()
})
return userInfo
}
Part 2 — Vue 3 (SPA): Vue SDK를 사용한 인증 추가
Vue SPA는 비밀을 안전하게 저장할 서버 런타임이 없으므로, 권장 설정은 Logto Vue SDK와 Logto 콘솔의 SPA 애플리케이션 설정을 사용하는 것입니다.
1️⃣ Install the Vue SDK
npm i @logto/vue
# or
pnpm add @logto/vue
# or
yarn add @logto/vue
2️⃣ Logto 콘솔에서 Redirect URIs 구성
Assuming your Vue app runs on http://localhost:5173:
| Redirect Type | URI |
|---|---|
| Redirect URI | http://localhost:5173/callback |
| Post sign‑out redirect URI | http://localhost:5173/ |
(실제 개발 서버 주소가 다르면 해당 주소를 사용하세요.)
3️⃣ main.ts에서 Logto 초기화
// src/main.ts
import { createApp } from 'vue'
import { createLogto } from '@logto/vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(
createLogto({
endpoint: import.meta.env.VITE_LOGTO_ENDPOINT,
appId: import.meta.env.VITE_LOGTO_APP_ID,
})
)
app.use(router)
app.mount('#app')
4️⃣ 환경 변수 추가
Create a .env file (Vite will expose variables prefixed with VITE_):
VITE_LOGTO_ENDPOINT="https://your-tenant.logto.app"
VITE_LOGTO_APP_ID="your-app-id"
콜백 라우트 추가
SPA에서는 리디렉션 콜백 라우트를 처리해야 합니다.
Vue Router 예시
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../pages/Home.vue'
import Callback from '../pages/Callback.vue'
export default createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/callback', component: Callback },
],
})
콜백 페이지
<script setup lang="ts">
import { onMounted } from 'vue'
import { useLogto } from '@logto/vue'
import { useRouter } from 'vue-router'
const { handleSignInCallback } = useLogto()
const router = useRouter()
onMounted(async () => {
await handleSignInCallback(window.location.href)
router.replace('/')
})
</script>
<template>
<p>Signing you in…</p>
</template>
Sign‑in / Sign‑out buttons
<script setup lang="ts">
import { useLogto } from '@logto/vue'
const { isAuthenticated, signIn, signOut } = useLogto()
const handleSignIn = async () => {
await signIn(import.meta.env.VITE_LOGTO_REDIRECT_URI)
}
const handleSignOut = async () => {
await signOut(import.meta.env.VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI)
}
</script>
<template>
<button @click="handleSignIn" v-if="!isAuthenticated">Sign in</button>
<button @click="handleSignOut" v-else>Sign out</button>
</template>
리다이렉트 관련 환경 변수 추가
VITE_LOGTO_REDIRECT_URI="http://localhost:5173/callback"
VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI="http://localhost:5173/"
현재 사용자 표시 (선택 사항)
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useLogto } from '@logto/vue'
const { isAuthenticated, getIdTokenClaims } = useLogto()
const claims = ref(null)
const loadClaims = async () => {
claims.value = await getIdTokenClaims()
}
</script>
<template>
<button @click="loadClaims" :disabled="!isAuthenticated">Load user claims</button>
<pre v-if="claims">{{ claims }}</pre>
</template>
Nuxt와 Vue 통합 중 선택
- Nuxt (SSR 또는 하이브리드 렌더링):
@logto/nuxt를 사용하세요. 서버‑사이드 흐름과 보안 쿠키 처리를 위해 설계되었습니다. - Vue SPA (서버 런타임 없음):
@logto/vue를 사용하고 Logto 콘솔에서 SPA 애플리케이션 유형을 선택하세요.
Quick sanity check
- Sign in을 클릭하면 Logto가 호스팅하는 로그인 화면으로 리다이렉트됩니다.
- 로그인 후에는 콜백 라우트에 도착하고 애플리케이션으로 돌아갑니다.
- Sign out을 클릭하면 공유 세션이 삭제되고 로그아웃 후 URL로 리다이렉트됩니다.
최종 메모
이 문서는 의도적으로 예시를 최소화하고 실제 운영 환경에 맞게 구성했습니다. 인증이 정상적으로 작동하면 일반적으로 다음 단계가 이어집니다:
- 라우트 보호 (Nuxt 미들웨어 / Vue 라우터 가드)
- 액세스 토큰을 사용하여 API 호출
- 제품에 필요하다면 조직/역할/권한 추가