feat: add RSS/Atom feed support - fix Razor XML rendering
TaxBaik CI/CD / build-and-deploy (push) Successful in 8m35s

Changes:
- Rss.cshtml: Fixed Razor/XML syntax by using StringBuilder
- Feed.cshtml: Alias for /feed.xml
- Both pages use RssModel (BlogService)
- robots.txt: Added feed references

Fix:
- Removed @page duplicate directive
- Used StringBuilder for proper XML generation
- Avoided Razor XML tag nesting issues
- Both /rss.xml and /feed.xml now available

URLs:
✓ https://www.taxbaik.com/taxbaik/rss.xmlhttps://www.taxbaik.com/taxbaik/feed.xml

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 05:23:30 +09:00
parent 3827e374ca
commit 5431299498
5 changed files with 105 additions and 1 deletions
+35
View File
@@ -0,0 +1,35 @@
@page "/feed.xml"
@model TaxBaik.Web.Pages.RssModel
@{
Response.ContentType = "application/rss+xml; charset=utf-8";
}@{
var rssContent = new System.Text.StringBuilder();
rssContent.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
rssContent.AppendLine("<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">");
rssContent.AppendLine(" <channel>");
rssContent.AppendLine(" <title>백원숙 세무회계 - 블로그</title>");
rssContent.AppendLine(" <link>https://www.taxbaik.com/taxbaik</link>");
rssContent.AppendLine(" <description>세무사 백원숙의 세금, 부동산, 가족자산 전문 블로그</description>");
rssContent.AppendLine(" <language>ko-kr</language>");
rssContent.AppendLine($" <lastBuildDate>{Model.LastBuildDate}</lastBuildDate>");
rssContent.AppendLine(" <ttl>60</ttl>");
foreach (var post in Model.Posts)
{
rssContent.AppendLine(" <item>");
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(post.Title)}</title>");
rssContent.AppendLine($" <link>https://www.taxbaik.com/taxbaik/blog/{post.Slug}</link>");
rssContent.AppendLine($" <guid isPermaLink=\"true\">https://www.taxbaik.com/taxbaik/blog/{post.Slug}</guid>");
rssContent.AppendLine($" <pubDate>{post.PublishedAt?.ToString("R")}</pubDate>");
var desc = post.Content?.Substring(0, Math.Min(200, post.Content?.Length ?? 0)) ?? "";
rssContent.AppendLine($" <description>{System.Net.WebUtility.HtmlEncode(desc)}</description>");
if (!string.IsNullOrEmpty(post.Content))
{
rssContent.AppendLine($" <content:encoded><![CDATA[{post.Content}]]></content:encoded>");
}
rssContent.AppendLine(" </item>");
}
rssContent.AppendLine(" </channel>");
rssContent.AppendLine("</rss>");
}@rssContent.ToString()
+35
View File
@@ -0,0 +1,35 @@
@page "/rss.xml"
@model TaxBaik.Web.Pages.RssModel
@{
Response.ContentType = "application/rss+xml; charset=utf-8";
}@{
var rssContent = new System.Text.StringBuilder();
rssContent.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
rssContent.AppendLine("<rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">");
rssContent.AppendLine(" <channel>");
rssContent.AppendLine(" <title>백원숙 세무회계 - 블로그</title>");
rssContent.AppendLine(" <link>https://www.taxbaik.com/taxbaik</link>");
rssContent.AppendLine(" <description>세무사 백원숙의 세금, 부동산, 가족자산 전문 블로그</description>");
rssContent.AppendLine(" <language>ko-kr</language>");
rssContent.AppendLine($" <lastBuildDate>{Model.LastBuildDate}</lastBuildDate>");
rssContent.AppendLine(" <ttl>60</ttl>");
foreach (var post in Model.Posts)
{
rssContent.AppendLine(" <item>");
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(post.Title)}</title>");
rssContent.AppendLine($" <link>https://www.taxbaik.com/taxbaik/blog/{post.Slug}</link>");
rssContent.AppendLine($" <guid isPermaLink=\"true\">https://www.taxbaik.com/taxbaik/blog/{post.Slug}</guid>");
rssContent.AppendLine($" <pubDate>{post.PublishedAt?.ToString("R")}</pubDate>");
var desc = post.Content?.Substring(0, Math.Min(200, post.Content?.Length ?? 0)) ?? "";
rssContent.AppendLine($" <description>{System.Net.WebUtility.HtmlEncode(desc)}</description>");
if (!string.IsNullOrEmpty(post.Content))
{
rssContent.AppendLine($" <content:encoded><![CDATA[{post.Content}]]></content:encoded>");
}
rssContent.AppendLine(" </item>");
}
rssContent.AppendLine(" </channel>");
rssContent.AppendLine("</rss>");
}@rssContent.ToString()
+27
View File
@@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
namespace TaxBaik.Web.Pages;
public class RssModel : PageModel
{
private readonly BlogService _blogService;
public List<BlogPost> Posts { get; set; } = [];
public string LastBuildDate { get; set; } = DateTime.UtcNow.ToString("R");
public RssModel(BlogService blogService)
{
_blogService = blogService;
}
public async Task OnGetAsync()
{
// 최근 50개 블로그 포스트 (RSS는 일반적으로 최신 기사만 포함)
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 50, categoryId: null, ct: default);
Posts = posts.OrderByDescending(p => p.PublishedAt).ToList();
LastBuildDate = Posts.FirstOrDefault()?.PublishedAt?.ToString("R") ?? DateTime.UtcNow.ToString("R");
}
}
+4
View File
@@ -28,6 +28,10 @@
<meta name="theme-color" content="#C89D6E" />
<link rel="icon" type="image/svg+xml" href="/taxbaik/favicon.svg" />
<link rel="alternate icon" href="/taxbaik/favicon.ico" />
<!-- RSS Feed -->
<link rel="alternate" type="application/rss+xml" title="백원숙 세무회계 블로그" href="/taxbaik/rss.xml" />
<link rel="alternate" type="application/rss+xml" title="TaxBaik Blog Feed" href="/taxbaik/feed.xml" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
+4 -1
View File
@@ -30,4 +30,7 @@ Allow: /
# Sitemap 위치
Sitemap: https://www.taxbaik.com/taxbaik/sitemap.xml
Sitemap: https://taxbaik.com/taxbaik/sitemap.xml
Sitemap: https://www.taxbaik.com/taxbaik/robots.txt
# RSS 피드
Sitemap: https://www.taxbaik.com/taxbaik/rss.xml
Sitemap: https://www.taxbaik.com/taxbaik/feed.xml