마크다운 저장컨텐츠 파싱을 CSR에서 SSR로 개선 -SEO와 인뎅싱을 위해
게시물 작성이 자동 푸시로 깃헙에 백업기능 추가

- TO DO
자동저장기능
공유하기기능
This commit is contained in:
jinia91
2021-11-24 19:47:11 +09:00
parent 4575a83ea5
commit 443ed035fe
12 changed files with 724 additions and 70 deletions

View File

@@ -40,6 +40,9 @@ dependencies {
implementation 'com.github.node-gradle:gradle-node-plugin:3.1.0'
implementation group: 'org.modelmapper', name: 'modelmapper', version: '2.4.4'
implementation group: 'org.kohsuke', name: 'github-api', version: '1.133'
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.9'
implementation group: 'com.atlassian.commonmark', name: 'commonmark', version: '0.17.0'
compileOnly 'org.projectlombok:lombok'

View File

@@ -14,6 +14,8 @@ import myblog.blog.member.auth.PrincipalDetails;
import myblog.blog.member.dto.MemberDto;
import myblog.blog.tags.dto.TagsDto;
import myblog.blog.tags.service.TagsService;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.modelmapper.ModelMapper;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;
@@ -38,6 +40,8 @@ public class ArticleController {
private final TagsService tagsService;
private final CategoryService categoryService;
private final CommentService commentService;
private final Parser parser;
private final HtmlRenderer htmlRenderer;
@GetMapping("article/write")
public String writeArticleForm(ArticleForm articleForm, Model model) {
@@ -79,9 +83,11 @@ public class ArticleController {
PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal();
articleForm.setMemberId(principal.getMemberId());
Long articleId = articleService.writeArticle(articleForm);
Article article = articleService.writeArticle(articleForm);
return "redirect:/article/view?articleId=" + articleId;
articleService.pushArticleToGithub(article);
return "redirect:/article/view?articleId=" + article.getId();
}
@@ -104,7 +110,7 @@ public class ArticleController {
PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleService.getTotalArticleCntByCategory(category, categoryForView));
model.addAttribute("pagingBox", pagingBoxDto);
Slice<ArticleDtoForMain> articleList = articleService.getArticles(category, tier, pagingBoxDto.getCurPageNum());
Slice<ArticleDtoForMain> articleList = articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum());
model.addAttribute("articleList", articleList);
@@ -193,19 +199,27 @@ public class ArticleController {
.collect(Collectors.toList());
model.addAttribute("commentsList", comments);
Article article = articleService.findArticleById(articleId);
Article article = articleService.readArticle(articleId);
ArticleDtoForDetail articleDtoForDetail = modelMapper.map(article, ArticleDtoForDetail.class);
List<String> tags = article.getArticleTagLists()
.stream()
.map(tag -> tag.getTags().getName())
.collect(Collectors.toList());
articleDtoForDetail.setTags(tags);
articleDtoForDetail
.setTags(tags);
articleDtoForDetail
.setContent(
htmlRenderer.render(parser.parse(article.getContent()))
);
model.addAttribute("article", articleDtoForDetail);
List<ArticleDtoByCategory> articleTitlesSortByCategory =
articleService
.getArticlesByCategory(article.getCategory())
.getArticlesByCategoryForDetailView(article.getCategory())
.stream()
.map(article1 -> modelMapper.map(article1, ArticleDtoByCategory.class))
.collect(Collectors.toList());
@@ -290,7 +304,7 @@ public class ArticleController {
public @ResponseBody
List<ArticleDtoForMain> mainNextPage(@PathVariable int pageNum) {
return articleService.getArticles(pageNum).getContent();
return articleService.getRecentArticles(pageNum).getContent();
}
private void addHitWithCookie(Article article, String cookie, HttpServletResponse response) {

View File

@@ -12,13 +12,18 @@ import myblog.blog.category.service.CategoryService;
import myblog.blog.member.doamin.Member;
import myblog.blog.member.repository.MemberRepository;
import myblog.blog.tags.service.TagsService;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -27,6 +32,11 @@ import java.util.List;
@RequiredArgsConstructor
public class ArticleService {
@Value("${git.gitToken}")
private String gitToken;
@Value("${git.repo}")
private String gitRepo;
private final ArticleRepository articleRepository;
private final MemberRepository memberRepository;
private final TagsService tagsService;
@@ -34,14 +44,14 @@ public class ArticleService {
private final ModelMapper modelMapper;
private final NaArticleRepository naArticleRepository;
public Long writeArticle(ArticleForm articleDto) {
public Article writeArticle(ArticleForm articleDto) {
Article newArticle = articleFrom(articleDto);
articleRepository.save(newArticle);
tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle);
return newArticle.getId();
return newArticle;
}
@@ -59,7 +69,7 @@ public class ArticleService {
}
public Slice<ArticleDtoForMain> getArticles(int page) {
public Slice<ArticleDtoForMain> getRecentArticles(int page) {
Slice<ArticleDtoForMain> articles = articleRepository
.findByOrderByIdDesc(PageRequest.of(page, 5))
@@ -87,7 +97,7 @@ public class ArticleService {
throw new IllegalArgumentException("카테고리별 아티클 수 에러");
}
public Slice<ArticleDtoForMain> getArticles(String category, Integer tier, Integer page) {
public Slice<ArticleDtoForMain> getArticlesByCategory(String category, Integer tier, Integer page) {
Slice<Article> articles = null;
@@ -110,7 +120,7 @@ public class ArticleService {
.map(article, ArticleDtoForMain.class));
}
public Article findArticleById(Long id){
public Article readArticle(Long id){
return articleRepository.findArticleByIdFetchCategoryAndTags(id);
}
@@ -143,12 +153,47 @@ public class ArticleService {
naArticleRepository.deleteArticle(articleId);
}
public List<Article> getArticlesByCategory(Category category){
public List<Article> getArticlesByCategoryForDetailView(Category category){
return articleRepository.findTop6ByCategoryOrderByIdDesc(category);
}
public Page<Article> getArticlesByTag(String tag, Integer page) {
Page<Article> articles =
articleRepository
.findAllByArticleTagsOrderById(PageRequest.of(pageResolver(page), 5), tag);
return articles;
}
public Page<Article> getArticlesByKeyword(String keyword, Integer page) {
Page<Article> articles =
articleRepository
.findAllByKeywordOrderById(PageRequest.of(pageResolver(page),5), keyword);
return articles;
}
public void pushArticleToGithub(Article article) {
try {
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
GHRepository repository = gitHub.getRepository(gitRepo);
repository.createContent()
.path(article.getCategory().getParents().getTitle()+"/"+article.getCategory().getTitle()+"/"+article.getTitle()+".md")
.content(article.getContent())
.message("test")
.branch("main")
.commit();
} catch (IOException e) {
e.printStackTrace();
}
}
private String makeDefaultThumb(String thumbnailUrl) {
// 메시지로 올리기
@@ -182,23 +227,4 @@ public class ArticleService {
.build();
}
public Page<Article> getArticlesByTag(String tag, Integer page) {
Page<Article> articles =
articleRepository
.findAllByArticleTagsOrderById(PageRequest.of(pageResolver(page), 5), tag);
return articles;
}
public Page<Article> getArticlesByKeyword(String keyword, Integer page) {
Page<Article> articles =
articleRepository
.findAllByKeywordOrderById(PageRequest.of(pageResolver(page),5), keyword);
return articles;
}
}

View File

@@ -1,5 +1,7 @@
package myblog.blog.base.config;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -10,4 +12,11 @@ public class AppConfig {
@Bean
public ModelMapper modelMapper(){ return new ModelMapper();}
@Bean
public Parser parser(){return Parser.builder().build();}
@Bean
public HtmlRenderer htmlRenderer(){return HtmlRenderer.builder().build();}
}

View File

@@ -11,7 +11,6 @@ import myblog.blog.member.doamin.Member;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.util.List;
@RestController
@@ -38,7 +37,7 @@ public class CommentController {
PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal();
Member member = principal.getMember();
Article article = articleService.findArticleById(articleId);
Article article = articleService.readArticle(articleId);
if(parentId != null){
commentService.saveCComment(commentForm, member, article, parentId);

View File

@@ -41,7 +41,7 @@ public class MainController {
List<ArticleDtoForMain> popularArticles = articleService.getPopularArticles();
model.addAttribute("popularArticles", popularArticles);
Slice<ArticleDtoForMain> recentArticles = articleService.getArticles(0);
Slice<ArticleDtoForMain> recentArticles = articleService.getRecentArticles(0);
model.addAttribute("recentArticles",recentArticles);
return "index";

View File

@@ -1,3 +1,631 @@
.toastui-editor-contents {
margin: 0;
padding: 0;
font-size: 1.1rem;
font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', 'Arial', '나눔바른고딕',
'Nanum Barun Gothic', '맑은고딕', 'Malgun Gothic', sans-serif;
z-index: 20;
}
.toastui-editor-contents *:not(table) {
line-height: 210%;
box-sizing: content-box;
}
.toastui-editor-contents i,
.toastui-editor-contents cite,
.toastui-editor-contents em,
.toastui-editor-contents var,
.toastui-editor-contents address,
.toastui-editor-contents dfn {
font-style: italic;
}
.toastui-editor-contents strong {
font-weight: bold;
}
.toastui-editor-contents p {
margin: 10px 0;
color: #222;
}
.toastui-editor-contents > h1:first-of-type,
.toastui-editor-contents > div > div:first-of-type h1 {
margin-top: 14px;
}
.toastui-editor-contents h1,
.toastui-editor-contents h2,
.toastui-editor-contents h3,
.toastui-editor-contents h4,
.toastui-editor-contents h5,
.toastui-editor-contents h6 {
font-weight: bold;
color: #222;
}
.toastui-editor-contents h1 {
font-size: 1.8rem;
line-height: 3.0rem;
border-bottom: 3px double #999;
margin: 52px 0 15px 0;
padding-bottom: 7px;
}
.toastui-editor-contents h2 {
font-size: 1.8rem;
line-height: 2.7rem;
border-bottom: 1px solid #dbdbdb;
margin: 20px 0 13px 0;
padding-bottom: 7px;
}
.toastui-editor-contents h3 {
font-size: 1.4rem;
line-height: 2.5rem;
margin: 18px 0 2px;
}
.toastui-editor-contents h4 {
font-size: 1.3rem;
line-height: 2.4rem;
margin: 10px 0 2px;
}
.toastui-editor-contents h5 {
font-size: 1.2rem;
}
.toastui-editor-contents h6 {
font-size: 1.1rem;
}
.toastui-editor-contents h5,
.toastui-editor-contents h6 {
line-height: 2.2px;
margin: 9px 0 -4px;
}
.toastui-editor-contents del {
color: #999;
}
.toastui-editor-contents blockquote {
margin: 14px 0;
border-left: 4px solid #e5e5e5;
padding: 0 16px;
color: #999;
}
.toastui-editor-contents blockquote p,
.toastui-editor-contents blockquote ul,
.toastui-editor-contents blockquote ol {
color: #999;
}
.toastui-editor-contents blockquote > :first-child {
margin-top: 0;
}
.toastui-editor-contents blockquote > :last-child {
margin-bottom: 0;
}
.toastui-editor-contents pre,
.toastui-editor-contents code {
font-family: Consolas, Courier, 'Apple SD 산돌고딕 Neo', -apple-system, 'Lucida Grande',
'Apple SD Gothic Neo', '맑은 고딕', 'Malgun Gothic', 'Segoe UI', '돋움', dotum, sans-serif;
border: 0;
border-radius: 0;
}
.toastui-editor-contents pre {
margin: 2px 0 8px;
padding: 18px;
background-color: #f4f7f8;
}
.toastui-editor-contents code {
color: #c1798b;
background-color: #f9f2f4;
padding: 2px 3px;
letter-spacing: -0.3px;
border-radius: 2px;
}
.toastui-editor-contents pre code {
padding: 0;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
}
.toastui-editor-contents img {
margin: 4px 0 10px;
box-sizing: border-box;
vertical-align: top;
max-width: 100%;
}
.toastui-editor-contents table {
border: 1px solid rgba(0, 0, 0, 0.1);
margin: 12px 0 14px;
color: #222;
width: auto;
border-collapse: collapse;
box-sizing: border-box;
}
.toastui-editor-contents table th,
.toastui-editor-contents table td {
border: 1px solid rgba(0, 0, 0, 0.1);
padding: 5px 14px 5px 12px;
height: 32px;
}
.toastui-editor-contents table th {
background-color: #555;
font-weight: 300;
color: #fff;
padding-top: 6px;
}
.toastui-editor-contents th p {
margin: 0;
color: #fff;
}
.toastui-editor-contents td p {
margin: 0;
padding: 0 2px;
}
.toastui-editor-contents td.toastui-editor-cell-selected {
background-color: #d8dfec;
}
.toastui-editor-contents th.toastui-editor-cell-selected {
background-color: #908f8f;
}
.toastui-editor-contents ul,
.toastui-editor-contents menu,
.toastui-editor-contents ol,
.toastui-editor-contents dir {
display: block;
list-style-type: none;
padding-left: 24px;
margin: 6px 0 10px;
color: #222;
}
.toastui-editor-contents ol {
list-style-type: none;
counter-reset: li;
}
.toastui-editor-contents ol > li {
counter-increment: li;
}
.toastui-editor-contents ul > li::before,
.toastui-editor-contents ol > li::before {
display: inline-block;
position: absolute;
}
.toastui-editor-contents ul > li::before {
content: '';
margin-top: 1rem;
margin-left: -17px;
width: 5px;
height: 5px;
border-radius: 50%;
background-color: black;
}
.toastui-editor-contents ol > li::before {
content: '.' counter(li);
margin-left: -28px;
width: 24px;
text-align: right;
direction: rtl;
color: black;
}
.toastui-editor-contents ul ul,
.toastui-editor-contents ul ol,
.toastui-editor-contents ol ol,
.toastui-editor-contents ol ul {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.toastui-editor-contents ul li,
.toastui-editor-contents ol li {
position: relative;
}
.toastui-editor-contents ul p,
.toastui-editor-contents ol p {
margin: 0;
}
.toastui-editor-contents hr {
border-top: 1px solid #eee;
margin: 16px 0;
}
.toastui-editor-contents a {
text-decoration: underline;
color: #4b96e6;
}
.toastui-editor-contents a:hover {
color: #1f70de;
}
.toastui-editor-contents .image-link {
position: relative;
}
.toastui-editor-contents .image-link:hover::before {
content: '';
position: absolute;
width: 30px;
height: 30px;
right: 0px;
border-radius: 50%;
border: 1px solid #c9ccd5;
background: #fff url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDIwIDIwIj4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KICAgICAgICA8ZyBzdHJva2U9IiM1NTUiIHN0cm9rZS13aWR0aD0iMS41Ij4KICAgICAgICAgICAgPGc+CiAgICAgICAgICAgICAgICA8Zz4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNNy42NjUgMTUuMDdsLTEuODE5LS4wMDJjLTEuNDg2IDAtMi42OTItMS4yMjgtMi42OTItMi43NDR2LS4xOTJjMC0xLjUxNSAxLjIwNi0yLjc0NCAyLjY5Mi0yLjc0NGgzLjg0NmMxLjQ4NyAwIDIuNjkyIDEuMjI5IDIuNjkyIDIuNzQ0di4xOTIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xMDAwIC00NTgxKSB0cmFuc2xhdGUoOTk1IDQ1NzYpIHRyYW5zbGF0ZSg1IDUpIHNjYWxlKDEgLTEpIHJvdGF0ZSg0NSAzNy4yOTMgMCkiLz4KICAgICAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTIuMzI2IDQuOTM0bDEuODIyLjAwMmMxLjQ4NyAwIDIuNjkzIDEuMjI4IDIuNjkzIDIuNzQ0di4xOTJjMCAxLjUxNS0xLjIwNiAyLjc0NC0yLjY5MyAyLjc0NGgtMy44NDVjLTEuNDg3IDAtMi42OTItMS4yMjktMi42OTItMi43NDRWNy42OCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEwMDAgLTQ1ODEpIHRyYW5zbGF0ZSg5OTUgNDU3NikgdHJhbnNsYXRlKDUgNSkgc2NhbGUoMSAtMSkgcm90YXRlKDQ1IDMwLjk5NiAwKSIvPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K') no-repeat;
background-position: center;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
cursor: pointer;
}
.toastui-editor-contents .task-list-item {
border: 0;
list-style: none;
padding-left: 24px;
margin-left: -24px;
}
.toastui-editor-contents .task-list-item::before {
background-repeat: no-repeat;
background-size: 18px 18px;
background-position: center;
content: '';
margin-left: 0;
margin-top: 0;
border-radius: 2px;
height: 18px;
width: 18px;
position: absolute;
left: 0;
top: 1px;
cursor: pointer;
background: transparent url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgZmlsbD0iI0ZGRiIgc3Ryb2tlPSIjQ0NDIj4KICAgICAgICAgICAgPGc+CiAgICAgICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTAzMCAtMjk2KSB0cmFuc2xhdGUoNzg4IDE5MikgdHJhbnNsYXRlKDI0MiAxMDQpIj4KICAgICAgICAgICAgICAgICAgICA8cmVjdCB3aWR0aD0iMTciIGhlaWdodD0iMTciIHg9Ii41IiB5PSIuNSIgcng9IjIiLz4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+Cg==');
}
.toastui-editor-contents .task-list-item.checked::before {
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij4KICAgIDxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgZmlsbD0iIzRCOTZFNiI+CiAgICAgICAgICAgIDxnPgogICAgICAgICAgICAgICAgPGc+CiAgICAgICAgICAgICAgICAgICAgPHBhdGggZD0iTTE2IDBjMS4xMDUgMCAyIC44OTUgMiAydjE0YzAgMS4xMDUtLjg5NSAyLTIgMkgyYy0xLjEwNSAwLTItLjg5NS0yLTJWMkMwIC44OTUuODk1IDAgMiAwaDE0em0tMS43OTMgNS4yOTNjLS4zOS0uMzktMS4wMjQtLjM5LTEuNDE0IDBMNy41IDEwLjU4NSA1LjIwNyA4LjI5M2wtLjA5NC0uMDgzYy0uMzkyLS4zMDUtLjk2LS4yNzgtMS4zMi4wODMtLjM5LjM5LS4zOSAxLjAyNCAwIDEuNDE0bDMgMyAuMDk0LjA4M2MuMzkyLjMwNS45Ni4yNzggMS4zMi0uMDgzbDYtNiAuMDgzLS4wOTRjLjMwNS0uMzkyLjI3OC0uOTYtLjA4My0xLjMyeiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEwNTAgLTI5NikgdHJhbnNsYXRlKDc4OCAxOTIpIHRyYW5zbGF0ZSgyNjIgMTA0KSIvPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4K');
}
.toastui-editor-custom-block .toastui-editor-custom-block-editor {
background: #f9f7fd;
color: #452d6b;
border: solid 1px #dbd4ea;
}
.toastui-editor-custom-block .toastui-editor-custom-block-view {
position: relative;
padding: 9px 13px 8px 12px;
}
.toastui-editor-custom-block.ProseMirror-selectednode .toastui-editor-custom-block-view {
border: solid 1px #dbd4ea;
border-radius: 2px;
}
.toastui-editor-custom-block .toastui-editor-custom-block-view .tool {
position: absolute;
right: 10px;
top: 7px;
display: none;
}
.toastui-editor-custom-block.ProseMirror-selectednode .toastui-editor-custom-block-view .tool {
display: block;
}
.toastui-editor-custom-block-view button {
vertical-align: middle;
width: 15px;
height: 15px;
margin-left: 8px;
padding: 3px;
border: solid 1px #cccccc;
background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IuugiOydtOyWtF8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiCgkgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMzAgMzAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMwIDMwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6IzU1NTU1NTt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPGc+CgkJCTxnPgoJCQkJPGc+CgkJCQkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTE1LjUsMTIuNWwyLDJMMTIsMjBoLTJ2LTJMMTUuNSwxMi41eiBNMTgsMTBsMiwybC0xLjUsMS41bC0yLTJMMTgsMTB6Ii8+CgkJCQk8L2c+CgkJCTwvZz4KCQk8L2c+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==')
no-repeat;
background-position: center;
background-size: 30px 30px;
}
.toastui-editor-custom-block-view .info {
font-size: 13px;
font-weight: bold;
color: #5200d0;
vertical-align: middle;
}
.toastui-editor-contents .toastui-editor-ww-code-block {
position: relative;
}
.toastui-editor-contents .toastui-editor-ww-code-block:after {
content: attr(data-language);
position: absolute;
display: inline-block;
top: 10px;
right: 10px;
height: 24px;
padding: 3px 35px 0 10px;
font-weight: bold;
font-size: 13px;
color: #333;
background: #e5e9ea url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IuugiOydtOyWtF8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiCgkgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMzAgMzAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMwIDMwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO2ZpbGw6IzU1NTU1NTt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPGc+CgkJCTxnPgoJCQkJPGc+CgkJCQkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTE1LjUsMTIuNWwyLDJMMTIsMjBoLTJ2LTJMMTUuNSwxMi41eiBNMTgsMTBsMiwybC0xLjUsMS41bC0yLTJMMTgsMTB6Ii8+CgkJCQk8L2c+CgkJCTwvZz4KCQk8L2c+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==') no-repeat;
background-position: right;
border-radius: 2px;
background-size: 30px 30px;
cursor: pointer;
}
.toastui-editor-ww-code-block-language {
position: fixed;
display: inline-block;
width: 100px;
height: 27px;
right: 35px;
border: 1px solid #ccc;
border-radius: 2px;
background-color: #fff;
z-index: 30;
}
.toastui-editor-ww-code-block-language input {
box-sizing: border-box;
margin: 0;
padding: 0 10px;
height: 100%;
width: 100%;
background-color: transparent;
border: none;
outline: none;
}
.toastui-editor-contents-placeholder::before {
content: attr(data-placeholder);
color: grey;
line-height: 160%;
position: absolute;
}
.toastui-editor-md-preview .toastui-editor-contents h1 {
min-height: 28px;
}
.toastui-editor-md-preview .toastui-editor-contents h2 {
min-height: 23px;
}
.toastui-editor-md-preview .toastui-editor-contents blockquote {
min-height: 20px;
}
.toastui-editor-md-preview .toastui-editor-contents li {
min-height: 22px;
}
.toastui-editor-pseudo-clipboard {
position: fixed;
opacity: 0;
width: 0;
height: 0;
left: -1000px;
top: -1000px;
z-index: -1;
}
.toastui-editor-contents .toastui-editor-md-preview-highlight {
position: relative;
z-index: 0;
}
.toastui-editor-contents .toastui-editor-md-preview-highlight::after {
content: '';
background-color: rgba(255, 245, 131, 0.5);
border-radius: 4px;
z-index: -1;
position: absolute;
top: -4px;
right: -4px;
left: -4px;
bottom: -4px;
}
.toastui-editor-contents h1.toastui-editor-md-preview-highlight::after,
.toastui-editor-contents h2.toastui-editor-md-preview-highlight::after {
bottom: 0;
}
.toastui-editor-contents td.toastui-editor-md-preview-highlight::after,
.toastui-editor-contents th.toastui-editor-md-preview-highlight::after {
display: none;
}
.toastui-editor-contents th.toastui-editor-md-preview-highlight,
.toastui-editor-contents td.toastui-editor-md-preview-highlight {
background-color: rgba(255, 245, 131, 0.5);
}
.toastui-editor-contents th.toastui-editor-md-preview-highlight {
color: #222;
}
.toastui-editor-md-heading1 {
font-size: 24px;
}
.toastui-editor-md-heading2 {
font-size: 22px;
}
.toastui-editor-md-heading3 {
font-size: 20px;
}
.toastui-editor-md-heading4 {
font-size: 18px;
}
.toastui-editor-md-heading5 {
font-size: 16px;
}
.toastui-editor-md-heading6 {
font-size: 14px;
}
.toastui-editor-md-heading.toastui-editor-md-delimiter.setext {
line-height: 15px;
}
.toastui-editor-md-strong,
.toastui-editor-md-heading,
.toastui-editor-md-list-item-style,
.toastui-editor-md-list-item .toastui-editor-md-meta {
font-weight: bold;
}
.toastui-editor-md-emph {
font-style: italic;
}
.toastui-editor-md-strike {
text-decoration: line-through;
}
.toastui-editor-md-strike.toastui-editor-md-delimiter {
text-decoration: none;
}
.toastui-editor-md-delimiter,
.toastui-editor-md-thematic-break,
.toastui-editor-md-link,
.toastui-editor-md-table,
.toastui-editor-md-block-quote {
color: #ccc;
}
.toastui-editor-md-code.toastui-editor-md-delimiter {
color: #aaa;
}
.toastui-editor-md-meta,
.toastui-editor-md-html,
.toastui-editor-md-link.toastui-editor-md-link-url.toastui-editor-md-marked-text {
color: #999;
}
.toastui-editor-md-block-quote .toastui-editor-md-marked-text,
.toastui-editor-md-list-item .toastui-editor-md-meta {
color: #555;
}
.toastui-editor-md-table .toastui-editor-md-table-cell {
color: #222;
}
.toastui-editor-md-link.toastui-editor-md-link-desc.toastui-editor-md-marked-text,
.toastui-editor-md-list-item-style.toastui-editor-md-list-item-odd {
color: #4b96e6;
}
.toastui-editor-md-list-item-style.toastui-editor-md-list-item-even {
color: #cb4848;
}
.toastui-editor-md-code.toastui-editor-md-marked-text {
color: #c1798b;
}
.toastui-editor-md-code {
background-color: rgba(243, 229, 233, 0.5);
padding: 2px 0;
letter-spacing: -0.3px;
}
.toastui-editor-md-code.toastui-editor-md-start {
padding-left: 2px;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.toastui-editor-md-code.toastui-editor-md-end {
padding-right: 2px;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
.toastui-editor-md-code-block-line-background {
background-color: #f5f7f8;
}
.toastui-editor-md-code-block-line-background.start,
.toastui-editor-md-custom-block-line-background.start {
margin-top: 2px;
}
.toastui-editor-md-code,
.toastui-editor-md-code-block {
font-family: Consolas, Courier, 'Lucida Grande', '나눔바른고딕', 'Nanum Barun Gothic', '맑은고딕',
'Malgun Gothic', sans-serif;
}
.toastui-editor-md-custom-block {
color: #452d6b;
}
.toastui-editor-md-custom-block-line-background {
background-color: #f9f7fd;
}
.toastui-editor-md-custom-block .toastui-editor-md-delimiter {
color: #b8b3c0;
}
.toastui-editor-md-custom-block .toastui-editor-md-meta {
color: #5200d0;
}
.form-color {
background-color: #e0dedd;
}

View File

@@ -1,24 +0,0 @@
const viewer = toastui.Editor.factory({
el: document.querySelector('#viewer'),
viewer: true,
height: '500px',
initialValue: ''
});
const contents = document.getElementById("contents");
viewer.setMarkdown(contents.value);
function deleteArticle() {
if (confirm("글을 정말 삭제하시겠습니까?") == true) {
document.getElementById("deleteArticle").submit();
} else {
return false;
}
}

View File

@@ -54,7 +54,7 @@ toastui.Editor.setLanguage(['ko', 'ko-KR'], {
const editor = new toastui.Editor({
el: document.querySelector('#editor'),
height: '500px',
height: '800px',
initialEditType: 'markdown',
previewStyle: 'vertical',
language: 'ko',

View File

@@ -73,12 +73,12 @@
</div>
<div class="d-flex flex-column align-items-center">
<div>
<button class="category-nav-btn " onclick="categoryUp()"><i class="fas fa-chevron-circle-up"></i></button>
<button class="category-nav-btn " onclick="categoryDown()"><i class="fas fa-chevron-circle-down"></i></button>
<button class="category-nav-btn " onclick="categoryUp()"><i class="fas fa-angle-up"></i></button>
<button class="category-nav-btn " onclick="categoryDown()"><i class="fas fa-angle-down"></i></i></button>
</div>
<div>
<button class="category-nav-btn " onclick="tierUp()"><i class="far fa-caret-square-left"></i></button>
<button class=" category-nav-btn " onclick="tierDown()"><i class="far fa-caret-square-right"></i></button>
<button class="category-nav-btn " onclick="tierUp()"><i class="fas fa-chevron-left"></i></button>
<button class=" category-nav-btn " onclick="tierDown()"><i class="fas fa-chevron-right"></i></button>
</div>
<div>
<button class=" category-nav-btn " onclick="addCategory()"><i class="fas fa-plus"></i></button>

View File

@@ -35,7 +35,6 @@
<link rel="short icon" type="image/x-icon" href=""/>
<!-- CSS RESET -->
<link rel="stylesheet" href="/node_modules/@toast-ui/editor/dist/toastui-editor.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"/>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/node_modules/@yaireo/tagify/dist/tagify.css"/>
@@ -61,12 +60,13 @@
</div>
</div>
<textarea name="contents" id="contents" hidden></textarea>
<div class="mt-5 ms-2 me-2 ms-sm-5 me-sm-5 mt-sm-5 d-flex justify-content-center">
<div class="paper">
<div class="ms-2 me-2 p-sm-4 p-2" id="viewer"></div>
<div class="container p-3 p-sm-5">
<div class="toastui-editor-contents" th:utext="${article.getContent()}"></div>
</div>
<div class="ms-2 me-2 mb-3 p-sm-4 p-2">
<div class="toolbox container pt-4 pb-4">
@@ -152,7 +152,6 @@
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
<script src="/js/getCsrf.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.0/moment.min.js"></script>
<script th:replace="layout/fragments.html :: view"></script>
<script th:replace="layout/fragments.html :: comment"></script>
</div>
</section>

View File

@@ -348,7 +348,7 @@
const editor = new toastui.Editor({
el: document.querySelector('#editor'),
height: '500px',
height: '800px',
initialEditType: 'markdown',
previewStyle: 'vertical',
language: 'ko',