Use combined RSS feed items
TaxBaik CI/CD / build-and-deploy (push) Successful in 1m22s

This commit is contained in:
2026-07-11 15:37:50 +09:00
parent 228dc6683d
commit fb3ee4aad6
3 changed files with 70 additions and 16 deletions
+1
View File
@@ -30,6 +30,7 @@ robots = read("src/TaxBaik.Web/wwwroot/robots.txt")
require("Layout = null" in sitemap_view, "sitemap.xml must render without the HTML layout")
require("Layout = null" in rss_view, "rss.xml must render without the HTML layout")
require("Html.Raw(rssContent.ToString())" in rss_view, "RSS XML must not be HTML-encoded")
require("Model.Items" in rss_view, "RSS view must render from the combined feed items")
require("StatusCodes.Status308PermanentRedirect" in feed_view, "feed.xml must permanently redirect to rss.xml")
require("DateTime.UtcNow:yyyy-MM-dd" not in sitemap_view, "sitemap lastmod must not use the current request date")
require("GetAllPublishedPostsAsync" in sitemap_model, "sitemap must page through all published blog posts")
+8 -8
View File
@@ -14,20 +14,20 @@
rssContent.AppendLine($" <lastBuildDate>{Model.LastBuildDate}</lastBuildDate>");
rssContent.AppendLine(" <ttl>60</ttl>");
foreach (var post in Model.Posts)
foreach (var item in Model.Items)
{
var postUrl = $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}";
var fullDescription = post.Content ?? string.Empty;
var itemUrl = item.Link;
var fullDescription = item.Description ?? string.Empty;
var safeDescription = fullDescription.Replace("]]>", "]]]]><![CDATA[>");
rssContent.AppendLine(" <item>");
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(post.Title)}</title>");
rssContent.AppendLine($" <link>{postUrl}</link>");
rssContent.AppendLine($" <guid isPermaLink=\"true\">{postUrl}</guid>");
rssContent.AppendLine($" <title>{System.Net.WebUtility.HtmlEncode(item.Title)}</title>");
rssContent.AppendLine($" <link>{itemUrl}</link>");
rssContent.AppendLine($" <guid isPermaLink=\"true\">{item.Guid}</guid>");
if (post.PublishedAt is not null)
if (item.PublishedAt is not null)
{
rssContent.AppendLine($" <pubDate>{post.PublishedAt.Value.ToUniversalTime():R}</pubDate>");
rssContent.AppendLine($" <pubDate>{item.PublishedAt.Value.ToUniversalTime():R}</pubDate>");
}
rssContent.AppendLine($" <description><![CDATA[{safeDescription}]]></description>");
+61 -8
View File
@@ -7,30 +7,83 @@ namespace TaxBaik.Web.Pages;
public class RssModel : PageModel
{
private readonly BlogService _blogService;
private readonly FaqService _faqService;
private readonly AnnouncementService _announcementService;
public List<BlogPost> Posts { get; set; } = [];
public List<RssItem> Items { get; set; } = [];
public string LastBuildDate { get; set; } = DateTime.UtcNow.ToString("R");
public RssModel(BlogService blogService)
public RssModel(
BlogService blogService,
FaqService faqService,
AnnouncementService announcementService)
{
_blogService = blogService;
_faqService = faqService;
_announcementService = announcementService;
}
public async Task OnGetAsync()
{
try
{
// 최근 50개 블로그 포스트 (RSS는 일반적으로 최신 기사만 포함)
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 50, categoryId: null, ct: default);
Posts = posts.OrderByDescending(p => p.PublishedAt).ToList();
var blogTask = _blogService.GetPublishedPagedAsync(1, 50, categoryId: null, ct: default);
var faqTask = _faqService.GetActiveAsync(HttpContext.RequestAborted);
var announcementTask = _announcementService.GetActiveAsync(HttpContext.RequestAborted);
await Task.WhenAll(blogTask, faqTask, announcementTask);
var blogPosts = (await blogTask).Item1
.OrderByDescending(p => p.PublishedAt)
.Select(post => new RssItem(
Title: post.Title,
Link: $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}",
Description: post.Content ?? string.Empty,
PublishedAt: post.PublishedAt,
Guid: $"https://www.taxbaik.com/blog/{Uri.EscapeDataString(post.Slug)}"))
.ToList();
var faqs = (await faqTask)
.OrderByDescending(f => f.UpdatedAt)
.Select(faq => new RssItem(
Title: $"FAQ: {faq.Question}",
Link: "https://www.taxbaik.com/#faq",
Description: faq.Answer,
PublishedAt: faq.UpdatedAt,
Guid: $"https://www.taxbaik.com/rss/faq/{faq.Id}"))
.ToList();
var announcements = (await announcementTask)
.OrderByDescending(a => a.UpdatedAt)
.Select(announcement => new RssItem(
Title: $"공지: {announcement.Title}",
Link: "https://www.taxbaik.com/#top",
Description: announcement.Content ?? announcement.Title,
PublishedAt: announcement.UpdatedAt,
Guid: $"https://www.taxbaik.com/rss/announcement/{announcement.Id}"))
.ToList();
Items = blogPosts
.Concat(faqs)
.Concat(announcements)
.OrderByDescending(item => item.PublishedAt ?? DateTime.MinValue)
.Take(50)
.ToList();
}
catch (Exception ex)
{
// DB 연결 실패: 빈 포스트 목록 반환 (정적 피드 구조는 유지)
Posts = [];
// DB 연결 실패: 빈 피드가 되지 않도록 로컬 서버 시간 기준 단일 항목을 유지하지는 않는다.
Items = [];
System.Diagnostics.Debug.WriteLine($"RSS Feed: Blog posts load failed: {ex.Message}");
}
LastBuildDate = Posts.FirstOrDefault()?.PublishedAt?.ToString("R") ?? DateTime.UtcNow.ToString("R");
LastBuildDate = Items.FirstOrDefault()?.PublishedAt?.ToString("R") ?? DateTime.UtcNow.ToString("R");
}
public sealed record RssItem(
string Title,
string Link,
string Description,
DateTime? PublishedAt,
string Guid);
}