Google 계정 로그인 연동 추가

This commit is contained in:
2026-07-29 14:02:07 +09:00
parent 99af25b8fa
commit 52ed053876
20 changed files with 897 additions and 9 deletions

View File

@@ -8,9 +8,11 @@ const removingAvatar = ref(false)
const profileMessage = ref('')
const passwordMessage = ref('')
const deleteMessage = ref('')
const googleMessage = ref('')
const actionMenuOpen = ref(false)
const passwordModalOpen = ref(false)
const deleteModalOpen = ref(false)
const route = useRoute()
const profileForm = reactive({
email: '',
@@ -24,6 +26,13 @@ const profileForm = reactive({
commentCount: 0
})
const googleProfile = reactive({
email: '',
name: '',
avatarUrl: '',
linkedAt: ''
})
const avatarInputRef = ref(null)
const passwordForm = reactive({
@@ -39,6 +48,10 @@ const deleteForm = reactive({
const displayName = computed(() => profileForm.username || profileForm.email || '멤버')
const avatarInitial = computed(() => String(displayName.value || '?').slice(0, 1).toUpperCase())
const previousLoginText = computed(() => formatRelativeTime(profileForm.previousLastSeenAt, '이전 로그인 기록 없음'))
const googleLinked = computed(() => Boolean(googleProfile.linkedAt))
const googleLinkedText = computed(() => googleLinked.value
? `${googleProfile.email || googleProfile.name || 'Google 계정'} 연결됨`
: 'Google 계정이 연결되지 않았습니다.')
/**
* 날짜 표시 형식을 변환한다.
@@ -118,6 +131,10 @@ const loadProfile = async () => {
profileForm.previousLastSeenAt = profile.previousLastSeenAt || ''
profileForm.previousLastSeenIp = profile.previousLastSeenIp || ''
profileForm.commentCount = Number(profile.commentCount || 0)
googleProfile.email = profile.googleEmail || ''
googleProfile.name = profile.googleName || ''
googleProfile.avatarUrl = profile.googleAvatarUrl || ''
googleProfile.linkedAt = profile.googleLinkedAt || ''
} catch {
await navigateTo('/signin')
} finally {
@@ -125,6 +142,34 @@ const loadProfile = async () => {
}
}
/**
* Google 계정 연결을 시작한다.
* @returns {void}
*/
const startGoogleLink = () => {
if (import.meta.client) {
window.location.href = '/api/auth/google/start?mode=link&redirect=/settings'
}
}
watch(
() => route.query.google,
(status) => {
const value = String(status || '')
if (value === 'linked') {
googleMessage.value = 'Google 계정이 연결되었습니다.'
loadProfile()
} else if (value === 'already-linked') {
googleMessage.value = '이미 다른 계정에 연결된 Google 계정입니다.'
} else if (value === 'email-unverified') {
googleMessage.value = 'Google 계정의 이메일 확인 상태를 확인할 수 없습니다.'
} else if (value === 'session-mismatch') {
googleMessage.value = '로그인 세션이 바뀌었습니다. 다시 로그인해 주세요.'
}
},
{ immediate: true }
)
/**
* 닉네임 중복 여부를 확인한다.
* @returns {Promise<boolean>} 사용 가능 여부
@@ -505,6 +550,26 @@ onMounted(loadProfile)
</div>
</div>
</section>
<section class="settings-page__google">
<h2 class="settings-page__section-title mb-4 text-xs font-semibold uppercase tracking-[0.04em]">Google 계정</h2>
<div class="settings-page__google-card rounded-[12px] border border-[var(--site-line)] bg-[var(--site-bg)] p-5 sm:p-6">
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="min-w-0">
<p class="text-sm font-semibold">{{ googleLinkedText }}</p>
<p class="mt-1 text-sm site-muted">
연결하면 다음 로그인부터 Google 계정으로 로그인할 수 있습니다.
</p>
</div>
<button class="settings-page__google-link-button h-10 rounded-[8px] border border-[var(--site-line)] px-4 text-sm font-semibold transition hover:bg-[var(--site-panel)]" type="button" @click="startGoogleLink">
{{ googleLinked ? '다시 연결' : 'Google 계정 연결' }}
</button>
</div>
<p v-if="googleMessage" class="mt-4 text-sm site-muted" aria-live="polite">
{{ googleMessage }}
</p>
</div>
</section>
</div>
</div>