RESTAPI_SERVER/public/index.html

1142 lines
46 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>REST API 관리 대시보드</title>
<link rel="stylesheet" href="/css/style.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<!-- 로딩 스피너 -->
<div id="loading" class="loading-overlay">
<div class="spinner"></div>
</div>
<!-- 로그인 화면 -->
<div id="loginScreen" class="login-screen">
<div class="login-container">
<div class="login-header">
<h1><i class="fas fa-server"></i> REST API 관리</h1>
<p>API 키 발급 및 관리 시스템</p>
</div>
<div class="login-tabs">
<button class="tab-btn active" onclick="showLoginTab()">로그인</button>
<button class="tab-btn" onclick="showRegisterTab()">회원가입</button>
</div>
<!-- 로그인 폼 -->
<form id="loginForm" class="auth-form">
<div class="form-group">
<label for="loginUsername">사용자명</label>
<input type="text" id="loginUsername" required>
</div>
<div class="form-group">
<label for="loginPassword">비밀번호</label>
<input type="password" id="loginPassword" required>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-sign-in-alt"></i> 로그인
</button>
</form>
<!-- 회원가입 폼 -->
<form id="registerForm" class="auth-form" style="display: none;">
<div class="form-group">
<label for="registerUsername">사용자명</label>
<input type="text" id="registerUsername" required>
</div>
<div class="form-group">
<label for="registerEmail">이메일</label>
<input type="email" id="registerEmail" required>
</div>
<div class="form-group">
<label for="registerPassword">비밀번호</label>
<input type="password" id="registerPassword" required minlength="8">
<small>최소 8자 이상</small>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-user-plus"></i> 회원가입
</button>
</form>
<div class="login-footer">
<p>기본 관리자 계정: admin / admin123!</p>
</div>
</div>
</div>
<!-- 메인 대시보드 -->
<div id="dashboard" class="dashboard" style="display: none;">
<!-- 헤더 -->
<header class="header">
<div class="header-left">
<h1><i class="fas fa-server"></i> REST API 대시보드</h1>
</div>
<div class="header-right">
<span class="user-info">
<i class="fas fa-user"></i>
<span id="currentUser"></span>
<span id="userRole" class="role-badge"></span>
</span>
<button id="logoutBtn" class="btn btn-secondary">
<i class="fas fa-sign-out-alt"></i> 로그아웃
</button>
</div>
</header>
<!-- 사이드바 -->
<nav class="sidebar">
<ul class="nav-menu">
<li><a href="#" class="nav-link active" data-section="overview">
<i class="fas fa-chart-dashboard"></i> 개요
</a></li>
<li><a href="#" class="nav-link" data-section="api-keys">
<i class="fas fa-key"></i> API 키 관리
</a></li>
<li><a href="#" class="nav-link" data-section="data-management">
<i class="fas fa-database"></i> 데이터 관리
</a></li>
<li><a href="#" class="nav-link" data-section="api-docs">
<i class="fas fa-book"></i> API 문서
</a></li>
<li><a href="#" class="nav-link" data-section="api-tester">
<i class="fas fa-play-circle"></i> API 테스터
</a></li>
<li class="admin-only"><a href="#" class="nav-link" data-section="users">
<i class="fas fa-users"></i> 사용자 관리
</a></li>
<li class="admin-only"><a href="#" class="nav-link" data-section="api-logs">
<i class="fas fa-history"></i> API 로그
</a></li>
<li class="admin-only"><a href="#" class="nav-link" data-section="monitoring">
<i class="fas fa-chart-line"></i> 모니터링
</a></li>
</ul>
</nav>
<!-- 메인 콘텐츠 -->
<main class="main-content">
<!-- 개요 섹션 -->
<section id="overview-section" class="content-section active">
<h2>대시보드 개요</h2>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-key"></i>
</div>
<div class="stat-info">
<h3 id="totalKeys">-</h3>
<p>총 API 키</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-chart-line"></i>
</div>
<div class="stat-info">
<h3 id="totalRequests">-</h3>
<p>총 요청 수</p>
</div>
</div>
<div class="stat-card admin-only">
<div class="stat-icon">
<i class="fas fa-users"></i>
</div>
<div class="stat-info">
<h3 id="totalUsers">-</h3>
<p>총 사용자</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-clock"></i>
</div>
<div class="stat-info">
<h3 id="activeKeysToday">-</h3>
<p>오늘 활성 키</p>
</div>
</div>
</div>
<div class="quick-actions">
<h3>빠른 작업</h3>
<div class="action-buttons">
<button class="btn btn-primary" onclick="showSection('api-keys')">
<i class="fas fa-plus"></i> API 키 생성
</button>
<button class="btn btn-secondary" onclick="showSection('data-management')">
<i class="fas fa-database"></i> 데이터 관리
</button>
<button class="btn btn-info" onclick="testApiConnection()">
<i class="fas fa-plug"></i> API 연결 테스트
</button>
</div>
</div>
</section>
<!-- API 키 관리 섹션 -->
<section id="api-keys-section" class="content-section api-keys-section">
<div class="section-header">
<h2>API 키 관리</h2>
<div class="header-actions">
<button class="copy-all-keys-btn" onclick="copyAllApiKeys()" title="모든 API 키 복사">
<i class="fas fa-copy"></i> 모든 키 복사
</button>
<button class="btn btn-primary" onclick="showCreateKeyModal()">
<i class="fas fa-plus"></i> 새 API 키 생성
</button>
</div>
</div>
<div class="api-keys-list">
<div class="table-container">
<table id="apiKeysTable" class="data-table">
<thead>
<tr>
<th>키 이름</th>
<th>API 키</th>
<th>권한</th>
<th>사용 횟수</th>
<th>마지막 사용</th>
<th>생성일</th>
<th>상태</th>
<th>작업</th>
</tr>
</thead>
<tbody>
<!-- API 키 목록이 여기에 동적으로 추가됩니다 -->
</tbody>
</table>
</div>
</div>
</section>
<!-- 데이터 관리 섹션 -->
<section id="data-management-section" class="content-section">
<div class="section-header">
<h2>데이터 관리</h2>
<button class="btn btn-primary" onclick="showCreateDataModal()">
<i class="fas fa-plus"></i> 새 데이터 추가
</button>
</div>
<div class="info-box">
<h4><i class="fas fa-info-circle"></i> 데이터 관리 기능 안내</h4>
<p><strong>데이터 추가</strong><code>API_DATA</code> 테이블에 샘플 데이터를 저장하는 기능입니다.</p>
<ul>
<li><strong>용도</strong>: REST API 테스트용 데이터 생성</li>
<li><strong>API 엔드포인트</strong>: <code>GET/POST/PUT/DELETE /api/data</code></li>
<li><strong>데이터 구조</strong>: 이름, 설명, 데이터 값, 생성/수정 시간</li>
<li><strong>활용</strong>: 외부 시스템에서 API 키를 사용하여 이 데이터에 접근 가능</li>
</ul>
</div>
<div class="data-list">
<div class="table-container">
<table id="dataTable" class="data-table">
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>설명</th>
<th>데이터 값</th>
<th>생성일</th>
<th>수정일</th>
<th>작업</th>
</tr>
</thead>
<tbody>
<!-- 데이터 목록이 여기에 동적으로 추가됩니다 -->
</tbody>
</table>
</div>
</div>
</section>
<!-- API 문서 섹션 -->
<section id="api-docs-section" class="content-section">
<h2>API 문서</h2>
<div class="api-docs">
<!-- API 키 사용법 -->
<div class="endpoint-group">
<h3>🔑 API 키 사용법</h3>
<div class="api-usage-info">
<p><strong>모든 데이터 API 요청에는 API 키가 필요합니다.</strong></p>
<div class="usage-methods">
<h4>방법 1: HTTP 헤더 (권장)</h4>
<pre><code>X-API-Key: ak_your_api_key_here</code></pre>
<h4>방법 2: 쿼리 파라미터</h4>
<pre><code>GET /api/data?api_key=ak_your_api_key_here</code></pre>
</div>
</div>
</div>
<!-- 인증 API -->
<div class="endpoint-group">
<h3>🔐 인증 API</h3>
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="path">/auth/login</span>
</div>
<div class="endpoint-description">
<p>사용자 로그인</p>
<pre><code>{
"username": "사용자명",
"password": "비밀번호"
}</code></pre>
</div>
</div>
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="path">/auth/api-keys</span>
</div>
<div class="endpoint-description">
<p>API 키 생성 (JWT 토큰 필요)</p>
<pre><code>{
"keyName": "키 이름",
"permissions": ["read", "write", "delete"]
}</code></pre>
</div>
</div>
</div>
<!-- USER_INFO_ORA 테이블 API -->
<div class="endpoint-group">
<h3>👤 사용자 정보 API (USER_INFO_ORA)</h3>
<!-- 조회 -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method get">GET</span>
<span class="path">/api/users</span>
</div>
<div class="endpoint-description">
<p>모든 사용자 조회</p>
<p><strong>헤더:</strong> <code>X-API-Key: your-api-key</code></p>
<p><strong>응답 예시:</strong></p>
<pre><code>[
{
"USER_ID": "user001",
"USER_NAME": "홍길동",
"DEPT_CODE": "IT001",
"REG_DATE": "2024-01-15"
}
]</code></pre>
</div>
</div>
<!-- 단일 조회 -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method get">GET</span>
<span class="path">/api/users/{userId}</span>
</div>
<div class="endpoint-description">
<p>특정 사용자 조회</p>
<p><strong>헤더:</strong> <code>X-API-Key: your-api-key</code></p>
</div>
</div>
<!-- 생성 -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="path">/api/users</span>
</div>
<div class="endpoint-description">
<p>새 사용자 생성</p>
<p><strong>헤더:</strong> <code>X-API-Key: your-api-key</code></p>
<p><strong>요청 본문:</strong></p>
<pre><code>{
"USER_ID": "user002",
"USER_NAME": "김철수",
"DEPT_CODE": "HR001"
}</code></pre>
</div>
</div>
<!-- 수정 -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method put">PUT</span>
<span class="path">/api/users/{userId}</span>
</div>
<div class="endpoint-description">
<p>사용자 정보 수정</p>
<p><strong>헤더:</strong> <code>X-API-Key: your-api-key</code></p>
<p><strong>요청 본문:</strong></p>
<pre><code>{
"USER_NAME": "김철수(수정)",
"DEPT_CODE": "IT002"
}</code></pre>
</div>
</div>
<!-- 삭제 -->
<div class="endpoint">
<div class="endpoint-header">
<span class="method delete">DELETE</span>
<span class="path">/api/users/{userId}</span>
</div>
<div class="endpoint-description">
<p>사용자 삭제</p>
<p><strong>헤더:</strong> <code>X-API-Key: your-api-key</code></p>
</div>
</div>
</div>
<!-- 언어별 사용 예시 -->
<div class="endpoint-group">
<h3>💻 언어별 사용 예시</h3>
<!-- Java 예시 -->
<div class="code-example">
<h4><i class="fab fa-java"></i> Java (Spring Boot)</h4>
<div class="code-tabs">
<div class="tab-buttons">
<button class="tab-btn active" onclick="showTab('java-insert')">INSERT</button>
<button class="tab-btn" onclick="showTab('java-select')">SELECT</button>
<button class="tab-btn" onclick="showTab('java-update')">UPDATE</button>
<button class="tab-btn" onclick="showTab('java-delete')">DELETE</button>
</div>
<div id="java-insert" class="tab-content active">
<pre><code>// RestTemplate 사용
@Service
public class UserService {
private static final String API_KEY = "ak_your_api_key_here";
private static final String BASE_URL = "http://localhost:5577/api";
@Autowired
private RestTemplate restTemplate;
public ResponseEntity&lt;String&gt; createUser(UserDto user) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Key", API_KEY);
HttpEntity&lt;UserDto&gt; request = new HttpEntity&lt;&gt;(user, headers);
return restTemplate.postForEntity(
BASE_URL + "/users",
request,
String.class
);
}
}
// DTO 클래스
public class UserDto {
private String USER_ID;
private String USER_NAME;
private String DEPT_CODE;
// getters and setters
}</code></pre>
</div>
<div id="java-select" class="tab-content">
<pre><code>// 모든 사용자 조회
public List&lt;UserDto&gt; getAllUsers() {
HttpHeaders headers = new HttpHeaders();
headers.set("X-API-Key", API_KEY);
HttpEntity&lt;String&gt; entity = new HttpEntity&lt;&gt;(headers);
ResponseEntity&lt;UserDto[]&gt; response = restTemplate.exchange(
BASE_URL + "/users",
HttpMethod.GET,
entity,
UserDto[].class
);
return Arrays.asList(response.getBody());
}
// 특정 사용자 조회
public UserDto getUserById(String userId) {
HttpHeaders headers = new HttpHeaders();
headers.set("X-API-Key", API_KEY);
HttpEntity&lt;String&gt; entity = new HttpEntity&lt;&gt;(headers);
return restTemplate.exchange(
BASE_URL + "/users/" + userId,
HttpMethod.GET,
entity,
UserDto.class
).getBody();
}</code></pre>
</div>
<div id="java-update" class="tab-content">
<pre><code>// 사용자 정보 수정
public ResponseEntity&lt;String&gt; updateUser(String userId, UserDto user) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-API-Key", API_KEY);
HttpEntity&lt;UserDto&gt; request = new HttpEntity&lt;&gt;(user, headers);
return restTemplate.exchange(
BASE_URL + "/users/" + userId,
HttpMethod.PUT,
request,
String.class
);
}</code></pre>
</div>
<div id="java-delete" class="tab-content">
<pre><code>// 사용자 삭제
public ResponseEntity&lt;String&gt; deleteUser(String userId) {
HttpHeaders headers = new HttpHeaders();
headers.set("X-API-Key", API_KEY);
HttpEntity&lt;String&gt; entity = new HttpEntity&lt;&gt;(headers);
return restTemplate.exchange(
BASE_URL + "/users/" + userId,
HttpMethod.DELETE,
entity,
String.class
);
}</code></pre>
</div>
</div>
</div>
<!-- React 예시 -->
<div class="code-example">
<h4><i class="fab fa-react"></i> React (JavaScript)</h4>
<div class="code-tabs">
<div class="tab-buttons">
<button class="tab-btn active" onclick="showTab('react-insert')">INSERT</button>
<button class="tab-btn" onclick="showTab('react-select')">SELECT</button>
<button class="tab-btn" onclick="showTab('react-update')">UPDATE</button>
<button class="tab-btn" onclick="showTab('react-delete')">DELETE</button>
</div>
<div id="react-insert" class="tab-content active">
<pre><code>// API 설정
const API_KEY = 'ak_your_api_key_here';
const BASE_URL = 'http://localhost:5577/api';
// 사용자 생성
const createUser = async (userData) => {
try {
const response = await fetch(`${BASE_URL}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error('사용자 생성 실패');
}
return await response.json();
} catch (error) {
console.error('Error:', error);
throw error;
}
};
// React 컴포넌트에서 사용
const UserForm = () => {
const handleSubmit = async (e) => {
e.preventDefault();
const userData = {
USER_ID: 'user003',
USER_NAME: '이영희',
DEPT_CODE: 'SALES001'
};
try {
await createUser(userData);
alert('사용자가 생성되었습니다!');
} catch (error) {
alert('생성 실패: ' + error.message);
}
};
return (
&lt;form onSubmit={handleSubmit}&gt;
{/* 폼 내용 */}
&lt;/form&gt;
);
};</code></pre>
</div>
<div id="react-select" class="tab-content">
<pre><code>// 모든 사용자 조회
const getAllUsers = async () => {
try {
const response = await fetch(`${BASE_URL}/users`, {
method: 'GET',
headers: {
'X-API-Key': API_KEY
}
});
if (!response.ok) {
throw new Error('사용자 조회 실패');
}
return await response.json();
} catch (error) {
console.error('Error:', error);
throw error;
}
};
// React Hook 사용
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUsers = async () => {
try {
const userData = await getAllUsers();
setUsers(userData);
} catch (error) {
console.error('사용자 로드 실패:', error);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return &lt;div&gt;로딩 중...&lt;/div&gt;;
return (
&lt;ul&gt;
{users.map(user =&gt; (
&lt;li key={user.USER_ID}&gt;
{user.USER_NAME} ({user.DEPT_CODE})
&lt;/li&gt;
))}
&lt;/ul&gt;
);
};</code></pre>
</div>
<div id="react-update" class="tab-content">
<pre><code>// 사용자 정보 수정
const updateUser = async (userId, userData) => {
try {
const response = await fetch(`${BASE_URL}/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error('사용자 수정 실패');
}
return await response.json();
} catch (error) {
console.error('Error:', error);
throw error;
}
};
// 사용 예시
const handleUpdate = async () => {
const updatedData = {
USER_NAME: '이영희(수정)',
DEPT_CODE: 'IT003'
};
try {
await updateUser('user003', updatedData);
alert('사용자 정보가 수정되었습니다!');
} catch (error) {
alert('수정 실패: ' + error.message);
}
};</code></pre>
</div>
<div id="react-delete" class="tab-content">
<pre><code>// 사용자 삭제
const deleteUser = async (userId) => {
try {
const response = await fetch(`${BASE_URL}/users/${userId}`, {
method: 'DELETE',
headers: {
'X-API-Key': API_KEY
}
});
if (!response.ok) {
throw new Error('사용자 삭제 실패');
}
return await response.json();
} catch (error) {
console.error('Error:', error);
throw error;
}
};
// 삭제 확인 후 실행
const handleDelete = async (userId) => {
if (window.confirm('정말 삭제하시겠습니까?')) {
try {
await deleteUser(userId);
alert('사용자가 삭제되었습니다!');
// 목록 새로고침
fetchUsers();
} catch (error) {
alert('삭제 실패: ' + error.message);
}
}
};</code></pre>
</div>
</div>
</div>
<!-- Python 예시 -->
<div class="code-example">
<h4><i class="fab fa-python"></i> Python (requests)</h4>
<pre><code>import requests
import json
API_KEY = 'ak_your_api_key_here'
BASE_URL = 'http://localhost:5577/api'
class UserAPI:
def __init__(self):
self.headers = {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
def create_user(self, user_data):
"""사용자 생성"""
response = requests.post(
f'{BASE_URL}/users',
headers=self.headers,
json=user_data
)
return response.json()
def get_all_users(self):
"""모든 사용자 조회"""
response = requests.get(
f'{BASE_URL}/users',
headers={'X-API-Key': API_KEY}
)
return response.json()
def get_user(self, user_id):
"""특정 사용자 조회"""
response = requests.get(
f'{BASE_URL}/users/{user_id}',
headers={'X-API-Key': API_KEY}
)
return response.json()
def update_user(self, user_id, user_data):
"""사용자 정보 수정"""
response = requests.put(
f'{BASE_URL}/users/{user_id}',
headers=self.headers,
json=user_data
)
return response.json()
def delete_user(self, user_id):
"""사용자 삭제"""
response = requests.delete(
f'{BASE_URL}/users/{user_id}',
headers={'X-API-Key': API_KEY}
)
return response.json()
# 사용 예시
api = UserAPI()
# 사용자 생성
new_user = {
'USER_ID': 'user004',
'USER_NAME': '박민수',
'DEPT_CODE': 'DEV001'
}
result = api.create_user(new_user)</code></pre>
</div>
<!-- cURL 예시 -->
<div class="code-example">
<h4><i class="fas fa-terminal"></i> cURL</h4>
<pre><code># 사용자 생성 (INSERT)
curl -X POST http://localhost:5577/api/users \
-H "Content-Type: application/json" \
-H "X-API-Key: ak_your_api_key_here" \
-d '{
"USER_ID": "user005",
"USER_NAME": "최수진",
"DEPT_CODE": "MKT001"
}'
# 모든 사용자 조회 (SELECT)
curl -X GET http://localhost:5577/api/users \
-H "X-API-Key: ak_your_api_key_here"
# 특정 사용자 조회
curl -X GET http://localhost:5577/api/users/user005 \
-H "X-API-Key: ak_your_api_key_here"
# 사용자 정보 수정 (UPDATE)
curl -X PUT http://localhost:5577/api/users/user005 \
-H "Content-Type: application/json" \
-H "X-API-Key: ak_your_api_key_here" \
-d '{
"USER_NAME": "최수진(수정)",
"DEPT_CODE": "MKT002"
}'
# 사용자 삭제 (DELETE)
curl -X DELETE http://localhost:5577/api/users/user005 \
-H "X-API-Key: ak_your_api_key_here"</code></pre>
</div>
</div>
<!-- 기존 데이터 API -->
<div class="endpoint-group">
<h3>📊 기본 데이터 API</h3>
<div class="endpoint">
<div class="endpoint-header">
<span class="method get">GET</span>
<span class="path">/api/data</span>
</div>
<div class="endpoint-description">
<p>모든 데이터 조회 (API 키 필요)</p>
<p>헤더: <code>X-API-Key: your-api-key</code></p>
</div>
</div>
<div class="endpoint">
<div class="endpoint-header">
<span class="method post">POST</span>
<span class="path">/api/data</span>
</div>
<div class="endpoint-description">
<p>데이터 생성 (API 키 필요)</p>
<pre><code>{
"name": "데이터 이름",
"description": "설명",
"dataValue": "값"
}</code></pre>
</div>
</div>
</div>
</div>
</section>
<!-- API 테스터 섹션 -->
<section id="api-tester-section" class="content-section">
<h2>API 테스터</h2>
<p class="section-description">브라우저에서 직접 REST API를 테스트해보세요.</p>
<!-- API 키 선택 -->
<div class="api-tester-form">
<div class="form-row">
<div class="form-group">
<label for="testApiKey">API 키 선택:</label>
<select id="testApiKey" class="form-control">
<option value="">API 키를 선택하세요</option>
</select>
<small>또는 직접 입력:</small>
<input type="text" id="customApiKey" class="form-control" placeholder="API 키를 직접 입력하세요">
</div>
</div>
<!-- 요청 설정 -->
<div class="form-row">
<div class="form-group method-group">
<label for="testMethod">HTTP 메소드:</label>
<select id="testMethod" class="form-control">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="form-group url-group">
<label for="testUrl">엔드포인트:</label>
<div class="url-input">
<span class="base-url">http://localhost:5577</span>
<input type="text" id="testUrl" class="form-control" placeholder="/api/data" value="/api/data">
</div>
</div>
</div>
<!-- 요청 본문 (POST/PUT용) -->
<div class="form-group" id="requestBodyGroup" style="display: none;">
<label for="testBody">요청 본문 (JSON):</label>
<textarea id="testBody" class="form-control" rows="8" placeholder='{"name": "테스트", "description": "설명", "dataValue": "값"}'></textarea>
</div>
<!-- 테스트 버튼 -->
<div class="form-actions">
<button onclick="sendApiRequest()" class="btn btn-primary">
<i class="fas fa-play"></i> API 호출
</button>
<button onclick="clearApiTest()" class="btn btn-secondary">
<i class="fas fa-eraser"></i> 초기화
</button>
</div>
</div>
<!-- 응답 결과 -->
<div class="api-response" id="apiResponse" style="display: none;">
<h3>응답 결과</h3>
<div class="response-info">
<span class="response-status" id="responseStatus"></span>
<span class="response-time" id="responseTime"></span>
</div>
<div class="response-headers">
<h4>응답 헤더</h4>
<pre id="responseHeaders"></pre>
</div>
<div class="response-body">
<h4>응답 본문</h4>
<pre id="responseBody"></pre>
</div>
</div>
<!-- 빠른 테스트 예제 -->
<div class="quick-tests">
2025-09-25 16:23:32 +09:00
<h3>빠른 테스트 예제 - User CRUD</h3>
<div class="test-examples">
2025-09-25 16:23:32 +09:00
<button onclick="loadExample('getAllUsers')" class="btn btn-info btn-sm">
<i class="fas fa-users"></i> 모든 사용자 조회 (GET)
</button>
2025-09-25 16:23:32 +09:00
<button onclick="loadExample('getUser')" class="btn btn-primary btn-sm">
<i class="fas fa-user"></i> 특정 사용자 조회 (GET)
</button>
2025-09-25 16:23:32 +09:00
<button onclick="loadExample('createUser')" class="btn btn-success btn-sm">
<i class="fas fa-user-plus"></i> 사용자 생성 (POST)
</button>
2025-09-25 16:23:32 +09:00
<button onclick="loadExample('updateUser')" class="btn btn-warning btn-sm">
<i class="fas fa-user-edit"></i> 사용자 수정 (PUT)
</button>
2025-09-25 16:23:32 +09:00
<button onclick="loadExample('deleteUser')" class="btn btn-danger btn-sm">
<i class="fas fa-user-times"></i> 사용자 삭제 (DELETE)
</button>
</div>
</div>
</section>
<!-- 사용자 관리 섹션 (관리자 전용) -->
<section id="users-section" class="content-section admin-only">
<h2>사용자 관리</h2>
<div class="table-container">
<table id="usersTable" class="data-table">
<thead>
<tr>
<th>ID</th>
<th>사용자명</th>
<th>이메일</th>
<th>역할</th>
<th>상태</th>
<th>가입일</th>
<th>마지막 로그인</th>
</tr>
</thead>
<tbody>
<!-- 사용자 목록이 여기에 동적으로 추가됩니다 -->
</tbody>
</table>
</div>
</section>
<!-- API 로그 섹션 (관리자 전용) -->
<section id="api-logs-section" class="content-section admin-only">
<h2>API 호출 로그</h2>
<!-- 필터 -->
<div class="log-filters">
<div class="filter-row">
<div class="filter-group">
<label>시작 날짜:</label>
<input type="date" id="startDate" class="filter-input">
</div>
<div class="filter-group">
<label>종료 날짜:</label>
<input type="date" id="endDate" class="filter-input">
</div>
<div class="filter-group">
<label>메소드:</label>
<select id="methodFilter" class="filter-input">
<option value="">전체</option>
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="filter-group">
<label>엔드포인트:</label>
<input type="text" id="endpointFilter" placeholder="엔드포인트 검색" class="filter-input">
</div>
<div class="filter-group">
<label>사용자:</label>
<input type="text" id="usernameFilter" placeholder="사용자명 검색" class="filter-input">
</div>
<button onclick="loadApiLogs()" class="btn btn-primary">
<i class="fas fa-search"></i> 검색
</button>
<button onclick="clearLogFilters()" class="btn btn-secondary">
<i class="fas fa-times"></i> 초기화
</button>
</div>
</div>
<!-- 로그 통계 -->
<div class="log-stats">
<div class="stat-card">
<div class="stat-info">
<h3 id="totalApiRequests">-</h3>
<p>총 API 요청</p>
</div>
</div>
<div class="stat-card">
<div class="stat-info">
<h3 id="todayApiRequests">-</h3>
<p>오늘 요청</p>
</div>
</div>
</div>
<!-- 로그 테이블 -->
<div class="table-container">
<table id="apiLogsTable" class="data-table">
<thead>
<tr>
<th>시간</th>
<th>메소드</th>
<th>엔드포인트</th>
<th>사용자</th>
<th>API 키</th>
<th>상태</th>
<th>응답시간</th>
<th>IP</th>
</tr>
</thead>
<tbody>
<!-- 로그 목록이 여기에 동적으로 추가됩니다 -->
</tbody>
</table>
</div>
<!-- 페이지네이션 -->
<div class="pagination" id="logPagination">
<!-- 페이지네이션이 여기에 동적으로 추가됩니다 -->
</div>
</section>
<!-- 모니터링 섹션 (관리자 전용) -->
<section id="monitoring-section" class="content-section admin-only">
<h2>시스템 모니터링</h2>
<div class="monitoring-grid">
<div class="monitor-card">
<h3>서버 상태</h3>
<div id="serverStatus" class="status-indicator">
<i class="fas fa-circle"></i> 확인 중...
</div>
</div>
<div class="monitor-card">
<h3>데이터베이스 연결</h3>
<div id="dbStatus" class="status-indicator">
<i class="fas fa-circle"></i> 확인 중...
</div>
</div>
</div>
</section>
</main>
</div>
<!-- 모달들 -->
<!-- API 키 생성 모달 -->
<div id="createKeyModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>새 API 키 생성</h3>
<span class="close" onclick="closeModal('createKeyModal')">&times;</span>
</div>
<form id="createKeyForm">
<div class="form-group">
<label for="keyName">키 이름</label>
<input type="text" id="keyName" required>
</div>
<div class="form-group">
<label>권한 선택</label>
<div class="checkbox-group">
<label><input type="checkbox" value="read"> 읽기</label>
<label><input type="checkbox" value="write"> 쓰기</label>
<label><input type="checkbox" value="delete"> 삭제</label>
</div>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal('createKeyModal')">취소</button>
<button type="submit" class="btn btn-primary">생성</button>
</div>
</form>
</div>
</div>
<!-- 데이터 생성 모달 -->
<div id="createDataModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>새 데이터 추가</h3>
<span class="close" onclick="closeModal('createDataModal')">&times;</span>
</div>
<form id="createDataForm">
<div class="form-group">
<label for="dataName">이름</label>
<input type="text" id="dataName" required>
</div>
<div class="form-group">
<label for="dataDescription">설명</label>
<textarea id="dataDescription" rows="3"></textarea>
</div>
<div class="form-group">
<label for="dataValue">데이터 값</label>
<textarea id="dataValue" rows="5" placeholder='{"key": "value"}'></textarea>
</div>
<div class="modal-actions">
<button type="button" class="btn btn-secondary" onclick="closeModal('createDataModal')">취소</button>
<button type="submit" class="btn btn-primary">추가</button>
</div>
</form>
</div>
</div>
<!-- 알림 토스트 -->
<div id="toast" class="toast"></div>
<script src="/js/app.js"></script>
</body>
</html>