구현: W3 공개 홈페이지 (Razor Pages SSR)

공개 사이트 (port 5001):
- Program.cs: AddRazorPages, AddInfrastructure, AddApplication, UsePathBase("/taxbaik")
- appsettings.json: PostgreSQL 연결 문자열

레이아웃 및 공통 컴포넌트:
- _Layout.cshtml: Bootstrap 5, Noto Sans KR, OG 메타 태그, 모바일 고정 CTA 바
- _Header.cshtml: sticky navbar, 로고, 네비게이션 링크, 상담신청 버튼
- _Footer.cshtml: 사업자정보, 연락처, KakaoTalk, 저작권
- _ViewImports.cshtml, _ViewStart.cshtml
- site.css: CSS 변수 (--color-primary, --color-cta 등), 반응형 스타일
- site.js: 모바일 CTA 바 제어, sticky 헤더 효과

페이지 구현:
1. Index.cshtml (메인 랜딩): Hero, 신뢰도 strip, 서비스 카드, 최근 블로그
2. Services.cshtml (서비스): 4개 서비스 소개, 상담료 안내
3. About.cshtml (소개): 세무사 프로필, 3개 자격증, 서비스 철학
4. Contact.cshtml (상담신청): 폼 (이름, 전화, 이메일, 분야, 문의), ValidationException 처리
5. Blog/Index.cshtml (블로그 목록): 카테고리 필터 탭, 12개 그리드, 페이지네이션
6. Blog/Post.cshtml (포스트 상세): 브레드크럼, 제목, 메타정보, 콘텐츠, CTA, 공유 버튼

SEO:
- robots.txt: /taxbaik 허용, /admin 차단, sitemap 링크
- Sitemap.cshtml: 동적 생성 (정적 페이지 + 모든 포스트)

기술:
- Dapper 기반 DB 접근
- 페이징: 12개/페이지
- 한국어 입력값 검증

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-26 15:10:39 +09:00
parent 06792e4e0f
commit 6a37cfead4
22 changed files with 862 additions and 1 deletions
+57
View File
@@ -0,0 +1,57 @@
@page
@{
ViewData["Title"] = "소개 | 백원숙 세무회계";
}
<div class="container py-5">
<h1 class="fw-bold mb-5">백원숙 세무사</h1>
<div class="row g-5">
<div class="col-md-6">
<p class="lead">사업자 세무, 부동산 거래, 가족 자산 관리 등 종합적인 세무 컨설팅을 제공합니다.</p>
<p>10년 이상의 풍부한 경험과 3개의 국가자격증을 바탕으로, 각 클라이언트의 상황에 맞는 맞춤형 솔루션을 제시합니다.</p>
</div>
<div class="col-md-6">
<div class="bg-light p-4 rounded">
<h5 class="fw-bold mb-3">보유 자격증</h5>
<div class="mb-3">
<p class="mb-1">🎓 <strong>세무사</strong></p>
<small class="text-muted">2015년 자격취득</small>
</div>
<div class="mb-3">
<p class="mb-1">🏠 <strong>부동산중개사</strong></p>
<small class="text-muted">부동산 거래 전문성</small>
</div>
<div>
<p class="mb-1">📊 <strong>보험설계사</strong></p>
<small class="text-muted">자산관리 전문성</small>
</div>
</div>
</div>
</div>
<hr class="my-5" />
<h2 class="fw-bold mb-4">서비스 철학</h2>
<div class="row g-4">
<div class="col-md-4 text-center">
<div class="mb-3" style="font-size: 2rem;">🎯</div>
<h5>명확한 설명</h5>
<p class="small">어려운 세법을 쉽게 설명하여 이해를 높입니다</p>
</div>
<div class="col-md-4 text-center">
<div class="mb-3" style="font-size: 2rem;">💰</div>
<h5>최대 절세</h5>
<p class="small">법적 범위 내에서 세금을 최소화합니다</p>
</div>
<div class="col-md-4 text-center">
<div class="mb-3" style="font-size: 2rem;">🤝</div>
<h5>신뢰 관계</h5>
<p class="small">장기적 파트너로서 성장을 함께 합니다</p>
</div>
</div>
<div class="text-center mt-5">
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 신청하기</a>
</div>
</div>
+59
View File
@@ -0,0 +1,59 @@
@page
@model BlogIndexModel
@{
ViewData["Title"] = "블로그 | 백원숙 세무회계";
}
<div class="container py-5">
<h1 class="fw-bold mb-5">세무 블로그</h1>
<!-- Category Tabs -->
<div class="mb-4">
<a href="/taxbaik/blog" class="btn btn-sm @(Model.SelectedCategoryId == null ? "btn-primary" : "btn-outline-primary")">전체</a>
@foreach (var cat in Model.Categories)
{
<a href="/taxbaik/blog?categoryId=@cat.Id" class="btn btn-sm @(Model.SelectedCategoryId == cat.Id ? "btn-primary" : "btn-outline-primary")">@cat.Name</a>
}
</div>
<!-- Posts Grid -->
<div class="row g-4">
@foreach (var post in Model.Posts)
{
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<small class="badge bg-primary">@post.CategoryName</small>
<h5 class="card-title mt-2">@post.Title</h5>
<p class="card-text small">@post.CreatedAt.ToString("yyyy-MM-dd")</p>
<a href="/taxbaik/blog/@post.Slug" class="btn btn-sm btn-primary">읽기</a>
</div>
</div>
</div>
}
</div>
<!-- Pagination -->
<nav aria-label="Page navigation" class="mt-5">
<ul class="pagination justify-content-center">
@if (Model.CurrentPage > 1)
{
<li class="page-item">
<a class="page-link" href="/taxbaik/blog?page=@(Model.CurrentPage - 1)">이전</a>
</li>
}
@for (int i = 1; i <= Model.TotalPages; i++)
{
<li class="page-item @(i == Model.CurrentPage ? "active" : "")">
<a class="page-link" href="/taxbaik/blog?page=@i">@i</a>
</li>
}
@if (Model.CurrentPage < Model.TotalPages)
{
<li class="page-item">
<a class="page-link" href="/taxbaik/blog?page=@(Model.CurrentPage + 1)">다음</a>
</li>
}
</ul>
</nav>
</div>
+35
View File
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
namespace TaxBaik.Web.Pages.Blog;
public class BlogIndexModel : PageModel
{
private readonly BlogService _blogService;
private readonly ICategoryRepository _categoryRepository;
public List<BlogPost> Posts { get; set; } = [];
public List<Category> Categories { get; set; } = [];
public int CurrentPage { get; set; } = 1;
public int TotalPages { get; set; }
public int? SelectedCategoryId { get; set; }
private const int PageSize = 12;
public BlogIndexModel(BlogService blogService, ICategoryRepository categoryRepository)
{
_blogService = blogService;
_categoryRepository = categoryRepository;
}
public async Task OnGetAsync(int page = 1, int? categoryId = null)
{
CurrentPage = page;
SelectedCategoryId = categoryId;
Categories = (await _categoryRepository.GetAllAsync()).ToList();
var (posts, total) = await _blogService.GetPublishedPagedAsync(page, PageSize, categoryId);
Posts = posts.ToList();
TotalPages = (total + PageSize - 1) / PageSize;
}
}
+62
View File
@@ -0,0 +1,62 @@
@page "{slug}"
@model BlogPostModel
@{
ViewData["Title"] = Model.Post?.SeoTitle ?? Model.Post?.Title;
ViewData["Description"] = Model.Post?.SeoDescription ?? "";
ViewData["OgImage"] = Model.Post?.ThumbnailUrl ?? "";
}
@if (Model.Post != null)
{
<article class="container py-5" style="max-width: 720px;">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/taxbaik">홈</a></li>
<li class="breadcrumb-item"><a href="/taxbaik/blog">블로그</a></li>
<li class="breadcrumb-item active">@Model.Post.CategoryName</li>
</ol>
</nav>
<h1 class="fw-bold mb-3">@Model.Post.Title</h1>
<div class="text-muted small mb-4">
<span>@Model.Post.CreatedAt.ToString("yyyy-MM-dd")</span>
<span class="ms-3">👁️ @Model.Post.ViewCount</span>
<span class="badge bg-primary ms-3">@Model.Post.CategoryName</span>
</div>
<hr />
<div class="article-body">
@Html.Raw(Model.Post.Content)
</div>
<hr />
<!-- CTA -->
<div class="bg-light p-4 rounded mb-5">
<h5 class="fw-bold mb-2">상담이 필요하신가요?</h5>
<p class="mb-3">이 글과 관련된 상담이 필요하면 언제든 연락주세요.</p>
<a href="/taxbaik/contact" class="btn btn-primary">상담 신청하기</a>
</div>
<!-- Share -->
<div class="text-center mb-5">
<p class="small text-muted">이 글을 공유하세요:</p>
<button class="btn btn-sm btn-outline-secondary" onclick="copyUrl()">📋 링크복사</button>
</div>
</article>
}
else
{
<div class="container py-5 text-center">
<p class="fs-5">포스트를 찾을 수 없습니다.</p>
<a href="/taxbaik/blog" class="btn btn-primary">블로그 돌아가기</a>
</div>
}
<script>
function copyUrl() {
navigator.clipboard.writeText(window.location.href);
alert('링크가 복사되었습니다.');
}
</script>
+26
View File
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
namespace TaxBaik.Web.Pages.Blog;
public class BlogPostModel : PageModel
{
private readonly BlogService _blogService;
public BlogPost? Post { get; set; }
public BlogPostModel(BlogService blogService)
{
_blogService = blogService;
}
public async Task OnGetAsync(string slug)
{
Post = await _blogService.GetBySlugAsync(slug);
if (Post != null)
{
_ = _blogService.IncrementViewCountAsync(Post.Id);
}
}
}
+66
View File
@@ -0,0 +1,66 @@
@page
@model ContactModel
@{
ViewData["Title"] = "상담 신청 | 백원숙 세무회계";
}
<div class="container py-5" style="max-width: 600px;">
<h1 class="fw-bold mb-5">상담 신청</h1>
@if (TempData["Success"] != null)
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
@TempData["Success"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
<form method="post">
<div class="mb-3">
<label for="name" class="form-label">이름 <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="name" name="Name" required />
</div>
<div class="mb-3">
<label for="phone" class="form-label">전화번호 <span class="text-danger">*</span></label>
<input type="tel" class="form-control" id="phone" name="Phone" placeholder="010-0000-0000" required />
<small class="form-text text-muted">형식: 010-0000-0000</small>
</div>
<div class="mb-3">
<label for="email" class="form-label">이메일</label>
<input type="email" class="form-control" id="email" name="Email" />
</div>
<div class="mb-3">
<label for="service" class="form-label">상담분야</label>
<select class="form-select" id="service" name="ServiceType">
<option value="기장">사업자 기장</option>
<option value="양도세">부동산 양도세</option>
<option value="종소세">종합소득세</option>
<option value="증여상속">증여·상속세</option>
<option value="기타">기타</option>
</select>
</div>
<div class="mb-3">
<label for="message" class="form-label">문의내용 <span class="text-danger">*</span></label>
<textarea class="form-control" id="message" name="Message" rows="5" required></textarea>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="agree" name="Agree" required />
<label class="form-check-label" for="agree">
개인정보 수집·이용에 동의합니다
</label>
</div>
<button type="submit" class="btn btn-primary btn-lg w-100">상담신청</button>
</form>
<hr class="my-5" />
<h5 class="fw-bold mb-3">빠른 상담을 원하시나요?</h5>
<p>카카오톡 채널을 통해 더 빠르게 상담받을 수 있습니다.</p>
<button class="btn btn-warning" onclick="openKakao()">카카오톡 채널 문의</button>
</div>
+54
View File
@@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
namespace TaxBaik.Web.Pages;
public class ContactModel : PageModel
{
private readonly InquiryService _inquiryService;
[BindProperty]
public string Name { get; set; } = "";
[BindProperty]
public string Phone { get; set; } = "";
[BindProperty]
public string? Email { get; set; }
[BindProperty]
public string ServiceType { get; set; } = "기타";
[BindProperty]
public string Message { get; set; } = "";
[BindProperty]
public bool Agree { get; set; }
public ContactModel(InquiryService inquiryService)
{
_inquiryService = inquiryService;
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid || !Agree)
{
return Page();
}
try
{
var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
await _inquiryService.SubmitAsync(Name, Phone, ServiceType, Message, ipAddress);
TempData["Success"] = "상담 신청이 접수되었습니다. 빠른 시간 내에 연락드리겠습니다.";
return RedirectToPage();
}
catch (ValidationException ex)
{
ModelState.AddModelError("", ex.Message);
return Page();
}
}
}
+111
View File
@@ -0,0 +1,111 @@
@page
@model IndexModel
@{
ViewData["Title"] = "백원숙 세무회계 | 사업자·부동산·증여 세무 상담";
ViewData["Description"] = "사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담. 온라인 맞춤 상담 제공.";
}
<!-- Hero Section -->
<section class="hero-section bg-primary text-white py-5">
<div class="container text-center">
<h1 class="display-4 fw-bold mb-3">세금 걱정, 한 번에 해결합니다</h1>
<p class="lead mb-4">사업자 세무, 부동산 세금, 가족자산 맞춤 상담</p>
<div class="gap-3 d-flex justify-content-center flex-wrap">
<a href="/taxbaik/contact" class="btn btn-warning btn-lg">무료 상담 신청</a>
<a href="javascript:void(0);" class="btn btn-light btn-lg" onclick="openKakao()">카카오 채널 문의</a>
</div>
</div>
</section>
<!-- Trust Strip -->
<section class="bg-light py-4">
<div class="container">
<div class="row text-center">
<div class="col-md-4 mb-3">
<div class="fs-2">🎓</div>
<p class="fw-bold">세무사</p>
<small class="text-muted">2015년 자격취득</small>
</div>
<div class="col-md-4 mb-3">
<div class="fs-2">🏠</div>
<p class="fw-bold">부동산중개사</p>
<small class="text-muted">부동산 거래 전문</small>
</div>
<div class="col-md-4 mb-3">
<div class="fs-2">📊</div>
<p class="fw-bold">보험설계사</p>
<small class="text-muted">자산관리 전문</small>
</div>
</div>
</div>
</section>
<!-- Services Section -->
<section class="py-5">
<div class="container">
<h2 class="text-center fw-bold mb-5">주요 서비스</h2>
<div class="row g-4">
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">사업자 세무</h5>
<p class="card-text">기장, 세금계산서, 경비처리</p>
<a href="/taxbaik/services" class="btn btn-sm btn-outline-primary">자세히보기</a>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">부동산 세금</h5>
<p class="card-text">양도세, 취득세, 임대소득</p>
<a href="/taxbaik/services" class="btn btn-sm btn-outline-primary">자세히보기</a>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">가족자산</h5>
<p class="card-text">증여세, 상속세, 자산관리</p>
<a href="/taxbaik/services" class="btn btn-sm btn-outline-primary">자세히보기</a>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Blog Preview -->
<section class="bg-light py-5">
<div class="container">
<h2 class="text-center fw-bold mb-5">최근 블로그</h2>
<div class="row g-4">
@foreach (var post in Model.RecentPosts)
{
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body">
<small class="badge bg-primary">@post.CategoryName</small>
<h5 class="card-title mt-2">@post.Title</h5>
<p class="card-text small text-muted">@post.CreatedAt.ToString("yyyy-MM-dd")</p>
<a href="/taxbaik/blog/@post.Slug" class="btn btn-sm btn-primary">읽기</a>
</div>
</div>
</div>
}
</div>
<div class="text-center mt-4">
<a href="/taxbaik/blog" class="btn btn-outline-primary">모든 글 보기</a>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="bg-primary text-white py-5">
<div class="container text-center">
<h2 class="fw-bold mb-3">지금 바로 상담 받으세요</h2>
<p class="lead mb-4">무료로 상황을 진단받고 맞춤 솔루션을 제안받을 수 있습니다</p>
<a href="/taxbaik/contact" class="btn btn-warning btn-lg">상담 신청하기</a>
</div>
</section>
+23
View File
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
namespace TaxBaik.Web.Pages;
public class IndexModel : PageModel
{
private readonly BlogService _blogService;
public List<BlogPost> RecentPosts { get; set; } = [];
public IndexModel(BlogService blogService)
{
_blogService = blogService;
}
public async Task OnGetAsync()
{
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 3);
RecentPosts = posts.ToList();
}
}
+57
View File
@@ -0,0 +1,57 @@
@page
@{
ViewData["Title"] = "서비스 소개 | 백원숙 세무회계";
}
<div class="container py-5">
<h1 class="fw-bold mb-5">서비스 안내</h1>
<div class="row g-5">
<div class="col-md-6">
<h3 class="fw-bold text-primary mb-3">사업자 세무</h3>
<p>개인사업자의 기장, 세금계산서, 경비처리, 결산 등을 전문적으로 지원합니다.</p>
<ul class="list-unstyled">
<li>✓ 매월 기장 대행</li>
<li>✓ 결산 및 신고</li>
<li>✓ 절세 컨설팅</li>
</ul>
</div>
<div class="col-md-6">
<h3 class="fw-bold text-primary mb-3">부동산 세금</h3>
<p>부동산 거래, 임대소득, 양도세 등 부동산 관련 세무를 종합적으로 관리합니다.</p>
<ul class="list-unstyled">
<li>✓ 양도세 계산 및 절세</li>
<li>✓ 임대소득 신고</li>
<li>✓ 취득세 최소화</li>
</ul>
</div>
<div class="col-md-6">
<h3 class="fw-bold text-primary mb-3">증여·상속세</h3>
<p>가족 간 자산이전 시 세무 전략을 수립하고 절세 방안을 제시합니다.</p>
<ul class="list-unstyled">
<li>✓ 증여세 계획</li>
<li>✓ 상속세 대비</li>
<li>✓ 자산분산 전략</li>
</ul>
</div>
<div class="col-md-6">
<h3 class="fw-bold text-primary mb-3">종합소득세</h3>
<p>프리랜서, 보험설계사 등 다양한 소득자의 세무신고를 지원합니다.</p>
<ul class="list-unstyled">
<li>✓ 신고 대행</li>
<li>✓ 경비 최적화</li>
<li>✓ 환급 최대화</li>
</ul>
</div>
</div>
<hr class="my-5" />
<h2 class="fw-bold mb-4">상담료 안내</h2>
<p class="lead">상담료는 7만~20만 원이며, 계약 체결 시 일부 차감됩니다.</p>
<p>초기 무료 전화 상담을 통해 상황을 진단 후 정확한 견적을 제시합니다.</p>
<div class="text-center mt-5">
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 신청하기</a>
</div>
</div>
+34
View File
@@ -0,0 +1,34 @@
<footer class="bg-light border-top mt-5 py-4">
<div class="container">
<div class="row g-4">
<div class="col-md-4">
<h6 class="fw-bold">백원숙 세무회계</h6>
<p class="small text-muted">
사업자 기장, 부동산 양도세·증여세,<br />
종합소득세 전문 상담
</p>
</div>
<div class="col-md-4">
<h6 class="fw-bold">연락처</h6>
<p class="small">
📞 <a href="tel:010-0000-0000" class="text-decoration-none">010-0000-0000</a><br />
📧 <a href="mailto:info@example.com" class="text-decoration-none">info@example.com</a>
</p>
</div>
<div class="col-md-4">
<h6 class="fw-bold">카카오톡</h6>
<p class="small">
<a href="javascript:void(0);" class="btn btn-sm btn-warning" onclick="openKakao()">
카카오톡 채널
</a>
</p>
</div>
</div>
<hr class="my-3" />
<div class="text-center small text-muted">
<p>© 2026 백원숙 세무회계. All rights reserved.</p>
<a href="/taxbaik/privacy" class="text-decoration-none text-muted me-2">개인정보처리방침</a>
<a href="/taxbaik/terms" class="text-decoration-none text-muted">이용약관</a>
</div>
</div>
</footer>
+34
View File
@@ -0,0 +1,34 @@
<header class="sticky-top bg-white border-bottom">
<nav class="navbar navbar-expand-lg navbar-light container-fluid px-3">
<a class="navbar-brand fw-bold" href="/taxbaik">
<span class="text-primary">백원숙</span> 세무회계
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto gap-2">
<li class="nav-item">
<a class="nav-link" href="/taxbaik">홈</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/taxbaik/about">소개</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/taxbaik/services">서비스</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/taxbaik/blog">블로그</a>
</li>
<li class="nav-item">
<a class="btn btn-primary btn-sm ms-2" href="/taxbaik/contact">상담신청</a>
</li>
</ul>
</div>
<div class="ms-3 d-none d-md-block">
<a href="tel:010-0000-0000" class="text-decoration-none text-dark fw-500">
📞 상담신청
</a>
</div>
</nav>
</header>
@@ -0,0 +1,3 @@
@using TaxBaik.Web
@namespace TaxBaik.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}
+14
View File
@@ -0,0 +1,14 @@
@page
@model SitemapModel
@{
Response.ContentType = "application/xml";
}<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
@foreach (var url in Model.Urls)
{
<url>
<loc>@url</loc>
<lastmod>@DateTime.UtcNow:yyyy-MM-dd</lastmod>
</url>
}
</urlset>
+34
View File
@@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
namespace TaxBaik.Web.Pages;
public class SitemapModel : PageModel
{
private readonly BlogService _blogService;
public List<string> Urls { get; set; } = [];
public SitemapModel(BlogService blogService)
{
_blogService = blogService;
}
public async Task OnGetAsync()
{
var baseUrl = "http://178.104.200.7/taxbaik";
Urls.AddRange(new[]
{
$"{baseUrl}",
$"{baseUrl}/about",
$"{baseUrl}/services",
$"{baseUrl}/contact",
$"{baseUrl}/blog"
});
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 1000);
foreach (var post in posts)
{
Urls.Add($"{baseUrl}/blog/{post.Slug}");
}
}
}
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@(ViewData["Title"] ?? "백원숙 세무회계")</title>
<meta name="description" content="@(ViewData["Description"] ?? "사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담.")" />
<meta property="og:title" content="@ViewData["Title"]" />
<meta property="og:description" content="@ViewData["Description"]" />
<meta property="og:image" content="@ViewData["OgImage"]" />
<meta property="og:url" content="@ViewData["OgUrl"]" />
<meta name="robots" content="index, follow" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body>
<partial name="_Header" />
<main role="main" class="pb-5">
@RenderBody()
</main>
<partial name="_Footer" />
<!-- Mobile Fixed CTA -->
<div class="mobile-cta-bar d-lg-none">
<a href="javascript:void(0);" class="btn-kakao-mobile" onclick="openKakao()">
💬 카카오 상담하기
</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
+14 -1
View File
@@ -1,6 +1,19 @@
using TaxBaik.Application;
using TaxBaik.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddMemoryCache();
builder.Services.AddInfrastructure();
builder.Services.AddApplication();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.UsePathBase("/taxbaik");
app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.Run();
+3
View File
@@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"Default": "Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=taxbaik123"
},
"AllowedHosts": "*"
}
+110
View File
@@ -0,0 +1,110 @@
:root {
--color-primary: #1B4F8A;
--color-primary-dark: #133970;
--color-accent: #C9A227;
--color-cta: #E05A2B;
--color-bg: #F7F9FC;
--color-text: #1A1A2E;
--color-text-muted: #5A6A7A;
}
* {
font-family: 'Noto Sans KR', 'Apple SD Gothic Neo', sans-serif;
}
body {
color: var(--color-text);
background-color: #fff;
}
.btn-primary {
background-color: var(--color-primary);
border-color: var(--color-primary);
}
.btn-primary:hover {
background-color: var(--color-primary-dark);
border-color: var(--color-primary-dark);
}
.btn-warning {
background-color: var(--color-cta);
border-color: var(--color-cta);
}
.btn-warning:hover {
background-color: #d45a1f;
border-color: #d45a1f;
}
.bg-primary {
background-color: var(--color-primary) !important;
}
.text-primary {
color: var(--color-primary) !important;
}
.hero-section {
padding: 80px 0;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
}
.mobile-cta-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
border-top: 1px solid #ddd;
padding: 10px;
z-index: 1000;
}
.btn-kakao-mobile {
display: block;
width: 100%;
padding: 12px;
background: #FFE812;
color: black;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
text-align: center;
border: none;
cursor: pointer;
}
.btn-kakao-mobile:hover {
background: #FDD835;
}
.card {
transition: transform 0.2s;
}
.card:hover {
transform: translateY(-4px);
}
.navbar-brand {
font-size: 1.3rem;
}
.navbar {
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
@media (max-width: 768px) {
body {
padding-bottom: 70px;
}
.hero-section {
padding: 40px 0;
}
.hero-section h1 {
font-size: 1.8rem;
}
}
+20
View File
@@ -0,0 +1,20 @@
function openKakao() {
const kakaoUrl = document.querySelector('[data-kakao-url]')?.dataset.kakaoUrl || '#';
if (kakaoUrl !== '#') {
window.open(kakaoUrl, '_blank');
} else {
alert('카카오톡 채널 URL이 설정되지 않았습니다.');
}
}
document.addEventListener('DOMContentLoaded', function() {
// Sticky header shadow
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', function() {
if (window.scrollY > 0) {
navbar.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
} else {
navbar.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)';
}
});
});
+6
View File
@@ -0,0 +1,6 @@
User-agent: *
Allow: /taxbaik/
Disallow: /taxbaik/admin/
Disallow: /taxbaik/manage/
Sitemap: http://178.104.200.7/taxbaik/sitemap.xml