Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[댓글 기능] 베디 미션 제출합니다. #105

Merged
merged 119 commits into from
Aug 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
21df515
remove : 기존 로직 삭제
vsh123 Jul 16, 2019
52f4012
feat : MainController 구현
vsh123 Jul 16, 2019
2159261
feat: Article 구현
vsh123 Jul 16, 2019
2fb0c09
feat: ArticleReposiory 구현
vsh123 Jul 16, 2019
95fd8d0
feat: ArticleController.write()
vsh123 Jul 16, 2019
1cad0c7
feat: ArticleController.show()
vsh123 Jul 16, 2019
44dc298
feat: ArticleController.editForm()
vsh123 Jul 16, 2019
a9fda55
ArticleController.edit()
vsh123 Jul 16, 2019
0386658
feat: ArticleController.delete()
vsh123 Jul 16, 2019
0b5b1bd
feat: ArticleController.writeForm()
vsh123 Jul 16, 2019
cd21f32
rename: ArticleControllerTests
vsh123 Jul 16, 2019
821ecce
feat: MainController Aritcles 조회
vsh123 Jul 16, 2019
908ed88
refactor: @transactional 추가
vsh123 Jul 17, 2019
7d3a682
rename: template 이름 변경
vsh123 Jul 17, 2019
49bf7b7
feat: UserController.signupForm()
vsh123 Jul 17, 2019
c0ee6a0
feat: 회원가입 유효성 검사
vsh123 Jul 17, 2019
38a5156
feat: 회원가입 이메일 중복, 패스워드 중복 검사
vsh123 Jul 17, 2019
946b966
feat: 회원가입 프론트 구현
vsh123 Jul 17, 2019
85d66f2
feat: 로그인 구현
vsh123 Jul 17, 2019
24d9d5f
feat: login 구현
vsh123 Jul 17, 2019
eff17e5
feat: 로그아웃
vsh123 Jul 17, 2019
8e92b91
feat: 회원 목록 조회
vsh123 Jul 18, 2019
5924c54
feat: 마이페이지, 수정폼 구현
vsh123 Jul 18, 2019
c5f9692
feat: 회원정보 수정
vsh123 Jul 18, 2019
d98f66c
feat: 회원탈퇴
vsh123 Jul 18, 2019
a7d29a4
refactor: UserDto
vsh123 Jul 18, 2019
724fc14
refactor: 테스트코드 중복제거
vsh123 Jul 18, 2019
39d11f7
feat: interceptor
vsh123 Jul 18, 2019
9fafd01
feat: logger 추가
dpudpu Jul 19, 2019
07aa7d2
test: LoginInterceptor 세션 테스트 추가
dpudpu Jul 19, 2019
da27b3f
feat: jupiter-params 추가
dpudpu Jul 19, 2019
f8eaf0f
refactor: html 중복 제거
dpudpu Jul 19, 2019
0d41c3c
feat: Articles Paging 구현
dpudpu Jul 19, 2019
64c43a4
test: UserControllerTest 세션 테스트 추가
dpudpu Jul 20, 2019
de6c1c0
refactor: uri 상수 처리
dpudpu Jul 20, 2019
ee00f77
test: UserServiceTest
dpudpu Jul 20, 2019
f5d62fa
style: 포맷팅
dpudpu Jul 20, 2019
81b5335
refactor: 샘플데이터 수정
dpudpu Jul 20, 2019
8ff0ca3
refactor: user session 상수 처리
dpudpu Jul 20, 2019
39ae181
rename: ValidSignupException -> ValidUserException
dpudpu Jul 20, 2019
5502bff
refactor: LoginInterceptor 매직넘버 상수처리
dpudpu Jul 21, 2019
80d6306
fix: logout AuthInterceptor에서 튕기는 문제 해결
dpudpu Jul 21, 2019
654fd03
style: build.gradle 정리
dpudpu Jul 21, 2019
1fc5673
refactor: errorAttributes() 를 ErrorConfig.class 로 분리
dpudpu Jul 21, 2019
296c66f
refactor: uri 상수 제거
dpudpu Jul 21, 2019
fe563ad
refactor: Article.update() 수정
dpudpu Jul 21, 2019
8db3f71
refactor: test 코드 필요 없는 어노테이션 제거
dpudpu Jul 21, 2019
28a1191
refactor: 인터셉터 클래스 commons 패키지에서 interceptor 로 변경
dpudpu Jul 21, 2019
86143aa
refactor: BeanUtils 제거, User setter 제거
dpudpu Jul 21, 2019
1a9bcb1
refactor: MainController.index Pageable 파라미터로 변경
dpudpu Jul 21, 2019
ff6f559
feat: User.authenticate 추가
dpudpu Jul 21, 2019
95d4f77
refactor: ControllerTests 에서 service 빈 제거하고 api를 직접 호출하는 방식으로 변경
dpudpu Jul 22, 2019
c5c801e
refactor: user session 처리용 UserSession 생성
dpudpu Jul 22, 2019
5c27961
style: 포맷팅
dpudpu Jul 22, 2019
fbf4518
feat: User 유효성검사 추가
dpudpu Jul 22, 2019
edf822e
refactor: Article @Builder 방식 변경
dpudpu Jul 22, 2019
864ba07
fix: LoginInterceptor 적용 패스 및 테스트코드 추가
dpudpu Jul 22, 2019
53bf60c
fix: UserDto.Update.toUser()
dpudpu Jul 22, 2019
9e31983
fix: test코드 session 추가
dpudpu Jul 22, 2019
bbe330e
docs: application.properties spring.jpa.show-sql=true 제거
dpudpu Jul 22, 2019
56e0055
refactor: UserDto.Update.toUser() 방식 수정
dpudpu Jul 22, 2019
09a288b
refactor: UserDto.Update에 id 필드 추가
dpudpu Jul 23, 2019
dde12ec
refactor: User unique 설정 방식 변경
dpudpu Jul 23, 2019
945cb29
refactor: SessionId 상수 UserSession 으로 이동
dpudpu Jul 23, 2019
3c6c1ba
refactor: UserService.login() UserSession 반환 하는 부분 수정
dpudpu Jul 23, 2019
13c0e9e
feat: UserSessionArgumentResolver
dpudpu Jul 23, 2019
3fd3101
refactor: Entity 기본생성자 private으로 변경
dpudpu Jul 23, 2019
a931277
Merge remote-tracking branch 'upstream/dpudpu' into dpudpu
dpudpu Jul 25, 2019
41598ee
remove : 모두 삭제하고 처음부터 시작
dpudpu Jul 26, 2019
cdf240c
feat: User 유효성 검사
dpudpu Jul 26, 2019
8230130
Revert "feat: User 유효성 검사"
dpudpu Jul 27, 2019
5896d72
Revert "remove : 모두 삭제하고 처음부터 시작"
dpudpu Jul 27, 2019
c42eb49
feat: UserValidator
dpudpu Jul 27, 2019
80f8fae
refactor: User.authenticate() 에서 로그인 검증하게 변경
dpudpu Jul 27, 2019
65419e3
refactor: LoginInterceptor 역할 분리
dpudpu Jul 27, 2019
3bec3d9
feat: Article -> User 단방향 관계 추가 + @Column 설정 추가
dpudpu Jul 28, 2019
61907a5
feat: BaseControllerTests 공통 기능(회원가입, getJSession) 추상클래스 추가
dpudpu Jul 28, 2019
5b18c28
feat: Article 글쓴이 추가
dpudpu Jul 28, 2019
07784bd
feat: app-head.html 프론트 디자인 변경
dpudpu Jul 28, 2019
5bfb005
feat: Article 수정시 작성자 아닌 경우 예외처리
dpudpu Jul 28, 2019
7010806
feat: ArticleController Delete 작성자 검증 추가
dpudpu Jul 28, 2019
6884a33
refactor: MainController 패키지 수정
dpudpu Jul 28, 2019
98d5376
rename: NotLoginInterceptor -> GuestInterceptor
dpudpu Jul 28, 2019
1a628db
refactor: Article.User nullable = false 로 변경
dpudpu Jul 29, 2019
281146e
feat: AuthExceptionAdvice 작성자 예외처리 해주는
dpudpu Jul 29, 2019
4c06a15
fix: @ExceptionHandler(AuthException.class) Illigal인거 수정
dpudpu Jul 29, 2019
c4873bb
rename: AuthExceptionAdvice -> ExceptionAdvice
dpudpu Jul 29, 2019
b4b1911
style: ArticleService 메소드 순서 변경
dpudpu Jul 29, 2019
fc61a82
feat: Comment entity
dpudpu Jul 29, 2019
dfbc68f
rename: Article.updateDate -> modifiedDate
dpudpu Jul 29, 2019
a4ff613
feat: CommentRepository
dpudpu Jul 29, 2019
d1ed186
feat: Comment 작성
dpudpu Jul 29, 2019
c9d3f49
refactor: Comment 구조 Article 하위로 변경
dpudpu Jul 29, 2019
ce61d44
test: CommentControllerTests 수정, 작성 테스트코드 추가
dpudpu Jul 29, 2019
20f29ce
feat: Comment 수정, 삭제 다른 작성자 접근 금지 추가
dpudpu Jul 29, 2019
72fddc8
feat: ArticleController.show() 댓글 목록 보여주기 프론트까지 완료
dpudpu Jul 29, 2019
f36cad4
feat: article.html 댓글 수정 구현
dpudpu Jul 29, 2019
69522d9
refactor: Comment 엔티티 어노테이션 수정
dpudpu Jul 29, 2019
ee0cadd
feat: error.html 에러페이지 추가
dpudpu Jul 29, 2019
da5c8c1
refactor: ExceptionAdvice.handleAuthException() 수정
dpudpu Jul 29, 2019
04d6f7f
feat: ExceptionAdvice.handleRuntimeException()
dpudpu Jul 29, 2019
fadad5b
refactor: CommentService 중복 제거
dpudpu Jul 29, 2019
51d4718
style: 포맷팅
dpudpu Jul 29, 2019
2be369e
refactor: ExceptionAdvice RuntimeException -> IllegalArgumentExceptio…
dpudpu Jul 30, 2019
e72d96c
feat: Article, Comment 양방향 관계 추가
dpudpu Jul 30, 2019
8b0bea5
feat: CommentDto.Update 추가
dpudpu Jul 30, 2019
6e23087
test: CommentServiceTest
dpudpu Jul 30, 2019
1832271
Revert "feat: CommentDto.Update 추가"
dpudpu Jul 30, 2019
eb21053
refactor: ExceptionAdvice.class log.info -> log.error
dpudpu Jul 30, 2019
a5f4bf3
refactor: 작성자 확인 후 예외처리 Comment, Article 에게 위임
dpudpu Jul 31, 2019
72d7366
refactor: Comment, Article @ManyToOne(fetch = FetchType.LAZY) 추가
dpudpu Jul 31, 2019
a35234b
refactor: 예외처리 메시지 영어에서 한글로 수정
dpudpu Jul 31, 2019
e0481ea
test: User.authenticate() 테스트 추가
dpudpu Jul 31, 2019
0e18a79
refactor: Comment.User FetchType.LAZY 제거
dpudpu Jul 31, 2019
26035ce
refactor: ValidUserException log.debug -> log.error 로 변경
dpudpu Jul 31, 2019
fc3a565
feat: BaseEntity (@MappedSuperclass) 구현
dpudpu Jul 31, 2019
3d47ce1
feat: ArticleDto 추가
dpudpu Jul 31, 2019
9c27bb1
refactor: 작성자 확인 isWrittenBy() 엔티티가 아닌 @Id로 확인
dpudpu Jul 31, 2019
1715714
refactor: Article.getComments() Collections.unmodifiableList() 추가
dpudpu Aug 1, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
## TODO

- 게시글 생성
- 게시글 조회
- 게시글 수정
- 게시글 삭제
- HTML 중복제거
- 정적 파일 수정 시 재시작 하지 않고 변경사항 반영하기
- class 파일 수정 시 자동으로 재시작 하기

- 작성자 아닌 경우 예외처리용 ControllerAdvice
- 작성자 확인 로직 중복 처리
- 댓글

- Service에 userId 대신에 UserSession을 직접 넘기는게 좋을까?
- userId와 articleId가 헷갈린다.
- 하지만 UserSession은 DTO가 아니다.
- 좋은 해결책은?


# 게시글 생성/조회기능 구현하기
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/techcourse/myblog/advice/ExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package techcourse.myblog.advice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import techcourse.myblog.exception.AuthException;

@ControllerAdvice
public class ExceptionAdvice {
private static final Logger log = LoggerFactory.getLogger(ExceptionAdvice.class);

@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(AuthException.class)
public String handleAuthException(Exception e, Model model) {

log.error(e.toString());

model.addAttribute("errorMessage", e.getMessage());
model.addAttribute("path", "/");
return "error";
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public String handleRuntimeException(Exception e, Model model) {

log.error(e.toString());

model.addAttribute("errorMessage", e.getMessage());
model.addAttribute("path", "/");
return "error";
}


}
58 changes: 34 additions & 24 deletions src/main/java/techcourse/myblog/articles/Article.java
Original file line number Diff line number Diff line change
@@ -1,54 +1,64 @@
package techcourse.myblog.articles;

import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import techcourse.myblog.articles.comments.Comment;
import techcourse.myblog.exception.AuthException;
import techcourse.myblog.users.User;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Entity
@Getter
@Setter
@ToString
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode(of = "id")
public class Article {
@NoArgsConstructor
public class Article extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column
@Column(nullable = false, length = 100)
private String title;

@Column
@Lob
private String contents;

@Column
@Column(nullable = false)
private String coverUrl;

@CreatedDate
@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", updatable = false)
private LocalDateTime regDate;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "fk_article_to_user"), nullable = false)
private User author;

@LastModifiedDate
@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime updateDate;
@OneToMany(mappedBy = "article", orphanRemoval = true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch type 에 대해 찾아볼까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@manytoone(fetch = FetchType.LAZY)
반영했습니다. 감사합니다

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

차이점에 대해서도 찾아보았죠 !? 👍

private List<Comment> comments = new ArrayList<>();

@Builder
private Article(final String title, final String contents, final String coverUrl) {
public Article(final String title, final String contents, final String coverUrl, final User author) {
this.title = title;
this.contents = contents;
this.coverUrl = coverUrl;
this.author = author;
}

public List<Comment> getComments() {
return Collections.unmodifiableList(comments);
}

public void update(Article other) {
this.updateDate = LocalDateTime.now();
void update(Article other) {
this.title = other.title;
this.coverUrl = other.coverUrl;
this.contents = other.contents;
}

boolean isWrittenBy(final Long other) {
if (author.getId().equals(other)) {
return true;
}
throw new AuthException("작성자가 아닙니다.");
}
}


42 changes: 29 additions & 13 deletions src/main/java/techcourse/myblog/articles/ArticleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import techcourse.myblog.articles.comments.Comment;
import techcourse.myblog.users.UserSession;

import javax.servlet.http.HttpSession;
import java.util.List;

@Controller
@RequestMapping("/articles")
Expand All @@ -18,34 +23,45 @@ public String writeForm() {
}

@PostMapping
public String write(Article article) {
Article savedArticle = articleService.save(article);
return "redirect:/articles/" + savedArticle.getId();
public String write(UserSession userSession, ArticleDto.Request articleDto) {
Long userId = userSession.getId();
Long articleId = articleService.save(userId, articleDto);

return "redirect:/articles/" + articleId;
}

@GetMapping("/{id}")
public String show(@PathVariable Long id, Model model) {
Article article = articleService.findById(id);
model.addAttribute(article);
final ArticleDto.Response articleDto = articleService.getOne(id);
final List<Comment> comments = articleDto.getComments();

model.addAttribute("article", articleDto);
model.addAttribute("comments", comments);
return "article";
}

@GetMapping("/{id}/edit")
public String editForm(@PathVariable Long id, Model model) {
Article article = articleService.findById(id);
model.addAttribute(article);
public String editForm(@PathVariable Long id, UserSession userSession, Model model) {
Long userId = userSession.getId();
ArticleDto.Response articleDto = articleService.getOne(userId, id);

model.addAttribute("article", articleDto);
return "article-edit";
}

@PutMapping("/{id}")
public String edit(Article editedArticle) {
Article article = articleService.edit(editedArticle);
return "redirect:/articles/" + article.getId();
public String edit(UserSession userSession, ArticleDto.Request editedArticle) {
Long userId = userSession.getId();
Long articleId = articleService.edit(userId, editedArticle);

return "redirect:/articles/" + articleId;
}

@DeleteMapping("/{id}")
public String delete(@PathVariable Long id) {
articleService.deleteById(id);
public String delete(@PathVariable Long id, UserSession userSession) {
Long userId = userSession.getId();
articleService.deleteById(userId, id);

return "redirect:/";
}
}
65 changes: 65 additions & 0 deletions src/main/java/techcourse/myblog/articles/ArticleDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package techcourse.myblog.articles;


import lombok.*;
import techcourse.myblog.articles.comments.Comment;
import techcourse.myblog.users.User;

import java.util.List;

public class ArticleDto {

@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Request {
private Long id;
private String contents;
private String title;
private String coverUrl;

public Article toArticle(final User author) {
return Article.builder()
.contents(contents)
.coverUrl(coverUrl)
.title(title)
.author(author)
.build();
}

public Article toArticle() {
return Article.builder()
.contents(contents)
.coverUrl(coverUrl)
.title(title)
.build();
}
}

@Getter
@NoArgsConstructor
public static class Response {
private Long id;
private String contents;
private String title;
private String coverUrl;
private List<Comment> comments;

Response(Article article) {
id = article.getId();
contents = article.getContents();
title = article.getTitle();
coverUrl = article.getCoverUrl();
}

public static Response createBy(Article article) {
return new ArticleDto.Response(article);
}

public void setComments(List<Comment> comments) {
this.comments = comments;
}
}
}
56 changes: 43 additions & 13 deletions src/main/java/techcourse/myblog/articles/ArticleService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,67 @@
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import techcourse.myblog.users.User;
import techcourse.myblog.users.UserRepository;

@Service
@Transactional
@RequiredArgsConstructor
public class ArticleService {
private final ArticleRepository articleRepository;
private final UserRepository userRepository;

public Article save(Article article) {
return articleRepository.save(article);
}
public Long save(final Long userId, final ArticleDto.Request articleDto) {
User author = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("등록된 유저가 아닙니다."));

@Transactional(readOnly = true)
public Article findById(Long id) {
return articleRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("Can't find Article : " + id));
Article article = articleDto.toArticle(author);

return articleRepository.save(article).getId();
}

public Article edit(Article editedArticle) {
Article article = findById(editedArticle.getId());
public Long edit(final Long userId, final ArticleDto.Request articleDto) {
Article article = findById(articleDto.getId());

article.update(editedArticle);
article.isWrittenBy(userId);
article.update(articleDto.toArticle());

return article;
return article.getId();
}

public void deleteById(Long id) {
articleRepository.deleteById(id);
public void deleteById(Long userId, Long articleId) {
Article article = findById(articleId);

article.isWrittenBy(userId);
articleRepository.deleteById(articleId);
}

@Transactional(readOnly = true)
public Page<Article> findAll(Pageable pageable) {
return articleRepository.findAll(pageable);
}

@Transactional(readOnly = true)
public ArticleDto.Response getOne(Long id) {
final Article article = findById(id);

ArticleDto.Response articleDto = ArticleDto.Response.createBy(article);
articleDto.setComments(article.getComments());
return articleDto;
}

@Transactional(readOnly = true)
public ArticleDto.Response getOne(final Long userId, final Long articleId) {
Article article = findById(articleId);

article.isWrittenBy(userId);
ArticleDto.Response articleDto = ArticleDto.Response.createBy(article);
articleDto.setComments(article.getComments());
return articleDto;
}

private Article findById(Long id) {
return articleRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("없는 글입니다." + id));
}
}
26 changes: 26 additions & 0 deletions src/main/java/techcourse/myblog/articles/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package techcourse.myblog.articles;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import java.time.LocalDateTime;

@Getter
@MappedSuperclass
@EqualsAndHashCode(of = "id")
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@CreationTimestamp
@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", updatable = false)
private LocalDateTime regDate;

@UpdateTimestamp
@Column(columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime modifiedDate;
}
Loading