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 @@