feat: WBS-UX-03 FAQ 관리 기능 구현 — 어드민 CRUD + 홈페이지 DB 연동

DB:
- V007__CreateFaqs.sql: faqs 테이블 (question, answer, category,
  sort_order, is_active) + 기본 FAQ 4개 시드

Domain:
- Faq 엔티티
- IFaqRepository (GetActiveAsync, GetAllAsync, CRUD)

Infrastructure:
- FaqRepository: sort_order 정렬, CRUD

Application:
- FaqService: Categories 상수, Validate (질문·답변 필수)

Admin UI (Blazor):
- FaqList.razor: 전체 목록, 활성/비활성 상태 칩, 삭제 확인
- FaqEdit.razor: 질문/답변/카테고리/순서/활성 토글 폼
- MainLayout: 홈페이지 그룹 하위에 FAQ 관리 메뉴 추가

홈페이지:
- Index.cshtml 하드코딩 FAQ → ActiveFaqs DB 루프로 교체
- FAQ 없으면 섹션 전체 숨김 (빈 DB에 안전)
- IndexModel: FaqService 주입, Task.WhenAll 병렬 로드

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 23:39:59 +09:00
parent b67002dcf5
commit ccba017e3e
12 changed files with 462 additions and 72 deletions
+20 -63
View File
@@ -352,7 +352,9 @@ else
</div>
</section>
<!-- 자주 묻는 질문 -->
<!-- 자주 묻는 질문 (DB 연동) -->
@if (Model.ActiveFaqs.Count > 0)
{
<section class="py-5" style="background: #F9F7F3;">
<div class="container">
<div class="text-center mb-5">
@@ -361,70 +363,24 @@ else
</div>
<div class="accordion faq-accordion" id="faqAccordion">
<div class="accordion-item faq-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed faq-question" type="button"
data-bs-toggle="collapse" data-bs-target="#faq1">
기장료가 얼마인지 미리 알 수 있나요?
</button>
</h3>
<div id="faq1" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body faq-answer">
업종과 매출 규모에 따라 다르지만, 무료 상담 후 정확한 견적을 안내드립니다.
일반적으로 소규모 사업자는 월 10만 원대부터 시작하며, 부가가치세·소득세 신고 시기에는 별도 수수료 없이 포함 처리합니다.
<strong>먼저 상담해 보시면 구체적인 금액을 바로 말씀드릴 수 있습니다.</strong>
@for (int i = 0; i < Model.ActiveFaqs.Count; i++)
{
var faqItem = Model.ActiveFaqs[i];
var collapseId = $"faq-{faqItem.Id}";
<div class="accordion-item faq-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed faq-question" type="button"
data-bs-toggle="collapse" data-bs-target="#@collapseId">
@faqItem.Question
</button>
</h3>
<div id="@collapseId" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body faq-answer">
@faqItem.Answer
</div>
</div>
</div>
</div>
<div class="accordion-item faq-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed faq-question" type="button"
data-bs-toggle="collapse" data-bs-target="#faq2">
양도세 상담은 어떻게 진행되나요?
</button>
</h3>
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body faq-answer">
등기부등본, 취득·양도 계약서, 보유 기간 확인 서류 등을 카카오 채널 또는 문의폼으로 전달해 주시면
예상 세액과 절세 방법을 검토해 드립니다.
<strong>매도 전에 상담하시면 취득세·비과세 요건 등을 사전에 확인할 수 있어 훨씬 유리합니다.</strong>
</div>
</div>
</div>
<div class="accordion-item faq-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed faq-question" type="button"
data-bs-toggle="collapse" data-bs-target="#faq3">
무료 상담도 가능한가요?
</button>
</h3>
<div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body faq-answer">
네, 초기 현황 파악과 방향성 검토까지는 무료로 진행합니다.
카카오 채널 또는 문의폼으로 연락 주시면 빠르게 확인해 드립니다.
<strong>실질적인 세무 처리·신고 대행이 시작되는 시점부터 수수료가 발생합니다.</strong>
</div>
</div>
</div>
<div class="accordion-item faq-item">
<h3 class="accordion-header">
<button class="accordion-button collapsed faq-question" type="button"
data-bs-toggle="collapse" data-bs-target="#faq4">
처음 상담 시 어떤 자료를 준비해야 하나요?
</button>
</h3>
<div id="faq4" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body faq-answer">
상담 목적에 따라 다르지만 일반적으로 아래 자료가 있으면 더 정확한 안내가 가능합니다:
<ul class="mt-2 mb-0">
<li><strong>사업자 세무:</strong> 사업자등록증, 최근 3개월 매출·매입 자료</li>
<li><strong>부동산:</strong> 등기부등본, 취득·매도 계약서, 보유 기간 확인 자료</li>
<li><strong>증여·상속:</strong> 재산 목록, 증여 예정 자산 내역</li>
</ul>
<span class="d-block mt-2"><strong>자료가 없어도 상담은 가능합니다. 먼저 연락 주세요.</strong></span>
</div>
</div>
</div>
}
</div>
<div class="text-center mt-5">
@@ -433,6 +389,7 @@ else
</div>
</div>
</section>
}
<!-- 최종 CTA -->
<section class="py-5" style="background: linear-gradient(135deg, #2E5C4E 0%, #1F3A30 100%); color: white;">