diff --git a/src/main/java/myblog/blog/article/controller/ArticleController.java b/src/main/java/myblog/blog/article/controller/ArticleController.java index 721170c..7fa23b2 100644 --- a/src/main/java/myblog/blog/article/controller/ArticleController.java +++ b/src/main/java/myblog/blog/article/controller/ArticleController.java @@ -23,6 +23,7 @@ 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; @@ -54,7 +55,7 @@ public class ArticleController { 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); @@ -62,7 +63,7 @@ public class ArticleController { tagsService .findAllTags() .stream() - .map(c -> new TagsDto(c.getName())) + .map(tag -> new TagsDto(tag.getName())) .collect(Collectors.toList()); model.addAttribute("tagsInput", tagsForInput); @@ -81,13 +82,15 @@ public class ArticleController { return "article/articleWriteForm"; } + + /* + - 넘어온 articleForm을 저장 + */ @PostMapping("article/write") @Transactional - public String writeArticle(@ModelAttribute ArticleForm articleForm, Authentication authentication) { + public String writeArticle(ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) { - PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal(); - articleForm.setMemberId(principal.getMemberId()); - Article article = articleService.writeArticle(articleForm); + Article article = articleService.writeArticle(articleForm, principal.getMember()); // articleService.pushArticleToGithub(article); tempArticleService.deleteTemp(); @@ -311,10 +314,8 @@ public class ArticleController { @PostMapping("/article/edit") @Transactional public String editArticle(@RequestParam Long articleId, - @ModelAttribute ArticleForm articleForm, Authentication authentication) { + @ModelAttribute ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) { - PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal(); - articleForm.setMemberId(principal.getMemberId()); articleService.editArticle(articleId, articleForm); diff --git a/src/main/java/myblog/blog/article/domain/Article.java b/src/main/java/myblog/blog/article/domain/Article.java index 18ef4cf..4666cd9 100644 --- a/src/main/java/myblog/blog/article/domain/Article.java +++ b/src/main/java/myblog/blog/article/domain/Article.java @@ -30,16 +30,20 @@ public class Article extends BasicEntity { @Column(nullable = false) private String title; + @Column(nullable = false, length = 10000) private String content; + @Column(columnDefinition = "bigint default 0",nullable = false) private Long hit; + private String toc; + @Column(nullable = false) private String thumbnailUrl; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + @JoinColumn(name = "member_id", nullable = false) private Member member; @OneToMany(mappedBy = "article", cascade = CascadeType.REMOVE, orphanRemoval = true) @@ -47,7 +51,7 @@ public class Article extends BasicEntity { private List articleTagLists = new ArrayList<>(); @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "category_id") + @JoinColumn(name = "category_id", nullable = false) private Category category; @OneToMany(mappedBy = "article", cascade = CascadeType.REMOVE, orphanRemoval = true) @@ -64,10 +68,12 @@ public class Article extends BasicEntity { this.toc = toc; this.member = member; this.thumbnailUrl = thumbnailUrl; - this.hit = 0L; this.category = category; + this.hit = 0L; } + // 비지니스 로직 // + public void addHit(){ this.hit++; } diff --git a/src/main/java/myblog/blog/article/dto/ArticleForm.java b/src/main/java/myblog/blog/article/dto/ArticleForm.java index 9d68f7f..2620244 100644 --- a/src/main/java/myblog/blog/article/dto/ArticleForm.java +++ b/src/main/java/myblog/blog/article/dto/ArticleForm.java @@ -2,6 +2,7 @@ package myblog.blog.article.dto; import lombok.Getter; import lombok.Setter; +import myblog.blog.article.domain.Article; import javax.validation.constraints.NotBlank; import java.util.Objects; @@ -15,13 +16,12 @@ public class ArticleForm { @NotBlank private String content; private String toc; - @NotBlank - private Long memberId; private String thumbnailUrl; + @NotBlank private String category; + @NotBlank private String tags; - } diff --git a/src/main/java/myblog/blog/article/repository/ArticleRepository.java b/src/main/java/myblog/blog/article/repository/ArticleRepository.java index 403d0a2..804c2ed 100644 --- a/src/main/java/myblog/blog/article/repository/ArticleRepository.java +++ b/src/main/java/myblog/blog/article/repository/ArticleRepository.java @@ -8,34 +8,51 @@ import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; - import java.util.List; - public interface ArticleRepository extends JpaRepository { + /* + - 최대 6개까지 조회수가 높은 게시물 가져오기 + */ List
findTop6ByOrderByHitDesc(); + /* + - 카테고리별 최대 6개 최신 게시물순으로 가져오기 페이징처리 x + */ + List
findTop6ByCategoryOrderByIdDesc(Category category); + + /* + - 가장 최신 게시물순으로 페이징처리해서 Slice로 가져오기 + 토탈 카운트 쿼리 안날라감 + */ Slice
findByOrderByIdDesc(Pageable pageable); + /* + - 카테고리별(하위 카테고리) 페이징 처리해서 최신게시물순으로 Slice 가져오기 + */ @Query("select a " + "from Article a " + "inner join a.category c " + "where c.title=:category " + "order by a.id desc ") - Slice
findByCategoryOrderByIdDesc(Pageable pageable, @Param("category") String category); + Slice
findBySubCategoryOrderByIdDesc(Pageable pageable, @Param("category") String category); + /* + - 카테고리별(상위 카테고리) 페이징 처리해서 최신게시물순으로 Slice 가져오기 + */ @Query("select a " + "from Article a " + "inner join a.category c " + "left join c.parents p " + "where p.title=:category " + "order by a.id desc ") - Slice
findByT1CategoryOrderByIdDesc(Pageable pageable, @Param("category") String category); - - Slice
findAllByOrderByIdDesc(Pageable pageable); - + Slice
findBySupCategoryOrderByIdDesc(Pageable pageable, @Param("category") String category); + /* + - 카테고리와 태그를 모두 페치조인해서 아티클 조회 + - 아티클 세부 조회용도 + */ @Query("select a " + "from Article a " + "join fetch a.category " + @@ -44,15 +61,10 @@ public interface ArticleRepository extends JpaRepository { "where a.id =:id ") Article findArticleByIdFetchCategoryAndTags(@Param("id") Long articleId); - @Query("select a " + - "from Article a " + - "join fetch a.category " + - "join fetch a.articleTagLists " + - "where a.id =:id ") - Article findArticleByIdFetchCategoryAndArticleTagLists(@Param("id") Long articleId); - - List
findTop6ByCategoryOrderByIdDesc(Category category); - + /* + - 태그별 아티클 페이징 처리해서 조회 + - 토탈 카운트 쿼리 o + */ @Query("select a " + "from Article a " + "join a.articleTagLists at " + @@ -61,7 +73,10 @@ public interface ArticleRepository extends JpaRepository { "order by a.id desc ") Page
findAllByArticleTagsOrderById(Pageable pageable, @Param("tag") String tag); - + /* + - 키워드별 아티클 페이징 처리해서 조회 + - 토탈 카운트 쿼리 o + */ @Query("select a " + "from Article a " + "where a.title like %:keyword% " + @@ -69,6 +84,4 @@ public interface ArticleRepository extends JpaRepository { "order by a.id desc ") Page
findAllByKeywordOrderById(Pageable pageable, @Param("keyword") String keyword); - - } diff --git a/src/main/java/myblog/blog/article/repository/NaArticleRepository.java b/src/main/java/myblog/blog/article/repository/NaArticleRepository.java index d9ffad6..9b2dca1 100644 --- a/src/main/java/myblog/blog/article/repository/NaArticleRepository.java +++ b/src/main/java/myblog/blog/article/repository/NaArticleRepository.java @@ -6,7 +6,9 @@ import org.apache.ibatis.annotations.Mapper; @Mapper public interface NaArticleRepository { - + /* + - 삭제처리시 불필요한 조회방지 위해 네이티브 쿼리 사용 cascade delete 처리 + */ @Delete("delete from article " + "where article_id = #{articleId} ") void deleteArticle(Long articleId); diff --git a/src/main/java/myblog/blog/article/repository/TempArticleRepository.java b/src/main/java/myblog/blog/article/repository/TempArticleRepository.java index 174089a..b81732d 100644 --- a/src/main/java/myblog/blog/article/repository/TempArticleRepository.java +++ b/src/main/java/myblog/blog/article/repository/TempArticleRepository.java @@ -3,5 +3,6 @@ package myblog.blog.article.repository; import myblog.blog.article.domain.TempArticle; import org.springframework.data.jpa.repository.JpaRepository; +// 기본 JPA 메소드 사용 public interface TempArticleRepository extends JpaRepository { } diff --git a/src/main/java/myblog/blog/article/service/ArticleService.java b/src/main/java/myblog/blog/article/service/ArticleService.java index 7e09cb4..32f4e14 100644 --- a/src/main/java/myblog/blog/article/service/ArticleService.java +++ b/src/main/java/myblog/blog/article/service/ArticleService.java @@ -22,10 +22,10 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; - import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @Transactional @@ -37,34 +37,49 @@ public class ArticleService { @Value("${git.repo}") private String gitRepo; - private final ArticleRepository articleRepository; - private final MemberRepository memberRepository; private final TagsService tagsService; private final CategoryService categoryService; - private final ModelMapper modelMapper; + private final ArticleRepository articleRepository; private final NaArticleRepository naArticleRepository; + private final ModelMapper modelMapper; - public Article writeArticle(ArticleForm articleDto) { - - Article newArticle = articleFrom(articleDto); + /* + - 아티클 작성 로직 + */ + public Article writeArticle(ArticleForm articleDto, Member writer) { + Article newArticle = articleFrom(articleDto, writer); articleRepository.save(newArticle); tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle); return newArticle; - } + /* + - 아티클 수정 로직 + */ + public void editArticle(Long articleId, ArticleForm articleForm) { + + Article article = articleRepository.findById(articleId).get(); + Category category = categoryService.findCategory(articleForm.getCategory()); + tagsService.deleteArticleTags(article); + tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article); + articleForm.setThumbnailUrl(makeDefaultThumb(articleForm.getThumbnailUrl())); + + // 더티 체킹으로 업데이트 + article.editArticle(articleForm,category); + } + + /* + - 메인화면 위한 인기 아티클 6개 목록 가져오기 + */ public List getPopularArticles() { List
top6ByOrderByHitDesc = articleRepository.findTop6ByOrderByHitDesc(); - List articles = new ArrayList<>(); - - for (Article article : top6ByOrderByHitDesc) { - articles.add(modelMapper.map(article, ArticleDtoForMain.class)); - } - - + List articles = top6ByOrderByHitDesc.stream() + .map(article -> modelMapper.map(article, ArticleDtoForMain.class)) + .collect(Collectors.toList()); + return articles; } @@ -101,19 +116,19 @@ public class ArticleService { Slice
articles = null; - if (tier.intValue() == 0) { + if (tier.equals(0)) { articles = articleRepository - .findAllByOrderByIdDesc( - PageRequest.of(pageResolver(page), 5)); - } else if (tier.intValue() == 1) { + .findByOrderByIdDesc( + PageRequest.of(pageResolve(page), 5)); + } else if (tier.equals(1)) { articles = articleRepository - .findByT1CategoryOrderByIdDesc( - PageRequest.of(pageResolver(page), 5), category); + .findBySupCategoryOrderByIdDesc( + PageRequest.of(pageResolve(page), 5), category); } else { articles = articleRepository - .findByCategoryOrderByIdDesc( - PageRequest.of(pageResolver(page), 5), category); + .findBySubCategoryOrderByIdDesc( + PageRequest.of(pageResolve(page), 5), category); } return articles.map(article -> modelMapper @@ -132,21 +147,10 @@ public class ArticleService { public Article getArticleForEdit(Long articleId){ - return articleRepository.findArticleByIdFetchCategoryAndArticleTagLists(articleId); + return articleRepository.findArticleByIdFetchCategoryAndTags(articleId); } - public void editArticle(Long articleId, ArticleForm articleForm) { - - Article article = articleRepository.findById(articleId).get(); - Category category = categoryService.findCategory(articleForm.getCategory()); - tagsService.deleteArticleTags(article); - tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article); - articleForm.setThumbnailUrl(makeDefaultThumb(articleForm.getThumbnailUrl())); - - article.editArticle(articleForm,category); - - } public void deleteArticle(Long articleId) { @@ -163,7 +167,7 @@ public class ArticleService { Page
articles = articleRepository - .findAllByArticleTagsOrderById(PageRequest.of(pageResolver(page), 5), tag); + .findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag); return articles; @@ -173,7 +177,7 @@ public class ArticleService { Page
articles = articleRepository - .findAllByKeywordOrderById(PageRequest.of(pageResolver(page),5), keyword); + .findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword); return articles; } @@ -194,7 +198,6 @@ public class ArticleService { } - private String makeDefaultThumb(String thumbnailUrl) { // 메시지로 올리기 String defaultThumbUrl = "https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg"; @@ -205,25 +208,21 @@ public class ArticleService { return thumbnailUrl; } - private int pageResolver(Integer rawPage) { + private int pageResolve(Integer rawPage) { if (rawPage == null || rawPage == 1) { return 0; } else return rawPage - 1; } - private Article articleFrom(ArticleForm articleDto) { - Member member = - memberRepository.findById(articleDto.getMemberId()).orElseThrow(() -> { - throw new IllegalArgumentException("작성자를 확인할 수 없습니다"); - }); + private Article articleFrom(ArticleForm articleDto, Member writer) { return Article.builder() .title(articleDto.getTitle()) .content(articleDto.getContent()) .toc(articleDto.getToc()) + .member(writer) .thumbnailUrl(makeDefaultThumb(articleDto.getThumbnailUrl())) .category(categoryService.findCategory(articleDto.getCategory())) - .member(member) .build(); } diff --git a/src/main/java/myblog/blog/article/service/TempArticleService.java b/src/main/java/myblog/blog/article/service/TempArticleService.java index 22109a3..6075275 100644 --- a/src/main/java/myblog/blog/article/service/TempArticleService.java +++ b/src/main/java/myblog/blog/article/service/TempArticleService.java @@ -5,7 +5,6 @@ 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; @Service @@ -14,23 +13,28 @@ public class TempArticleService { private final TempArticleRepository tempArticleRepository; - public TempArticle saveTemp(TempArticleDto tempArticleDto){ + /* + - 자동 저장 로직 + - ID값 고정으로 머지를 작동시켜 임시글 DB에 1개 유지 + */ + public void saveTemp(TempArticleDto tempArticleDto){ TempArticle tempArticle = new TempArticle(tempArticleDto.getContent()); - - // 머지로 쿼리 한번만 날리기 tempArticleRepository.save(tempArticle); - return tempArticle; - } + /* + - 임시글 가져오기 + */ public Optional getTempArticle(){ return tempArticleRepository.findById(1L); - } + /* + - 임시글 삭제 + */ public void deleteTemp(){ Optional deleteArticle = tempArticleRepository.findById(1L); deleteArticle.ifPresent(tempArticleRepository::delete); diff --git a/src/main/java/myblog/blog/category/service/CategoryService.java b/src/main/java/myblog/blog/category/service/CategoryService.java index 3d7936e..cba5b2b 100644 --- a/src/main/java/myblog/blog/category/service/CategoryService.java +++ b/src/main/java/myblog/blog/category/service/CategoryService.java @@ -168,7 +168,9 @@ public class CategoryService { } - @PostConstruct + + + @PostConstruct public void insertCategory() { Category category0 = Category.builder() diff --git a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java index b78d459..890c86e 100644 --- a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java +++ b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java @@ -5,10 +5,7 @@ import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Component; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -19,7 +16,7 @@ public class UserInfoFactory { private final static Map> userInfoFactoryMap; static { - userInfoFactoryMap = new HashMap<>(); + userInfoFactoryMap = new EnumMap<>(ProviderType.class); userInfoFactoryMap.put(ProviderType.GOOGLE, GoogleUserInfo::new); userInfoFactoryMap.put(ProviderType.FACEBOOK, FacebookUserInfo::new); userInfoFactoryMap.put(ProviderType.KAKAO, KakaoUserInfo::new); diff --git a/src/main/resources/templates/article/articleListByTag.html b/src/main/resources/templates/article/articleListByTag.html index 28707cd..3f5c5c5 100644 --- a/src/main/resources/templates/article/articleListByTag.html +++ b/src/main/resources/templates/article/articleListByTag.html @@ -6,25 +6,25 @@ - + - - + + - - + + - + - - - + + + @@ -51,7 +51,7 @@
-

+


@@ -85,7 +85,7 @@