diff --git a/src/main/java/myblog/blog/article/controller/ArticleController.java b/src/main/java/myblog/blog/article/controller/ArticleController.java index 7fa23b2..b99e152 100644 --- a/src/main/java/myblog/blog/article/controller/ArticleController.java +++ b/src/main/java/myblog/blog/article/controller/ArticleController.java @@ -1,7 +1,6 @@ package myblog.blog.article.controller; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import myblog.blog.article.domain.Article; import myblog.blog.article.dto.*; import myblog.blog.article.service.ArticleService; @@ -18,15 +17,15 @@ import myblog.blog.tags.service.TagsService; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; import org.modelmapper.ModelMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Slice; -import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; +import org.springframework.validation.Errors; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.Cookie; @@ -36,210 +35,222 @@ import java.util.stream.Collectors; @Controller @RequiredArgsConstructor -@Slf4j public class ArticleController { - private final ModelMapper modelMapper; private final ArticleService articleService; private final TagsService tagsService; private final CategoryService categoryService; private final CommentService commentService; - private final Parser parser; - private final HtmlRenderer htmlRenderer; private final TempArticleService tempArticleService; + private final ModelMapper modelMapper; + private final Parser parser; + private final HtmlRenderer htmlRenderer; + + /* + - 아티클 작성 폼 조회 + */ @GetMapping("article/write") - public String writeArticleForm(ArticleForm articleForm, Model model) { + public String writeArticleForm(Model model) { - List categoryForInput = - categoryService - .findCategoryByTier(2) - .stream() - .map(category -> modelMapper.map(category, CategoryNormalDto.class)) - .collect(Collectors.toList()); - model.addAttribute("categoryInput", categoryForInput); - - List tagsForInput = - tagsService - .findAllTags() - .stream() - .map(tag -> new TagsDto(tag.getName())) - .collect(Collectors.toList()); - model.addAttribute("tagsInput", tagsForInput); - - CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); - model.addAttribute("category", categoryForView); - - List comments = commentService.recentCommentList() - .stream() - .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret() )) - .collect(Collectors.toList()); - model.addAttribute("commentsList", comments); - - model.addAttribute("articleDto", articleForm); + modelsForArticleForm(model); + modelsForLayout(model); + model.addAttribute("articleDto", new ArticleForm()); return "article/articleWriteForm"; } - /* - - 넘어온 articleForm을 저장 + - 아티클 작성 post 요청 */ @PostMapping("article/write") @Transactional - public String writeArticle(ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) { + public String writeArticle(@Validated ArticleForm articleForm, + Errors errors, + @AuthenticationPrincipal PrincipalDetails principal, + Model model) { - Article article = articleService.writeArticle(articleForm, principal.getMember()); + if (errors.hasErrors()) { + modelsForArticleForm(model); + modelsForLayout(model); + model.addAttribute("articleDto", new ArticleForm()); + return "article/articleWriteForm"; + } + Long articleId = articleService.writeArticle(articleForm, principal.getMember()); // articleService.pushArticleToGithub(article); tempArticleService.deleteTemp(); - return "redirect:/article/view?articleId=" + article.getId(); + return "redirect:/article/view?articleId=" + articleId; + } + + /* + - 아티클 수정 폼 조회 + */ + @GetMapping("/article/edit") + public String updateArticle(@RequestParam Long articleId, + Model model) { + + // 기존 아티클 DTO 전처리 + Article article = articleService.readArticle(articleId); + + ArticleDtoForEdit articleDto = modelMapper.map(article, ArticleDtoForEdit.class); + articleDto.setArticleTagList(article.getArticleTagLists() + .stream() + .map(articleTag -> articleTag.getTags().getName()) + .collect(Collectors.toList())); + // + + modelsForArticleForm(model); + modelsForLayout(model); + model.addAttribute("articleDto", articleDto); + + return "article/articleEditForm"; + } + + /* + - 아티클 수정 요청 + */ + @PostMapping("/article/edit") + @Transactional + public String editArticle(@RequestParam Long articleId, + @ModelAttribute ArticleForm articleForm) { + + articleService.editArticle(articleId, articleForm); + + return "redirect:/article/view?articleId=" + articleId; } + /* + - 아티클 삭제 요청 + */ + @PostMapping("/article/delete") + @Transactional + public String deleteArticle(@RequestParam Long articleId) { + + articleService.deleteArticle(articleId); + + return "redirect:/"; + } + + /* + - 카테고리별 게시물 조회하기 + */ @Transactional @GetMapping("article/list") - public String getArticlesList(@RequestParam String category, - @RequestParam Integer tier, - @RequestParam Integer page, - Model model) { + public String getArticlesListByCategory(@RequestParam String category, + @RequestParam Integer tier, + @RequestParam Integer page, + Model model) { - CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); - model.addAttribute("category", categoryForView); - List comments = commentService.recentCommentList() - .stream() - .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret())) - .collect(Collectors.toList()); - model.addAttribute("commentsList", comments); + // DTO 매핑 전처리 + CategoryForView categoryForView = modelsForLayout(model); + + PagingBoxDto pagingBoxDto = PagingBoxDto + .createOf(page, getTotalArticleCntByCategory(category, categoryForView)); + + Slice articleList = articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum()) + .map(article -> + modelMapper.map(article, ArticleDtoForMain.class)); + // - PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleService.getTotalArticleCntByCategory(category, categoryForView)); model.addAttribute("pagingBox", pagingBoxDto); - - Slice articleList = articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum()); model.addAttribute("articleList", articleList); - return "article/articleList"; - } + /* + - 태그별 게시물 조회하기 + */ @Transactional @GetMapping("article/list/tag/") public String getArticlesListByTag(@RequestParam Integer page, @RequestParam String tagName, Model model) { - - CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); - model.addAttribute("category", categoryForView); - List comments = commentService.recentCommentList() - .stream() - .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret())) - .collect(Collectors.toList()); - model.addAttribute("commentsList", comments); - + // DTO 매핑 전처리 Page articleList = articleService.getArticlesByTag(tagName, page) .map(article -> modelMapper.map(article, ArticleDtoForMain.class)); - model.addAttribute("articleList", articleList); PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleList.getTotalPages()); + + modelsForLayout(model); + // + + model.addAttribute("articleList", articleList); model.addAttribute("pagingBox", pagingBoxDto); return "article/articleListByTag"; - } + /* + - 검색어별 게시물 조회하기 + */ @Transactional @GetMapping("article/list/search/") public String getArticlesListByKeyword(@RequestParam Integer page, @RequestParam String keyword, Model model) { - - CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); - model.addAttribute("category", categoryForView); - List comments = commentService.recentCommentList() - .stream() - .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret())) - .collect(Collectors.toList()); - model.addAttribute("commentsList", comments); + // DTO 매핑 전처리 Page articleList = articleService.getArticlesByKeyword(keyword, page) .map(article -> modelMapper.map(article, ArticleDtoForMain.class)); - model.addAttribute("articleList", articleList); PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleList.getTotalPages()); + + modelsForLayout(model); + // + + model.addAttribute("articleList", articleList); model.addAttribute("pagingBox", pagingBoxDto); return "article/articleListByKeyword"; } - + /* + - 아티클 상세 조회 + 1. 로그인여부 검토 + 2. 게시물 상세조회에 필요한 Dto 전처리 + 3. 메타태그 작성위한 Dto 전처리 + 4. Dto 담기 + 5. 조회수 증가 검토 + */ @GetMapping("/article/view") public String readArticle(@RequestParam Long articleId, - Authentication authentication, - @CookieValue(required = false, name = "view") String cookie, HttpServletResponse response, + @AuthenticationPrincipal PrincipalDetails principal, + @CookieValue(required = false, name = "view") String cookie, + HttpServletResponse response, Model model) { - - if (authentication != null) { - PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal(); - MemberDto memberDto = modelMapper.map(principal.getMember(), MemberDto.class); - model.addAttribute("member", memberDto); + // 1. 로그인 여부에 따라 뷰단에 출력 여부 결정 + if (principal != null) { + model.addAttribute("member", modelMapper.map(principal.getMember(), MemberDto.class)); } else { model.addAttribute("member", null); } - CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); - model.addAttribute("category", categoryForView); - List comments = commentService.recentCommentList() - .stream() - .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret())) - .collect(Collectors.toList()); - model.addAttribute("commentsList", comments); - + /* + DTO 매핑 전처리 + 2. 게시물 상세조회용 + */ Article article = articleService.readArticle(articleId); - ArticleDtoForDetail articleDtoForDetail = modelMapper.map(article, ArticleDtoForDetail.class); + + ArticleDtoForDetail articleDtoForDetail = + modelMapper.map(article, ArticleDtoForDetail.class); List tags = article.getArticleTagLists() .stream() .map(tag -> tag.getTags().getName()) .collect(Collectors.toList()); - articleDtoForDetail - .setTags(tags); - - articleDtoForDetail - .setContent( - htmlRenderer.render(parser.parse(article.getContent())) - ); - - model.addAttribute("article", articleDtoForDetail); - -// 메타태그 삽입 - StringBuilder sb = new StringBuilder(); - for (String tag : tags) { - sb.append(tag).append(", "); - } - model.addAttribute("metaTags",sb); - - String substringContents = null; - if(articleDtoForDetail.getContent().length()>200) { - substringContents = articleDtoForDetail.getContent().substring(0, 200); - } - else substringContents = articleDtoForDetail.getContent(); - - model.addAttribute("metaContents",Jsoup.parse(substringContents).text()); -// + articleDtoForDetail.setTags(tags); + articleDtoForDetail.setContent(htmlRenderer.render(parser.parse(article.getContent()))); List articleTitlesSortByCategory = articleService @@ -247,96 +258,80 @@ public class ArticleController { .stream() .map(article1 -> modelMapper.map(article1, ArticleDtoByCategory.class)) .collect(Collectors.toList()); + + // 3. 메타 태그용 Dto 전처리 + StringBuilder metaTags = new StringBuilder(); + for (String tag : tags) { + metaTags.append(tag).append(", "); + } + + String substringContents = null; + if(articleDtoForDetail.getContent().length()>200) { + substringContents = articleDtoForDetail.getContent().substring(0, 200); + } + else substringContents = articleDtoForDetail.getContent(); + + // 4. 모델 담기 + modelsForLayout(model); + model.addAttribute("article", articleDtoForDetail); + model.addAttribute("metaTags",metaTags); + model.addAttribute("metaContents",Jsoup.parse(substringContents).text()); model.addAttribute("articlesSortBycategory", articleTitlesSortByCategory); + // 5. 조회수 증가 검토 addHitWithCookie(article, cookie, response); return "article/articleView"; } - @GetMapping("/article/edit") - public String updateArticle(@RequestParam Long articleId, - Authentication authentication, - Model model) { - - List categoryForInput = + /* + - 아티클 폼에 필요한 모델 담기 + */ + private void modelsForArticleForm(Model model) { + List categoryForForm = categoryService .findCategoryByTier(2) .stream() - .map(c -> modelMapper.map(c, CategoryNormalDto.class)) + .map(category -> modelMapper.map(category, CategoryNormalDto.class)) .collect(Collectors.toList()); - model.addAttribute("categoryInput", categoryForInput); + model.addAttribute("categoryInput", categoryForForm); - List tagsForInput = + List tagsForForm = tagsService .findAllTags() .stream() - .map(c -> modelMapper.map(c, TagsDto.class)) + .map(tag -> new TagsDto(tag.getName())) .collect(Collectors.toList()); - model.addAttribute("tagsInput", tagsForInput); - - - Article article = articleService.getArticleForEdit(articleId); - ArticleDtoForEdit articleDto = modelMapper.map(article, ArticleDtoForEdit.class); - - List tagList = article.getArticleTagLists() - .stream() - .map(articleTag -> articleTag.getTags().getName()) - .collect(Collectors.toList()); - - articleDto.setArticleTagList(tagList); - - model.addAttribute("articleDto", articleDto); - + model.addAttribute("tagsInput", tagsForForm); + } + /* + - 레이아웃에 필요한 모델 담기 + */ + private CategoryForView modelsForLayout(Model model) { CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView()); model.addAttribute("category", categoryForView); + List comments = commentService.recentCommentList() .stream() .map(comment -> - new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret())) + new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret())) .collect(Collectors.toList()); model.addAttribute("commentsList", comments); - return "article/articleEditForm"; - } - - @PostMapping("/article/delete") - @Transactional - public String deleteArticle(@RequestParam Long articleId, - Authentication authentication) { - - articleService.deleteArticle(articleId); - - return "redirect:/"; + return categoryForView; } - @PostMapping("/article/edit") - @Transactional - public String editArticle(@RequestParam Long articleId, - @ModelAttribute ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) { - - articleService.editArticle(articleId, articleForm); - - - return "redirect:/article/view?articleId=" + articleId; - - } - - @GetMapping("/main/article/{pageNum}") - public @ResponseBody - List mainNextPage(@PathVariable int pageNum) { - - return articleService.getRecentArticles(pageNum).getContent(); - } - + /* + - 쿠키 추가 검토 + */ private void addHitWithCookie(Article article, String cookie, HttpServletResponse response) { Long articleId = article.getId(); if (cookie == null) { Cookie viewCookie = new Cookie("view", articleId + "/"); viewCookie.setComment("게시물 조회 확인용"); viewCookie.setMaxAge(60 * 60); - articleService.addHit(article); + article.addHit(); response.addCookie(viewCookie); } else { boolean isRead = false; @@ -346,15 +341,33 @@ public class ArticleController { isRead = true; break; } - ; } if (!isRead) { cookie += articleId + "/"; - articleService.addHit(article); + article.addHit(); } response.addCookie(new Cookie("view", cookie)); } } + /* + - 카테고리별 아티클 갯수 구하기 + */ + private int getTotalArticleCntByCategory(String category, CategoryForView categorys) { + if (categorys.getTitle().equals(category)) { + return categorys.getCount(); + } else { + for (CategoryForView categoryCnt : + categorys.getCategoryTCountList()) { + if (categoryCnt.getTitle().equals(category)) + return categoryCnt.getCount(); + for (CategoryForView categoryCntSub : categoryCnt.getCategoryTCountList()) { + if (categoryCntSub.getTitle().equals(category)) + return categoryCntSub.getCount(); + } + } + } + throw new IllegalArgumentException("카테고리별 아티클 수 에러"); + } } diff --git a/src/main/java/myblog/blog/article/controller/TempArticleController.java b/src/main/java/myblog/blog/article/controller/TempArticleController.java index d47edc3..2c8f248 100644 --- a/src/main/java/myblog/blog/article/controller/TempArticleController.java +++ b/src/main/java/myblog/blog/article/controller/TempArticleController.java @@ -8,21 +8,29 @@ import org.springframework.web.bind.annotation.*; import java.util.Optional; +/* + - 임시 게시물 조회, 저장을 위한 rest 컨트롤러 +*/ @RestController @RequiredArgsConstructor public class TempArticleController { private final TempArticleService tempArticleService; + /* + - 임시 아티클 저장 요청 + */ @PostMapping("/article/temp/autoSave") public String autoSaveTemp(@RequestBody TempArticleDto tempArticleDto){ - tempArticleService.saveTemp(tempArticleDto); - - return "OK"; + tempArticleService.saveTemp(new TempArticle(tempArticleDto.getContent())); + return "저장성공"; } + /* + - 임시 아티클 조회 + */ @GetMapping("/article/temp/getTemp") public @ResponseBody TempArticleDto getTempArticle(){ @@ -32,7 +40,5 @@ public class TempArticleController { tempArticleDto.setContent(tempArticle.orElse(new TempArticle()).getContent()); return tempArticleDto; - } - } diff --git a/src/main/java/myblog/blog/article/domain/Article.java b/src/main/java/myblog/blog/article/domain/Article.java index 4666cd9..bb2389c 100644 --- a/src/main/java/myblog/blog/article/domain/Article.java +++ b/src/main/java/myblog/blog/article/domain/Article.java @@ -15,6 +15,11 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; + +/* + - 아티클 Entity + - toc 추후 개발 예정 +*/ @Entity @Getter @SequenceGenerator( @@ -37,6 +42,7 @@ public class Article extends BasicEntity { @Column(columnDefinition = "bigint default 0",nullable = false) private Long hit; + // 추후 개발 예정 private String toc; @Column(nullable = false) @@ -67,23 +73,42 @@ public class Article extends BasicEntity { this.content = content; this.toc = toc; this.member = member; - this.thumbnailUrl = thumbnailUrl; + this.thumbnailUrl = makeDefaultThumb(thumbnailUrl); this.category = category; this.hit = 0L; } // 비지니스 로직 // + /* + - 아티클 수정을 위한 로직 + */ + public void editArticle(ArticleForm articleForm, Category category){ + this.content = articleForm.getContent(); + this.title = articleForm.getTitle(); + this.toc = articleForm.getToc(); + this.category = category; + + if(articleForm.getThumbnailUrl() != null){ + this.thumbnailUrl = articleForm.getThumbnailUrl(); + } + } + /* + - 아티클 조회수 증가 + */ public void addHit(){ this.hit++; } - public void editArticle(ArticleForm articleForm, Category category){ - this.content = articleForm.getContent(); - this.title = articleForm.getTitle(); - this.thumbnailUrl = articleForm.getThumbnailUrl(); - this.toc = articleForm.getToc(); - this.category = category; - } + /* + - 썸네일 기본 작성 + */ + private String makeDefaultThumb(String thumbnailUrl) { + String defaultThumbUrl = "https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg"; + if (thumbnailUrl == null || thumbnailUrl.equals("")) { + thumbnailUrl = defaultThumbUrl; + } + return thumbnailUrl; + } } diff --git a/src/main/java/myblog/blog/article/domain/TempArticle.java b/src/main/java/myblog/blog/article/domain/TempArticle.java index 85e50c3..d435f7d 100644 --- a/src/main/java/myblog/blog/article/domain/TempArticle.java +++ b/src/main/java/myblog/blog/article/domain/TempArticle.java @@ -1,20 +1,14 @@ package myblog.blog.article.domain; -import lombok.Builder; import lombok.Getter; -import myblog.blog.article.dto.ArticleForm; import myblog.blog.base.domain.BasicEntity; -import myblog.blog.category.domain.Category; -import myblog.blog.comment.domain.Comment; -import myblog.blog.member.doamin.Member; -import myblog.blog.tags.domain.ArticleTagList; -import org.hibernate.annotations.OnDelete; -import org.hibernate.annotations.OnDeleteAction; import javax.persistence.*; -import java.util.ArrayList; -import java.util.List; +/* + - 임시 아티클 저장 Entity + - 임시 아티클은 한개만 유지할 예정 +*/ @Entity @Getter public class TempArticle extends BasicEntity { diff --git a/src/main/java/myblog/blog/article/dto/ArticleDtoByCategory.java b/src/main/java/myblog/blog/article/dto/ArticleDtoByCategory.java index 73f1eb7..8094a03 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleDtoByCategory.java +++ b/src/main/java/myblog/blog/article/dto/ArticleDtoByCategory.java @@ -3,6 +3,9 @@ package myblog.blog.article.dto; import lombok.Getter; import lombok.Setter; +/* + - 카테고리별 게시물 표시용 DTO +*/ @Getter @Setter public class ArticleDtoByCategory { diff --git a/src/main/java/myblog/blog/article/dto/ArticleDtoForDetail.java b/src/main/java/myblog/blog/article/dto/ArticleDtoForDetail.java index 27239f6..4bb00dc 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleDtoForDetail.java +++ b/src/main/java/myblog/blog/article/dto/ArticleDtoForDetail.java @@ -7,6 +7,9 @@ import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; import java.util.List; +/* + - 아티클 상세조회용 DTO +*/ @Getter @Setter public class ArticleDtoForDetail { @@ -19,6 +22,4 @@ public class ArticleDtoForDetail { private String category; private List tags; private LocalDateTime createdDate; - - } diff --git a/src/main/java/myblog/blog/article/dto/ArticleDtoForEdit.java b/src/main/java/myblog/blog/article/dto/ArticleDtoForEdit.java index 59cec2d..bb8ca41 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleDtoForEdit.java +++ b/src/main/java/myblog/blog/article/dto/ArticleDtoForEdit.java @@ -9,6 +9,9 @@ import myblog.blog.tags.dto.TagsDto; import java.util.ArrayList; import java.util.List; +/* + - 아티클 수정 폼을 위한 DTO +*/ @Getter @Setter public class ArticleDtoForEdit { @@ -20,6 +23,4 @@ public class ArticleDtoForEdit { private List articleTagList = new ArrayList<>(); private Category category; - - } diff --git a/src/main/java/myblog/blog/article/dto/ArticleDtoForMain.java b/src/main/java/myblog/blog/article/dto/ArticleDtoForMain.java index 94eedde..9af282f 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleDtoForMain.java +++ b/src/main/java/myblog/blog/article/dto/ArticleDtoForMain.java @@ -6,6 +6,9 @@ import lombok.Setter; import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; +/* + - 메인 화면 출력용 아티클 DTO +*/ @Getter @Setter public class ArticleDtoForMain { diff --git a/src/main/java/myblog/blog/article/dto/ArticleForm.java b/src/main/java/myblog/blog/article/dto/ArticleForm.java index 2620244..d864499 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleForm.java +++ b/src/main/java/myblog/blog/article/dto/ArticleForm.java @@ -11,17 +11,17 @@ import java.util.Objects; @Getter public class ArticleForm { - @NotBlank + @NotBlank(message = "제목을 입력해주세요") private String title; - @NotBlank + @NotBlank(message = "내용을 입력해주세요") private String content; + private String toc; private String thumbnailUrl; - - @NotBlank + @NotBlank(message = "카테고리를 입력해주세요") private String category; - @NotBlank + @NotBlank(message = "태그를 하나이상 입력해주세요") private String tags; } diff --git a/src/main/java/myblog/blog/article/dto/PagingBoxDto.java b/src/main/java/myblog/blog/article/dto/PagingBoxDto.java index cac86dc..c13d9be 100644 --- a/src/main/java/myblog/blog/article/dto/PagingBoxDto.java +++ b/src/main/java/myblog/blog/article/dto/PagingBoxDto.java @@ -4,7 +4,9 @@ import lombok.Getter; import lombok.Setter; import org.springframework.data.domain.Page; - +/* + - 뷰단 페이징 박스 처리를 위한 핸들러 +*/ @Getter @Setter public class PagingBoxDto { @@ -18,6 +20,7 @@ public class PagingBoxDto { private final int displayPageBoxCnt = 5; private final int displayArticlePerPage = 5; + // 스태틱 생성 메소드 public static PagingBoxDto createOf(int page, int totalArticles) { PagingBoxDto box = new PagingBoxDto(); @@ -39,7 +42,6 @@ public class PagingBoxDto { box.boxStartNum = (box.curPageNum / box.displayPageBoxCnt) * box.displayPageBoxCnt +1; } - // 페이징 박스 끝번호 계산 box.boxEndNum = (int) (Math.ceil(box.curPageNum / (double) box.displayPageBoxCnt) * box.displayPageBoxCnt); @@ -51,12 +53,9 @@ public class PagingBoxDto { box.boxEndNum = 1; } - // 페이징박스 다음버튼 만드는 마지막 페이지 번호 box.pNumForNextBtn =box.boxEndNum - (box.boxEndNum % box.displayPageBoxCnt); return box; - } - } diff --git a/src/main/java/myblog/blog/article/dto/TempArticleDto.java b/src/main/java/myblog/blog/article/dto/TempArticleDto.java index 3d3f7a1..8c9996c 100644 --- a/src/main/java/myblog/blog/article/dto/TempArticleDto.java +++ b/src/main/java/myblog/blog/article/dto/TempArticleDto.java @@ -2,10 +2,11 @@ package myblog.blog.article.dto; import lombok.Getter; import lombok.Setter; - +/* + - 임시 저장 아티클 조회용 DTO +*/ @Getter @Setter public class TempArticleDto { - private String content; } diff --git a/src/main/java/myblog/blog/article/service/ArticleService.java b/src/main/java/myblog/blog/article/service/ArticleService.java index 32f4e14..39077f5 100644 --- a/src/main/java/myblog/blog/article/service/ArticleService.java +++ b/src/main/java/myblog/blog/article/service/ArticleService.java @@ -2,30 +2,24 @@ package myblog.blog.article.service; import lombok.RequiredArgsConstructor; import myblog.blog.article.domain.Article; -import myblog.blog.article.dto.ArticleDtoForMain; +import myblog.blog.category.domain.Category; +import myblog.blog.member.doamin.Member; import myblog.blog.article.dto.ArticleForm; import myblog.blog.article.repository.ArticleRepository; import myblog.blog.article.repository.NaArticleRepository; -import myblog.blog.category.domain.Category; -import myblog.blog.category.dto.CategoryForView; 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 org.springframework.beans.factory.annotation.Value; import java.io.IOException; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Service @Transactional @@ -41,18 +35,17 @@ public class ArticleService { private final CategoryService categoryService; private final ArticleRepository articleRepository; private final NaArticleRepository naArticleRepository; - private final ModelMapper modelMapper; /* - 아티클 작성 로직 */ - public Article writeArticle(ArticleForm articleDto, Member writer) { + public Long writeArticle(ArticleForm articleDto, Member writer) { Article newArticle = articleFrom(articleDto, writer); articleRepository.save(newArticle); tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle); - return newArticle; + return newArticle.getId(); } /* @@ -64,7 +57,6 @@ public class ArticleService { Category category = categoryService.findCategory(articleForm.getCategory()); tagsService.deleteArticleTags(article); tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article); - articleForm.setThumbnailUrl(makeDefaultThumb(articleForm.getThumbnailUrl())); // 더티 체킹으로 업데이트 article.editArticle(articleForm,category); @@ -73,46 +65,24 @@ public class ArticleService { /* - 메인화면 위한 인기 아티클 6개 목록 가져오기 */ - public List getPopularArticles() { - List
top6ByOrderByHitDesc = articleRepository.findTop6ByOrderByHitDesc(); + public List
getPopularArticles() { - List articles = top6ByOrderByHitDesc.stream() - .map(article -> modelMapper.map(article, ArticleDtoForMain.class)) - .collect(Collectors.toList()); - - return articles; + return articleRepository.findTop6ByOrderByHitDesc(); } - public Slice getRecentArticles(int page) { - - Slice articles = articleRepository - .findByOrderByIdDesc(PageRequest.of(page, 5)) - .map(article -> modelMapper - .map(article, ArticleDtoForMain.class)); - return articles; - + /* + - 메인화면 위한 최신 아티클 페이징처리해서 가져오기 + */ + public Slice
getRecentArticles(int page) { + return articleRepository + .findByOrderByIdDesc(PageRequest.of(page, 5)); } - public int getTotalArticleCntByCategory(String category, CategoryForView categorys) { - - if (categorys.getTitle().equals(category)) { - return categorys.getCount(); - } else { - for (CategoryForView categoryCnt : - categorys.getCategoryTCountList()) { - if (categoryCnt.getTitle().equals(category)) - return categoryCnt.getCount(); - for (CategoryForView categoryCntSub : categoryCnt.getCategoryTCountList()) { - if (categoryCntSub.getTitle().equals(category)) - return categoryCntSub.getCount(); - } - } - } - throw new IllegalArgumentException("카테고리별 아티클 수 에러"); - } - - public Slice getArticlesByCategory(String category, Integer tier, Integer page) { + /* + - 카테고리별 게시물 페이징 처리해서 가져오기 + */ + public Slice
getArticlesByCategory(String category, Integer tier, Integer page) { Slice
articles = null; @@ -131,57 +101,54 @@ public class ArticleService { PageRequest.of(pageResolve(page), 5), category); } - return articles.map(article -> modelMapper - .map(article, ArticleDtoForMain.class)); + return articles; } + /* + - 아티클 읽기 위한 페치로 전체 가져오기 + */ public Article readArticle(Long id){ return articleRepository.findArticleByIdFetchCategoryAndTags(id); } - - public void addHit(Article article) { - - article.addHit(); - - } - - public Article getArticleForEdit(Long articleId){ - - return articleRepository.findArticleByIdFetchCategoryAndTags(articleId); - - } - - + /* + - 아티클 삭제 로직 + */ public void deleteArticle(Long articleId) { naArticleRepository.deleteArticle(articleId); } + /* + - 카테고리별 최신게시물 6개만 아티클 상세뷰 위해 가져오는로직 + */ public List
getArticlesByCategoryForDetailView(Category category){ return articleRepository.findTop6ByCategoryOrderByIdDesc(category); } + /* + - 태그별 게시물 페이징 처리해서 가져오기 + */ public Page
getArticlesByTag(String tag, Integer page) { - Page
articles = - articleRepository - .findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag); - - return articles; + return articleRepository + .findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag); } + /* + - 검색어별 게시물 페이징 처리해서 가져오기 + */ public Page
getArticlesByKeyword(String keyword, Integer page) { - Page
articles = - articleRepository - .findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword); - - return articles; + return articleRepository + .findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword); } + /* + - 깃헙에 아티클 푸시하기 + */ public void pushArticleToGithub(Article article) { try { GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build(); @@ -198,22 +165,18 @@ public class ArticleService { } - private String makeDefaultThumb(String thumbnailUrl) { - // 메시지로 올리기 - String defaultThumbUrl = "https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg"; - - if (thumbnailUrl == null || thumbnailUrl.equals("")) { - thumbnailUrl = defaultThumbUrl; - } - return thumbnailUrl; - } - + /* + - 페이지 시작점 0~1변경 메서드 + */ private int pageResolve(Integer rawPage) { if (rawPage == null || rawPage == 1) { return 0; } else return rawPage - 1; } + /* + - 새로운 아티클 도메인 생성 메서드 + */ private Article articleFrom(ArticleForm articleDto, Member writer) { return Article.builder() @@ -221,7 +184,7 @@ public class ArticleService { .content(articleDto.getContent()) .toc(articleDto.getToc()) .member(writer) - .thumbnailUrl(makeDefaultThumb(articleDto.getThumbnailUrl())) + .thumbnailUrl(articleDto.getThumbnailUrl()) .category(categoryService.findCategory(articleDto.getCategory())) .build(); } diff --git a/src/main/java/myblog/blog/article/service/TempArticleService.java b/src/main/java/myblog/blog/article/service/TempArticleService.java index 6075275..d5a4be6 100644 --- a/src/main/java/myblog/blog/article/service/TempArticleService.java +++ b/src/main/java/myblog/blog/article/service/TempArticleService.java @@ -2,7 +2,6 @@ package myblog.blog.article.service; import lombok.RequiredArgsConstructor; import myblog.blog.article.domain.TempArticle; -import myblog.blog.article.dto.TempArticleDto; import myblog.blog.article.repository.TempArticleRepository; import org.springframework.stereotype.Service; import java.util.Optional; @@ -17,18 +16,14 @@ public class TempArticleService { - 자동 저장 로직 - ID값 고정으로 머지를 작동시켜 임시글 DB에 1개 유지 */ - public void saveTemp(TempArticleDto tempArticleDto){ - - TempArticle tempArticle = new TempArticle(tempArticleDto.getContent()); + public void saveTemp(TempArticle tempArticle){ tempArticleRepository.save(tempArticle); - } /* - 임시글 가져오기 */ public Optional getTempArticle(){ - return tempArticleRepository.findById(1L); } diff --git a/src/main/java/myblog/blog/base/config/AppConfig.java b/src/main/java/myblog/blog/base/config/AppConfig.java index 718602a..ad7faed 100644 --- a/src/main/java/myblog/blog/base/config/AppConfig.java +++ b/src/main/java/myblog/blog/base/config/AppConfig.java @@ -8,22 +8,39 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Arrays; +import java.util.List; @Configuration public class AppConfig { + /* + - DTO <-> 엔티티 매퍼 빈등록 + */ @Bean - public ModelMapper modelMapper(){ return new ModelMapper();} + public ModelMapper modelMapper(){ + ModelMapper modelMapper = new ModelMapper(); + modelMapper.getConfiguration() + .setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE) + .setFieldMatchingEnabled(true); + return modelMapper; + } + + /* + - HTML -> 마크다운 파싱 & 렌더러 빈등록 + */ + @Bean + public Parser parser(){ + return Parser.builder() + .extensions(List.of(TablesExtension.create())) + .build(); + } @Bean - public Parser parser(){return Parser.builder() - .extensions(Arrays.asList(TablesExtension.create())) - .build();} - - @Bean - public HtmlRenderer htmlRenderer(){return HtmlRenderer.builder() - .extensions(Arrays.asList(TablesExtension.create())) - .build();} + public HtmlRenderer htmlRenderer(){ + return HtmlRenderer.builder() + .extensions(List.of(TablesExtension.create())) + .build(); + } } diff --git a/src/main/java/myblog/blog/base/config/SecurityConfig.java b/src/main/java/myblog/blog/base/config/SecurityConfig.java index b21dde0..8504745 100644 --- a/src/main/java/myblog/blog/base/config/SecurityConfig.java +++ b/src/main/java/myblog/blog/base/config/SecurityConfig.java @@ -26,6 +26,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final LoginFailHandler loginFailHandler; private final DataSource dataSource; + /* + - 인가 절차 제외 리소스 + */ @Override public void configure(WebSecurity web) throws Exception { web @@ -37,43 +40,29 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http + // 인가 .authorizeRequests() - .antMatchers("/article/write").hasRole(Role.ADMIN.name()) + .antMatchers("/article/write", "/article/edit","/article/delete","/edit/category", "/category/edit").hasRole(Role.ADMIN.name()) .anyRequest().permitAll() - .and() - .formLogin() - .loginPage("/login") - + // 로그아웃 .and() .logout() .logoutSuccessUrl("/") .deleteCookies("JSESSIONID","remember-me") + //csrf .and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - - + // oauth2 로그인 인증 .and() .oauth2Login() .loginPage("/login") .failureHandler(loginFailHandler) .userInfoEndpoint() .userService(oauth2MemberService) - - ; } - - - @Bean - public PersistentTokenRepository tokenRepository() { - JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); - jdbcTokenRepository.setDataSource(dataSource); - return jdbcTokenRepository; - } - - } diff --git a/src/main/java/myblog/blog/base/domain/BasicEntity.java b/src/main/java/myblog/blog/base/domain/BasicEntity.java index 818f847..f0b5d59 100644 --- a/src/main/java/myblog/blog/base/domain/BasicEntity.java +++ b/src/main/java/myblog/blog/base/domain/BasicEntity.java @@ -9,7 +9,9 @@ import javax.persistence.Column; import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; - +/* + - auditing 용 추상 엔티티 +*/ @EntityListeners(AuditingEntityListener.class) @MappedSuperclass @Getter diff --git a/src/main/java/myblog/blog/category/controller/CategoryController.java b/src/main/java/myblog/blog/category/controller/CategoryController.java index 3d0c90e..36f97a4 100644 --- a/src/main/java/myblog/blog/category/controller/CategoryController.java +++ b/src/main/java/myblog/blog/category/controller/CategoryController.java @@ -16,7 +16,6 @@ import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; @Controller @RequiredArgsConstructor @@ -26,41 +25,49 @@ public class CategoryController { private final CommentService commentService; private final ModelMapper modelMapper; + /* + - 카테고리 수정폼 조회 + */ @GetMapping("/edit/category") public String editCategoryForm(Model model) { + // DTO 매핑 전처리 List categoryList = categoryService.getCategoryForView(); - List copyList = categoryList - .stream() - .map(categoryNormalDto -> - modelMapper.map(categoryNormalDto, CategoryNormalDto.class)) - .collect(Collectors.toList()); + + List copyList = cloneList(categoryList); copyList.remove(0); - model.addAttribute("categoryForEdit", copyList); - CategoryForView categoryForView = CategoryForView.createCategory(categoryList); - model.addAttribute("category", categoryForView); List comments = commentService.recentCommentList() .stream() .map(comment -> new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret())) .collect(Collectors.toList()); + // + + model.addAttribute("categoryForEdit", copyList); + model.addAttribute("category", categoryForView); model.addAttribute("commentsList", comments); return "admin/categoryEdit"; } + /* + - 카테고리 수정 요청 + */ @PostMapping("/category/edit") public @ResponseBody String editCategory(@RequestBody List categoryList){ - categoryService.changeCategory(categoryList); - - return "ok"; - + return "변경 성공"; } - + private List cloneList(List categoryList) { + return categoryList + .stream() + .map(categoryNormalDto -> + modelMapper.map(categoryNormalDto, CategoryNormalDto.class)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/myblog/blog/category/domain/Category.java b/src/main/java/myblog/blog/category/domain/Category.java index 4e47cee..7b0ffad 100644 --- a/src/main/java/myblog/blog/category/domain/Category.java +++ b/src/main/java/myblog/blog/category/domain/Category.java @@ -10,6 +10,9 @@ import javax.persistence.*; import java.util.ArrayList; import java.util.List; +/* + - 카테고리 엔티티 +*/ @Entity @Getter @SequenceGenerator( @@ -23,6 +26,7 @@ public class Category extends BasicEntity { @Column(name = "category_id") private Long id; + @Column(nullable = false, unique = true) private String title; @OneToMany(mappedBy = "category") @@ -59,14 +63,17 @@ public class Category extends BasicEntity { return title; } - public void updateCategory(String title, int tier, int pSortNum, int cSortNum, Category parents){ + // 도메인 비지니스 로직 + /* + - 카테고리 더티체킹 업데이트 + */ + public void updateCategory(String title, int tier, int pSortNum, int cSortNum, Category parents){ this.title = title; this.tier = tier; this.pSortNum = pSortNum; this.cSortNum = cSortNum; this.parents = parents; - } } diff --git a/src/main/java/myblog/blog/category/dto/CategoryForView.java b/src/main/java/myblog/blog/category/dto/CategoryForView.java index 857e57a..e0f46f4 100644 --- a/src/main/java/myblog/blog/category/dto/CategoryForView.java +++ b/src/main/java/myblog/blog/category/dto/CategoryForView.java @@ -7,6 +7,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +/* + - 레이아웃용 트리구조 카테고리 리스트 +*/ @Getter @Setter public class CategoryForView { @@ -16,24 +19,30 @@ public class CategoryForView { private Long id; private int pOrder; private int cOrder; + // 트리구조를 갖기 위한 리스트 private List categoryTCountList = new ArrayList<>(); + /* + - 스태틱 생성 메서드 + */ public static CategoryForView createCategory(List crList) { - return recursiveBuildFromCategoryDto(0, crList); - } - private CategoryForView() { - } - private static CategoryForView recursiveBuildFromCategoryDto(int d, List crList) { + /* + - 재귀호출로 트리구조 생성 + 1. DTO객체 생성후 소스를 큐처리로 순차적 매핑 + 2. Depth 변화시 재귀 호출 / 재귀 탈출 + 3. 탈출시 상위 카테고리 list로 삽입하여 트리구조 작성 + */ + private static CategoryForView recursiveBuildFromCategoryDto(int tier, List source) { CategoryForView categoryForView = new CategoryForView(); - while (!crList.isEmpty()) { - CategoryNormalDto cSource = crList.get(0); + while (!source.isEmpty()) { + CategoryNormalDto cSource = source.get(0); - if (cSource.getTier() == d) { + if (cSource.getTier() == tier) { if(categoryForView.getTitle() != null && !categoryForView.getTitle().equals(cSource.getTitle())){ return categoryForView; @@ -43,17 +52,18 @@ public class CategoryForView { categoryForView.setId(cSource.getId()); categoryForView.setCOrder(cSource.getCOrder()); categoryForView.setPOrder(cSource.getPOrder()); - crList.remove(0); - } else if (cSource.getTier() > d) { - CategoryForView sub = recursiveBuildFromCategoryDto(d + 1, crList); + source.remove(0); + } else if (cSource.getTier() > tier) { + CategoryForView sub = recursiveBuildFromCategoryDto(tier + 1, source); categoryForView.getCategoryTCountList().add(sub); } else { return categoryForView; } - } return categoryForView; } + private CategoryForView() { + } } diff --git a/src/main/java/myblog/blog/category/dto/CategoryNormalDto.java b/src/main/java/myblog/blog/category/dto/CategoryNormalDto.java index 3450798..c61839d 100644 --- a/src/main/java/myblog/blog/category/dto/CategoryNormalDto.java +++ b/src/main/java/myblog/blog/category/dto/CategoryNormalDto.java @@ -3,7 +3,9 @@ package myblog.blog.category.dto; import lombok.Getter; import lombok.Setter; import lombok.ToString; - +/* + - 범용 카테고리 DTO +*/ @Getter @Setter @ToString diff --git a/src/main/java/myblog/blog/category/repository/CategoryRepository.java b/src/main/java/myblog/blog/category/repository/CategoryRepository.java index c00b28f..cb14902 100644 --- a/src/main/java/myblog/blog/category/repository/CategoryRepository.java +++ b/src/main/java/myblog/blog/category/repository/CategoryRepository.java @@ -8,10 +8,19 @@ import java.util.List; public interface CategoryRepository extends JpaRepository { + /* + - 카테고리 이름으로 카테고리 찾기 + */ Category findByTitle(String title); + + /* + - 티어별 카테고리들 가져오기 + */ List findAllByTierIs(int tier); - + /* + - ID == 0 인 더미카테고리를 제외한 모든 카테고리 불러오기 + */ @Query("select c " + "from Category c " + "where c.id >0 ") diff --git a/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java b/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java index de7823c..9ddb376 100644 --- a/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java +++ b/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java @@ -11,6 +11,9 @@ import java.util.List; @Repository public interface NaCategoryRepository { + /* + - 카테고리별 아티클 갯수 통계 쿼리 + */ @Select("select ifnull(f.title,'total') as title, ifnull(tier,0) as tier, ifnull(f.category_id, 0) as id, ifnull(count,0) as count, ifnull(f.p_sort_num, 0) as pOrder, ifnull(f.c_sort_num, 0) as cOrder\n" + " from \n" + " (select ifnull(ifnull(b.title, c.title),'total') as title, count(*) as 'count'\n" + diff --git a/src/main/java/myblog/blog/category/service/CategoryService.java b/src/main/java/myblog/blog/category/service/CategoryService.java index cba5b2b..014fa45 100644 --- a/src/main/java/myblog/blog/category/service/CategoryService.java +++ b/src/main/java/myblog/blog/category/service/CategoryService.java @@ -21,7 +21,11 @@ public class CategoryService { private final CategoryRepository categoryRepository; private final NaCategoryRepository naCategoryRepository; - public Long createNewCategory(String title, String parent, int pOrder, int cOrder, int tier) { + /* + - 새로운 카테고리 생성하기 + - 상위 카테고리 존재 유무 분기 + */ + public Category createNewCategory(String title, String parent, int pOrder, int cOrder, int tier) { Category parentCategory = null; if (parent != null) { @@ -38,44 +42,73 @@ public class CategoryService { categoryRepository.save(category); - return category.getId(); + return category; } + /* + - 카테고리 이름으로 카테고리 찾기 + */ public Category findCategory(String title) { return categoryRepository.findByTitle(title); } + /* + - 카테고리와 카테고리별 아티클 수 찾기 + */ public List getCategoryForView() { return naCategoryRepository.getCategoryCount(); - } + /* + - 티어별 카테고리 목록 찾기 + */ public List findCategoryByTier(int tier) { return categoryRepository.findAllByTierIs(tier); } + /* + - 카테고리 변경 로직 + 1. 카테고리 리스트의 순서 작성 + 2. 입력받은 카테고리리스트와 DB의 전체카테고리 리스트 두개를 큐로 처리하여 비교대조 + 3. 해당 카테고리 변경처리 + 3-1 해당 카테고리 존재시 더티체킹으로 업데이트 처리 + 3-2 DB에 존재하지 않는경우 새로 생성 + 3-3 DB에만 존재하는 카테고리는 삭제처리 + */ @Transactional public void changeCategory(List categoryList) { + // 1.카테고리 리스트 순서 작성 sortingOrder(categoryList); + // 2. 기존 DB 저장된 카테고리 리스트 불러오기 List allWithoutDummy = categoryRepository.findAllWithoutDummy(); + // 3. 카테고리 변경 루프 while (!categoryList.isEmpty()) { CategoryNormalDto categoryNormalDto = categoryList.get(0); categoryList.remove(0); + // 부모카테고리인경우 if (categoryNormalDto.getTier() == 1) { Category pCategory = null; - if (categoryNormalDto.getId() == null) { - Long newCategoryId = createNewCategory(categoryNormalDto.getTitle(), null, categoryNormalDto.getPOrder(), categoryNormalDto.getCOrder(), categoryNormalDto.getTier()); - pCategory = categoryRepository.findById(newCategoryId).get(); - } else { + // 부모카테고리가 기존에 존재 x + if (categoryNormalDto.getId() == null) { + pCategory + = createNewCategory(categoryNormalDto.getTitle(), + null, + categoryNormalDto.getPOrder(), + categoryNormalDto.getCOrder(), + categoryNormalDto.getTier()); + + } + // 부모카테고리가 기존에 존재 o + else { for (int i = 0; i < allWithoutDummy.size(); i++) { if (allWithoutDummy.get(i).getId().equals(categoryNormalDto.getId())) { pCategory = allWithoutDummy.get(i); @@ -83,7 +116,6 @@ public class CategoryService { break; } } - pCategory.updateCategory(categoryNormalDto.getTitle(), categoryNormalDto.getTier(), categoryNormalDto.getPOrder(), @@ -95,18 +127,21 @@ public class CategoryService { CategoryNormalDto subCategoryDto = categoryList.get(0); if (subCategoryDto.getTier() == 1) break; - categoryList.remove(0); + // 자식 카테고리인경우 Category cCategory = null; + // 카테고리가 기존에 존재 x if (subCategoryDto.getId() == null) { - Long newCategoryId = createNewCategory(subCategoryDto.getTitle(), + cCategory = createNewCategory(subCategoryDto.getTitle(), pCategory.getTitle(), subCategoryDto.getPOrder(), - subCategoryDto.getCOrder(), subCategoryDto.getTier()); - cCategory = categoryRepository.findById(newCategoryId).get(); + subCategoryDto.getCOrder(), + subCategoryDto.getTier()); - } else { + } + // 카테고리가 기존에 존재 o + else { for (int i = 0; i < allWithoutDummy.size(); i++) { if (allWithoutDummy.get(i).getId().equals(subCategoryDto.getId())) { cCategory = allWithoutDummy.get(i); @@ -120,32 +155,22 @@ public class CategoryService { subCategoryDto.getCOrder(), pCategory); } - } - - } - } - + // 3-3 불일치 카테고리 전부 삭제 categoryRepository.deleteAll(allWithoutDummy); - } + /* + - 카테고리 변경을 위해 카테고리의 순번을 작성하는 로직 + */ private void sortingOrder(List categoryList) { int pOrderIndex = 0; int cOrderIndex = 0; - boolean isTier1 = false; - for (CategoryNormalDto categoryNormalDto : categoryList) { - System.out.println("categoryNormalDto = " + categoryNormalDto); - - } - - - for (int i = 0; i < categoryList.size(); i++) { - - CategoryNormalDto categoryDto = categoryList.get(i); + //티어별 트리구조로 순서 작성 로직 + for (CategoryNormalDto categoryDto : categoryList) { if (categoryDto.getTier() == 1) { cOrderIndex = 0; @@ -155,21 +180,12 @@ public class CategoryService { categoryDto.setPOrder(pOrderIndex); categoryDto.setCOrder(++cOrderIndex); } - } - - for (CategoryNormalDto categoryNormalDto : categoryList) { - - System.out.println("categoryNormalDto = " + categoryNormalDto); - - } - - } - - - + /* + - 최초 더미 카테고리 추가 코드 + */ @PostConstruct public void insertCategory() { diff --git a/src/main/java/myblog/blog/main/MainController.java b/src/main/java/myblog/blog/main/MainController.java index 26c6d64..9b88a77 100644 --- a/src/main/java/myblog/blog/main/MainController.java +++ b/src/main/java/myblog/blog/main/MainController.java @@ -8,10 +8,13 @@ import myblog.blog.category.dto.CategoryForView; import myblog.blog.category.service.CategoryService; import myblog.blog.comment.dto.CommentDtoForSide; import myblog.blog.comment.service.CommentService; +import org.modelmapper.ModelMapper; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; import java.util.stream.Collectors; @@ -24,6 +27,7 @@ public class MainController { private final ArticleService articleService; private final CategoryService categoryService; private final CommentService commentService; + private final ModelMapper modelMapper; @GetMapping("/") public String main(Model model) { @@ -38,16 +42,29 @@ public class MainController { .collect(Collectors.toList()); model.addAttribute("commentsList", comments); - List popularArticles = articleService.getPopularArticles(); + List popularArticles = articleService.getPopularArticles() + .stream() + .map(article -> modelMapper.map(article, ArticleDtoForMain.class)) + .collect(Collectors.toList()); model.addAttribute("popularArticles", popularArticles); - Slice recentArticles = articleService.getRecentArticles(0); + Slice recentArticles = articleService.getRecentArticles(0) + .map(article -> modelMapper.map(article, ArticleDtoForMain.class)); model.addAttribute("recentArticles",recentArticles); return "index"; } + @GetMapping("/main/article/{pageNum}") + public @ResponseBody + List mainNextPage(@PathVariable int pageNum) { + + return articleService.getRecentArticles(pageNum).getContent() + .stream() + .map(article -> modelMapper.map(article, ArticleDtoForMain.class)) + .collect(Collectors.toList()); + } } diff --git a/src/main/resources/templates/admin/categoryEdit.html b/src/main/resources/templates/admin/categoryEdit.html index 253f00d..8f00dc0 100644 --- a/src/main/resources/templates/admin/categoryEdit.html +++ b/src/main/resources/templates/admin/categoryEdit.html @@ -6,40 +6,21 @@ - Jinia's Log - 카테고리 편집 - - - - + 카테고리 편집 - Jinia's Log - - - - - - - - - - - - - - - - - - - + + + + + - @@ -65,7 +46,6 @@ th:id="|subCategory-${subCategory.getId()}|" onclick="clickCategory(this)"> -
@@ -107,54 +87,72 @@ let idxNewCategoryNum = 0; const categoryName = document.getElementById("categoryName"); - // 셀렉터 - let seclector; - function clickCategory(div) { - selector = div; - console.log(categorysList); - categoryName.value = selector.innerText; + // DIV 셀렉터 + let selector; + // DTO 카테고리 셀렉터 + let category; + // 카테고리명 중복 검사 + function isDuplicatedCategoryName(){ + const categoryList = document.getElementById("categoryBox").childNodes; + for (let i = 0; i x.title === selector.innerText); const childNodes = selector.parentNode.childNodes; + for (const childNode of childNodes) { childNode.classList.remove("active"); } selector.classList.add("active"); - console.log(selector); } categoryName.addEventListener("keyup", () => { - const category = categorysList.find(x => x.title == selector.innerText); selector.innerText = categoryName.value; category.title = selector.innerText; - }) function swapToPrevious(title){ - const index = categorysList.findIndex(x=>x.title == title); + const index = categorysList.findIndex(x=>x.title === title); let tmp = categorysList[index-1]; categorysList[index-1] = categorysList[index]; categorysList[index] = tmp; } - function swapToNextInDto(title){ - const index = categorysList.findIndex(x=>x.title == title); + const index = categorysList.findIndex(x=>x.title === title); let tmp = categorysList[index+1]; categorysList[index+1] = categorysList[index]; categorysList[index] = tmp; } - function categoryUp() { const categoryTitle = selector.innerText; - const category = categorysList.find(x => x.title == selector.innerText); // 선택 카테고리가 부모일때 if (selector.previousSibling != null) { @@ -168,7 +166,6 @@ function categoryDown() { const categoryTitle = selector.innerText; - const category = categorysList.find(x => x.title == selector.innerText); // 선택 카테고리가 부모일때 if (selector.nextSibling.nextSibling != null) { @@ -183,25 +180,22 @@ } function tierUp(){ - const category = categorysList.find(x => x.title == selector.innerText); category.tier = 1; selector.classList.add("fw-bold"); } + function tierDown(){ - const category = categorysList.find(x => x.title == selector.innerText); category.tier = 2; selector.classList.remove("fw-bold"); } - - function addCategory(){ const box = document.getElementById("categoryBox"); box.innerHTML +=`` - let newCategory = new Object(); + let newCategory = {}; newCategory.id = null; newCategory.title = '새 카테고리'+idxNewCategoryNum++; newCategory.tier = 2; @@ -212,17 +206,19 @@ categorysList.push(newCategory); } - function deleteCategory(){ - const categoryIndex = categorysList.findIndex(x=>x.title == selector.innerText); + function deleteCategory(){ + const categoryIndex = categorysList.findIndex(x=>x.title === selector.innerText); categorysList.splice(categoryIndex,1); selector.remove(); } function changeCategory(){ + if(isDuplicatedCategoryName()){ + return; + } let token = getCsrfToken(); - const xhr = new XMLHttpRequest(); xhr.open("POST", "/category/edit"); xhr.setRequestHeader("content-type", "application/json"); @@ -231,7 +227,6 @@ xhr.onload = () => { if (xhr.status === 200 || xhr.status === 201 || xhr.status === 202) { - location.href='/'; } else{ @@ -242,7 +237,6 @@ - diff --git a/src/main/resources/templates/article/articleEditForm.html b/src/main/resources/templates/article/articleEditForm.html index b5b1bbe..c3c651d 100644 --- a/src/main/resources/templates/article/articleEditForm.html +++ b/src/main/resources/templates/article/articleEditForm.html @@ -6,33 +6,15 @@ - Jinia's Log - 글 수정 - - - - + 글 수정 - Jinia's Log - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/src/main/resources/templates/article/articleWriteForm.html b/src/main/resources/templates/article/articleWriteForm.html index a89156d..68d6286 100644 --- a/src/main/resources/templates/article/articleWriteForm.html +++ b/src/main/resources/templates/article/articleWriteForm.html @@ -6,33 +6,16 @@ - Jinia's Log - 글 작성 - - - - + 글 작성 - Jinia's Log + - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/src/main/resources/templates/layout/fragments.html b/src/main/resources/templates/layout/fragments.html index ed3412c..9af306f 100644 --- a/src/main/resources/templates/layout/fragments.html +++ b/src/main/resources/templates/layout/fragments.html @@ -356,34 +356,48 @@ } }); - + // 유효성 검사 function checkTitle() { - - let title = document.getElementById("title"); - - if (title.value === "") { - return false; - } - return true; + return document.getElementById("title").value !== ""; } + function checkContent() { + return contents.value !== ""; + } + + function checkCategory() { + return document.getElementById("category").value !== "카테고리를 선택해주세요"; + } + + function checkTags() { + return document.getElementById("tags").value !== ""; + } + + // 전송 function post() { + contents.value = editor.getMarkdown(); + if (!checkTitle()) { alert("제목을 입력해주세요") return; } + else if (!checkContent()) { + alert("내용을 입력해주세요") + return; + }else if (!checkCategory()) { + alert("카테고리를 입력해주세요") + return; + }else if (!checkTags()) { + alert("태그를 입력해주세요") + return; + } - console.log(thumbUrl.value + "????????") - contents.value = editor.getMarkdown(); document.getElementById("writeArticleForm").submit(); - } - editor.setMarkdown([[${articleDto.getContent()}]]); - @@ -396,10 +410,6 @@ - - - -