diff --git a/src/TaxBaik.Web/Controllers/AdminDashboardController.cs b/src/TaxBaik.Web/Controllers/AdminDashboardController.cs
deleted file mode 100644
index 5d5d089..0000000
--- a/src/TaxBaik.Web/Controllers/AdminDashboardController.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using TaxBaik.Application.Services;
-
-namespace TaxBaik.Web.Controllers;
-
-///
-/// 관리자 대시보드 API
-/// SOLID: Single Responsibility - 대시보드 데이터만 담당
-///
-[ApiController]
-[Route("api/admin-dashboard")]
-[Authorize]
-public class AdminDashboardController : ControllerBase
-{
- private readonly AdminDashboardService _dashboardService;
- private readonly TaxFilingService _taxFilingService;
-
- public AdminDashboardController(
- AdminDashboardService dashboardService,
- TaxFilingService taxFilingService)
- {
- _dashboardService = dashboardService;
- _taxFilingService = taxFilingService;
- }
-
- ///
- /// 대시보드 요약 정보 조회
- /// GET /api/admin-dashboard/summary
- ///
- [HttpGet("summary")]
- public async Task GetSummary()
- {
- try
- {
- var summary = await _dashboardService.GetSummaryAsync();
- return Ok(summary);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails
- {
- Title = "대시보드 요약 조회 실패",
- Detail = ex.Message,
- Status = StatusCodes.Status500InternalServerError
- });
- }
- }
-
- ///
- /// 30일 이내 마감 임박 신고 조회
- /// GET /api/admin-dashboard/upcoming-filings?days=30
- ///
- [HttpGet("upcoming-filings")]
- public async Task GetUpcomingFilings([FromQuery] int days = 30)
- {
- try
- {
- if (days <= 0) days = 30;
- var filings = await _taxFilingService.GetUpcomingAsync(days);
- return Ok(new { data = filings, days });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails
- {
- Title = "마감 임박 신고 조회 실패",
- Detail = ex.Message,
- Status = StatusCodes.Status500InternalServerError
- });
- }
- }
-
- ///
- /// 최근 문의 조회
- /// GET /api/admin-dashboard/recent-inquiries?limit=10
- ///
- [HttpGet("recent-inquiries")]
- public async Task GetRecentInquiries([FromQuery] int limit = 10)
- {
- try
- {
- if (limit <= 0) limit = 10;
- if (limit > 100) limit = 100; // 보안: 최대 100개
-
- var inquiries = await _dashboardService.GetRecentInquiriesAsync(limit);
- return Ok(new { data = inquiries, limit });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails
- {
- Title = "최근 문의 조회 실패",
- Detail = ex.Message,
- Status = StatusCodes.Status500InternalServerError
- });
- }
- }
-
- ///
- /// 월별 통계
- /// GET /api/admin-dashboard/monthly-stats?month=2026-06
- ///
- [HttpGet("monthly-stats")]
- public async Task GetMonthlyStats([FromQuery] string? month = null)
- {
- try
- {
- var stats = await _dashboardService.GetMonthlyStatsAsync(month);
- return Ok(stats);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails
- {
- Title = "월별 통계 조회 실패",
- Detail = ex.Message,
- Status = StatusCodes.Status500InternalServerError
- });
- }
- }
-}
diff --git a/src/TaxBaik.Web/Controllers/CommonCodeController.cs b/src/TaxBaik.Web/Controllers/CommonCodeController.cs
deleted file mode 100644
index 38ab6c6..0000000
--- a/src/TaxBaik.Web/Controllers/CommonCodeController.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using TaxBaik.Application.Services;
-using TaxBaik.Domain.Entities;
-
-namespace TaxBaik.Web.Controllers;
-
-[ApiController]
-[Route("api/[controller]")]
-[Authorize]
-public class CommonCodeController(CommonCodeService commonCodeService) : ControllerBase
-{
- [HttpGet]
- public async Task GetAllActive()
- {
- try
- {
- var codes = await commonCodeService.GetAllActiveAsync();
- return Ok(codes);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new { error = "공통코드 조회 실패", message = ex.Message });
- }
- }
-
- [HttpGet("group/{group}")]
- public async Task GetByGroup(string group)
- {
- try
- {
- var codes = await commonCodeService.GetByGroupAsync(group);
- return Ok(codes);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new { error = "그룹별 공통코드 조회 실패", message = ex.Message });
- }
- }
-
- [HttpGet("groups")]
- public async Task GetGroups()
- {
- try
- {
- var groups = await commonCodeService.GetAllGroupsAsync();
- return Ok(groups);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new { error = "공통코드 그룹 조회 실패", message = ex.Message });
- }
- }
-
- [HttpGet("{group}/{value}")]
- public async Task Get(string group, string value)
- {
- var code = await commonCodeService.GetAsync(group, value);
- return code is null ? NotFound() : Ok(code);
- }
-
- [HttpPost]
- public async Task Upsert([FromBody] CommonCode code)
- {
- if (string.IsNullOrWhiteSpace(code.CodeGroup) || string.IsNullOrWhiteSpace(code.CodeValue) || string.IsNullOrWhiteSpace(code.CodeName))
- return BadRequest(new { error = "코드 그룹, 값, 이름은 필수입니다." });
- if (code.CodeGroup.Any(char.IsWhiteSpace))
- return BadRequest(new { error = "code_group에는 공백을 사용할 수 없습니다." });
- if (code.CodeValue.Contains(' '))
- return BadRequest(new { error = "code_value에는 공백을 사용할 수 없습니다." });
-
- await commonCodeService.UpsertAsync(code);
- return Ok(code);
- }
-
- [HttpDelete("{group}/{value}")]
- public async Task Delete(string group, string value)
- {
- await commonCodeService.DeleteAsync(group, value);
- return NoContent();
- }
-}
diff --git a/src/TaxBaik.Web/Controllers/CompanyController.cs b/src/TaxBaik.Web/Controllers/CompanyController.cs
deleted file mode 100644
index 373c352..0000000
--- a/src/TaxBaik.Web/Controllers/CompanyController.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using TaxBaik.Application.Services;
-
-namespace TaxBaik.Web.Controllers;
-
-[ApiController]
-[Route("api/[controller]")]
-[Authorize]
-public class CompanyController(CompanyService companyService) : ControllerBase
-{
- [HttpGet("{id:int}")]
- public async Task GetById(int id)
- {
- try
- {
- var company = await companyService.GetByIdAsync(id);
- if (company == null)
- return NotFound(new ProblemDetails { Title = "회사를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
- return Ok(company);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 조회 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- [HttpGet("code/{code}")]
- public async Task GetByCode(string code)
- {
- try
- {
- var company = await companyService.GetByCodeAsync(code);
- if (company == null)
- return NotFound(new ProblemDetails { Title = "회사를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
- return Ok(company);
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 조회 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- [HttpGet]
- public async Task GetPaged([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
- {
- try
- {
- var (companies, total) = await companyService.GetPagedAsync(page, pageSize);
- return Ok(new { data = companies, total, page, pageSize });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 목록 조회 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- [HttpPost]
- public async Task Create([FromBody] CreateCompanyRequest request)
- {
- try
- {
- var id = await companyService.CreateAsync(
- request.CompanyCode, request.CompanyName, request.ContactPerson,
- request.Phone, request.Email, request.Memo);
- return CreatedAtAction(nameof(GetById), new { id }, new { message = "회사가 등록되었습니다.", id });
- }
- catch (ValidationException ex)
- {
- return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 등록 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- [HttpPut("{id:int}")]
- public async Task Update(int id, [FromBody] UpdateCompanyRequest request)
- {
- try
- {
- await companyService.UpdateAsync(id, request.CompanyCode, request.CompanyName,
- request.ContactPerson, request.Phone, request.Email, request.Memo, request.IsActive);
- return Ok(new { message = "회사가 수정되었습니다." });
- }
- catch (ValidationException ex)
- {
- return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 수정 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- [HttpDelete("{id:int}")]
- public async Task Delete(int id)
- {
- try
- {
- await companyService.DeleteAsync(id);
- return Ok(new { message = "회사가 삭제되었습니다." });
- }
- catch (ValidationException ex)
- {
- return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
- }
- catch (Exception ex)
- {
- return StatusCode(500, new ProblemDetails { Title = "회사 삭제 실패", Detail = ex.Message, Status = StatusCodes.Status500InternalServerError });
- }
- }
-
- public record CreateCompanyRequest(string CompanyCode, string CompanyName, string? ContactPerson, string? Phone, string? Email, string? Memo);
- public record UpdateCompanyRequest(string CompanyCode, string CompanyName, string? ContactPerson, string? Phone, string? Email, string? Memo, bool IsActive);
-}
diff --git a/src/TaxBaik.Web/Controllers/SiteSettingsController.cs b/src/TaxBaik.Web/Controllers/SiteSettingsController.cs
deleted file mode 100644
index 0b3f5e0..0000000
--- a/src/TaxBaik.Web/Controllers/SiteSettingsController.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using TaxBaik.Application.Services;
-
-namespace TaxBaik.Web.Controllers;
-
-[ApiController]
-[Route("api/[controller]")]
-[Authorize]
-public class SiteSettingsController : ControllerBase
-{
- private readonly SiteSettingService _siteSettingService;
-
- public SiteSettingsController(SiteSettingService siteSettingService)
- {
- _siteSettingService = siteSettingService;
- }
-
- [HttpGet]
- public async Task Get()
- {
- var settings = await _siteSettingService.GetAllAsync();
- return Ok(settings);
- }
-
- [HttpPut]
- public async Task Save([FromBody] SaveSiteSettingsRequest request)
- {
- if (request is null)
- return BadRequest(new { message = "요청 본문이 비어 있습니다." });
-
- await _siteSettingService.SaveAsync(request.Phone, request.Email, request.KakaoUrl, request.InstagramUrl);
- return Ok(new { message = "사이트 설정이 저장되었습니다." });
- }
-}
-
-public class SaveSiteSettingsRequest
-{
- public string Phone { get; set; } = string.Empty;
- public string Email { get; set; } = string.Empty;
- public string KakaoUrl { get; set; } = string.Empty;
- public string InstagramUrl { get; set; } = string.Empty;
-}
diff --git a/src/TaxBaik.Web/Controllers/TaxFilingController.cs b/src/TaxBaik.Web/Controllers/TaxFilingController.cs
deleted file mode 100644
index 1727b0c..0000000
--- a/src/TaxBaik.Web/Controllers/TaxFilingController.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using TaxBaik.Application.Services;
-using TaxBaik.Domain.Entities;
-
-namespace TaxBaik.Web.Controllers;
-
-[ApiController]
-[Route("api/[controller]")]
-[Authorize]
-public class TaxFilingController : ControllerBase
-{
- private readonly TaxFilingService _taxFilingService;
-
- public TaxFilingController(TaxFilingService taxFilingService)
- {
- _taxFilingService = taxFilingService;
- }
-
- [HttpGet("upcoming")]
- public async Task GetUpcoming([FromQuery] int daysAhead = 30)
- {
- var filings = await _taxFilingService.GetUpcomingAsync(daysAhead);
- return Ok(new { data = filings });
- }
-
- [HttpGet("client/{clientId}")]
- public async Task GetByClientId(int clientId)
- {
- var filings = await _taxFilingService.GetByClientIdAsync(clientId);
- return Ok(new { data = filings });
- }
-
- [HttpGet("{id}")]
- public async Task GetById(int id)
- {
- var filing = await _taxFilingService.GetByIdAsync(id);
- if (filing == null)
- return NotFound(new ProblemDetails { Title = "신고 일정을 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
-
- return Ok(filing);
- }
-
- [HttpPost]
- public async Task Create([FromBody] TaxFiling filing)
- {
- try
- {
- var filingId = await _taxFilingService.CreateAsync(filing);
- var result = await _taxFilingService.GetByIdAsync(filingId);
- return CreatedAtAction(nameof(GetById), new { id = filingId }, result);
- }
- catch (Exception ex)
- {
- return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
- }
- }
-
- [HttpPut("{id}")]
- public async Task Update(int id, [FromBody] TaxFiling filing)
- {
- filing.Id = id;
- try
- {
- await _taxFilingService.UpdateAsync(filing);
- var result = await _taxFilingService.GetByIdAsync(id);
- if (result == null)
- return NotFound(new ProblemDetails { Title = "신고 일정을 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
-
- return Ok(result);
- }
- catch (Exception ex)
- {
- return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
- }
- }
-
- [HttpDelete("{id}")]
- public async Task Delete(int id)
- {
- await _taxFilingService.DeleteAsync(id);
- return NoContent();
- }
-}
diff --git a/src/TaxBaik.Web/Endpoints/AdminDashboard/AdminDashboardDtos.cs b/src/TaxBaik.Web/Endpoints/AdminDashboard/AdminDashboardDtos.cs
new file mode 100644
index 0000000..a318d4d
--- /dev/null
+++ b/src/TaxBaik.Web/Endpoints/AdminDashboard/AdminDashboardDtos.cs
@@ -0,0 +1,51 @@
+using TaxBaik.Application.Services;
+using TaxBaik.Domain.Entities;
+using InquiryEntity = TaxBaik.Domain.Entities.Inquiry;
+
+namespace TaxBaik.Web.Endpoints.AdminDashboard;
+
+public class AdminDashboardSummaryResponse
+{
+ public int ThisMonthInquiries { get; set; }
+ public int NewInquiries { get; set; }
+ public int TotalPosts { get; set; }
+ public int PublishedPosts { get; set; }
+ public List RecentInquiries { get; set; } = [];
+}
+
+public class UpcomingFilingsResponse
+{
+ public List