Rss feed 서비스 리팩토링; 의존성 분리, parser renderer static 유틸 클래스로 변경, facade 패턴 사용
This commit is contained in:
@@ -75,6 +75,7 @@ public class CacheConfig {
|
||||
- 4. seo 캐시
|
||||
- key 0 : rss
|
||||
- key 1 : sitemap
|
||||
- key 2 :
|
||||
*/
|
||||
CacheConfiguration rssConfiguration = new CacheConfiguration()
|
||||
.eternal(false)
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
package myblog.blog.seo.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
import myblog.blog.seo.service.RssService;
|
||||
import myblog.blog.seo.service.SeoFacadeService;
|
||||
import myblog.blog.seo.service.SiteMapService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
public class SEOController {
|
||||
|
||||
private final RssService rssService;
|
||||
private final SeoFacadeService seoFacadeService;
|
||||
private final SiteMapService siteMapService;
|
||||
|
||||
/*
|
||||
@@ -21,7 +26,7 @@ public class SEOController {
|
||||
*/
|
||||
@GetMapping(value = "/rss",produces = "application/xml;charset=utf-8")
|
||||
public @ResponseBody String rssFeed() {
|
||||
return rssService.makeRssFeed();
|
||||
return seoFacadeService.getRssFeed();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -2,16 +2,8 @@ package myblog.blog.seo.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
import org.jdom2.Attribute;
|
||||
import org.jdom2.CDATA;
|
||||
import org.jdom2.Document;
|
||||
import org.jdom2.Element;
|
||||
import org.jdom2.output.Format;
|
||||
import org.jdom2.output.XMLOutputter;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.jdom2.*;
|
||||
import org.jdom2.output.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -19,50 +11,43 @@ import java.sql.Timestamp;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
import static myblog.blog.utils.MarkdownUtils.*;
|
||||
|
||||
/*
|
||||
- rss 서비스 로직
|
||||
1. jdom2 사용하여 xml파일 작성
|
||||
2. 모든 게시물 item 변환후 xml에 삽입
|
||||
3. 발행
|
||||
*/
|
||||
@Service
|
||||
@Transactional
|
||||
@RequiredArgsConstructor
|
||||
public class RssService {
|
||||
|
||||
private final HtmlRenderer htmlRenderer;
|
||||
private final Parser parser;
|
||||
private final ArticleService articleService;
|
||||
static final String ITEM_ROOT = "https://www.jiniaslog.co.kr/article/view?articleId=";
|
||||
|
||||
/*
|
||||
- rss 빌드 서비스 로직
|
||||
1. 모든 게시물 조회
|
||||
2. jdom2 사용하여 xml파일 작성
|
||||
3. 모든 게시물 item 변환후 xml에 삽입
|
||||
4. 발행
|
||||
*/
|
||||
@Cacheable(value = "seoCaching", key = "0")
|
||||
public String makeRssFeed(){
|
||||
|
||||
List<Article> articles = articleService.getTotalArticle();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
|
||||
|
||||
// rss 레이아웃 루트 작성
|
||||
Element rss = new Element("rss");
|
||||
rss.setAttribute(new Attribute("version","2.0"));
|
||||
|
||||
// 채널 작성 밑 루트에 삽입
|
||||
Element channel = buildChannel();
|
||||
rss.addContent(channel);
|
||||
|
||||
// 채널에 아이템 삽입
|
||||
addItemToChannel(articles, dateFormat, channel);
|
||||
|
||||
// 포매팅해서 발행
|
||||
Document doc = new Document();
|
||||
doc.setRootElement(rss);
|
||||
public String getRssFeed(List<Article> articles) {
|
||||
Document doc = makeRssFeedDocument(articles);
|
||||
XMLOutputter xmlOutputter = getXmlOutputter();
|
||||
|
||||
return xmlOutputter.outputString(doc);
|
||||
}
|
||||
|
||||
/*
|
||||
- 채널 작성 로직
|
||||
*/
|
||||
private Document makeRssFeedDocument(List<Article> articles) {
|
||||
Document doc = new Document();
|
||||
Element rss = buildRssRoot();
|
||||
doc.setRootElement(rss);
|
||||
Element channel = buildChannel();
|
||||
rss.addContent(channel);
|
||||
addArticleItemsToChannel(articles, channel, new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH));
|
||||
return doc;
|
||||
}
|
||||
|
||||
private Element buildRssRoot() {
|
||||
Element rss = new Element("rss");
|
||||
rss.setAttribute(new Attribute("version", "2.0"));
|
||||
return rss;
|
||||
}
|
||||
|
||||
private Element buildChannel() {
|
||||
Element channel = new Element("channel");
|
||||
channel.addContent(new Element("title").addContent(new CDATA("Jinia's LOG")));
|
||||
@@ -71,31 +56,28 @@ public class RssService {
|
||||
return channel;
|
||||
}
|
||||
|
||||
/*
|
||||
- xml 발행기 생성
|
||||
*/
|
||||
private void addArticleItemsToChannel(List<Article> articles, Element channel, SimpleDateFormat dateFormat) {
|
||||
for (Article article : articles) {
|
||||
Element item = createItemTo(article, dateFormat);
|
||||
channel.addContent(item);
|
||||
}
|
||||
}
|
||||
|
||||
private Element createItemTo(Article article, SimpleDateFormat dateFormat) {
|
||||
Element item = new Element("item");
|
||||
item
|
||||
.addContent(new Element("title").addContent(new CDATA(article.getTitle())))
|
||||
.addContent(new Element("link").setText(ITEM_ROOT + article.getId()))
|
||||
.addContent(new Element("description").addContent(new CDATA(getHtmlRenderer().render(getParser().parse(article.getContent())))))
|
||||
.addContent(new Element("pubDate").setText(dateFormat.format(Timestamp.valueOf(article.getCreatedDate()))))
|
||||
.addContent(new Element("guid").setText(ITEM_ROOT + article.getId()));
|
||||
return item;
|
||||
}
|
||||
|
||||
private XMLOutputter getXmlOutputter() {
|
||||
Format format = Format.getPrettyFormat();
|
||||
format.setEncoding("UTF-8");
|
||||
format.setLineSeparator("\r\n");
|
||||
return new XMLOutputter(format);
|
||||
}
|
||||
|
||||
/*
|
||||
- item 작성 , 채널에 추가
|
||||
*/
|
||||
private void addItemToChannel(List<Article> articles, SimpleDateFormat dateFormat, Element channel) {
|
||||
for (Article article : articles) {
|
||||
|
||||
Element item = new Element("item");
|
||||
|
||||
item.addContent(new Element("title").addContent(new CDATA(article.getTitle())));
|
||||
item.addContent(new Element("link").setText("https://www.jiniaslog.co.kr/article/view?articleId=" + article.getId()));
|
||||
item.addContent(new Element("description").addContent(new CDATA(htmlRenderer.render(parser.parse(article.getContent())))));
|
||||
item.addContent(new Element("pubDate").setText(dateFormat.format(Timestamp.valueOf(article.getCreatedDate()))));
|
||||
item.addContent(new Element("guid").setText("https://www.jiniaslog.co.kr/article/view?articleId=" + article.getId()));
|
||||
|
||||
channel.addContent(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
src/main/java/myblog/blog/seo/service/SeoFacadeService.java
Normal file
25
src/main/java/myblog/blog/seo/service/SeoFacadeService.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package myblog.blog.seo.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SeoFacadeService {
|
||||
|
||||
private final RssService rssService;
|
||||
private final SiteMapService siteMapService;
|
||||
private final ArticleService articleService;
|
||||
|
||||
@Cacheable(value = "seoCaching", key = "0")
|
||||
public String getRssFeed(){
|
||||
List<Article> articles = articleService.getTotalArticle();
|
||||
return rssService.getRssFeed(articles);
|
||||
}
|
||||
|
||||
}
|
||||
30
src/main/java/myblog/blog/utils/MarkdownUtils.java
Normal file
30
src/main/java/myblog/blog/utils/MarkdownUtils.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package myblog.blog.utils;
|
||||
|
||||
import org.commonmark.ext.gfm.tables.TablesExtension;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MarkdownUtils {
|
||||
|
||||
private static final HtmlRenderer htmlRenderer;
|
||||
private static final Parser parser;
|
||||
|
||||
static {
|
||||
htmlRenderer = HtmlRenderer.builder()
|
||||
.extensions(List.of(TablesExtension.create()))
|
||||
.build();
|
||||
parser = Parser.builder()
|
||||
.extensions(List.of(TablesExtension.create()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static HtmlRenderer getHtmlRenderer() {
|
||||
return htmlRenderer;
|
||||
}
|
||||
|
||||
public static Parser getParser() {
|
||||
return parser;
|
||||
}
|
||||
}
|
||||
28
src/test/java/myblog/blog/seo/service/RssServiceTest.java
Normal file
28
src/test/java/myblog/blog/seo/service/RssServiceTest.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package myblog.blog.seo.service;
|
||||
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
class RssServiceTest {
|
||||
|
||||
@InjectMocks
|
||||
RssService rssService;
|
||||
|
||||
@Mock
|
||||
ArticleService articleService;
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void RssFeed생성성공테스트() throws Exception {
|
||||
// given
|
||||
|
||||
|
||||
// when
|
||||
|
||||
// then
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user