diff --git a/src/main/java/myblog/blog/article/adapter/incomming/web/ArticleController.java b/src/main/java/myblog/blog/article/adapter/incomming/web/ArticleController.java index 2ce3fe9..7a00e9d 100644 --- a/src/main/java/myblog/blog/article/adapter/incomming/web/ArticleController.java +++ b/src/main/java/myblog/blog/article/adapter/incomming/web/ArticleController.java @@ -13,14 +13,13 @@ import myblog.blog.article.application.port.response.ArticleResponseForCardBox; import myblog.blog.article.application.port.response.ArticleResponseForDetail; import myblog.blog.article.application.port.response.ArticleResponseForEdit; -import myblog.blog.category.service.CategoryService; -import myblog.blog.category.dto.*; +import myblog.blog.category.appliacation.port.incomming.CategoryUseCase; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; import myblog.blog.member.auth.PrincipalDetails; import myblog.blog.member.dto.MemberVo; import myblog.blog.shared.queries.LayoutRenderingQueries; import org.jsoup.Jsoup; -import org.modelmapper.ModelMapper; import org.springframework.data.domain.*; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -33,7 +32,6 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.util.List; -import java.util.stream.Collectors; import static myblog.blog.shared.utils.MarkdownUtils.*; @@ -45,14 +43,13 @@ public class ArticleController { private final ArticleQueriesUseCase articleQueriesUseCase; private final TempArticleUseCase tempArticleUseCase; private final TagsQueriesUseCase tagsQueriesUseCase; - private final CategoryService categoryService; + private final CategoryUseCase categoryUseCase; private final LayoutRenderingQueries layoutRenderingQueries; - private final ModelMapper modelMapper; @GetMapping("article/write") String getArticleWriteForm(Model model) { layoutRenderingQueries.AddLayoutTo(model); - model.addAttribute("categoryInput", getCategoryDtosForForm()); + model.addAttribute("categoryInput", categoryUseCase.findCategoryByTier(2)); model.addAttribute("tagsInput", tagsQueriesUseCase.findAllTagDtos()); model.addAttribute("articleDto", new ArticleForm()); return "article/articleWriteForm"; @@ -80,12 +77,11 @@ public class ArticleController { String updateArticle(@RequestParam Long articleId, Model model) { ArticleResponseForEdit articleDto = articleQueriesUseCase.getArticleForEdit(articleId); layoutRenderingQueries.AddLayoutTo(model); - model.addAttribute("categoryInput", getCategoryDtosForForm()); + model.addAttribute("categoryInput", categoryUseCase.findCategoryByTier(2)); model.addAttribute("tagsInput", tagsQueriesUseCase.findAllTagDtos()); model.addAttribute("articleDto", articleDto); return "article/articleEditForm"; } - /* - 아티클 수정 요청 */ @@ -96,7 +92,6 @@ public class ArticleController { articleUseCase.editArticle(ArticleEditRequest.from(articleId, articleForm)); return "redirect:/article/view?articleId=" + articleId; } - /* - 아티클 삭제 요청 */ @@ -106,7 +101,6 @@ public class ArticleController { articleUseCase.deleteArticle(articleId); return "redirect:/"; } - /* - 카테고리별 게시물 조회하기 */ @@ -117,7 +111,7 @@ public class ArticleController { @RequestParam Integer page, Model model) { PagingBoxHandler pagingBoxHandler = - PagingBoxHandler.createOf(page, getTotalArticleCntByCategory(category, categoryService.getCategoryForView())); + PagingBoxHandler.createOf(page, getTotalArticleCntByCategory(category, categoryUseCase.getCategoryViewForLayout())); Slice articleDtoList = articleQueriesUseCase.getArticlesByCategory(category, tier, pagingBoxHandler.getCurPageNum()); @@ -132,7 +126,23 @@ public class ArticleController { return "article/articleList"; } + private int getTotalArticleCntByCategory(String category, CategoryViewForLayout categorys) { + if (categorys.getTitle().equals(category)) { + return categorys.getCount(); + } else { + for (CategoryViewForLayout categoryCnt : + categorys.getCategoryTCountList()) { + if (categoryCnt.getTitle().equals(category)) + return categoryCnt.getCount(); + for (CategoryViewForLayout categoryCntSub : categoryCnt.getCategoryTCountList()) { + if (categoryCntSub.getTitle().equals(category)) + return categoryCntSub.getCount(); + } + } + } + throw new IllegalArgumentException("'"+category+"' 라는 카테고리는 존재하지 않습니다."); + } /* - 태그별 게시물 조회하기 */ @@ -157,7 +167,6 @@ public class ArticleController { return "article/articleListByTag"; } - /* - 검색어별 게시물 조회하기 */ @@ -183,7 +192,6 @@ public class ArticleController { return "article/articleListByKeyword"; } - /* - 아티클 상세 조회 1. 로그인여부 검토 @@ -240,7 +248,6 @@ public class ArticleController { return "article/articleView"; } - /* - 쿠키 추가 / 조회수 증가 검토 */ @@ -269,36 +276,4 @@ public class ArticleController { return addHitAvailable; } } - - /* - - 카테고리별 아티클 갯수 구하기 - */ - 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("'"+category+"' 라는 카테고리는 존재하지 않습니다."); - } - - /* - - 아티클 폼에 필요한 카테고리 dtos - */ - private List getCategoryDtosForForm() { - return categoryService - .findCategoryByTier(2) - .stream() - .map(category -> modelMapper.map(category, CategorySimpleView.class)) - .collect(Collectors.toList()); - } } diff --git a/src/main/java/myblog/blog/article/adapter/incomming/web/MainController.java b/src/main/java/myblog/blog/article/adapter/incomming/web/MainController.java index 196c010..79a2c8a 100644 --- a/src/main/java/myblog/blog/article/adapter/incomming/web/MainController.java +++ b/src/main/java/myblog/blog/article/adapter/incomming/web/MainController.java @@ -24,7 +24,7 @@ public class MainController { - 메인 화면 제어용 컨트롤러 */ @GetMapping("/") - public String main(Model model) { + String main(Model model) { // Dto 전처리 List popularArticles = articleQueriesUseCase.getPopularArticles(); // @@ -37,13 +37,11 @@ public class MainController { - 최신 아티클 무한스크롤로 조회 */ @GetMapping("/main/article/{lastArticleId}") - public @ResponseBody - List mainNextPage(@PathVariable(required = false) Long lastArticleId) { + @ResponseBody List mainNextPage(@PathVariable(required = false) Long lastArticleId) { // Entity to Dto List articles = articleQueriesUseCase.getRecentArticles(lastArticleId); - // 화면렌더링을 위한 파싱 for(ArticleResponseForCardBox article : articles){ String content = Jsoup.parse(getHtmlRenderer().render(getParser().parse(article.getContent()))).text(); @@ -52,7 +50,6 @@ public class MainController { } article.setContent(content); } - return articles; } } diff --git a/src/main/java/myblog/blog/article/adapter/incomming/web/TempArticleController.java b/src/main/java/myblog/blog/article/adapter/incomming/web/TempArticleController.java index b56c68f..055a314 100644 --- a/src/main/java/myblog/blog/article/adapter/incomming/web/TempArticleController.java +++ b/src/main/java/myblog/blog/article/adapter/incomming/web/TempArticleController.java @@ -24,7 +24,6 @@ public class TempArticleController { public String autoSaveTemp(@RequestBody TempArticleResponse tempArticleResponse){ tempArticleService.saveTemp(new TempArticle(tempArticleResponse.getContent())); - return "저장성공"; } @@ -36,10 +35,8 @@ public class TempArticleController { TempArticleResponse getTempArticle(){ Optional tempArticle = tempArticleService.getTempArticle(); - TempArticleResponse tempArticleResponse = new TempArticleResponse(); tempArticleResponse.setContent(tempArticle.orElse(new TempArticle()).getContent()); - return tempArticleResponse; } } diff --git a/src/main/java/myblog/blog/article/adapter/outgoing/persistence/ArticleRepositoryAdapter.java b/src/main/java/myblog/blog/article/adapter/outgoing/persistence/ArticleRepositoryAdapter.java index 5006dc1..45b643a 100644 --- a/src/main/java/myblog/blog/article/adapter/outgoing/persistence/ArticleRepositoryAdapter.java +++ b/src/main/java/myblog/blog/article/adapter/outgoing/persistence/ArticleRepositoryAdapter.java @@ -51,7 +51,7 @@ public class ArticleRepositoryAdapter implements ArticleRepositoryPort { } @Override - public Slice
findBySupCategoryOrderByIdDesc(Pageable pageable, String category) { + public Slice
findBySuperCategoryOrderByIdDesc(Pageable pageable, String category) { return jpaArticleRepository.findBySupCategoryOrderByIdDesc(pageable,category); } diff --git a/src/main/java/myblog/blog/article/application/ArticleQueries.java b/src/main/java/myblog/blog/article/application/ArticleQueries.java index c416784..88932ed 100644 --- a/src/main/java/myblog/blog/article/application/ArticleQueries.java +++ b/src/main/java/myblog/blog/article/application/ArticleQueries.java @@ -12,7 +12,7 @@ import myblog.blog.article.application.port.response.ArticleResponseByCategory; import myblog.blog.article.application.port.response.ArticleResponseForDetail; import myblog.blog.article.application.port.response.ArticleResponseForEdit; -import myblog.blog.category.service.CategoryService; +import myblog.blog.category.appliacation.CategoryService; import org.modelmapper.ModelMapper; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; @@ -78,7 +78,7 @@ public class ArticleQueries implements ArticleQueriesUseCase { } if (tier.equals(1)) { articles = articleRepositoryPort - .findBySupCategoryOrderByIdDesc( + .findBySuperCategoryOrderByIdDesc( PageRequest.of(pageResolve(page), 5), category); } if (tier.equals(2)) { diff --git a/src/main/java/myblog/blog/article/application/ArticleService.java b/src/main/java/myblog/blog/article/application/ArticleService.java index d106b96..fa8b294 100644 --- a/src/main/java/myblog/blog/article/application/ArticleService.java +++ b/src/main/java/myblog/blog/article/application/ArticleService.java @@ -13,7 +13,7 @@ import myblog.blog.article.domain.Article; import myblog.blog.category.domain.Category; import myblog.blog.member.doamin.Member; -import myblog.blog.category.service.CategoryService; +import myblog.blog.category.appliacation.CategoryService; import myblog.blog.member.service.Oauth2MemberService; import org.springframework.cache.annotation.CacheEvict; diff --git a/src/main/java/myblog/blog/article/application/port/outgoing/ArticleRepositoryPort.java b/src/main/java/myblog/blog/article/application/port/outgoing/ArticleRepositoryPort.java index 8f240a6..9ea4e6b 100644 --- a/src/main/java/myblog/blog/article/application/port/outgoing/ArticleRepositoryPort.java +++ b/src/main/java/myblog/blog/article/application/port/outgoing/ArticleRepositoryPort.java @@ -16,7 +16,7 @@ public interface ArticleRepositoryPort { List
findByOrderByIdDescWithList(Pageable pageable); List
findByOrderByIdDesc(Long articleId, Pageable pageable); Slice
findBySubCategoryOrderByIdDesc(Pageable pageable, String category); - Slice
findBySupCategoryOrderByIdDesc(Pageable pageable, String category); + Slice
findBySuperCategoryOrderByIdDesc(Pageable pageable, String category); Article findArticleByIdFetchCategoryAndTags(Long articleId); Page
findAllByArticleTagsOrderById(Pageable pageable, String tag); Page
findAllByKeywordOrderById(Pageable pageable, String keyword); diff --git a/src/main/java/myblog/blog/article/domain/Article.java b/src/main/java/myblog/blog/article/domain/Article.java index 4f49504..7ad0103 100644 --- a/src/main/java/myblog/blog/article/domain/Article.java +++ b/src/main/java/myblog/blog/article/domain/Article.java @@ -83,6 +83,14 @@ public class Article extends BasicEntity { this.hit = 0L; } + private String makeDefaultThumbOf(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; + } /* - 아티클 수정을 위한 로직 */ @@ -95,22 +103,8 @@ public class Article extends BasicEntity { this.thumbnailUrl = getThumbnailUrl(); } } - /* - - 아티클 조회수 증가 - */ + public void addHit(){ this.hit++; } - - /* - - 썸네일 기본 작성 - */ - private String makeDefaultThumbOf(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/category/adapter/imcomming/CategoryController.java b/src/main/java/myblog/blog/category/adapter/imcomming/CategoryController.java new file mode 100644 index 0000000..3a884ca --- /dev/null +++ b/src/main/java/myblog/blog/category/adapter/imcomming/CategoryController.java @@ -0,0 +1,55 @@ +package myblog.blog.category.adapter.imcomming; + +import lombok.RequiredArgsConstructor; + +import myblog.blog.category.appliacation.port.incomming.CategoryUseCase; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; +import myblog.blog.comment.dto.CommentDtoForLayout; +import myblog.blog.comment.service.CommentService; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.Errors; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +@Controller +@RequiredArgsConstructor +public class CategoryController { + + private final CategoryUseCase categoryUseCase; + private final CommentService commentService; + private final CategoryListValidator categorylistValidator; + + /* + - 카테고리 수정폼 조회 + */ + @GetMapping("/category/edit") + public String editCategoryForm(Model model) { + + List categoryList = categoryUseCase.getCategorytCountList(); + List copyList = new ArrayList<>(List.copyOf(categoryList)); + copyList.remove(0); + CategoryViewForLayout categoryViewForLayout = CategoryViewForLayout.from(categoryList); + List comments = commentService.recentCommentList(); + + model.addAttribute("categoryForEdit", copyList); + model.addAttribute("category", categoryViewForLayout); + model.addAttribute("commentsList", comments); + + return "admin/categoryEdit"; + } + /* + - 카테고리 수정 요청 + */ + @PostMapping("/category/edit") + public @ResponseBody + String editCategory(@RequestBody List categoryList, Errors errors) { + // List DTO 검증을 위한 커스텀 validator + categorylistValidator.validate(categoryList, errors); + categoryUseCase.changeCategory(categoryList); + return "변경 성공"; + } +} diff --git a/src/main/java/myblog/blog/shared/exception/ListValidator.java b/src/main/java/myblog/blog/category/adapter/imcomming/CategoryListValidator.java similarity index 69% rename from src/main/java/myblog/blog/shared/exception/ListValidator.java rename to src/main/java/myblog/blog/category/adapter/imcomming/CategoryListValidator.java index d16a61b..1d94a72 100644 --- a/src/main/java/myblog/blog/shared/exception/ListValidator.java +++ b/src/main/java/myblog/blog/category/adapter/imcomming/CategoryListValidator.java @@ -1,12 +1,14 @@ -package myblog.blog.shared.exception; +package myblog.blog.category.adapter.imcomming; import lombok.RequiredArgsConstructor; +import myblog.blog.shared.exception.CustomFormException; import org.springframework.stereotype.Component; import org.springframework.validation.Errors; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import java.util.List; +import java.util.Objects; /* @@ -14,7 +16,7 @@ import java.util.List; */ @Component @RequiredArgsConstructor -public class ListValidator implements Validator { +public class CategoryListValidator implements Validator { private final SpringValidatorAdapter springValidatorAdapter; @@ -28,5 +30,8 @@ public class ListValidator implements Validator { for(Object object : (List)target){ springValidatorAdapter.validate(object,errors); } + if (errors.hasErrors()) { + throw new CustomFormException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage()); + } } } diff --git a/src/main/java/myblog/blog/category/adapter/outgoing/persistence/CategoryRepositoryAdapter.java b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/CategoryRepositoryAdapter.java new file mode 100644 index 0000000..6698e9b --- /dev/null +++ b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/CategoryRepositoryAdapter.java @@ -0,0 +1,54 @@ +package myblog.blog.category.adapter.outgoing.persistence; + +import lombok.RequiredArgsConstructor; +import myblog.blog.category.appliacation.port.outgoing.CategoryRepositoryPort; +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; +import myblog.blog.category.domain.Category; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class CategoryRepositoryAdapter implements CategoryRepositoryPort { + + private final JpaCategoryRepository jpaCategoryRepository; + private final MybatisCategoryRepository mybatisCategoryRepository; + + + @Override + public Optional findByTitle(String title) { + return jpaCategoryRepository.findByTitle(title); + } + + @Override + public List findAll() { + return jpaCategoryRepository.findAll(); + } + + @Override + public List getCategoryCount() { + return mybatisCategoryRepository.getCategoryCount(); + } + + @Override + public List findAllByTierIs(int tier) { + return jpaCategoryRepository.findAllByTierIs(tier); + } + + @Override + public List findAllWithoutDummy() { + return jpaCategoryRepository.findAllWithoutDummy(); + } + + @Override + public void deleteAll(List categoryListFromDb) { + jpaCategoryRepository.deleteAll(categoryListFromDb); + } + + @Override + public void save(Category category) { + jpaCategoryRepository.save(category); + } +} diff --git a/src/main/java/myblog/blog/category/repository/CategoryRepository.java b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/JpaCategoryRepository.java similarity index 83% rename from src/main/java/myblog/blog/category/repository/CategoryRepository.java rename to src/main/java/myblog/blog/category/adapter/outgoing/persistence/JpaCategoryRepository.java index 0877564..fae7ace 100644 --- a/src/main/java/myblog/blog/category/repository/CategoryRepository.java +++ b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/JpaCategoryRepository.java @@ -1,4 +1,4 @@ -package myblog.blog.category.repository; +package myblog.blog.category.adapter.outgoing.persistence; import myblog.blog.category.domain.Category; import org.springframework.data.jpa.repository.JpaRepository; @@ -7,7 +7,7 @@ import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; -public interface CategoryRepository extends JpaRepository { +public interface JpaCategoryRepository extends JpaRepository { /* - 카테고리 이름으로 카테고리 찾기 diff --git a/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/MybatisCategoryRepository.java similarity index 81% rename from src/main/java/myblog/blog/category/repository/NaCategoryRepository.java rename to src/main/java/myblog/blog/category/adapter/outgoing/persistence/MybatisCategoryRepository.java index 42393d0..be9dabe 100644 --- a/src/main/java/myblog/blog/category/repository/NaCategoryRepository.java +++ b/src/main/java/myblog/blog/category/adapter/outgoing/persistence/MybatisCategoryRepository.java @@ -1,6 +1,6 @@ -package myblog.blog.category.repository; +package myblog.blog.category.adapter.outgoing.persistence; -import myblog.blog.category.dto.CategorySimpleView; +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; @@ -9,7 +9,7 @@ import java.util.List; @Mapper @Repository -public interface NaCategoryRepository { +public interface MybatisCategoryRepository { /* - 카테고리별 아티클 갯수 통계 쿼리 @@ -23,6 +23,6 @@ public interface NaCategoryRepository { " group by c.title, b.title with rollup) e\n" + " right join category f on (e.title = f.title)\n" + " order by pOrder, cOrder ") - List getCategoryCount(); + List getCategoryCount(); } diff --git a/src/main/java/myblog/blog/category/service/CategoryService.java b/src/main/java/myblog/blog/category/appliacation/CategoryService.java similarity index 50% rename from src/main/java/myblog/blog/category/service/CategoryService.java rename to src/main/java/myblog/blog/category/appliacation/CategoryService.java index 5e14b6e..2ca5a85 100644 --- a/src/main/java/myblog/blog/category/service/CategoryService.java +++ b/src/main/java/myblog/blog/category/appliacation/CategoryService.java @@ -1,65 +1,74 @@ -package myblog.blog.category.service; +package myblog.blog.category.appliacation; import lombok.RequiredArgsConstructor; + import myblog.blog.category.domain.Category; -import myblog.blog.category.dto.CategorySimpleView; -import myblog.blog.category.dto.CategoryForView; -import myblog.blog.category.repository.CategoryRepository; -import myblog.blog.category.repository.NaCategoryRepository; +import myblog.blog.category.appliacation.port.incomming.CategoryUseCase; +import myblog.blog.category.appliacation.port.outgoing.CategoryRepositoryPort; +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; + +import org.modelmapper.ModelMapper; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - import java.util.List; -import java.util.Optional; +import java.util.stream.Collectors; @Service @Transactional @RequiredArgsConstructor -public class CategoryService { +public class CategoryService implements CategoryUseCase { - private final CategoryRepository categoryRepository; - private final NaCategoryRepository naCategoryRepository; + private final CategoryRepositoryPort categoryRepositoryPort; + private final ModelMapper modelMapper; /* - 카테고리 이름으로 카테고리 찾기 */ + @Override public Category findCategory(String title) { - return categoryRepository.findByTitle(title) + return categoryRepositoryPort.findByTitle(title) .orElseThrow(() -> new IllegalArgumentException("NotFoundCategoryException")); } /* - 카테고리 이름으로 카테고리 찾기 */ + @Override public List getAllCategories() { - return categoryRepository.findAll(); + return categoryRepositoryPort.findAll(); } /* - 카테고리와 카테고리별 아티클 수 찾기 */ - public List getCategorytCountList() { - return naCategoryRepository.getCategoryCount(); + @Override + public List getCategorytCountList() { + return categoryRepositoryPort.getCategoryCount(); } /* - getCategorytCountList()의 캐싱을 위한 전처리 매핑 로직 - - 본래는 컨트롤러단에서 존재해야할 dto 매핑코드지만 캐싱을 위해 서비스단으로 이동 - 레이아웃 렌더링 성능 향상을 위해 캐싱작업 카테고리 변경 / 아티클 변경이 존재할경우 레이아웃 캐시 초기화 */ @Cacheable(value = "layoutCaching", key = "0") - public CategoryForView getCategoryForView() { - return CategoryForView.createCategory(naCategoryRepository.getCategoryCount()); + @Override + public CategoryViewForLayout getCategoryViewForLayout() { + return CategoryViewForLayout.from(categoryRepositoryPort.getCategoryCount()); } /* - 티어별 카테고리 목록 찾기 */ - public List findCategoryByTier(int tier) { - return categoryRepository.findAllByTierIs(tier); + @Override + public List findCategoryByTier(int tier) { + return categoryRepositoryPort.findAllByTierIs(tier) + .stream() + .map(category -> modelMapper.map(category, CategorySimpleDto.class)) + .collect(Collectors.toList()); } /* @@ -71,138 +80,114 @@ public class CategoryService { 3-2 DB에 존재하지 않는경우 새로 생성 3-3 DB에만 존재하는 카테고리는 삭제처리 */ + @Override @Transactional @CacheEvict(value = {"layoutCaching", "seoCaching"}, allEntries = true) - public void changeCategory(List categoryList) { + public void changeCategory(List categoryList) { // 1.카테고리 리스트 순서 작성 - sortingOrder(categoryList); + CategorySimpleDto.sortByOrder(categoryList); // 2. 기존 DB 저장된 카테고리 리스트 불러오기 - List categoryListFromDb = categoryRepository.findAllWithoutDummy(); + List categoryListFromDb = categoryRepositoryPort.findAllWithoutDummy(); - // 3. 카테고리 변경 루프 + // 3. 카테고리 변경 while (!categoryList.isEmpty()) { - CategorySimpleView categorySimpleView = categoryList.get(0); + CategorySimpleDto categorySimpleDto = categoryList.get(0); categoryList.remove(0); // 부모카테고리인경우 - if (categorySimpleView.getTier() == 1) { - + if (categorySimpleDto.getTier() == 1) { Category pCategory = null; - - // 부모카테고리가 기존에 존재 x - if (categorySimpleView.getId() == null) { - pCategory = createNewCategory(categorySimpleView, null); + // 신규 부모인경우 + if (categorySimpleDto.getId() == null) { + pCategory = createNewCategory(categorySimpleDto, null); } - // 부모카테고리가 기존에 존재 o + // 기존 부모인경우 else { for (int i = 0; i < categoryListFromDb.size(); i++) { - if (categoryListFromDb.get(i).getId().equals(categorySimpleView.getId())) { + if (categoryListFromDb.get(i).getId().equals(categorySimpleDto.getId())) { pCategory = categoryListFromDb.get(i); categoryListFromDb.remove(i); break; } } pCategory.updateCategory( - categorySimpleView.getTitle(), - categorySimpleView.getTier(), - categorySimpleView.getPOrder(), - categorySimpleView.getCOrder(), + categorySimpleDto.getTitle(), + categorySimpleDto.getTier(), + categorySimpleDto.getPOrder(), + categorySimpleDto.getCOrder(), null ); } while (!categoryList.isEmpty()) { - CategorySimpleView subCategorySimpleView = categoryList.get(0); - if (subCategorySimpleView.getTier() == 1) break; + CategorySimpleDto subCategorySimpleDto = categoryList.get(0); + if (subCategorySimpleDto.getTier() == 1) break; categoryList.remove(0); - // 자식 카테고리인경우 Category cCategory = null; // 카테고리가 기존에 존재 x - if (subCategorySimpleView.getId() == null) { - cCategory = createNewCategory(subCategorySimpleView, pCategory.getTitle()); + if (subCategorySimpleDto.getId() == null) { + cCategory = createNewCategory(subCategorySimpleDto, pCategory.getTitle()); } // 카테고리가 기존에 존재 o else { for (int i = 0; i < categoryListFromDb.size(); i++) { - if (categoryListFromDb.get(i).getId().equals(subCategorySimpleView.getId())) { + if (categoryListFromDb.get(i).getId().equals(subCategorySimpleDto.getId())) { cCategory = categoryListFromDb.get(i); categoryListFromDb.remove(i); break; } } cCategory.updateCategory( - subCategorySimpleView.getTitle(), - subCategorySimpleView.getTier(), - subCategorySimpleView.getPOrder(), - subCategorySimpleView.getCOrder(), + subCategorySimpleDto.getTitle(), + subCategorySimpleDto.getTier(), + subCategorySimpleDto.getPOrder(), + subCategorySimpleDto.getCOrder(), pCategory); } } } } // 3-3 불일치 카테고리 전부 삭제 - categoryRepository.deleteAll(categoryListFromDb); + categoryRepositoryPort.deleteAll(categoryListFromDb); } - /* - 새로운 카테고리 생성하기 - 상위 카테고리 존재 유무 분기 */ - private Category createNewCategory(CategorySimpleView categorySimpleView, String parent) { + private Category createNewCategory(CategorySimpleDto categorySimpleDto, String parent) { Category parentCategory = null; if (parent != null) { - parentCategory = categoryRepository.findByTitle(parent) + parentCategory = categoryRepositoryPort.findByTitle(parent) .orElseThrow(() -> new IllegalArgumentException("NotFoundCategoryException")); } Category category = Category.builder() - .title(categorySimpleView.getTitle()) - .pSortNum(categorySimpleView.getPOrder()) - .cSortNum(categorySimpleView.getCOrder()) - .tier(categorySimpleView.getTier()) + .title(categorySimpleDto.getTitle()) + .pSortNum(categorySimpleDto.getPOrder()) + .cSortNum(categorySimpleDto.getCOrder()) + .tier(categorySimpleDto.getTier()) .parents(parentCategory) .build(); - categoryRepository.save(category); + categoryRepositoryPort.save(category); return category; } - /* - - 카테고리 변경을 위해 카테고리의 순번을 작성하는 로직 - */ - private void sortingOrder(List categoryList) { - int pOrderIndex = 0; - int cOrderIndex = 0; - - //티어별 트리구조로 순서 작성 로직 - for (CategorySimpleView categoryDto : categoryList) { - - if (categoryDto.getTier() == 1) { - cOrderIndex = 0; - categoryDto.setPOrder(++pOrderIndex); - categoryDto.setCOrder(cOrderIndex); - } else { - categoryDto.setPOrder(pOrderIndex); - categoryDto.setCOrder(++cOrderIndex); - } - } - } - - /* - - 최초 필수 더미 카테고리 추가 코드 - */ +// /* +// - 최초 필수 더미 카테고리 추가 코드 +// */ // @PostConstruct - private void insertDummyCategory() { - if(categoryRepository.findByTitle("total")==null) { - Category category0 = Category.builder() - .tier(0) - .title("total") - .pSortNum(0) - .cSortNum(0) - .build(); - categoryRepository.save(category0); - } - } +// private void insertDummyCategory() { +// if(categoryRepositoryPort.findByTitle("total")==null) { +// Category category0 = Category.builder() +// .tier(0) +// .title("total") +// .pSortNum(0) +// .cSortNum(0) +// .build(); +// categoryRepositoryPort.save(category0); +// } +// } } diff --git a/src/main/java/myblog/blog/category/appliacation/port/incomming/CategoryUseCase.java b/src/main/java/myblog/blog/category/appliacation/port/incomming/CategoryUseCase.java new file mode 100644 index 0000000..8163884 --- /dev/null +++ b/src/main/java/myblog/blog/category/appliacation/port/incomming/CategoryUseCase.java @@ -0,0 +1,16 @@ +package myblog.blog.category.appliacation.port.incomming; + +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; +import myblog.blog.category.domain.Category; + +import java.util.List; + +public interface CategoryUseCase { + Category findCategory(String title); + List getAllCategories(); + List getCategorytCountList(); + CategoryViewForLayout getCategoryViewForLayout(); + List findCategoryByTier(int tier); + void changeCategory(List categoryList); +} diff --git a/src/main/java/myblog/blog/category/appliacation/port/outgoing/CategoryRepositoryPort.java b/src/main/java/myblog/blog/category/appliacation/port/outgoing/CategoryRepositoryPort.java new file mode 100644 index 0000000..dc387ce --- /dev/null +++ b/src/main/java/myblog/blog/category/appliacation/port/outgoing/CategoryRepositoryPort.java @@ -0,0 +1,17 @@ +package myblog.blog.category.appliacation.port.outgoing; + +import myblog.blog.category.appliacation.port.response.CategorySimpleDto; +import myblog.blog.category.domain.Category; + +import java.util.List; +import java.util.Optional; + +public interface CategoryRepositoryPort { + Optional findByTitle(String title); + List findAll(); + List getCategoryCount(); + List findAllByTierIs(int tier); + List findAllWithoutDummy(); + void deleteAll(List categoryListFromDb); + void save(Category category); +} diff --git a/src/main/java/myblog/blog/category/appliacation/port/response/CategorySimpleDto.java b/src/main/java/myblog/blog/category/appliacation/port/response/CategorySimpleDto.java new file mode 100644 index 0000000..8f2657d --- /dev/null +++ b/src/main/java/myblog/blog/category/appliacation/port/response/CategorySimpleDto.java @@ -0,0 +1,53 @@ +package myblog.blog.category.appliacation.port.response; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/* + - 범용 카테고리 DTO +*/ +@Getter +@Setter +@ToString +public class CategorySimpleDto implements Cloneable { + + private Long id; + @NotBlank(message = "카테고리명은 공백일 수 없습니다.") + private String title; + private int tier; + private int count; + private int pOrder; + private int cOrder; + + @Override + public CategorySimpleDto clone() { + try { + return (CategorySimpleDto) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + /* + - 카테고리 변경을 위해 카테고리의 순번을 작성하는 로직 + */ + static public void sortByOrder(List categoryList) { + int pOrderIndex = 0; + int cOrderIndex = 0; + + //티어별 트리구조로 순서 작성 로직 + for (CategorySimpleDto categorySimpleDto : categoryList) { + if (categorySimpleDto.getTier() == 1) { + cOrderIndex = 0; + categorySimpleDto.setPOrder(++pOrderIndex); + categorySimpleDto.setCOrder(cOrderIndex); + } else { + categorySimpleDto.setPOrder(pOrderIndex); + categorySimpleDto.setCOrder(++cOrderIndex); + } + } + } +} diff --git a/src/main/java/myblog/blog/category/appliacation/port/response/CategoryViewForLayout.java b/src/main/java/myblog/blog/category/appliacation/port/response/CategoryViewForLayout.java new file mode 100644 index 0000000..b446527 --- /dev/null +++ b/src/main/java/myblog/blog/category/appliacation/port/response/CategoryViewForLayout.java @@ -0,0 +1,67 @@ +package myblog.blog.category.appliacation.port.response; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/* + - 레이아웃용 트리구조 카테고리 리스트 +*/ +@Getter +@Setter +public class CategoryViewForLayout { + + private int count; + private String title; + private Long id; + private int pOrder; + private int cOrder; + // 트리구조를 갖기 위한 리스트 + private List categoryTCountList = new ArrayList<>(); + + /* + - 스태틱 생성 메서드 + */ + public static CategoryViewForLayout from(List crList) { + return recursiveBuildFromCategoryDto(0, crList); + } + + /* + - 재귀호출로 트리구조 생성 + 1. DTO객체 생성후 소스를 큐처리로 순차적 매핑 + 2. Depth 변화시 재귀 호출 / 재귀 탈출 + 3. 탈출시 상위 카테고리 list로 삽입하여 트리구조 작성 + */ + private static CategoryViewForLayout recursiveBuildFromCategoryDto(int tier, List source) { + + CategoryViewForLayout categoryViewForLayout = new CategoryViewForLayout(); + + while (!source.isEmpty()) { + CategorySimpleDto cSource = source.get(0); + + if (cSource.getTier() == tier) { + if(categoryViewForLayout.getTitle() != null + && !categoryViewForLayout.getTitle().equals(cSource.getTitle())){ + return categoryViewForLayout; + } + categoryViewForLayout.setTitle(cSource.getTitle()); + categoryViewForLayout.setCount(cSource.getCount()); + categoryViewForLayout.setId(cSource.getId()); + categoryViewForLayout.setCOrder(cSource.getCOrder()); + categoryViewForLayout.setPOrder(cSource.getPOrder()); + source.remove(0); + } else if (cSource.getTier() > tier) { + CategoryViewForLayout sub = recursiveBuildFromCategoryDto(tier + 1, source); + categoryViewForLayout.getCategoryTCountList().add(sub); + } else { + return categoryViewForLayout; + } + } + return categoryViewForLayout; + } + + private CategoryViewForLayout() { + } +} diff --git a/src/main/java/myblog/blog/category/controller/CategoryController.java b/src/main/java/myblog/blog/category/controller/CategoryController.java deleted file mode 100644 index 1e87203..0000000 --- a/src/main/java/myblog/blog/category/controller/CategoryController.java +++ /dev/null @@ -1,77 +0,0 @@ -package myblog.blog.category.controller; - -import lombok.RequiredArgsConstructor; -import myblog.blog.shared.exception.CustomFormException; -import myblog.blog.shared.exception.ListValidator; -import myblog.blog.category.dto.CategoryForView; -import myblog.blog.category.dto.CategorySimpleView; -import myblog.blog.category.service.CategoryService; -import myblog.blog.comment.dto.CommentDtoForLayout; -import myblog.blog.comment.service.CommentService; -import org.modelmapper.ModelMapper; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.Errors; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -@Controller -@RequiredArgsConstructor -public class CategoryController { - - private final CategoryService categoryService; - private final CommentService commentService; - private final ModelMapper modelMapper; - private final ListValidator listValidator; - - /* - - 카테고리 수정폼 조회 - */ - @GetMapping("/edit/category") - public String editCategoryForm(Model model) { - - // DTO 매핑 전처리 - List categoryList = categoryService.getCategorytCountList(); - List copyList = cloneList(categoryList); - copyList.remove(0); - CategoryForView categoryForView = CategoryForView.createCategory(categoryList); - List comments = commentService.recentCommentList(); - // - - 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, Errors errors) { - // List DTO 검증을 위한 커스텀 validator - listValidator.validate(categoryList, errors); - // 유효성 검사 - if (errors.hasErrors()) { - throw new CustomFormException(Objects.requireNonNull(errors.getFieldError()).getDefaultMessage()); - } - - categoryService.changeCategory(categoryList); - return "변경 성공"; - } - private List cloneList(List categoryList) { - return categoryList - .stream() - .map(categoryNormalDto -> - modelMapper.map(categoryNormalDto, CategorySimpleView.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 4c980c5..4c03db7 100644 --- a/src/main/java/myblog/blog/category/domain/Category.java +++ b/src/main/java/myblog/blog/category/domain/Category.java @@ -3,8 +3,9 @@ package myblog.blog.category.domain; import lombok.Builder; import lombok.Getter; -import myblog.blog.article.domain.Article; + import myblog.blog.shared.BasicEntity; +import myblog.blog.article.domain.Article; import javax.persistence.*; import java.util.ArrayList; diff --git a/src/main/java/myblog/blog/category/dto/CategoryForView.java b/src/main/java/myblog/blog/category/dto/CategoryForView.java deleted file mode 100644 index 8424b62..0000000 --- a/src/main/java/myblog/blog/category/dto/CategoryForView.java +++ /dev/null @@ -1,67 +0,0 @@ -package myblog.blog.category.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -/* - - 레이아웃용 트리구조 카테고리 리스트 -*/ -@Getter -@Setter -public class CategoryForView { - - private int count; - private String title; - private Long id; - private int pOrder; - private int cOrder; - // 트리구조를 갖기 위한 리스트 - private List categoryTCountList = new ArrayList<>(); - - /* - - 스태틱 생성 메서드 - */ - public static CategoryForView createCategory(List crList) { - return recursiveBuildFromCategoryDto(0, crList); - } - - /* - - 재귀호출로 트리구조 생성 - 1. DTO객체 생성후 소스를 큐처리로 순차적 매핑 - 2. Depth 변화시 재귀 호출 / 재귀 탈출 - 3. 탈출시 상위 카테고리 list로 삽입하여 트리구조 작성 - */ - private static CategoryForView recursiveBuildFromCategoryDto(int tier, List source) { - - CategoryForView categoryForView = new CategoryForView(); - - while (!source.isEmpty()) { - CategorySimpleView cSource = source.get(0); - - if (cSource.getTier() == tier) { - if(categoryForView.getTitle() != null - && !categoryForView.getTitle().equals(cSource.getTitle())){ - return categoryForView; - } - categoryForView.setTitle(cSource.getTitle()); - categoryForView.setCount(cSource.getCount()); - categoryForView.setId(cSource.getId()); - categoryForView.setCOrder(cSource.getCOrder()); - categoryForView.setPOrder(cSource.getPOrder()); - 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/CategorySimpleView.java b/src/main/java/myblog/blog/category/dto/CategorySimpleView.java deleted file mode 100644 index e7d5c83..0000000 --- a/src/main/java/myblog/blog/category/dto/CategorySimpleView.java +++ /dev/null @@ -1,25 +0,0 @@ -package myblog.blog.category.dto; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import javax.validation.constraints.NotBlank; - -/* - - 범용 카테고리 DTO -*/ -@Getter -@Setter -@ToString -public class CategorySimpleView { - - private Long id; - @NotBlank(message = "카테고리명은 공백일 수 없습니다.") - private String title; - private int tier; - private int count; - private int pOrder; - private int cOrder; - -} diff --git a/src/main/java/myblog/blog/member/controller/MemberController.java b/src/main/java/myblog/blog/member/controller/MemberController.java index f353736..d04ec30 100644 --- a/src/main/java/myblog/blog/member/controller/MemberController.java +++ b/src/main/java/myblog/blog/member/controller/MemberController.java @@ -1,9 +1,8 @@ package myblog.blog.member.controller; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import myblog.blog.category.dto.CategoryForView; -import myblog.blog.category.service.CategoryService; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; +import myblog.blog.category.appliacation.CategoryService; import myblog.blog.comment.dto.CommentDtoForLayout; import myblog.blog.comment.service.CommentService; import org.springframework.stereotype.Controller; @@ -12,7 +11,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; -import java.util.stream.Collectors; @Controller @RequiredArgsConstructor @@ -34,11 +32,11 @@ public class MemberController { } // 레이아웃 DTO 전처리 - CategoryForView categoryForView = categoryService.getCategoryForView(); + CategoryViewForLayout categoryViewForLayout = categoryService.getCategoryViewForLayout(); List comments = commentService.recentCommentList(); // - model.addAttribute("category",categoryForView); + model.addAttribute("category", categoryViewForLayout); model.addAttribute("commentsList", comments); return "login"; diff --git a/src/main/java/myblog/blog/seo/application/SiteMapService.java b/src/main/java/myblog/blog/seo/application/SiteMapService.java index 013859c..3f46f99 100644 --- a/src/main/java/myblog/blog/seo/application/SiteMapService.java +++ b/src/main/java/myblog/blog/seo/application/SiteMapService.java @@ -9,7 +9,7 @@ import lombok.RequiredArgsConstructor; import myblog.blog.article.domain.Article; import myblog.blog.category.domain.Category; -import myblog.blog.category.service.CategoryService; +import myblog.blog.category.appliacation.CategoryService; import org.jdom2.*; import org.jdom2.output.*; diff --git a/src/main/java/myblog/blog/shared/queries/LayoutRenderingQueries.java b/src/main/java/myblog/blog/shared/queries/LayoutRenderingQueries.java index 757044f..27c3611 100644 --- a/src/main/java/myblog/blog/shared/queries/LayoutRenderingQueries.java +++ b/src/main/java/myblog/blog/shared/queries/LayoutRenderingQueries.java @@ -1,8 +1,8 @@ package myblog.blog.shared.queries; import lombok.RequiredArgsConstructor; -import myblog.blog.category.dto.CategoryForView; -import myblog.blog.category.service.CategoryService; +import myblog.blog.category.appliacation.port.response.CategoryViewForLayout; +import myblog.blog.category.appliacation.CategoryService; import myblog.blog.comment.dto.CommentDtoForLayout; import myblog.blog.comment.service.CommentService; import org.springframework.stereotype.Component; @@ -21,9 +21,9 @@ public class LayoutRenderingQueries { - 레이아웃에 필요한 모델 담기 */ public void AddLayoutTo(Model model) { - CategoryForView categoryForView = categoryService.getCategoryForView(); + CategoryViewForLayout categoryViewForLayout = categoryService.getCategoryViewForLayout(); List comments = commentService.recentCommentList(); - model.addAttribute("category", categoryForView); + model.addAttribute("category", categoryViewForLayout); model.addAttribute("commentsList", comments); } } diff --git a/src/main/resources/templates/layout/sideBar.html b/src/main/resources/templates/layout/sideBar.html index 3f4ca2d..cd97c13 100644 --- a/src/main/resources/templates/layout/sideBar.html +++ b/src/main/resources/templates/layout/sideBar.html @@ -47,7 +47,7 @@
  • 토탈
  • - +
  • diff --git a/src/test/kotlin/myblog/blog/sitemap/SiteMapServiceTests.kt b/src/test/kotlin/myblog/blog/sitemap/SiteMapServiceTests.kt index 452dc3c..8b672bb 100644 --- a/src/test/kotlin/myblog/blog/sitemap/SiteMapServiceTests.kt +++ b/src/test/kotlin/myblog/blog/sitemap/SiteMapServiceTests.kt @@ -4,7 +4,7 @@ import myblog.blog.article.domain.Article import myblog.blog.article.application.ArticleService import myblog.blog.seo.application.SiteMapService import myblog.blog.category.domain.Category -import myblog.blog.category.service.CategoryService +import myblog.blog.category.appliacation.CategoryService import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test //import org.junit.Test