fix. useTokenValidation 수정

This commit is contained in:
clkim
2025-11-04 21:13:35 +09:00
parent 80e464902c
commit e76ac480b7
7 changed files with 147 additions and 76 deletions

View File

@@ -69,7 +69,7 @@ const handleClick = () => {
validateLauncher() validateLauncher()
return return
} }
if (props.platform === 'stove') { if (props.platform === 'stove' && !isDuplication.value) {
const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl const stoveClientDownloadUrl = runtimeConfig.public.stoveClientDownloadUrl
location.href = stoveClientDownloadUrl location.href = stoveClientDownloadUrl
return return

View File

@@ -14,6 +14,8 @@ const props = withDefaults(defineProps<props>(), {
const emit = defineEmits(['confirmButtonEvent']) const emit = defineEmits(['confirmButtonEvent'])
const { tm } = useI18n()
const isOpen = defineModel<boolean>('isOpen', { default: false }) const isOpen = defineModel<boolean>('isOpen', { default: false })
const setButtonEvent = (event?: () => void | void) => { const setButtonEvent = (event?: () => void | void) => {
@@ -49,7 +51,7 @@ const handleOutsideClick = () => {
<AtomsButtonVariant <AtomsButtonVariant
@click="setButtonEvent(() => emit('confirmButtonEvent'))" @click="setButtonEvent(() => emit('confirmButtonEvent'))"
> >
{{ props.confirmButtonText || '확인' }} {{ props.confirmButtonText || tm('Text_Confirm') }}
</AtomsButtonVariant> </AtomsButtonVariant>
</div> </div>
</div> </div>

View File

@@ -15,6 +15,8 @@ const props = withDefaults(defineProps<props>(), {
const emit = defineEmits(['cancelButtonEvent', 'confirmButtonEvent']) const emit = defineEmits(['cancelButtonEvent', 'confirmButtonEvent'])
const { tm } = useI18n()
const isOpen = defineModel<boolean>('isOpen', { default: false }) const isOpen = defineModel<boolean>('isOpen', { default: false })
const setButtonEvent = (event?: () => void) => { const setButtonEvent = (event?: () => void) => {
@@ -51,12 +53,12 @@ const handleOutsideClick = () => {
variant="outlined" variant="outlined"
@click="setButtonEvent(() => emit('cancelButtonEvent'))" @click="setButtonEvent(() => emit('cancelButtonEvent'))"
> >
{{ props.cancelButtonText || '취소' }} {{ props.cancelButtonText || tm('Text_Cancel') }}
</AtomsButtonVariant> </AtomsButtonVariant>
<AtomsButtonVariant <AtomsButtonVariant
@click="setButtonEvent(() => emit('confirmButtonEvent'))" @click="setButtonEvent(() => emit('confirmButtonEvent'))"
> >
{{ props.confirmButtonText || '확인' }} {{ props.confirmButtonText || tm('Text_Confirm') }}
</AtomsButtonVariant> </AtomsButtonVariant>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,4 @@
import { useTokenValidation } from '#layers/composables/useTokenValidation' import { useTokenValidation } from '#layers/composables/useTokenValidation'
import { csrGoStoveLogin } from '#layers/utils/stoveUtil'
interface ReqCheckSpec { interface ReqCheckSpec {
schemeFormat: string schemeFormat: string
@@ -8,8 +7,8 @@ interface ReqCheckSpec {
locale: string locale: string
} }
export const useCheckPCSpec = (tm: Function) => { export const useCheckPCSpec = (tm: (key: string) => string) => {
const { isTokenValid, validateToken } = useTokenValidation() const { handleTokenValidation } = useTokenValidation()
// Store // Store
const modalStore = useModalStore() const modalStore = useModalStore()
@@ -17,22 +16,6 @@ export const useCheckPCSpec = (tm: Function) => {
const isButtonDisabled = ref(false) // 버튼 비활성화 여부 const isButtonDisabled = ref(false) // 버튼 비활성화 여부
/**
* STOVE 미로그인 상태일 경우 로그인 모달을 표시합니다.
* 케이스에 따라 모달 내용을 변경할 수 있습니다.
* @param content<string> 모달 내용(로그인 안내 메시지)
*/
const showLoginModal = (content: string) => {
handleOpenConfirm({
contentText: content,
confirmButtonText: tm('Download_Text_StoveLogin'),
modalName: 'modal-login',
confirmButtonEvent: () => {
csrGoStoveLogin()
},
})
}
/** /**
* PC사양 프로그램이 미설치일 경우 설치 안내 모달을 표시합니다. * PC사양 프로그램이 미설치일 경우 설치 안내 모달을 표시합니다.
* @param {string} setupUrl 설치 프로그램 URL * @param {string} setupUrl 설치 프로그램 URL
@@ -165,11 +148,11 @@ export const useCheckPCSpec = (tm: Function) => {
*/ */
const checkPCSpec = async (req: ReqCheckSpec) => { const checkPCSpec = async (req: ReqCheckSpec) => {
const accessToken = useCookie('SUAT') const accessToken = useCookie('SUAT')
await validateToken(accessToken.value || '') const validateTokenResult = await handleTokenValidation(
accessToken.value || ''
)
// 로그인 상태 아닐 경우 if (validateTokenResult === false) {
if (!isTokenValid.value) {
showLoginModal(tm('Download_Alert_StoveLogin'))
return return
} }

View File

@@ -3,26 +3,15 @@ import { useTokenValidation } from '#layers/composables/useTokenValidation'
import { csrGoStoveLogin } from '#layers/utils/stoveUtil' import { csrGoStoveLogin } from '#layers/utils/stoveUtil'
export const useCheckGameStart = () => { export const useCheckGameStart = () => {
// const { tm } = useI18n() const { tm } = useI18n()
const modalStore = useModalStore() const modalStore = useModalStore()
const runtimeConfig = useRuntimeConfig() const runtimeConfig = useRuntimeConfig()
const { handleTokenValidation } = useTokenValidation()
const isProcessing = ref(false) // 연속 클릭 방지 const isProcessing = ref(false) // 연속 클릭 방지
const isShowCheckLauncher = ref(false) // 런처 실행 로딩 표시 const isShowCheckLauncher = ref(false) // 런처 실행 로딩 표시
const isShowDownloadLauncher = ref(false) // 런처 다운로드 표시 const isShowDownloadLauncher = ref(false) // 런처 다운로드 표시
const customerService = { title: '확인', link: 'https://www.google.com' } //[TODO] 고객센터 링크 const customerService = 'https://www.google.com' //[TODO] 고객센터 링크
// 로그인 모달 표시
const showLoginModal = () => {
modalStore.handleOpenConfirm({
contentText: '로그인이 필요합니다.',
confirmButtonText: '스토브 로그인',
modalName: 'modal-login',
confirmButtonEvent: () => {
csrGoStoveLogin()
},
})
}
// 에러 처리 // 에러 처리
const errorHandler = (errorCode: number) => { const errorHandler = (errorCode: number) => {
@@ -31,9 +20,8 @@ export const useCheckGameStart = () => {
break break
case 40101: // 로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요. case 40101: // 로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요.
modalStore.handleOpenConfirm({ modalStore.handleOpenConfirm({
contentText: contentText: tm('Alert_40101'),
'로그인 정보 확인 중 오류가 발생했습니다. 재로그인 후 다시 이용해 주세요.', confirmButtonText: tm('Text_StoveLogin'),
confirmButtonText: '스토브 로그인',
modalName: 'modal-login', modalName: 'modal-login',
confirmButtonEvent: () => { confirmButtonEvent: () => {
csrGoStoveLogin() csrGoStoveLogin()
@@ -42,9 +30,8 @@ export const useCheckGameStart = () => {
break break
case 40103: // 로그인 정보가 만료되었습니다. 재로그인 후 다시 이용해 주세요. case 40103: // 로그인 정보가 만료되었습니다. 재로그인 후 다시 이용해 주세요.
modalStore.handleOpenConfirm({ modalStore.handleOpenConfirm({
contentText: contentText: tm('Alert_40103'),
'로그인 정보가 만료되었습니다. 재로그인 후 다시 이용해 주세요.', confirmButtonText: tm('Text_StoveLogin'),
confirmButtonText: '스토브 로그인',
modalName: 'modal-login', modalName: 'modal-login',
confirmButtonEvent: () => { confirmButtonEvent: () => {
csrGoStoveLogin() csrGoStoveLogin()
@@ -60,12 +47,10 @@ export const useCheckGameStart = () => {
default: default:
// 일시적으로 오류가 발생했습니다. 잠시 후 다시 이용해 주세요. 동일한 현상이 계속 발생할 경우 고객센터로 문의해 주세요. // 일시적으로 오류가 발생했습니다. 잠시 후 다시 이용해 주세요. 동일한 현상이 계속 발생할 경우 고객센터로 문의해 주세요.
modalStore.handleOpenConfirm({ modalStore.handleOpenConfirm({
contentText: contentText: tm('Alert_Error'),
'일시적으로 오류가 발생했습니다. 잠시 후 다시 이용해 주세요. 동일한 현상이 계속 발생할 경우 고객센터로 문의해 주세요.', confirmButtonText: tm('Text_StoveLogin'),
confirmButtonText: customerService.title,
cancelButtonText: '취소',
confirmButtonEvent: () => { confirmButtonEvent: () => {
window.open(customerService.link, '_blank') window.open(customerService, '_blank')
}, },
}) })
break break
@@ -110,12 +95,12 @@ export const useCheckGameStart = () => {
window.stoveJsService = window.stoveJsService || {} window.stoveJsService = window.stoveJsService || {}
// 토큰 유효성 체크 // 토큰 유효성 체크
const { validateToken } = useTokenValidation() const validateTokenResult = await handleTokenValidation(
const validateTokenResult = await validateToken(accessTokenSub.value || '') accessTokenSub.value || ''
)
// 토큰 유효성 체크 실패 시 로그인 모달 표시 // 토큰 유효성 체크 실패 시
if (!validateTokenResult) { if (validateTokenResult === false) {
showLoginModal()
isProcessing.value = false isProcessing.value = false
return return
} }

View File

@@ -1,30 +1,129 @@
export function useTokenValidation() { // Token validation error codes
const TOKEN_ERROR_CODE = {
SUCCESS: 0,
LOGIN_INFO_ERROR: 40101,
LOGIN_INFO_INVALID: 40102,
TOKEN_EXPIRED: 40103,
TOKEN_INVALID: 40104,
} as const
type TokenErrorCode = (typeof TOKEN_ERROR_CODE)[keyof typeof TOKEN_ERROR_CODE]
interface TokenValidationResponse {
code: number
}
export const useTokenValidation = () => {
const config = useRuntimeConfig() const config = useRuntimeConfig()
const apiBaseUrl = `${config.public.stoveApiUrl}` const modalStore = useModalStore()
const { tm } = useI18n()
const apiBaseUrl = config.public.stoveApiUrl
const customerServiceUrl = 'https://www.google.com' // TODO: 고객센터 링크로 변경
const isTokenValid = ref(false) const isTokenValid = ref(false)
const validateToken = async (token: string) => { // 로그인 모달 표시
try { const showLoginModal = (alertKey: string) => {
const result = await $fetch<{ code: number }>( modalStore.handleOpenConfirm({
`${apiBaseUrl}/auth/v5/user_token/check`, contentText: tm(alertKey),
{ confirmButtonText: tm('Text_StoveLogin'),
method: 'GET', modalName: 'modal-login',
headers: { confirmButtonEvent: () => {
Authorization: `bearer ${token}`, csrGoStoveLogin()
'Content-Type': 'application/json;charset=UTF-8', },
}, })
} }
)
isTokenValid.value = result.code === 0 // 에러 모달 표시
return isTokenValid.value const showErrorModal = () => {
} catch (error) { modalStore.handleOpenConfirm({
isTokenValid.value = false contentText: tm('Alert_Error'),
confirmButtonText: tm('Text_StoveLogin'),
confirmButtonEvent: () => {
window.open(customerServiceUrl, '_blank')
},
})
}
// 토큰 검증 API 호출
const validateToken = async (
token: string
): Promise<TokenErrorCode | false> => {
if (!token) {
return false return false
} }
try {
const result = (await commonFetch(
'GET',
`${apiBaseUrl}/auth/v5/user_token/check`,
{
headers: {
Authorization: `bearer ${token}`,
},
}
)) as TokenValidationResponse
isTokenValid.value = result.code === TOKEN_ERROR_CODE.SUCCESS
return result.code as TokenErrorCode
} catch (error) {
isTokenValid.value = false
if (process.env.NODE_ENV === 'development') {
console.error('[useTokenValidation] API 호출 실패:', error)
}
return false
}
}
// 토큰 검증 및 에러 처리
const handleTokenValidation = async (token: string): Promise<boolean> => {
// 토큰 없음
if (!token) {
showLoginModal('Alert_StoveLogin')
return false
}
const result = await validateToken(token)
// API 호출 실패
if (result === false) {
showErrorModal()
return false
}
// 검증 성공
if (result === TOKEN_ERROR_CODE.SUCCESS) {
return true
}
// 로그인 정보 오류
if (
result === TOKEN_ERROR_CODE.LOGIN_INFO_ERROR ||
result === TOKEN_ERROR_CODE.LOGIN_INFO_INVALID
) {
showLoginModal('Alert_40101')
return false
}
// 토큰 만료
if (
result === TOKEN_ERROR_CODE.TOKEN_EXPIRED ||
result === TOKEN_ERROR_CODE.TOKEN_INVALID
) {
showLoginModal('Alert_40103')
return false
}
// 기타 에러
showErrorModal()
return false
} }
return { return {
isTokenValid, isTokenValid,
validateToken, validateToken,
handleTokenValidation,
} }
} }

View File

@@ -98,7 +98,7 @@ export interface PageDataResourceGroup {
color_code?: string color_code?: string
color_name?: string color_name?: string
} }
tracking: PageDataTracking tracking?: PageDataTracking
} }
export type PageDataResourceGroups = PageDataResourceGroup[] export type PageDataResourceGroups = PageDataResourceGroup[]