feat(service): Console_SendMail을 Windows 서비스로 변환

- 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>
This commit is contained in:
ChiKyun Kim
2025-09-11 09:08:40 +09:00
parent 777fcd5d89
commit 6bd4f84192
49 changed files with 46882 additions and 102 deletions

View File

@@ -0,0 +1,358 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
namespace Project.Web.Controllers
{
public class ReactController : ApiController
{
private string GetWwwRootPath()
{
// 실행 파일 기준으로 wwwroot 경로 찾기
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var wwwrootPath = Path.Combine(baseDir, "Web", "wwwroot");
// 디버그 모드에서는 소스 경로 사용
if (!Directory.Exists(wwwrootPath))
{
wwwrootPath = Path.Combine(Directory.GetCurrentDirectory(), "Web", "wwwroot");
}
// 여전히 찾지 못하면 프로젝트 루트에서 찾기
if (!Directory.Exists(wwwrootPath))
{
var projectRoot = Directory.GetCurrentDirectory();
while (projectRoot != null && !Directory.Exists(Path.Combine(projectRoot, "Web", "wwwroot")))
{
projectRoot = Directory.GetParent(projectRoot)?.FullName;
}
if (projectRoot != null)
{
wwwrootPath = Path.Combine(projectRoot, "Web", "wwwroot");
}
}
return wwwrootPath;
}
[HttpGet]
[Route("react/test")]
public HttpResponseMessage Test()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-test.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React test file not found. Searched path: {filePath}. WWWRoot: {wwwrootPath}. Current Dir: {Directory.GetCurrentDirectory()}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React test page: {ex.Message}");
}
}
[HttpGet]
[Route("react/jsx-test")]
public HttpResponseMessage JsxTest()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-jsx-test.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "React JSX test file not found");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React JSX test page: {ex.Message}");
}
}
[HttpGet]
[Route("react/component/{filename}")]
public HttpResponseMessage Component(string filename)
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react", $"{filename}.jsx");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, $"React component {filename} not found at {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/javascript");
// CORS 헤더 추가
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React component {filename}: {ex.Message}");
}
}
[HttpGet]
[Route("react/login")]
public HttpResponseMessage Login()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-login.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React login page not found. Searched path: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React login page: {ex.Message}");
}
}
[HttpGet]
[Route("react/dashboard")]
public HttpResponseMessage Dashboard()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-dashboard.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React dashboard page not found. Searched path: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React dashboard page: {ex.Message}");
}
}
[HttpGet]
[Route("react/common")]
public HttpResponseMessage Common()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-common.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React common page not found: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React common page: {ex.Message}");
}
}
[HttpGet]
[Route("react/jobreport")]
public HttpResponseMessage JobReport()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-jobreport.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React jobreport page not found: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React jobreport page: {ex.Message}");
}
}
[HttpGet]
[Route("react/kuntae")]
public HttpResponseMessage Kuntae()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-kuntae.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React kuntae page not found: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React kuntae page: {ex.Message}");
}
}
[HttpGet]
[Route("react/todo")]
public HttpResponseMessage Todo()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-todo.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React todo page not found: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React todo page: {ex.Message}");
}
}
[HttpGet]
[Route("react/project")]
public HttpResponseMessage Project()
{
try
{
var wwwrootPath = GetWwwRootPath();
var filePath = Path.Combine(wwwrootPath, "react-project.html");
if (!File.Exists(filePath))
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound,
$"React project page not found: {filePath}");
}
var content = File.ReadAllText(filePath, Encoding.UTF8);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(content, Encoding.UTF8, "text/html");
return response;
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
$"Error serving React project page: {ex.Message}");
}
}
[HttpGet]
[Route("react/status")]
public IHttpActionResult Status()
{
return Ok(new
{
status = "React Controller Active",
timestamp = DateTime.Now,
routes = new[]
{
"/react/test - React 기본 테스트 페이지",
"/react/jsx-test - React JSX 모듈화 테스트 페이지",
"/react/login - React 로그인 페이지",
"/react/dashboard - React 대시보드 페이지",
"/react/common - React 공용코드 페이지",
"/react/jobreport - React 업무일지 페이지",
"/react/kuntae - React 근태관리 페이지",
"/react/todo - React 할일관리 페이지",
"/react/project - React 프로젝트 페이지",
"/react/component/{filename} - JSX 컴포넌트 파일 서빙",
"/react/status - 이 상태 페이지"
}
});
}
}
}