[메뉴 권한 1차 작업]
This commit is contained in:
@@ -1,414 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4"
|
||||
>
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- 페이지 헤더 -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold text-gray-900 mb-4">공용 기능 테스트</h1>
|
||||
<p class="text-xl text-gray-600">
|
||||
API 및 공용 기능들의 동작을 테스트할 수 있습니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- API 테스트 섹션 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
|
||||
<!-- 좌측: 자동 에러 처리 테스트 -->
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<!-- 자동 에러 처리 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">1</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
자동 에러 처리 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
useApi 함수의 자동 에러 처리 기능을 테스트합니다. 에러가 발생하면
|
||||
자동으로 alert가 표시됩니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoading"
|
||||
@click="apiTest"
|
||||
>
|
||||
{{ isLoading ? "테스트 중..." : "자동 에러 처리 테스트" }}
|
||||
</button>
|
||||
<div
|
||||
v-if="autoErrorResult"
|
||||
class="mt-3 p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<h4 class="font-medium text-gray-900 mb-2">결과:</h4>
|
||||
<pre class="text-sm text-gray-700 whitespace-pre-wrap">{{
|
||||
autoErrorResult
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-blue-50 rounded border">
|
||||
<h5 class="font-medium text-blue-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-blue-800 whitespace-pre-wrap">
|
||||
{`// 자동 에러 처리 예제
|
||||
const apiTest = async () => {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222"
|
||||
);
|
||||
|
||||
if (response) {
|
||||
console.log("response:", response);
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 직접 에러 처리 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-green-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">2</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
직접 에러 처리 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
useApi 함수의 직접 에러 처리 기능을 테스트합니다. 에러 타입에 따른
|
||||
세밀한 처리를 확인할 수 있습니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-green-500 hover:bg-green-600 disabled:bg-gray-400 text-white font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingCustom"
|
||||
@click="apiTestWithCustomError"
|
||||
>
|
||||
{{ isLoadingCustom ? "테스트 중..." : "직접 에러 처리 테스트" }}
|
||||
</button>
|
||||
<div
|
||||
v-if="customErrorResult"
|
||||
class="mt-3 p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<h4 class="font-medium text-gray-900 mb-2">결과:</h4>
|
||||
<pre class="text-sm text-gray-700 whitespace-pre-wrap">{{
|
||||
customErrorResult
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-green-50 rounded border">
|
||||
<h5 class="font-medium text-green-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-green-800 whitespace-pre-wrap">
|
||||
{`// 직접 에러 처리 예제
|
||||
const apiTestWithCustomError = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222",
|
||||
{
|
||||
handleError: false, // 에러를 직접 처리
|
||||
showAlert: false, // alert 표시 안함
|
||||
}
|
||||
);
|
||||
|
||||
if (response) {
|
||||
console.log("response:", response);
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 에러 타입에 따른 세밀한 처리
|
||||
if (error.response?.status === 404) {
|
||||
alert("요청한 코드를 찾을 수 없습니다.");
|
||||
} else if (error.response?.status === 403) {
|
||||
alert("접근 권한이 없습니다.");
|
||||
} else if (error.response?.status >= 500) {
|
||||
alert("서버 오류가 발생했습니다.");
|
||||
} else {
|
||||
alert("알 수 없는 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 우측: 추가 API 테스트 -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-purple-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">3</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
추가 API 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
다양한 API 엔드포인트를 테스트할 수 있습니다.
|
||||
</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-purple-500 hover:bg-purple-600 disabled:bg-gray-400 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingValid"
|
||||
@click="testValidEndpoint"
|
||||
>
|
||||
{{ isLoadingValid ? "테스트 중..." : "유효한 엔드포인트" }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-full bg-red-500 hover:bg-red-600 disabled:bg-gray-400 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingNetwork"
|
||||
@click="testNetworkError"
|
||||
>
|
||||
{{ isLoadingNetwork ? "테스트 중..." : "네트워크 에러" }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
@click="clearResults"
|
||||
>
|
||||
결과 초기화
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="additionalResults.length > 0" class="mt-4 space-y-3">
|
||||
<h4 class="font-medium text-gray-900">추가 테스트 결과:</h4>
|
||||
<div
|
||||
v-for="(result, index) in additionalResults"
|
||||
:key="index"
|
||||
class="p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="font-medium text-gray-900 text-sm">{{
|
||||
result.title
|
||||
}}</span>
|
||||
<span class="text-xs text-gray-500">{{
|
||||
result.timestamp
|
||||
}}</span>
|
||||
</div>
|
||||
<pre class="text-xs text-gray-700 whitespace-pre-wrap">{{
|
||||
result.data
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-purple-50 rounded border">
|
||||
<h5 class="font-medium text-purple-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-purple-800 whitespace-pre-wrap">
|
||||
{`// 추가 API 테스트 예제
|
||||
const testValidEndpoint = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
console.log("성공:", response);
|
||||
} catch (error: any) {
|
||||
console.log("에러:", error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const testNetworkError = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/non-existent-endpoint",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.log("네트워크 에러:", error.message);
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 사용법 가이드 -->
|
||||
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold text-yellow-800 mb-3">
|
||||
사용법 가이드
|
||||
</h3>
|
||||
<div class="text-yellow-700 space-y-2">
|
||||
<p>
|
||||
<strong>자동 에러 처리:</strong> useApi 함수가 에러를 자동으로
|
||||
처리하고 사용자에게 알림을 표시합니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>직접 에러 처리:</strong> handleError: false 옵션을 사용하여
|
||||
에러를 직접 처리할 수 있습니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>에러 타입:</strong> 404 (Not Found), 403 (Forbidden), 500+
|
||||
(Server Error) 등 다양한 에러 상황을 테스트할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import { useUserStore } from "~/stores/user"; // 현재 사용하지 않음
|
||||
|
||||
// 페이지 메타데이터 설정
|
||||
definePageMeta({
|
||||
title: "공용 기능 테스트",
|
||||
description: "API 및 공용 기능들의 동작을 테스트하는 페이지",
|
||||
});
|
||||
|
||||
// const userStore = useUserStore(); // 현재 사용하지 않음
|
||||
|
||||
// 반응형 데이터
|
||||
const isLoading = ref(false);
|
||||
const isLoadingCustom = ref(false);
|
||||
const isLoadingValid = ref(false);
|
||||
const isLoadingNetwork = ref(false);
|
||||
const autoErrorResult = ref("");
|
||||
const customErrorResult = ref("");
|
||||
const additionalResults = ref<
|
||||
Array<{ title: string; data: string; timestamp: string }>
|
||||
>([]);
|
||||
|
||||
// 테스트 다운로드 함수 (자동 에러 처리)
|
||||
const apiTest = async () => {
|
||||
isLoading.value = true;
|
||||
autoErrorResult.value = "";
|
||||
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222"
|
||||
);
|
||||
|
||||
if (response) {
|
||||
autoErrorResult.value = `성공: ${JSON.stringify(response, null, 2)}`;
|
||||
} else {
|
||||
autoErrorResult.value = "응답이 없습니다.";
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
};
|
||||
|
||||
// 직접 에러 처리하는 함수 예시
|
||||
const apiTestWithCustomError = async () => {
|
||||
isLoadingCustom.value = true;
|
||||
customErrorResult.value = "";
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222",
|
||||
{
|
||||
handleError: false, // 에러를 직접 처리하겠다는 의미
|
||||
showAlert: false, // alert는 표시하지 않음
|
||||
}
|
||||
);
|
||||
|
||||
if (response) {
|
||||
customErrorResult.value = `성공: ${JSON.stringify(response, null, 2)}`;
|
||||
} else {
|
||||
customErrorResult.value = "응답이 없습니다.";
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 에러 타입에 처리
|
||||
let errorMessage = "";
|
||||
if (error.response?.status === 404) {
|
||||
errorMessage = "[errorCustomHandler]요청한 코드를 찾을 수 없습니다.";
|
||||
} else if (error.response?.status === 403) {
|
||||
errorMessage = "[errorCustomHandler]접근 권한이 없습니다.";
|
||||
} else if (error.response?.status >= 500) {
|
||||
errorMessage =
|
||||
"[errorCustomHandler]서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.";
|
||||
} else {
|
||||
errorMessage = "[errorCustomHandler]알 수 없는 오류가 발생했습니다.";
|
||||
}
|
||||
|
||||
customErrorResult.value = `에러 처리됨: ${errorMessage}\n상세 정보: ${JSON.stringify(error.response?.data || error.message, null, 2)}`;
|
||||
} finally {
|
||||
isLoadingCustom.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 유효한 엔드포인트 테스트
|
||||
const testValidEndpoint = async () => {
|
||||
isLoadingValid.value = true;
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
additionalResults.value.unshift({
|
||||
title: "유효한 엔드포인트 테스트",
|
||||
data: response ? JSON.stringify(response, null, 2) : "응답 없음",
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
additionalResults.value.unshift({
|
||||
title: "유효한 엔드포인트 테스트 (에러)",
|
||||
data: `에러: ${error.message || "알 수 없는 에러"}`,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} finally {
|
||||
isLoadingValid.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 네트워크 에러 테스트
|
||||
const testNetworkError = async () => {
|
||||
isLoadingNetwork.value = true;
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/non-existent-endpoint",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
additionalResults.value.unshift({
|
||||
title: "네트워크 에러 테스트",
|
||||
data: response ? JSON.stringify(response, null, 2) : "응답 없음",
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
additionalResults.value.unshift({
|
||||
title: "네트워크 에러 테스트 (에러)",
|
||||
data: `에러: ${error.message || "알 수 없는 에러"}\n상태 코드: ${error.response?.status || "N/A"}`,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} finally {
|
||||
isLoadingNetwork.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 결과 초기화
|
||||
const clearResults = () => {
|
||||
autoErrorResult.value = "";
|
||||
customErrorResult.value = "";
|
||||
additionalResults.value = [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 추가 스타일이 필요한 경우 여기에 작성 */
|
||||
</style>
|
||||
497
pages/[tabId]/test/common-test.vue
Normal file
497
pages/[tabId]/test/common-test.vue
Normal file
@@ -0,0 +1,497 @@
|
||||
<template>
|
||||
<div
|
||||
class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4"
|
||||
>
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- 페이지 헤더 -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold text-gray-900 mb-4">공용 기능 테스트</h1>
|
||||
<p class="text-xl text-gray-600">
|
||||
API 및 공용 기능들의 동작을 테스트할 수 있습니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- API 테스트 섹션 -->
|
||||
<div class="mb-12">
|
||||
<div class="text-center mb-6">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-2">API 테스트</h2>
|
||||
<p class="text-lg text-gray-600">
|
||||
useApi 함수의 다양한 기능을 테스트합니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<!-- 좌측: 자동 에러 처리 테스트 -->
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<!-- 자동 에러 처리 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">1</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
자동 에러 처리 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
useApi 함수의 자동 에러 처리 기능을 테스트합니다. 에러가
|
||||
발생하면 자동으로 alert가 표시됩니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoading"
|
||||
@click="apiTest"
|
||||
>
|
||||
{{ isLoading ? "테스트 중..." : "자동 에러 처리 테스트" }}
|
||||
</button>
|
||||
<div
|
||||
v-if="autoErrorResult"
|
||||
class="mt-3 p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<h4 class="font-medium text-gray-900 mb-2">결과:</h4>
|
||||
<pre class="text-sm text-gray-700 whitespace-pre-wrap">{{
|
||||
autoErrorResult
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-blue-50 rounded border">
|
||||
<h5 class="font-medium text-blue-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-blue-800 whitespace-pre-wrap">
|
||||
{`// 자동 에러 처리 예제
|
||||
const apiTest = async () => {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222"
|
||||
);
|
||||
|
||||
if (response) {
|
||||
console.log("response:", response);
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 직접 에러 처리 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-green-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">2</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
직접 에러 처리 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
useApi 함수의 직접 에러 처리 기능을 테스트합니다. 에러 타입에
|
||||
따른 세밀한 처리를 확인할 수 있습니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-green-500 hover:bg-green-600 disabled:bg-gray-400 text-white font-medium py-3 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingCustom"
|
||||
@click="apiTestWithCustomError"
|
||||
>
|
||||
{{
|
||||
isLoadingCustom ? "테스트 중..." : "직접 에러 처리 테스트"
|
||||
}}
|
||||
</button>
|
||||
<div
|
||||
v-if="customErrorResult"
|
||||
class="mt-3 p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<h4 class="font-medium text-gray-900 mb-2">결과:</h4>
|
||||
<pre class="text-sm text-gray-700 whitespace-pre-wrap">{{
|
||||
customErrorResult
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-green-50 rounded border">
|
||||
<h5 class="font-medium text-green-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-green-800 whitespace-pre-wrap">
|
||||
{`// 직접 에러 처리 예제
|
||||
const apiTestWithCustomError = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222",
|
||||
{
|
||||
handleError: false, // 에러를 직접 처리
|
||||
showAlert: false, // alert 표시 안함
|
||||
}
|
||||
);
|
||||
|
||||
if (response) {
|
||||
console.log("response:", response);
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 에러 타입에 따른 세밀한 처리
|
||||
if (error.response?.status === 404) {
|
||||
alert("요청한 코드를 찾을 수 없습니다.");
|
||||
} else if (error.response?.status === 403) {
|
||||
alert("접근 권한이 없습니다.");
|
||||
} else if (error.response?.status >= 500) {
|
||||
alert("서버 오류가 발생했습니다.");
|
||||
} else {
|
||||
alert("알 수 없는 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 우측: 추가 API 테스트 -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-purple-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">3</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
추가 API 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
다양한 API 엔드포인트를 테스트할 수 있습니다.
|
||||
</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<button
|
||||
class="w-full bg-purple-500 hover:bg-purple-600 disabled:bg-gray-400 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingValid"
|
||||
@click="testValidEndpoint"
|
||||
>
|
||||
{{ isLoadingValid ? "테스트 중..." : "유효한 엔드포인트" }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-full bg-red-500 hover:bg-red-600 disabled:bg-gray-400 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
:disabled="isLoadingNetwork"
|
||||
@click="testNetworkError"
|
||||
>
|
||||
{{ isLoadingNetwork ? "테스트 중..." : "네트워크 에러" }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-full bg-gray-500 hover:bg-gray-600 text-white font-medium py-2 px-4 rounded-lg transition-colors"
|
||||
@click="clearResults"
|
||||
>
|
||||
결과 초기화
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="additionalResults.length > 0" class="mt-4 space-y-3">
|
||||
<h4 class="font-medium text-gray-900">추가 테스트 결과:</h4>
|
||||
<div
|
||||
v-for="(result, index) in additionalResults"
|
||||
:key="index"
|
||||
class="p-3 bg-gray-50 rounded border"
|
||||
>
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="font-medium text-gray-900 text-sm">{{
|
||||
result.title
|
||||
}}</span>
|
||||
<span class="text-xs text-gray-500">{{
|
||||
result.timestamp
|
||||
}}</span>
|
||||
</div>
|
||||
<pre class="text-xs text-gray-700 whitespace-pre-wrap">{{
|
||||
result.data
|
||||
}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 예제 소스 -->
|
||||
<div class="mt-4 p-3 bg-purple-50 rounded border">
|
||||
<h5 class="font-medium text-purple-900 mb-2">예제 소스:</h5>
|
||||
<pre class="text-xs text-purple-800 whitespace-pre-wrap">
|
||||
{`// 추가 API 테스트 예제
|
||||
const testValidEndpoint = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
console.log("성공:", response);
|
||||
} catch (error: any) {
|
||||
console.log("에러:", error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const testNetworkError = async () => {
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/non-existent-endpoint",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
} catch (error: any) {
|
||||
console.log("네트워크 에러:", error.message);
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 권한 테스트 섹션 -->
|
||||
<div class="mb-12">
|
||||
<div class="text-center mb-6">
|
||||
<h2 class="text-3xl font-bold text-gray-900 mb-2">
|
||||
권한 시스템 테스트
|
||||
</h2>
|
||||
<p class="text-lg text-gray-600">
|
||||
API 권한, 메뉴 권한, 컴포넌트 권한의 동작을 테스트합니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-md p-8">
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="w-16 h-16 bg-indigo-500 rounded-full flex items-center justify-center mx-auto mb-4"
|
||||
>
|
||||
<span class="text-white font-bold text-2xl">🔐</span>
|
||||
</div>
|
||||
<h3 class="text-2xl font-semibold text-gray-900 mb-4">
|
||||
권한 시스템 테스트
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-6">
|
||||
로그인 후 권한 데이터가 자동으로 로드되며, API 권한, 메뉴 권한,
|
||||
컴포넌트 권한의 동작을 확인할 수 있습니다.
|
||||
</p>
|
||||
<NuxtLink
|
||||
to="/admin/permission-test"
|
||||
class="inline-flex items-center bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-3 px-6 rounded-lg transition-colors"
|
||||
>
|
||||
<span class="mr-2">권한 테스트 시작</span>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
></path>
|
||||
</svg>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 사용법 가이드 -->
|
||||
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold text-yellow-800 mb-3">
|
||||
사용법 가이드
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h4 class="font-semibold text-yellow-800 mb-2">API 테스트</h4>
|
||||
<div class="text-yellow-700 space-y-2 text-sm">
|
||||
<p>
|
||||
<strong>자동 에러 처리:</strong> useApi 함수가 에러를 자동으로
|
||||
처리하고 사용자에게 알림을 표시합니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>직접 에러 처리:</strong> handleError: false 옵션을
|
||||
사용하여 에러를 직접 처리할 수 있습니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>에러 타입:</strong> 404 (Not Found), 403 (Forbidden),
|
||||
500+ (Server Error) 등 다양한 에러 상황을 테스트할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold text-yellow-800 mb-2">
|
||||
권한 시스템 테스트
|
||||
</h4>
|
||||
<div class="text-yellow-700 space-y-2 text-sm">
|
||||
<p>
|
||||
<strong>API 권한:</strong> 페이지 라우터 접근 권한을 제어합니다.
|
||||
권한이 없으면 홈으로 리다이렉트됩니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>메뉴 권한:</strong> 메뉴 표시 여부를 제어합니다. 권한이
|
||||
없으면 메뉴가 숨겨집니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>컴포넌트 권한:</strong> 버튼 등 UI 컴포넌트의 표시
|
||||
여부를 제어합니다. 권한이 없으면 컴포넌트가 렌더링되지 않습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// import { useUserStore } from "~/stores/user"; // 현재 사용하지 않음
|
||||
|
||||
// 페이지 메타데이터 설정
|
||||
definePageMeta({
|
||||
title: "공용 기능 테스트",
|
||||
description: "API 및 공용 기능들의 동작을 테스트하는 페이지",
|
||||
});
|
||||
|
||||
// const userStore = useUserStore(); // 현재 사용하지 않음
|
||||
|
||||
// 반응형 데이터
|
||||
const isLoading = ref(false);
|
||||
const isLoadingCustom = ref(false);
|
||||
const isLoadingValid = ref(false);
|
||||
const isLoadingNetwork = ref(false);
|
||||
const autoErrorResult = ref("");
|
||||
const customErrorResult = ref("");
|
||||
const additionalResults = ref<
|
||||
Array<{ title: string; data: string; timestamp: string }>
|
||||
>([]);
|
||||
|
||||
// 테스트 다운로드 함수 (자동 에러 처리)
|
||||
const apiTest = async () => {
|
||||
isLoading.value = true;
|
||||
autoErrorResult.value = "";
|
||||
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222"
|
||||
);
|
||||
|
||||
if (response) {
|
||||
autoErrorResult.value = `성공: ${JSON.stringify(response, null, 2)}`;
|
||||
} else {
|
||||
autoErrorResult.value = "응답이 없습니다.";
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
};
|
||||
|
||||
// 직접 에러 처리하는 함수 예시
|
||||
const apiTestWithCustomError = async () => {
|
||||
isLoadingCustom.value = true;
|
||||
customErrorResult.value = "";
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE222",
|
||||
{
|
||||
handleError: false, // 에러를 직접 처리하겠다는 의미
|
||||
showAlert: false, // alert는 표시하지 않음
|
||||
}
|
||||
);
|
||||
|
||||
if (response) {
|
||||
customErrorResult.value = `성공: ${JSON.stringify(response, null, 2)}`;
|
||||
} else {
|
||||
customErrorResult.value = "응답이 없습니다.";
|
||||
}
|
||||
} catch (error: any) {
|
||||
// 에러 타입에 처리
|
||||
let errorMessage = "";
|
||||
if (error.response?.status === 404) {
|
||||
errorMessage = "[errorCustomHandler]요청한 코드를 찾을 수 없습니다.";
|
||||
} else if (error.response?.status === 403) {
|
||||
errorMessage = "[errorCustomHandler]접근 권한이 없습니다.";
|
||||
} else if (error.response?.status >= 500) {
|
||||
errorMessage =
|
||||
"[errorCustomHandler]서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.";
|
||||
} else {
|
||||
errorMessage = "[errorCustomHandler]알 수 없는 오류가 발생했습니다.";
|
||||
}
|
||||
|
||||
customErrorResult.value = `에러 처리됨: ${errorMessage}\n상세 정보: ${JSON.stringify(error.response?.data || error.message, null, 2)}`;
|
||||
} finally {
|
||||
isLoadingCustom.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 유효한 엔드포인트 테스트
|
||||
const testValidEndpoint = async () => {
|
||||
isLoadingValid.value = true;
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/admin/common-codes/USER_STATUS_ACTIVE",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
additionalResults.value.unshift({
|
||||
title: "유효한 엔드포인트 테스트",
|
||||
data: response ? JSON.stringify(response, null, 2) : "응답 없음",
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
additionalResults.value.unshift({
|
||||
title: "유효한 엔드포인트 테스트 (에러)",
|
||||
data: `에러: ${error.message || "알 수 없는 에러"}`,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} finally {
|
||||
isLoadingValid.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 네트워크 에러 테스트
|
||||
const testNetworkError = async () => {
|
||||
isLoadingNetwork.value = true;
|
||||
|
||||
try {
|
||||
const response = await useApi<ApiResponse<object>>(
|
||||
"/non-existent-endpoint",
|
||||
{
|
||||
handleError: false,
|
||||
showAlert: false,
|
||||
}
|
||||
);
|
||||
|
||||
additionalResults.value.unshift({
|
||||
title: "네트워크 에러 테스트",
|
||||
data: response ? JSON.stringify(response, null, 2) : "응답 없음",
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
additionalResults.value.unshift({
|
||||
title: "네트워크 에러 테스트 (에러)",
|
||||
data: `에러: ${error.message || "알 수 없는 에러"}\n상태 코드: ${error.response?.status || "N/A"}`,
|
||||
timestamp: new Date().toLocaleTimeString(),
|
||||
});
|
||||
} finally {
|
||||
isLoadingNetwork.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 결과 초기화
|
||||
const clearResults = () => {
|
||||
autoErrorResult.value = "";
|
||||
customErrorResult.value = "";
|
||||
additionalResults.value = [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 추가 스타일이 필요한 경우 여기에 작성 */
|
||||
</style>
|
||||
589
pages/admin/permission-test.vue
Normal file
589
pages/admin/permission-test.vue
Normal file
@@ -0,0 +1,589 @@
|
||||
<template>
|
||||
<div
|
||||
class="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 py-12 px-4"
|
||||
>
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- 페이지 헤더 -->
|
||||
<div class="text-center mb-8">
|
||||
<h1 class="text-4xl font-bold text-gray-900 mb-4">
|
||||
권한 시스템 테스트
|
||||
</h1>
|
||||
<p class="text-xl text-gray-600">
|
||||
페이지 권한, 메뉴 권한, 컴포넌트 권한의 동작을 테스트할 수 있습니다
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 현재 상태 표시 -->
|
||||
<div class="bg-gray-100 p-4 rounded-lg mb-6">
|
||||
<h2 class="text-xl font-semibold mb-2">현재 상태</h2>
|
||||
<div class="space-y-2">
|
||||
<p>
|
||||
<strong>로그인 상태:</strong>
|
||||
<span
|
||||
:class="userStore.isLoggedIn ? 'text-green-600' : 'text-red-600'"
|
||||
>
|
||||
{{ userStore.isLoggedIn ? "로그인됨" : "로그인 안됨" }}
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="userStore.user">
|
||||
<strong>사용자:</strong> {{ userStore.user.name }} ({{
|
||||
userStore.user.userId
|
||||
}})
|
||||
</p>
|
||||
<p>
|
||||
<strong>권한 로딩:</strong>
|
||||
{{ permission.isLoading ? "로딩 중..." : "완료" }}
|
||||
</p>
|
||||
<p v-if="!userStore.isLoggedIn" class="text-orange-600 text-sm">
|
||||
<strong>참고:</strong> 로그인이 필요합니다. 로그인 후 권한 데이터가
|
||||
자동으로 로드됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 권한 테스트 섹션 -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
|
||||
<!-- 좌측: 권한 상태 및 체크 -->
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<!-- 현재 권한 상태 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-blue-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">1</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
현재 권한 상태
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
현재 사용자의 권한 상태와 페이지 권한 체크를 확인합니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2">기본 정보:</h4>
|
||||
<div class="space-y-1 text-sm">
|
||||
<p>
|
||||
<strong>현재 경로:</strong> <code>{{ $route.path }}</code>
|
||||
</p>
|
||||
<p>
|
||||
<strong>로그인 상태:</strong>
|
||||
<span
|
||||
:class="
|
||||
userStore.isLoggedIn ? 'text-green-600' : 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{ userStore.isLoggedIn ? "로그인됨" : "로그인 안됨" }}
|
||||
</span>
|
||||
</p>
|
||||
<p v-if="userStore.user">
|
||||
<strong>사용자:</strong> {{ userStore.user.name }} ({{
|
||||
userStore.user.userId
|
||||
}})
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2">권한 체크 결과:</h4>
|
||||
<div class="space-y-1 text-sm">
|
||||
<p>
|
||||
<strong>페이지 권한({{ $route.path }}):</strong>
|
||||
<span
|
||||
:class="
|
||||
permission.hasPagePermission($route.path)
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasPagePermission($route.path)
|
||||
? "있음"
|
||||
: "없음"
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<strong>관리자 메뉴 권한(M01):</strong>
|
||||
<span
|
||||
:class="
|
||||
permission.hasMenuPermission('M01')
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasMenuPermission("M01") ? "있음" : "없음"
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 권한별 버튼 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-green-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">2</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
컴포넌트 권한 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
각 컴포넌트 권한에 따라 버튼이 표시되거나 숨겨지는 것을
|
||||
확인합니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010105')"
|
||||
class="bg-green-500 text-white px-3 py-1 rounded text-sm hover:bg-green-600"
|
||||
>
|
||||
생성 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>생성 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010102')"
|
||||
class="bg-blue-500 text-white px-3 py-1 rounded text-sm hover:bg-blue-600"
|
||||
>
|
||||
수정 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>수정 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010101')"
|
||||
class="bg-red-500 text-white px-3 py-1 rounded text-sm hover:bg-red-600"
|
||||
>
|
||||
삭제 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>삭제 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010103')"
|
||||
class="bg-purple-500 text-white px-3 py-1 rounded text-sm hover:bg-purple-600"
|
||||
>
|
||||
내보내기 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>내보내기 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010104')"
|
||||
class="bg-orange-500 text-white px-3 py-1 rounded text-sm hover:bg-orange-600"
|
||||
>
|
||||
가져오기 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>가져오기 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<button
|
||||
v-if="permission.hasComponentPermission('C010106')"
|
||||
class="bg-gray-500 text-white px-3 py-1 rounded text-sm hover:bg-gray-600"
|
||||
>
|
||||
보기 버튼
|
||||
</button>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>보기 버튼 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 메뉴 권한 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-orange-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">3</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
메뉴 권한 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
메뉴 권한에 따라 메뉴가 표시되거나 숨겨지는 것을 확인합니다.
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
v-if="permission.hasMenuPermission('M01')"
|
||||
class="bg-blue-100 text-blue-800 px-3 py-1 rounded text-sm"
|
||||
>
|
||||
관리자 메뉴
|
||||
</div>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>관리자 메뉴 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
v-if="permission.hasMenuPermission('M02')"
|
||||
class="bg-green-100 text-green-800 px-3 py-1 rounded text-sm"
|
||||
>
|
||||
사용자 메뉴
|
||||
</div>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>사용자 메뉴 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
v-if="permission.hasMenuPermission('M03')"
|
||||
class="bg-purple-100 text-purple-800 px-3 py-1 rounded text-sm"
|
||||
>
|
||||
설정 메뉴
|
||||
</div>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>설정 메뉴 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<div
|
||||
v-if="permission.hasMenuPermission('M04')"
|
||||
class="bg-yellow-100 text-yellow-800 px-3 py-1 rounded text-sm"
|
||||
>
|
||||
보고서 메뉴
|
||||
</div>
|
||||
<span v-else class="text-gray-400 text-sm"
|
||||
>보고서 메뉴 (권한 없음)</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 우측: 권한 체크 로직 테스트 -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="bg-white rounded-lg shadow-md p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-purple-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">4</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
권한 체크 로직
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
다양한 권한 체크 함수들의 동작을 테스트합니다.
|
||||
</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2 text-sm">
|
||||
페이지 권한 체크:
|
||||
</h4>
|
||||
<p class="text-xs text-gray-600 mb-1">
|
||||
<code>hasPagePermission('/admin/codes')</code>
|
||||
</p>
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="
|
||||
permission.hasPagePermission('/admin/codes')
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasPagePermission("/admin/codes")
|
||||
? "true"
|
||||
: "false"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2 text-sm">
|
||||
메뉴 권한 체크:
|
||||
</h4>
|
||||
<p class="text-xs text-gray-600 mb-1">
|
||||
<code>hasMenuPermission('M000001')</code>
|
||||
</p>
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="
|
||||
permission.hasMenuPermission('M000001')
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasMenuPermission("M000001") ? "true" : "false"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2 text-sm">
|
||||
여러 권한 중 하나라도 있는지:
|
||||
</h4>
|
||||
<p class="text-xs text-gray-600 mb-1">
|
||||
<code>hasAnyComponentPermission(['C010105', 'C010102'])</code>
|
||||
</p>
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="
|
||||
permission.hasAnyComponentPermission(['C010105', 'C010102'])
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasAnyComponentPermission(["C010105", "C010102"])
|
||||
? "true"
|
||||
: "false"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="p-3 bg-gray-50 rounded border">
|
||||
<h4 class="font-medium text-gray-900 mb-2 text-sm">
|
||||
모든 권한이 있는지:
|
||||
</h4>
|
||||
<p class="text-xs text-gray-600 mb-1">
|
||||
<code
|
||||
>hasAllComponentPermissions(['C010105', 'C010102'])</code
|
||||
>
|
||||
</p>
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="
|
||||
permission.hasAllComponentPermissions([
|
||||
'C010105',
|
||||
'C010102',
|
||||
])
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasAllComponentPermissions([
|
||||
"C010105",
|
||||
"C010102",
|
||||
])
|
||||
? "true"
|
||||
: "false"
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- API 권한 테스트 -->
|
||||
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
|
||||
<div class="flex items-center mb-4">
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-500 rounded-full flex items-center justify-center mr-3"
|
||||
>
|
||||
<span class="text-white font-bold text-sm">5</span>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900">
|
||||
페이지 권한 테스트
|
||||
</h3>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-4">
|
||||
다양한 페이지 경로에 대한 권한을 테스트합니다. 권한이 없으면 홈으로
|
||||
리다이렉트됩니다.
|
||||
</p>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div
|
||||
v-for="pagePath in [
|
||||
'/',
|
||||
'/login',
|
||||
'/register',
|
||||
'/about',
|
||||
'/sampleList',
|
||||
'/admin/codes',
|
||||
'/admin/logs',
|
||||
'/admin/programs',
|
||||
'/admin/resource',
|
||||
'/admin/permission-test',
|
||||
'/test/culture-graph',
|
||||
'/test/pathway',
|
||||
'/test/test01',
|
||||
'/test/common-test',
|
||||
'/popup/addSamplePopup',
|
||||
'/nonexistent',
|
||||
]"
|
||||
:key="pagePath"
|
||||
class="p-3 rounded border text-center"
|
||||
:class="
|
||||
permission.hasPagePermission(pagePath)
|
||||
? 'bg-green-50 border-green-200'
|
||||
: 'bg-red-50 border-red-200'
|
||||
"
|
||||
>
|
||||
<div class="text-sm font-medium text-gray-900 mb-1">
|
||||
{{ pagePath }}
|
||||
</div>
|
||||
<div
|
||||
class="text-xs"
|
||||
:class="
|
||||
permission.hasPagePermission(pagePath)
|
||||
? 'text-green-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{
|
||||
permission.hasPagePermission(pagePath)
|
||||
? "✓ 접근 가능"
|
||||
: "✗ 접근 불가"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 p-3 bg-blue-50 rounded border">
|
||||
<p class="text-sm text-blue-800">
|
||||
<strong>참고:</strong> 실제로는 권한이 없는 경로에 접근하면 자동으로
|
||||
홈으로 리다이렉트됩니다. 이 페이지는
|
||||
<code>/admin/permission-test</code> 경로로, 페이지 권한이 있어야
|
||||
접근할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 권한 데이터 표시 -->
|
||||
<div
|
||||
v-if="userStore.isLoggedIn"
|
||||
class="bg-white rounded-lg shadow-md p-6 mb-8"
|
||||
>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-4">
|
||||
현재 권한 데이터
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- 페이지 권한 -->
|
||||
<div class="bg-blue-50 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-semibold mb-3 text-blue-800">
|
||||
페이지 권한
|
||||
</h4>
|
||||
<ul class="space-y-1">
|
||||
<li
|
||||
v-for="page in permission.permissions.resources.pages"
|
||||
:key="page.oid"
|
||||
class="text-sm bg-white p-2 rounded border"
|
||||
>
|
||||
<div class="font-medium">{{ page.name }} ({{ page.code }})</div>
|
||||
<div class="text-gray-600">{{ page.path }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 메뉴 권한 -->
|
||||
<div class="bg-green-50 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-semibold mb-3 text-green-800">메뉴 권한</h4>
|
||||
<ul class="space-y-1">
|
||||
<li
|
||||
v-for="menu in permission.permissions.resources.menus"
|
||||
:key="menu.oid"
|
||||
class="text-sm bg-white p-2 rounded border"
|
||||
>
|
||||
<div class="font-medium">{{ menu.name }} ({{ menu.code }})</div>
|
||||
<div class="text-gray-600">{{ menu.description }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 컴포넌트 권한 -->
|
||||
<div class="bg-purple-50 p-4 rounded-lg">
|
||||
<h4 class="text-lg font-semibold mb-3 text-purple-800">
|
||||
컴포넌트 권한
|
||||
</h4>
|
||||
<ul class="space-y-1">
|
||||
<li
|
||||
v-for="component in permission.permissions.resources.components"
|
||||
:key="component.oid"
|
||||
class="text-sm bg-white p-2 rounded border"
|
||||
>
|
||||
<div class="font-medium">
|
||||
{{ component.name }} ({{ component.code }})
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
{{ component.componentType }} - {{ component.description }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 사용법 가이드 -->
|
||||
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold text-yellow-800 mb-3">
|
||||
권한 시스템 가이드
|
||||
</h3>
|
||||
<div class="text-yellow-700 space-y-2">
|
||||
<p>
|
||||
<strong>로그인 필요:</strong> 이 페이지를 사용하려면 먼저 로그인해야
|
||||
합니다. 로그인 시 가데이터 권한이 자동으로 로드됩니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>페이지 권한:</strong> 페이지 라우터 접근 권한을 제어합니다.
|
||||
권한이 없으면 홈으로 리다이렉트됩니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>메뉴 권한:</strong> 메뉴 표시 여부를 제어합니다. 권한이
|
||||
없으면 메뉴가 숨겨집니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>컴포넌트 권한:</strong> 버튼 등 UI 컴포넌트의 표시 여부를
|
||||
제어합니다. 권한이 없으면 컴포넌트가 렌더링되지 않습니다.
|
||||
</p>
|
||||
<p>
|
||||
<strong>백엔드 연동:</strong> 나중에 백엔드 API가 준비되면
|
||||
<code>stores/permissions.ts</code>의
|
||||
<code>fetchPermissions</code> 함수를 수정하여 실제 API를 호출하도록
|
||||
변경할 수 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 페이지 메타데이터 설정
|
||||
definePageMeta({
|
||||
title: "권한 시스템 테스트",
|
||||
description:
|
||||
"페이지 권한, 메뉴 권한, 컴포넌트 권한의 동작을 테스트하는 페이지",
|
||||
});
|
||||
|
||||
const permission = usePermission();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 이 페이지는 /admin 경로이므로 페이지 권한이 필요합니다
|
||||
// middleware/auth.ts에서 자동으로 권한을 체크합니다
|
||||
// 로그인 시 권한 데이터가 자동으로 로드됩니다
|
||||
</script>
|
||||
Reference in New Issue
Block a user