import { getHeader, getRequestHost, type H3Event } from 'h3' /** * URL이 외부 URL인지 확인하는 함수 * @param url - 확인할 URL 문자열 * @returns 외부 URL 여부 */ export const isExternalUrl = (url: string): boolean => { return url.startsWith('http://') || url.startsWith('https://') } /** * 경로에 쿼리스트링을 추가하는 함수 */ export const addQueryString = (path: string, queryString: string): string => { return queryString ? `${path}?${queryString}` : path } /** * 게임 도메인을 추출하는 함수 * 서버와 클라이언트 환경에서 모두 동작 * @param event - H3 이벤트 객체 (서버 전달 필수) * @returns 게임 도메인 문자열 */ export const getGameDomain = (event?: H3Event): string => { try { let host = '' // 클라이언트 환경에서는 window.location.host를 사용 if (import.meta.client) { host = (window.location.host || '').split(':')[0] } // 서버 환경에서는 event 객체를 사용 if (import.meta.server) { if (!event) return '' // 미들웨어에서 설정한 gameDomain이 있다면 우선 사용 if (event.context.gameDomain) { host = event.context.gameDomain } else { const serverHost = getHeader(event, 'host') || getRequestHost(event) || '' host = serverHost.split(':')[0] } } if (!host) return '' // dev2 호스트명인 경우 l9-dev.onstove.com을 사용 if (host === 'samplegame-dev2.onstove.com') { return 'l9-dev.onstove.com' } return host } catch (error) { console.error('getGameDomain error:', error) return '' } } /** * URL에서 언어 코드를 추출하는 함수 * @param url - URL 문자열 * @returns 언어 코드 문자열 */ export const getPathLocale = (url: string): string => { if (!url) return '' const cleanUrl = url.endsWith('/') ? url.slice(0, -1) : url return cleanUrl.split('/')[1] || '' } /** * URL에서 언어 코드 이후의 경로를 추출하는 함수 * 서버와 클라이언트 환경에서 모두 동작 * @param url - URL 문자열 * @returns 언어 코드 이후의 경로 문자열 */ export const getPathAfterLanguage = (url: string): string => { if (!url) return '' const cleanUrl = url.split('?')[0].endsWith('/') ? url.slice(0, -1) : url // URL에서 언어 코드 패턴을 찾아서 그 뒤의 경로를 추출 // 예: /ko/about/story -> /about/story // 예: /ko -> "" (빈 문자열) const languagePattern = /^\/[a-z]{2}(-[a-z]{2})?\/(.+)$/ const match = cleanUrl.match(languagePattern) if (match && match[2]) { return `/${match[2]}` } else { // 언어 코드만 있고 뒤에 아무것도 없는 경우 (예: /ko, /en, /zh-tw, /zh-cn) const languageOnlyPattern = /^\/[a-z]{2}(-[a-z]{2})?$/ if (languageOnlyPattern.test(cleanUrl)) { return '' } else { // 언어 코드가 없는 경우 원본 경로 그대로 반환 (이미 /로 시작) return cleanUrl } } } /** * URL을 파싱하여 최종 경로를 반환하는 함수 * 경로가 '/' 또는 ''이고 인트로 URL이 있으면 인트로로 리다이렉트, * 그렇지 않으면 언어 코드를 추가한 URL을 반환 * SSR과 CSR 모두에서 작동 * @param url - URL 문자열 * @param finalLocale - 최종 언어 코드 * @param langCodes - 지원하는 언어 코드 배열 * @param ISO_LANGUAGE_CODES - ISO 언어 코드 배열 * @param introPageUrl - 인트로 페이지 URL (선택) * @returns 최종 URL 문자열 */ export const parseUrl = ( url: string, finalLocale: string, langCodes: string[], ISO_LANGUAGE_CODES: string[], introPageUrl?: string ): string => { const [pathPart, queryString = ''] = url.split('?') const pathSegments = pathPart.split('/').filter(Boolean) const currentLocale = pathSegments[0] const isKnownLocale = langCodes.includes(currentLocale) || ISO_LANGUAGE_CODES.includes(currentLocale) const isEmptyPath = pathSegments.length === 0 || (pathSegments.length === 1 && isKnownLocale) // 경로가 '/' 또는 ''인 경우 인트로 URL 처리 if (isEmptyPath) { const hasIntroUrl = introPageUrl?.trim() if (hasIntroUrl) { // 외부 URL인 경우 그대로 반환 if (isExternalUrl(introPageUrl)) { return introPageUrl } // 내부 경로인 경우 언어 코드 추가 // 인트로 URL이 이미 언어 코드로 시작하는지 확인 const introPathSegments = introPageUrl.split('/').filter(Boolean) const introFirstSegment = introPathSegments[0] const introHasLocale = introFirstSegment && (langCodes.includes(introFirstSegment) || ISO_LANGUAGE_CODES.includes(introFirstSegment)) let introPath: string if (introHasLocale) { // 이미 언어 코드가 있으면 언어 코드만 교체 introPath = '/' + [finalLocale, ...introPathSegments.slice(1)].join('/') } else { // 언어 코드가 없으면 추가 introPath = `/${finalLocale}${introPageUrl}` } return addQueryString(introPath, queryString) } // 인트로 URL이 없으면 기본 홈 경로 반환 return addQueryString(`/${finalLocale}/home`, queryString) } // 리다이렉트 경로 생성 const remainingPath = pathSegments.slice(1) const hasRemainingPath = remainingPath.length > 0 let newPath: string if (isKnownLocale) { // 현재 언어 코드와 최종 언어가 같으면 리다이렉트 불필요 if (currentLocale === finalLocale) return url // 유효한 언어 코드가 있지만 다른 언어인 경우 if (hasRemainingPath) { // 경로가 있으면 언어 코드만 교체 (/vi/story -> /finalLocale/story) newPath = '/' + [finalLocale, ...remainingPath].join('/') } } else { // 언어 코드가 없거나 유효하지 않은 경우: 언어 코드를 앞에 추가 const pathWithoutSlash = pathPart === '/' ? '' : pathPart newPath = `/${finalLocale}${pathWithoutSlash}` } return addQueryString(newPath, queryString) }