frontend backend connection
This commit is contained in:
parent
0f291383a1
commit
9f64f93ed8
@ -1,14 +1,16 @@
|
||||
//* public/runtime-env.js
|
||||
window.__runtime_config__ = {
|
||||
// API 서버 URL들
|
||||
AUTH_URL: 'http://20.1.2.3/auth',
|
||||
STORE_URL: 'http://20.1.2.3/store',
|
||||
CONTENT_URL: 'http://20.1.2.3/content',
|
||||
RECOMMEND_URL: 'http://20.1.2.3/recommend',
|
||||
//* public/runtime-env.js - 디버깅 포함 버전
|
||||
console.log('=== RUNTIME-ENV.JS 로드됨 ===');
|
||||
|
||||
// 외부 API 설정
|
||||
CLAUDE_AI_ENABLED: true,
|
||||
WEATHER_API_ENABLED: true,
|
||||
window.__runtime_config__ = {
|
||||
// 로컬 개발 환경 설정
|
||||
AUTH_URL: 'http://localhost:8081/api/auth',
|
||||
MEMBER_URL: 'http://localhost:8081/api/member',
|
||||
STORE_URL: 'http://localhost:8082/api/store',
|
||||
CONTENT_URL: 'http://localhost:8083/api/content',
|
||||
RECOMMEND_URL: 'http://localhost:8084/api/recommendation',
|
||||
|
||||
// Gateway 주석 처리 (로컬에서는 사용 안함)
|
||||
// GATEWAY_URL: 'http://20.1.2.3',
|
||||
|
||||
// 기능 플래그
|
||||
FEATURES: {
|
||||
@ -19,10 +21,15 @@ window.__runtime_config__ = {
|
||||
},
|
||||
|
||||
// 환경 설정
|
||||
ENV: 'production',
|
||||
DEBUG: false,
|
||||
ENV: 'development',
|
||||
DEBUG: true,
|
||||
|
||||
// 버전 정보
|
||||
VERSION: '1.0.0',
|
||||
BUILD_DATE: new Date().toISOString(),
|
||||
}
|
||||
};
|
||||
|
||||
console.log('=== 설정된 API URLs ===');
|
||||
console.log('AUTH_URL:', window.__runtime_config__.AUTH_URL);
|
||||
console.log('MEMBER_URL:', window.__runtime_config__.MEMBER_URL);
|
||||
console.log('전체 설정:', window.__runtime_config__);
|
||||
@ -6,10 +6,12 @@ const getApiUrls = () => {
|
||||
const config = window.__runtime_config__ || {}
|
||||
return {
|
||||
GATEWAY_URL: config.GATEWAY_URL || 'http://20.1.2.3',
|
||||
MEMBER_URL: config.MEMBER_URL || 'http://20.1.2.3/api/member',
|
||||
AUTH_URL: config.AUTH_URL || 'http://20.1.2.3/api/auth',
|
||||
AUTH_URL: 'http://localhost:8081/api/auth',
|
||||
MEMBER_URL: 'http://localhost:8081/api/member',
|
||||
STORE_URL: config.STORE_URL || 'http://20.1.2.3/api/store',
|
||||
CONTENT_URL: config.CONTENT_URL || 'http://20.1.2.3/api/content',
|
||||
MENU_URL: config.MENU_URL || 'http://20.1.2.3/api/menu',
|
||||
SALES_URL: config.SALES_URL || 'http://20.1.2.3/api/sales',
|
||||
RECOMMEND_URL: config.RECOMMEND_URL || 'http://20.1.2.3/api/recommendation',
|
||||
}
|
||||
}
|
||||
@ -26,7 +28,7 @@ const createApiInstance = (baseURL) => {
|
||||
})
|
||||
|
||||
// 요청 인터셉터 - JWT 토큰 자동 추가
|
||||
instance.interceptors.request.use(
|
||||
instance.interceptors.request.use(
|
||||
(config) => {
|
||||
const token = localStorage.getItem('accessToken')
|
||||
if (token) {
|
||||
@ -58,8 +60,9 @@ const createApiInstance = (baseURL) => {
|
||||
refreshToken,
|
||||
})
|
||||
|
||||
const { accessToken } = refreshResponse.data.data
|
||||
const { accessToken, refreshToken: newRefreshToken } = refreshResponse.data.data
|
||||
localStorage.setItem('accessToken', accessToken)
|
||||
localStorage.setItem('refreshToken', newRefreshToken)
|
||||
|
||||
// 원래 요청에 새 토큰으로 재시도
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`
|
||||
@ -69,6 +72,7 @@ const createApiInstance = (baseURL) => {
|
||||
// 토큰 갱신 실패 시 로그아웃 처리
|
||||
localStorage.removeItem('accessToken')
|
||||
localStorage.removeItem('refreshToken')
|
||||
localStorage.removeItem('userInfo')
|
||||
window.location.href = '/login'
|
||||
}
|
||||
}
|
||||
@ -86,6 +90,8 @@ export const memberApi = createApiInstance(apiUrls.MEMBER_URL)
|
||||
export const authApi = createApiInstance(apiUrls.AUTH_URL)
|
||||
export const storeApi = createApiInstance(apiUrls.STORE_URL)
|
||||
export const contentApi = createApiInstance(apiUrls.CONTENT_URL)
|
||||
export const menuApi = createApiInstance(apiUrls.MENU_URL)
|
||||
export const salesApi = createApiInstance(apiUrls.SALES_URL)
|
||||
export const recommendApi = createApiInstance(apiUrls.RECOMMEND_URL)
|
||||
|
||||
// 기본 API 인스턴스 (Gateway URL 사용)
|
||||
@ -99,7 +105,7 @@ export const handleApiError = (error) => {
|
||||
return {
|
||||
success: false,
|
||||
message: '네트워크 연결을 확인해주세요.',
|
||||
code: 'NETWORK_ERROR',
|
||||
error: error.message
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,46 +117,47 @@ export const handleApiError = (error) => {
|
||||
return {
|
||||
success: false,
|
||||
message: data?.message || '잘못된 요청입니다.',
|
||||
code: 'BAD_REQUEST',
|
||||
error: data?.error
|
||||
}
|
||||
case 401:
|
||||
return {
|
||||
success: false,
|
||||
message: '인증이 필요합니다.',
|
||||
code: 'UNAUTHORIZED',
|
||||
error: 'UNAUTHORIZED'
|
||||
}
|
||||
case 403:
|
||||
return {
|
||||
success: false,
|
||||
message: '접근 권한이 없습니다.',
|
||||
code: 'FORBIDDEN',
|
||||
error: 'FORBIDDEN'
|
||||
}
|
||||
case 404:
|
||||
return {
|
||||
success: false,
|
||||
message: '요청하신 정보를 찾을 수 없습니다.',
|
||||
code: 'NOT_FOUND',
|
||||
message: '요청한 리소스를 찾을 수 없습니다.',
|
||||
error: 'NOT_FOUND'
|
||||
}
|
||||
case 500:
|
||||
return {
|
||||
success: false,
|
||||
message: '서버 오류가 발생했습니다.',
|
||||
code: 'SERVER_ERROR',
|
||||
error: 'INTERNAL_SERVER_ERROR'
|
||||
}
|
||||
default:
|
||||
return {
|
||||
success: false,
|
||||
message: data?.message || '알 수 없는 오류가 발생했습니다.',
|
||||
code: 'UNKNOWN_ERROR',
|
||||
message: data?.message || `오류가 발생했습니다. (${status})`,
|
||||
error: data?.error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 성공 응답 포맷터
|
||||
export const formatSuccessResponse = (data, message = '성공적으로 처리되었습니다.') => {
|
||||
export const formatSuccessResponse = (data, message = '요청이 성공적으로 처리되었습니다.') => {
|
||||
return {
|
||||
success: true,
|
||||
message,
|
||||
data,
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,22 @@
|
||||
//* src/views/LoginView.vue
|
||||
//* src/views/LoginView.vue - 수정된 회원가입 기능
|
||||
<template>
|
||||
<v-container fluid class="login-container">
|
||||
<v-row justify="center" align="center" class="fill-height">
|
||||
<v-row justify="center" align="center" style="min-height: 100vh">
|
||||
<v-col cols="12" sm="8" md="6" lg="4" xl="3">
|
||||
<v-card class="login-card" elevation="8">
|
||||
<!-- 로고 섹션 -->
|
||||
<v-card-text class="text-center pa-8">
|
||||
<div class="logo-section mb-6">
|
||||
<v-img
|
||||
src="/images/logo192.png"
|
||||
alt="AI 마케팅 로고"
|
||||
width="80"
|
||||
height="80"
|
||||
class="mx-auto mb-4"
|
||||
/>
|
||||
<h1 class="text-h4 font-weight-bold primary--text mb-2">AI 마케팅</h1>
|
||||
<p class="text-subtitle-1 text-grey-darken-1">소상공인을 위한 스마트 마케팅 솔루션</p>
|
||||
</div>
|
||||
<!-- 로고 및 제목 -->
|
||||
<div class="text-center logo-section">
|
||||
<v-img src="/images/logo192.png" alt="AI 마케팅 로고" max-width="80" class="mx-auto mb-4" />
|
||||
<h1 class="text-h4 font-weight-bold text-primary mb-2">AI 마케팅</h1>
|
||||
<p class="text-subtitle-1 text-grey-darken-1">소상공인을 위한 스마트 마케팅 솔루션</p>
|
||||
</div>
|
||||
|
||||
<!-- 로그인 폼 -->
|
||||
<v-form ref="loginForm" v-model="isFormValid" @submit.prevent="handleLogin">
|
||||
<!-- 로그인 카드 -->
|
||||
<v-card class="login-card" elevation="8">
|
||||
<v-card-text class="pa-8">
|
||||
<v-form v-model="isFormValid" ref="loginForm" @submit.prevent="handleLogin">
|
||||
<h2 class="text-h5 font-weight-bold text-center mb-6">로그인</h2>
|
||||
|
||||
<!-- 아이디 입력 -->
|
||||
<v-text-field
|
||||
v-model="credentials.username"
|
||||
label="아이디"
|
||||
@ -32,6 +29,7 @@
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
|
||||
<!-- 비밀번호 입력 -->
|
||||
<v-text-field
|
||||
v-model="credentials.password"
|
||||
label="비밀번호"
|
||||
@ -77,15 +75,6 @@
|
||||
{{ loginError }}
|
||||
</v-alert>
|
||||
|
||||
<!-- 디버그 정보 (개발 중에만 표시) -->
|
||||
<v-card v-if="showDebugInfo" variant="outlined" class="mb-4 pa-3">
|
||||
<div class="text-caption">디버그 정보:</div>
|
||||
<div class="text-caption">아이디: "{{ credentials.username }}"</div>
|
||||
<div class="text-caption">비밀번호: "{{ credentials.password }}"</div>
|
||||
<div class="text-caption">폼 유효성: {{ isFormValid }}</div>
|
||||
<div class="text-caption">로딩 상태: {{ authStore.isLoading }}</div>
|
||||
</v-card>
|
||||
|
||||
<!-- 로그인 버튼 -->
|
||||
<v-btn
|
||||
type="submit"
|
||||
@ -102,7 +91,7 @@
|
||||
|
||||
<!-- 회원가입 링크 -->
|
||||
<div class="text-center">
|
||||
<span class="text-body-2 text-grey-darken-1"> 계정이 없으신가요? </span>
|
||||
<span class="text-body-2 text-grey-darken-1">계정이 없으신가요? </span>
|
||||
<v-btn variant="text" color="primary" size="small" @click="showSignup = true">
|
||||
회원가입
|
||||
</v-btn>
|
||||
@ -157,23 +146,147 @@
|
||||
</v-dialog>
|
||||
|
||||
<!-- 회원가입 다이얼로그 -->
|
||||
<v-dialog v-model="showSignup" max-width="500">
|
||||
<v-dialog v-model="showSignup" max-width="600" persistent>
|
||||
<v-card>
|
||||
<v-card-title>회원가입</v-card-title>
|
||||
<v-card-title class="d-flex justify-space-between align-center">
|
||||
<span>회원가입</span>
|
||||
<v-btn icon variant="text" @click="closeSignupDialog">
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="mb-4">AI 마케팅 서비스에 오신 것을 환영합니다!</p>
|
||||
<v-form>
|
||||
<v-text-field label="아이디" variant="outlined" class="mb-2" />
|
||||
<v-text-field label="비밀번호" type="password" variant="outlined" class="mb-2" />
|
||||
<v-text-field label="비밀번호 확인" type="password" variant="outlined" class="mb-2" />
|
||||
<v-text-field label="이메일" type="email" variant="outlined" class="mb-2" />
|
||||
<v-text-field label="매장명" variant="outlined" class="mb-2" />
|
||||
|
||||
<!-- 회원가입 폼 -->
|
||||
<v-form v-model="isSignupFormValid" ref="signupForm">
|
||||
<!-- 아이디 (중복체크 임시 제거) -->
|
||||
<v-text-field
|
||||
v-model="signupData.userId"
|
||||
label="아이디"
|
||||
variant="outlined"
|
||||
:rules="signupUserIdRules"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<!-- 이메일 (중복체크 임시 제거) -->
|
||||
<v-text-field
|
||||
v-model="signupData.email"
|
||||
label="이메일"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
:rules="emailRules"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<!-- 비밀번호 -->
|
||||
<v-text-field
|
||||
v-model="signupData.password"
|
||||
label="비밀번호"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
:rules="signupPasswordRules"
|
||||
class="mb-2"
|
||||
hint="8자 이상, 영문+숫자+특수문자(@$!%*?&) 조합"
|
||||
persistent-hint
|
||||
/>
|
||||
|
||||
<!-- 비밀번호 확인 -->
|
||||
<v-text-field
|
||||
v-model="signupData.passwordConfirm"
|
||||
label="비밀번호 확인"
|
||||
type="password"
|
||||
variant="outlined"
|
||||
:rules="passwordConfirmRules"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<!-- 이름 -->
|
||||
<v-text-field
|
||||
v-model="signupData.name"
|
||||
label="이름"
|
||||
variant="outlined"
|
||||
:rules="nameRules"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
|
||||
<!-- 사업자 번호 -->
|
||||
<v-text-field
|
||||
v-model="signupData.businessNumber"
|
||||
label="사업자 번호 (선택사항)"
|
||||
variant="outlined"
|
||||
:rules="businessNumberRules"
|
||||
class="mb-2"
|
||||
hint="10자리 숫자, '-' 없이 입력"
|
||||
persistent-hint
|
||||
/>
|
||||
|
||||
<!-- 중복 확인 상태 표시 (임시 숨김) -->
|
||||
<!--
|
||||
<div class="mb-4">
|
||||
<v-chip
|
||||
v-if="userIdChecked"
|
||||
color="success"
|
||||
size="small"
|
||||
class="mr-2"
|
||||
>
|
||||
<v-icon start size="small">mdi-check</v-icon>
|
||||
아이디 확인완료
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-if="emailChecked"
|
||||
color="success"
|
||||
size="small"
|
||||
>
|
||||
<v-icon start size="small">mdi-check</v-icon>
|
||||
이메일 확인완료
|
||||
</v-chip>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- 디버깅 정보 (개발 중에만 표시) -->
|
||||
<v-card v-if="isDev" variant="outlined" class="mb-4 pa-3">
|
||||
<div class="text-caption">디버깅 정보:</div>
|
||||
<div class="text-caption">폼 유효성: {{ isSignupFormValid }}</div>
|
||||
<div class="text-caption">아이디 확인: {{ userIdChecked }}</div>
|
||||
<div class="text-caption">이메일 확인: {{ emailChecked }}</div>
|
||||
<div class="text-caption">가입 가능: {{ canSignup }}</div>
|
||||
<div class="text-caption">로딩 상태: {{ signupLoading }}</div>
|
||||
</v-card>
|
||||
|
||||
<!-- 에러/성공 메시지 -->
|
||||
<v-alert
|
||||
v-if="signupError"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
class="mb-4"
|
||||
closable
|
||||
@click:close="signupError = ''"
|
||||
>
|
||||
{{ signupError }}
|
||||
</v-alert>
|
||||
|
||||
<v-alert
|
||||
v-if="signupSuccess"
|
||||
type="success"
|
||||
variant="tonal"
|
||||
class="mb-4"
|
||||
>
|
||||
{{ signupSuccess }}
|
||||
</v-alert>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="showSignup = false">취소</v-btn>
|
||||
<v-btn color="primary" @click="handleSignup">가입하기</v-btn>
|
||||
<v-btn variant="text" @click="closeSignupDialog">취소</v-btn>
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="handleSignup"
|
||||
:loading="signupLoading"
|
||||
:disabled="!canSignup"
|
||||
>
|
||||
가입하기
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
@ -181,30 +294,51 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/store/auth'
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { memberApi } from '@/services/api'
|
||||
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
// 반응형 데이터
|
||||
// 로그인 관련 반응형 데이터
|
||||
const loginForm = ref(null)
|
||||
const isFormValid = ref(false)
|
||||
const showPassword = ref(false)
|
||||
const rememberMe = ref(false)
|
||||
const loginError = ref('')
|
||||
const showForgotPassword = ref(false)
|
||||
const showSignup = ref(false)
|
||||
const forgotEmail = ref('')
|
||||
const showDebugInfo = ref(true) // 개발 중에는 true로 설정
|
||||
|
||||
// 로그인 자격 증명 - 기본값 설정
|
||||
// 회원가입 관련 반응형 데이터
|
||||
const showSignup = ref(false)
|
||||
const signupForm = ref(null)
|
||||
const isSignupFormValid = ref(false)
|
||||
const signupLoading = ref(false)
|
||||
const signupError = ref('')
|
||||
const signupSuccess = ref('')
|
||||
const checkingUserId = ref(false)
|
||||
const checkingEmail = ref(false)
|
||||
const userIdChecked = ref(false)
|
||||
const emailChecked = ref(false)
|
||||
|
||||
// 로그인 자격 증명
|
||||
const credentials = ref({
|
||||
username: 'user01', // 기본값 설정
|
||||
password: 'passw0rd', // 기본값 설정
|
||||
username: 'user01',
|
||||
password: 'passw0rd',
|
||||
})
|
||||
|
||||
// 회원가입 데이터
|
||||
const signupData = ref({
|
||||
userId: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
name: '',
|
||||
email: '',
|
||||
businessNumber: '',
|
||||
})
|
||||
|
||||
const fieldErrors = ref({
|
||||
@ -212,7 +346,19 @@ const fieldErrors = ref({
|
||||
password: [],
|
||||
})
|
||||
|
||||
// 유효성 검사 규칙
|
||||
// 개발 모드 여부
|
||||
const isDev = ref(import.meta.env.DEV)
|
||||
|
||||
// 가입 가능 여부 computed (중복체크 조건 임시 제거)
|
||||
const canSignup = computed(() => {
|
||||
return isSignupFormValid.value &&
|
||||
!signupLoading.value
|
||||
// 임시로 중복체크 조건 제거
|
||||
// userIdChecked.value &&
|
||||
// emailChecked.value &&
|
||||
})
|
||||
|
||||
// 로그인 유효성 검사 규칙
|
||||
const usernameRules = [
|
||||
(v) => !!v || '아이디를 입력해주세요',
|
||||
(v) => (v && v.length >= 3) || '아이디는 3자 이상이어야 합니다',
|
||||
@ -223,60 +369,77 @@ const passwordRules = [
|
||||
(v) => (v && v.length >= 6) || '비밀번호는 6자 이상이어야 합니다',
|
||||
]
|
||||
|
||||
// 메서드
|
||||
// 회원가입 유효성 검사 규칙
|
||||
const signupUserIdRules = [
|
||||
(v) => !!v || '아이디를 입력해주세요',
|
||||
(v) => (v && v.length >= 3) || '아이디는 3자 이상이어야 합니다',
|
||||
(v) => (v && v.length <= 20) || '아이디는 20자 이하여야 합니다',
|
||||
(v) => /^[a-zA-Z0-9_]+$/.test(v) || '아이디는 영문, 숫자, _만 사용 가능합니다',
|
||||
]
|
||||
|
||||
const signupPasswordRules = [
|
||||
(v) => !!v || '비밀번호를 입력해주세요',
|
||||
(v) => (v && v.length >= 8) || '비밀번호는 8자 이상이어야 합니다',
|
||||
(v) => (v && v.length <= 20) || '비밀번호는 20자 이하여야 합니다',
|
||||
// 임시로 완화된 규칙 (영문과 숫자만 체크)
|
||||
(v) => /^(?=.*[a-zA-Z])(?=.*\d)[A-Za-z\d@$!%*?&]/.test(v) ||
|
||||
'영문과 숫자를 포함해야 합니다 (특수문자 선택사항)',
|
||||
]
|
||||
|
||||
const passwordConfirmRules = [
|
||||
(v) => !!v || '비밀번호 확인을 입력해주세요',
|
||||
(v) => v === signupData.value.password || '비밀번호가 일치하지 않습니다',
|
||||
]
|
||||
|
||||
const nameRules = [
|
||||
(v) => !!v || '이름을 입력해주세요',
|
||||
(v) => (v && v.length >= 2) || '이름은 2자 이상이어야 합니다',
|
||||
(v) => (v && v.length <= 10) || '이름은 10자 이하여야 합니다',
|
||||
]
|
||||
|
||||
const emailRules = [
|
||||
(v) => !!v || '이메일을 입력해주세요',
|
||||
(v) => /.+@.+\..+/.test(v) || '올바른 이메일 형식이 아닙니다',
|
||||
]
|
||||
|
||||
const businessNumberRules = [
|
||||
(v) => !v || v.length === 10 || '사업자 번호는 10자리여야 합니다',
|
||||
(v) => !v || /^\d{10}$/.test(v) || '사업자 번호는 숫자만 입력 가능합니다',
|
||||
]
|
||||
|
||||
// 로그인 관련 메서드
|
||||
const fillDemoCredentials = () => {
|
||||
credentials.value.username = 'user01'
|
||||
credentials.value.password = 'passw0rd'
|
||||
loginError.value = ''
|
||||
console.log('데모 계정 정보 자동 입력 완료')
|
||||
}
|
||||
|
||||
const handleLogin = async () => {
|
||||
console.log('=== 로그인 시도 시작 ===')
|
||||
console.log('폼 유효성:', isFormValid.value)
|
||||
console.log('입력된 자격증명:', {
|
||||
username: credentials.value.username,
|
||||
password: credentials.value.password,
|
||||
usernameLength: credentials.value.username?.length,
|
||||
passwordLength: credentials.value.password?.length,
|
||||
})
|
||||
if (!isFormValid.value) return
|
||||
|
||||
// 폼 유효성 검사
|
||||
if (!isFormValid.value) {
|
||||
console.log('폼 유효성 검사 실패')
|
||||
return
|
||||
}
|
||||
|
||||
// 빈 값 체크
|
||||
if (!credentials.value.username || !credentials.value.password) {
|
||||
loginError.value = '아이디와 비밀번호를 모두 입력해주세요'
|
||||
return
|
||||
}
|
||||
|
||||
// 에러 초기화
|
||||
loginError.value = ''
|
||||
fieldErrors.value = { username: [], password: [] }
|
||||
|
||||
try {
|
||||
console.log('auth store 로그인 호출 중...')
|
||||
const result = await authStore.login({
|
||||
username: credentials.value.username.trim(), // 공백 제거
|
||||
password: credentials.value.password.trim(), // 공백 제거
|
||||
username: credentials.value.username.trim(),
|
||||
password: credentials.value.password.trim(),
|
||||
})
|
||||
|
||||
console.log('로그인 결과:', result)
|
||||
|
||||
if (result.success) {
|
||||
console.log('로그인 성공!')
|
||||
appStore.showSnackbar('로그인되었습니다', 'success')
|
||||
router.push('/dashboard')
|
||||
} else {
|
||||
console.log('로그인 실패:', result.error)
|
||||
loginError.value = result.error || '로그인에 실패했습니다'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('로그인 에러:', error)
|
||||
loginError.value = '서버 오류가 발생했습니다'
|
||||
loginError.value = '로그인 실패: ' + (error.message || '서버 오류가 발생했습니다')
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,26 +449,149 @@ const handleForgotPassword = () => {
|
||||
forgotEmail.value = ''
|
||||
}
|
||||
|
||||
const handleSignup = () => {
|
||||
appStore.showSnackbar('회원가입 기능은 곧 제공될 예정입니다', 'info')
|
||||
// 회원가입 관련 메서드
|
||||
const checkUserIdDuplicate = async () => {
|
||||
console.log('아이디 중복 확인 시작:', signupData.value.userId)
|
||||
|
||||
if (!signupData.value.userId || signupData.value.userId.length < 3) {
|
||||
signupError.value = '아이디를 3자 이상 입력해주세요'
|
||||
return
|
||||
}
|
||||
|
||||
checkingUserId.value = true
|
||||
signupError.value = ''
|
||||
|
||||
try {
|
||||
const response = await memberApi.get(`/check-duplicate/user-id?userId=${encodeURIComponent(signupData.value.userId)}`)
|
||||
|
||||
console.log('아이디 중복 확인 응답:', response.data)
|
||||
|
||||
if (response.data.success) {
|
||||
const isDuplicate = response.data.data.isDuplicate
|
||||
if (isDuplicate) {
|
||||
signupError.value = '이미 사용 중인 아이디입니다'
|
||||
userIdChecked.value = false
|
||||
} else {
|
||||
userIdChecked.value = true
|
||||
appStore.showSnackbar('사용 가능한 아이디입니다', 'success')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('아이디 중복 확인 실패:', error)
|
||||
signupError.value = '아이디 중복 확인에 실패했습니다'
|
||||
userIdChecked.value = false
|
||||
} finally {
|
||||
checkingUserId.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkEmailDuplicate = async () => {
|
||||
console.log('이메일 중복 확인 시작:', signupData.value.email)
|
||||
|
||||
if (!signupData.value.email || !/.+@.+\..+/.test(signupData.value.email)) {
|
||||
signupError.value = '올바른 이메일을 입력해주세요'
|
||||
return
|
||||
}
|
||||
|
||||
checkingEmail.value = true
|
||||
signupError.value = ''
|
||||
|
||||
try {
|
||||
const response = await memberApi.get(`/check-duplicate/email?email=${encodeURIComponent(signupData.value.email)}`)
|
||||
|
||||
console.log('이메일 중복 확인 응답:', response.data)
|
||||
|
||||
if (response.data.success) {
|
||||
const isDuplicate = response.data.data.isDuplicate
|
||||
if (isDuplicate) {
|
||||
signupError.value = '이미 사용 중인 이메일입니다'
|
||||
emailChecked.value = false
|
||||
} else {
|
||||
emailChecked.value = true
|
||||
appStore.showSnackbar('사용 가능한 이메일입니다', 'success')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('이메일 중복 확인 실패:', error)
|
||||
signupError.value = '이메일 중복 확인에 실패했습니다'
|
||||
emailChecked.value = false
|
||||
} finally {
|
||||
checkingEmail.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleSignup = async () => {
|
||||
console.log('회원가입 시도 시작')
|
||||
console.log('가입 데이터:', signupData.value)
|
||||
// console.log('중복 확인 상태 - 아이디:', userIdChecked.value, '이메일:', emailChecked.value) // 임시 주석
|
||||
|
||||
if (!canSignup.value) {
|
||||
signupError.value = '모든 필드를 올바르게 입력해주세요'
|
||||
return
|
||||
}
|
||||
|
||||
signupLoading.value = true
|
||||
signupError.value = ''
|
||||
signupSuccess.value = ''
|
||||
|
||||
try {
|
||||
const requestData = {
|
||||
userId: signupData.value.userId,
|
||||
password: signupData.value.password,
|
||||
name: signupData.value.name,
|
||||
email: signupData.value.email,
|
||||
businessNumber: signupData.value.businessNumber || null,
|
||||
}
|
||||
|
||||
console.log('회원가입 요청 데이터:', requestData)
|
||||
|
||||
const response = await memberApi.post('/register', requestData)
|
||||
|
||||
console.log('회원가입 응답:', response.data)
|
||||
|
||||
if (response.data.success) {
|
||||
signupSuccess.value = '회원가입이 완료되었습니다! 로그인해주세요'
|
||||
|
||||
// 3초 후 다이얼로그 닫기
|
||||
setTimeout(() => {
|
||||
closeSignupDialog()
|
||||
appStore.showSnackbar('회원가입이 완료되었습니다', 'success')
|
||||
}, 3000)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('회원가입 실패:', error)
|
||||
|
||||
if (error.response?.data?.message) {
|
||||
signupError.value = error.response.data.message
|
||||
} else {
|
||||
signupError.value = '회원가입에 실패했습니다. 다시 시도해주세요'
|
||||
}
|
||||
} finally {
|
||||
signupLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const closeSignupDialog = () => {
|
||||
showSignup.value = false
|
||||
signupData.value = {
|
||||
userId: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
name: '',
|
||||
email: '',
|
||||
businessNumber: '',
|
||||
}
|
||||
signupError.value = ''
|
||||
signupSuccess.value = ''
|
||||
userIdChecked.value = false
|
||||
emailChecked.value = false
|
||||
}
|
||||
|
||||
// 컴포넌트 마운트 시 실행
|
||||
onMounted(() => {
|
||||
console.log('LoginView 마운트됨')
|
||||
console.log('초기 자격증명:', credentials.value)
|
||||
|
||||
// 이미 로그인된 상태라면 대시보드로 리다이렉트
|
||||
if (authStore.isAuthenticated) {
|
||||
console.log('이미 로그인된 상태 - 대시보드로 이동')
|
||||
router.push('/dashboard')
|
||||
}
|
||||
|
||||
// 디버그용 - 3초 후 디버그 정보 숨기기
|
||||
setTimeout(() => {
|
||||
showDebugInfo.value = false
|
||||
}, 10000)
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -336,6 +622,10 @@ onMounted(() => {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.login-card {
|
||||
margin: 16px;
|
||||
@ -344,5 +634,14 @@ onMounted(() => {
|
||||
.logo-section {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.d-flex.align-center.gap-2 {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.d-flex.align-center.gap-2 .v-btn {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user