StoreManagementView.vue services/api.js edit

This commit is contained in:
unknown 2025-06-18 10:17:32 +09:00
parent bfc4d600e2
commit ccdc2c91bb
5 changed files with 1933 additions and 1505 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -1,4 +1,5 @@
//* src/services/api.js - 수정된 API URL 설정
//* src/services/api.js - 수정된 버전 (createImageApiInstance 함수 추가)
import axios from 'axios'
// 런타임 환경 설정에서 API URL 가져오기
@ -11,10 +12,9 @@ const getApiUrls = () => {
STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store',
CONTENT_URL: config.CONTENT_URL || 'http://localhost:8083/api/content',
MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu',
// ⚠️ 수정: 매출 API는 store 서비스 (포트 8082)
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
// ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084)
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations'
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations',
IMAGE_URL: config.IMAGE_URL || 'http://localhost:8082/api/images'
}
}
@ -37,7 +37,6 @@ const createApiInstance = (baseURL) => {
config.headers.Authorization = `Bearer ${token}`
}
// ⚠️ 추가: 요청 로깅 (개발 환경에서만)
if (import.meta.env.DEV) {
console.log(`🌐 [API_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
}
@ -52,14 +51,12 @@ const createApiInstance = (baseURL) => {
// 응답 인터셉터 - 토큰 갱신 및 에러 처리
instance.interceptors.response.use(
(response) => {
// ⚠️ 추가: 응답 로깅 (개발 환경에서만)
if (import.meta.env.DEV) {
console.log(`✅ [API_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
}
return response
},
async (error) => {
// ⚠️ 추가: 에러 로깅 (개발 환경에서만)
if (import.meta.env.DEV) {
console.error(`❌ [API_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
}
@ -102,10 +99,155 @@ const createApiInstance = (baseURL) => {
return instance
}
// ✅ 이미지 업로드 전용 API 인스턴스 생성 함수 추가
const createImageApiInstance = (baseURL) => {
const instance = axios.create({
baseURL,
timeout: 60000, // 이미지 업로드는 시간이 더 걸릴 수 있음
headers: {
Accept: 'application/json',
},
})
// 요청 인터셉터 - JWT 토큰 자동 추가
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
if (import.meta.env.DEV) {
console.log(`🌐 [IMG_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
console.log('FormData 포함:', config.data instanceof FormData)
}
return config
},
(error) => {
return Promise.reject(error)
},
)
// 응답 인터셉터
instance.interceptors.response.use(
(response) => {
if (import.meta.env.DEV) {
console.log(`✅ [IMG_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
}
return response
},
async (error) => {
if (import.meta.env.DEV) {
console.error(`❌ [IMG_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
}
// 토큰 갱신 로직은 기존과 동일
const originalRequest = error.config
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
try {
const refreshToken = localStorage.getItem('refreshToken')
if (refreshToken) {
const refreshResponse = await axios.post(`${getApiUrls().AUTH_URL}/refresh`, {
refreshToken,
})
const { accessToken, refreshToken: newRefreshToken } = refreshResponse.data.data
localStorage.setItem('accessToken', accessToken)
localStorage.setItem('refreshToken', newRefreshToken)
originalRequest.headers.Authorization = `Bearer ${accessToken}`
return instance(originalRequest)
}
} catch (refreshError) {
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
localStorage.removeItem('userInfo')
window.location.href = '/login'
}
}
return Promise.reject(error)
},
)
return instance
}
// ✅ 메뉴 이미지 업로드 전용 API 인스턴스 생성 함수 추가
const createMenuImageApiInstance = (baseURL) => {
const instance = axios.create({
baseURL,
timeout: 60000, // 이미지 업로드는 시간이 더 걸릴 수 있음
headers: {
Accept: 'application/json',
},
})
// 요청 인터셉터 - JWT 토큰 자동 추가
instance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
if (import.meta.env.DEV) {
console.log(`🌐 [MENU_IMG_REQ] ${config.method?.toUpperCase()} ${config.baseURL}${config.url}`)
console.log('FormData 포함:', config.data instanceof FormData)
}
return config
},
(error) => {
return Promise.reject(error)
},
)
// 응답 인터셉터
instance.interceptors.response.use(
(response) => {
if (import.meta.env.DEV) {
console.log(`✅ [MENU_IMG_RES] ${response.status} ${response.config?.method?.toUpperCase()} ${response.config?.url}`)
}
return response
},
async (error) => {
if (import.meta.env.DEV) {
console.error(`❌ [MENU_IMG_ERR] ${error.response?.status || 'Network'} ${error.config?.method?.toUpperCase()} ${error.config?.url}`, error.response?.data)
}
// 토큰 갱신 로직은 기존과 동일
const originalRequest = error.config
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
try {
const refreshToken = localStorage.getItem('refreshToken')
if (refreshToken) {
const refreshResponse = await axios.post(`${getApiUrls().AUTH_URL}/refresh`, {
refreshToken,
})
const { accessToken, refreshToken: newRefreshToken } = refreshResponse.data.data
localStorage.setItem('accessToken', accessToken)
localStorage.setItem('refreshToken', newRefreshToken)
originalRequest.headers.Authorization = `Bearer ${accessToken}`
return instance(originalRequest)
}
} catch (refreshError) {
localStorage.removeItem('accessToken')
localStorage.removeItem('refreshToken')
localStorage.removeItem('userInfo')
window.location.href = '/login'
}
}
return Promise.reject(error)
},
)
return instance
}
// API 인스턴스들 생성
const apiUrls = getApiUrls()
// ⚠️ 추가: API URL 확인 로깅 (개발 환경에서만)
if (import.meta.env.DEV) {
console.log('🔧 [API_CONFIG] API URLs 설정:', apiUrls)
}
@ -115,8 +257,11 @@ 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 menuImageApi = createMenuImageApiInstance(apiUrls.MENU_URL) // ✅ 추가
export const salesApi = createApiInstance(apiUrls.SALES_URL)
export const recommendApi = createApiInstance(apiUrls.RECOMMEND_URL)
export const imageApi = createApiInstance(apiUrls.IMAGE_URL)
export const apiWithImage = imageApi // 별칭 (기존 코드 호환성)
// 기본 API 인스턴스 (Gateway URL 사용)
export const api = createApiInstance(apiUrls.GATEWAY_URL)
@ -185,7 +330,7 @@ export const formatSuccessResponse = (data, message = '요청이 성공적으로
}
}
// ⚠️ 추가: API 상태 확인 함수
// API 상태 확인 함수
export const checkApiHealth = async () => {
const results = {}
@ -214,11 +359,14 @@ export const checkApiHealth = async () => {
return results
}
// ⚠️ 추가: 개발 환경에서 전역 노출
// 개발 환경에서 전역 노출
if (import.meta.env.DEV) {
window.__api_debug__ = {
urls: apiUrls,
instances: { memberApi, authApi, storeApi, contentApi, menuApi, salesApi, recommendApi },
instances: {
memberApi, authApi, storeApi, contentApi, menuApi, menuImageApi,
salesApi, recommendApi, imageApi
},
checkHealth: checkApiHealth
}
console.log('🔧 [DEBUG] API 인스턴스가 window.__api_debug__에 노출됨')

View File

@ -79,80 +79,88 @@ export const useStoreStore = defineStore('store', {
}
},
/**
* 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) - ID 필드 보장
*/
async fetchMenus() {
console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===')
// src/store/index.js에서 fetchMenus 부분만 수정
/**
* 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) - 이미지 필드 매핑 수정
*/
async fetchMenus() {
console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===')
try {
// 매장 정보에서 storeId 가져오기
const storeId = this.storeInfo?.storeId
if (!storeId) {
console.warn('매장 ID가 없습니다. 매장 정보를 먼저 조회해주세요.')
return { success: false, message: '매장 정보가 필요합니다', data: [] }
}
// 메뉴 서비스 임포트
const { menuService } = await import('@/services/menu')
console.log('메뉴 목록 API 호출, 매장 ID:', storeId)
const result = await menuService.getMenus(storeId)
console.log('=== Store 스토어: 메뉴 API 응답 분석 ===')
console.log('Result:', result)
console.log('Result.success:', result.success)
console.log('Result.data:', result.data)
console.log('Result.message:', result.message)
if (result.success && result.data) {
// ✅ 백엔드 MenuResponse의 필드명에 맞게 매핑 수정
const menusWithId = (result.data || []).map(menu => {
// ID 필드가 확실히 있도록 보장
const menuId = menu.menuId || menu.id
if (!menuId) {
console.warn('⚠️ 메뉴 ID가 없는 항목 발견:', menu)
}
console.log('메뉴 원본 데이터:', menu) // 디버깅용
return {
...menu,
id: menuId, // ✅ id 필드 확실히 설정
menuId: menuId, // ✅ menuId 필드도 설정
// 기타 필드들 보장
menuName: menu.menuName || menu.name || '이름 없음',
category: menu.category || '기타',
price: menu.price || 0,
description: menu.description || '',
available: menu.available !== undefined ? menu.available : true,
recommended: menu.recommended !== undefined ? menu.recommended : false,
// ✅ 이미지 필드 수정: 백엔드는 'image' 필드 사용
imageUrl: menu.image || menu.imageUrl || '/images/menu-placeholder.png',
image: menu.image || menu.imageUrl, // 백엔드 호환성
createdAt: menu.createdAt,
updatedAt: menu.updatedAt
}
})
try {
// 매장 정보에서 storeId 가져오기
const storeId = this.storeInfo?.storeId
if (!storeId) {
console.warn('매장 ID가 없습니다. 매장 정보를 먼저 조회해주세요.')
return { success: false, message: '매장 정보가 필요합니다', data: [] }
}
// 메뉴 서비스 임포트
const { menuService } = await import('@/services/menu')
console.log('메뉴 목록 API 호출, 매장 ID:', storeId)
const result = await menuService.getMenus(storeId)
console.log('=== Store 스토어: 메뉴 API 응답 분석 ===')
console.log('Result:', result)
console.log('Result.success:', result.success)
console.log('Result.data:', result.data)
console.log('Result.message:', result.message)
if (result.success && result.data) {
// ✅ 메뉴 데이터 ID 필드 보장 처리
const menusWithId = (result.data || []).map(menu => {
// ID 필드가 확실히 있도록 보장
const menuId = menu.menuId || menu.id
if (!menuId) {
console.warn('⚠️ 메뉴 ID가 없는 항목 발견:', menu)
}
return {
...menu,
id: menuId, // ✅ id 필드 확실히 설정
menuId: menuId, // ✅ menuId 필드도 설정
// 기타 필드들 보장
menuName: menu.menuName || menu.name || '이름 없음',
category: menu.category || '기타',
price: menu.price || 0,
description: menu.description || '',
available: menu.available !== undefined ? menu.available : true,
recommended: menu.recommended !== undefined ? menu.recommended : false,
imageUrl: menu.imageUrl || '/images/menu-placeholder.png'
}
})
// 메뉴 목록이 있는 경우
console.log('✅ 메뉴 목록 설정 (ID 보장됨):', menusWithId)
this.menus = menusWithId
return { success: true, data: menusWithId }
} else {
// 메뉴가 없거나 조회 실패한 경우
console.log('⚠️ 메뉴 목록 없음 또는 조회 실패')
this.menus = []
if (result.message === '등록된 메뉴가 없습니다') {
return { success: false, message: '등록된 메뉴가 없습니다', data: [] }
} else {
return { success: false, message: result.message || '메뉴 목록 조회에 실패했습니다', data: [] }
}
}
} catch (error) {
console.error('=== Store 스토어: 메뉴 목록 조회 실패 ===')
console.error('Error:', error)
this.menus = []
return { success: false, message: error.message || '메뉴 목록을 불러오는데 실패했습니다', data: [] }
// 메뉴 목록이 있는 경우
console.log('✅ 메뉴 목록 설정 (이미지 필드 매핑 완료):', menusWithId)
this.menus = menusWithId
return { success: true, data: menusWithId }
} else {
// 메뉴가 없거나 조회 실패한 경우
console.log('⚠️ 메뉴 목록 없음 또는 조회 실패')
this.menus = []
if (result.message === '등록된 메뉴가 없습니다') {
return { success: false, message: '등록된 메뉴가 없습니다', data: [] }
} else {
return { success: false, message: result.message || '메뉴 목록 조회에 실패했습니다', data: [] }
}
},
}
} catch (error) {
console.error('=== Store 스토어: 메뉴 목록 조회 실패 ===')
console.error('Error:', error)
this.menus = []
return { success: false, message: error.message || '메뉴 목록을 불러오는데 실패했습니다', data: [] }
}
},
/**
* 매장 등록

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff