- MailService.cs 추가: ServiceBase 상속받는 Windows 서비스 클래스 - Program.cs 수정: 서비스/콘솔 모드 지원, 설치/제거 기능 추가 - 프로젝트 설정: System.ServiceProcess 참조 추가 - 배치 파일 추가: 서비스 설치/제거/콘솔실행 스크립트 주요 기능: - Windows 서비스로 백그라운드 실행 - 명령행 인수로 모드 선택 (-install, -uninstall, -console) - EventLog를 통한 서비스 로깅 - 안전한 서비스 시작/중지 처리 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
270 lines
9.4 KiB
HTML
270 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
<meta http-equiv="Pragma" content="no-cache">
|
|
<meta http-equiv="Expires" content="0">
|
|
<title>공용코드관리 (React)</title>
|
|
<link rel="stylesheet" href="/lib/css/tailwind.min.css">
|
|
<script src="/lib/js/tailwind-config.js"></script>
|
|
|
|
<!-- Tailwind 설정 -->
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: {
|
|
50: '#eff6ff',
|
|
100: '#dbeafe',
|
|
200: '#bfdbfe',
|
|
300: '#93c5fd',
|
|
400: '#60a5fa',
|
|
500: '#3b82f6',
|
|
600: '#2563eb',
|
|
700: '#1d4ed8',
|
|
800: '#1e40af',
|
|
900: '#1e3a8a',
|
|
},
|
|
success: {
|
|
50: '#f0fdf4',
|
|
100: '#dcfce7',
|
|
200: '#bbf7d0',
|
|
300: '#86efac',
|
|
400: '#4ade80',
|
|
500: '#22c55e',
|
|
600: '#16a34a',
|
|
700: '#15803d',
|
|
800: '#166534',
|
|
900: '#14532d',
|
|
},
|
|
warning: {
|
|
50: '#fffbeb',
|
|
100: '#fef3c7',
|
|
200: '#fde68a',
|
|
300: '#fcd34d',
|
|
400: '#fbbf24',
|
|
500: '#f59e0b',
|
|
600: '#d97706',
|
|
700: '#b45309',
|
|
800: '#92400e',
|
|
900: '#78350f',
|
|
},
|
|
danger: {
|
|
50: '#fef2f2',
|
|
100: '#fee2e2',
|
|
200: '#fecaca',
|
|
300: '#fca5a5',
|
|
400: '#f87171',
|
|
500: '#ef4444',
|
|
600: '#dc2626',
|
|
700: '#b91c1c',
|
|
800: '#991b1b',
|
|
900: '#7f1d1d',
|
|
}
|
|
},
|
|
animation: {
|
|
'fade-in': 'fadeIn 0.5s ease-in-out',
|
|
'slide-up': 'slideUp 0.3s ease-out',
|
|
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
|
},
|
|
keyframes: {
|
|
fadeIn: {
|
|
'0%': { opacity: '0' },
|
|
'100%': { opacity: '1' },
|
|
},
|
|
slideUp: {
|
|
'0%': { transform: 'translateY(10px)', opacity: '0' },
|
|
'100%': { transform: 'translateY(0)', opacity: '1' },
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.glass-effect {
|
|
background: rgba(255, 255, 255, 0.25);
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(255, 255, 255, 0.18);
|
|
}
|
|
.gradient-bg {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
.card-hover {
|
|
transition: all 0.3s ease;
|
|
}
|
|
.card-hover:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
}
|
|
|
|
/* 스크롤바 스타일링 */
|
|
.custom-scrollbar::-webkit-scrollbar {
|
|
width: 16px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-track {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
background: rgba(255, 255, 255, 0.3);
|
|
border-radius: 8px;
|
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
background: rgba(255, 255, 255, 0.5);
|
|
}
|
|
|
|
/* 애니메이션 */
|
|
.animate-fade-in {
|
|
animation: fadeIn 0.5s ease-in-out;
|
|
}
|
|
|
|
.animate-slide-up {
|
|
animation: slideUp 0.3s ease-out;
|
|
}
|
|
|
|
/* 셀렉트 박스 옵션 스타일링 */
|
|
select option {
|
|
background-color: #374151 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
select option:hover {
|
|
background-color: #4B5563 !important;
|
|
}
|
|
|
|
select option:checked {
|
|
background-color: #6366F1 !important;
|
|
}
|
|
|
|
/* 테이블 셀 텍스트 오버플로우 처리 */
|
|
.truncate {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 값(문자열) 열 최대 너비 제한 */
|
|
.svalue-cell {
|
|
max-width: 128px; /* w-32 = 128px */
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 개발중 경고 스타일 */
|
|
.dev-warning {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
background: linear-gradient(135deg, #fbbf24, #f59e0b);
|
|
color: white;
|
|
padding: 12px 20px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 25px rgba(245, 158, 11, 0.3);
|
|
z-index: 1000;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.dev-warning .icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
margin-right: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dev-warning .title {
|
|
font-weight: 600;
|
|
margin: 0;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.dev-warning .description {
|
|
font-size: 12px;
|
|
opacity: 0.9;
|
|
margin: 2px 0 0 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gradient-to-br from-blue-900 via-purple-900 to-indigo-900 min-h-screen text-white">
|
|
<!-- 로딩 스켈레톤 UI -->
|
|
<div id="react-common-app" class="animate-fade-in">
|
|
<div class="container mx-auto px-4 py-8">
|
|
<div class="flex gap-6 h-[calc(100vh-200px)]">
|
|
<!-- 좌측 스켈레톤 -->
|
|
<div class="w-80">
|
|
<div class="glass-effect rounded-2xl h-full animate-pulse">
|
|
<div class="p-4 border-b border-white/10">
|
|
<div class="h-6 bg-white/20 rounded w-3/4"></div>
|
|
</div>
|
|
<div class="p-4 space-y-3">
|
|
<div class="h-16 bg-white/10 rounded-lg"></div>
|
|
<div class="h-16 bg-white/10 rounded-lg"></div>
|
|
<div class="h-16 bg-white/10 rounded-lg"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 우측 스켈레톤 -->
|
|
<div class="flex-1">
|
|
<div class="glass-effect rounded-2xl h-full animate-pulse">
|
|
<div class="p-4 border-b border-white/10 flex justify-between items-center">
|
|
<div class="h-6 bg-white/20 rounded w-1/3"></div>
|
|
<div class="h-8 bg-white/20 rounded w-20"></div>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="space-y-3">
|
|
<div class="h-10 bg-white/10 rounded"></div>
|
|
<div class="h-10 bg-white/10 rounded"></div>
|
|
<div class="h-10 bg-white/10 rounded"></div>
|
|
<div class="h-10 bg-white/10 rounded"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- React Local -->
|
|
<script crossorigin src="/lib/js/react.development.js"></script>
|
|
<script crossorigin src="/lib/js/react-dom.development.js"></script>
|
|
<script src="/lib/js/babel.min.js"></script>
|
|
|
|
<!-- 공통 네비게이션 컴포넌트 -->
|
|
<script type="text/babel" src="/react/component/CommonNavigation"></script>
|
|
|
|
<!-- 개발중 경고 컴포넌트 -->
|
|
<script type="text/babel" src="/react/component/DevWarning"></script>
|
|
|
|
<!-- App 컴포넌트 -->
|
|
<script type="text/babel">
|
|
const { useState, useEffect } = React;
|
|
|
|
function App() {
|
|
return (
|
|
<div>
|
|
<CommonNavigation currentPage="common" />
|
|
<CommonCode />
|
|
</div>
|
|
);
|
|
}
|
|
</script>
|
|
|
|
<!-- 공용코드 컴포넌트 로드 -->
|
|
<script type="text/babel" src="/react/component/CommonCode"></script>
|
|
|
|
<!-- 앱 초기화 -->
|
|
<script type="text/babel">
|
|
const root = ReactDOM.createRoot(document.getElementById('react-common-app'));
|
|
root.render(<App />);
|
|
</script>
|
|
</body>
|
|
</html> |