From fb3ee4aad6937a33bef1095c32a8eaebbcab5491 Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Sat, 11 Jul 2026 15:37:50 +0900 Subject: [PATCH] Use combined RSS feed items --- scripts/validate_seo.py | 1 + src/TaxBaik.Web/Pages/Rss.cshtml | 16 +++---- src/TaxBaik.Web/Pages/Rss.cshtml.cs | 69 +++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 16 deletions(-) diff --git a/scripts/validate_seo.py b/scripts/validate_seo.py index 08eb54e..6f58d1c 100644 --- a/scripts/validate_seo.py +++ b/scripts/validate_seo.py @@ -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") diff --git a/src/TaxBaik.Web/Pages/Rss.cshtml b/src/TaxBaik.Web/Pages/Rss.cshtml index 2fb1cc3..0c7c59e 100644 --- a/src/TaxBaik.Web/Pages/Rss.cshtml +++ b/src/TaxBaik.Web/Pages/Rss.cshtml @@ -14,20 +14,20 @@ rssContent.AppendLine($" {Model.LastBuildDate}"); rssContent.AppendLine(" 60"); - 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("]]>", "]]]]>"); rssContent.AppendLine(" "); - rssContent.AppendLine($" {System.Net.WebUtility.HtmlEncode(post.Title)}"); - rssContent.AppendLine($" {postUrl}"); - rssContent.AppendLine($" {postUrl}"); + rssContent.AppendLine($" {System.Net.WebUtility.HtmlEncode(item.Title)}"); + rssContent.AppendLine($" {itemUrl}"); + rssContent.AppendLine($" {item.Guid}"); - if (post.PublishedAt is not null) + if (item.PublishedAt is not null) { - rssContent.AppendLine($" {post.PublishedAt.Value.ToUniversalTime():R}"); + rssContent.AppendLine($" {item.PublishedAt.Value.ToUniversalTime():R}"); } rssContent.AppendLine($" "); diff --git a/src/TaxBaik.Web/Pages/Rss.cshtml.cs b/src/TaxBaik.Web/Pages/Rss.cshtml.cs index 2acdbd3..4d21c2a 100644 --- a/src/TaxBaik.Web/Pages/Rss.cshtml.cs +++ b/src/TaxBaik.Web/Pages/Rss.cshtml.cs @@ -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 Posts { get; set; } = []; + public List 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); }