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

[댓글 기능] 효오 미션 제출합니다. #154

Merged
merged 34 commits into from
Aug 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
908827b
revert: Project revert
hyojaekim Jul 30, 2019
e4de223
[게시글 생성/조회/수정/삭제] 지노 미션 제출합니다. (#45)
qpwoeiru789 Jul 16, 2019
a9efec4
[게시판 회원 기능] 지노 미션 제출합니다. (#79)
qpwoeiru789 Jul 24, 2019
4788a14
[add] comment 클래스
qpwoeiru789 Jul 27, 2019
d83cf82
refactor
qpwoeiru789 Jul 27, 2019
a0db829
refactor
qpwoeiru789 Jul 29, 2019
cc9d448
refactor
qpwoeiru789 Jul 29, 2019
040d229
add articles user 관계 추가
qpwoeiru789 Jul 30, 2019
e4bb05f
delete ArticleConverter 삭제
qpwoeiru789 Jul 30, 2019
85543e4
add 작성자만 글 수정 삭제 기능 추가
qpwoeiru789 Jul 30, 2019
080acc2
add comment 추가 삭제 수정 기능 추가
qpwoeiru789 Jul 30, 2019
394927a
refactor 주석 제거, 어노테이션 추가
qpwoeiru789 Jul 30, 2019
cf8cfb6
refactor 불 필요 코드 제거
qpwoeiru789 Jul 30, 2019
c96f11d
refactor NotExistUserIdException class 수정
qpwoeiru789 Jul 30, 2019
47c5a9f
add NotExistCommentIdException class 수정
qpwoeiru789 Jul 30, 2019
44545fe
fix 예외처리 클래스 수정
qpwoeiru789 Jul 30, 2019
4919e88
refactor 접근제어자 변경
qpwoeiru789 Jul 30, 2019
be72340
add comment 작성자만 추가, 삭제, 수정 기능 추가
qpwoeiru789 Jul 30, 2019
c70ad88
add comment test 작성중..
qpwoeiru789 Jul 30, 2019
d500f11
refactor
hyojaekim Aug 1, 2019
ea40f45
refactor
hyojaekim Aug 1, 2019
1bcf940
refactor 예외 누락된 부분 custom exception 적용
hyojaekim Aug 1, 2019
cbea537
refactor Transactional 공통된 부분 적용
hyojaekim Aug 1, 2019
068dbd7
refactor redirect 리팩토링
hyojaekim Aug 1, 2019
5a9e6e6
refactor Path 중복제거
hyojaekim Aug 1, 2019
0bd10ed
refactor
hyojaekim Aug 1, 2019
d711281
refactor 불필요한 부분 제거
hyojaekim Aug 1, 2019
a00d704
refactor 댓글 생성 테스트 추가
hyojaekim Aug 2, 2019
9e4fcab
refactor
hyojaekim Aug 2, 2019
b18424b
refactor Service
hyojaekim Aug 2, 2019
94c5acc
refactor 도메인 리팩토링
hyojaekim Aug 2, 2019
c4b9d50
refactor 변수명 rename
hyojaekim Aug 2, 2019
1da775b
refactor controller 리팩토링
hyojaekim Aug 2, 2019
f9c330b
refactor 모든 테스트 추가
hyojaekim Aug 2, 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
Binary file added .DS_Store
Binary file not shown.
113 changes: 37 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,38 @@
# 나만의 블로그 만들기

## 게시글 관련 기능 구현 요구사항

- `/`: 메인 페이지(`index.html`)
- [x] 게시글 생성 버튼을 누르면 `/writing`으로 GET 요청을 보낸다.
- [x] 작성된 게시글 목록이 노출된다.
- [x] 게시글을 클릭하면 게시글 페이지(`/articles/{articleId}`)로 이동한다.

- `/write`: 게시글 작성 페이지(`article-edit.html`)
- [x] 저장 버튼을 누르면 `/articles`로 POST 요청을 보낸다.
- [x] X 버튼을 누르면 메인 페이지로 이동한다.
- [x] request받은 게시글을 ArticleRespository에 저장한다.

- `/articles/{articleId}`: 게시글 페이지(`article.html`)
- [x] 수정 버튼을 누르면 게시글 수정 페이지(`/articles/{articleId}/edit`)로 이동
- [x] 삭제 버튼을 누르면 `articles/{articleId}`로 DELETE 요청을 보낸다.
- [x] 요청이 처리되면 메인 페이지(`/`)로 리다이렉트한다.

- `/articles/{articleId}/edit`: 게시글 수정 페이지(`article-edit.html`)
- [x] 수정을 완료하면 `/articles/{articleId}`로 PUT 요청을 보낸다.
- [x] 요청이 처리되면 게시글 페이지(`/article/{articleId}`)로 리다이렉트한다.

- 기타
- [x] 정적 파일 수정시 재시작하지 않고 변경사항 바로 반영되게 한다.
- [x] class파일 수정시 자동으로 서버 재기동 되도록 한다.

### 2주차 요구사항 - 회원 관리

#### 1단계: 등록/조회

- [x] 기존에 구현한 내용을 Mysql로 옮긴다.
- [x] 실행 쿼리를 볼 수 있도록 설정한다.

- `/signup`: 회원가입 페이지(`signup.html`)
- [x] '가입하기' 버튼을 누르면 `/users`로 POST 요청을 보낸다.
- [x] Spring Data JPA를 이용하여 DB에 사용자 정보를 저장한다.
- [x] 생성된 후에 로그인 화면(`/login`)으로 이동한다.
- [x] 입력 정보는 다음 규칙을 따르며, 위반 시 사용자에게 알린다.
- [x] 동일한 email로 중복 가입 불가능.
- [x] 이름은 2~10글자이며, 숫자와 특수문자가 포함될 수 없다.
- [x] 비밀번호는 8글자 이상 소문자, 대문자, 숫자, 특수문자 조합이어야 한다.
- [x] 비밀번호 확인 기능이 기능이 동작해야 한다.
- [x] 회원 등록 실패 시 에러 메시지를 `Model`에 담아서 페이지에 노출한다 - 부트스트랩의 Alerts를 이용
- [x] 프론트엔드에서도 유효성을 체크할 수 있도록 한다.
- [x] HTML5에서 제공하는 form validation 기능을 최대한 활용한다.

- `/users`: 회원 목록 페이지(`user-list.html`)
- [x] DB에 저장된 회원 정보를 노출한다.

#### 2단계: 로그인

- `/login`: 로그인 페이지(`login.html`)
- [x] 로그인 성공 시 메인 페이지(`/`)로 이동하고 우측 상단에 사용자 이름을 띄운다.
- [x] 로그인 실패 시 상황에 맞는 메시지를 띄운다.
- 이메일이 없는 경우
- 비밀번호가 틀린 경우
- [x] 로그아웃 시 메인 페이지로 이동한다.
- [x] 로그인된 사용자가 로그인/회원가입 화면에 접근할 경우 메인 페이지로 이동한다.

- `/mypage`: 마이페이지(사용자 정보 확인, `mypage.html`)
- [x] 로그인된 사용자 정보가 보여진다.
- [x] 로그인 여부를 판단하여 다른 사람이 접근하는 경우를 제한한다.
- [x] 수정 아이콘을 클릭하면 수정 페이지(`/mypage-edit`)로 이동한다.
- [x] 탈퇴 버튼을 추가한다.

#### 3단계: 회원정보 수정 및 탈퇴

- `/mypage-edit`: 마이페이지(사용자 정보 수정, `mypage-edit.html`)
- [x] 수정 버튼을 클릭하면 사용자 정보를 새로 수정한다.
- [x] 로그인 여부를 판단하여 다른 사람이 접근하는 경우를 제한한다.
- [x] 잘못된 이름을 입력하면 에러 메시지를 띄웁니다.

- `/withdrawal`: 사용자 탈퇴(별도 페이지 없음)
- [x] 사용자 정보를 DB에서 제거한다.
- [x] 로그인 여부를 판단하여 다른 사람이 접근하는 경우를 제한한다.
## 나만의 블로그 서비스 (1주차)

### 기능 요구사항
- 게시글 생성
- 게시글 작성 페이지 이동
- 메인페이지(index.html)에서 게시글 생성 버튼을 누르기
- GET /writing 으로 요청
- 작성 페이지(article-edit.html)로 이동
- 게시글 작성
- POST /articles 으로 요청
- 게시글 생성 시 게시글은 ArticleRepository의 List<Article> articles에 저장한다.
- 게시글 페이지(article.html)로 이동
- 게시글 목록 조회
- 메인 페이지 이동
- GET / 으로 요청으로 이동 시 메인 페이지에 게시글 목록이 노출
- 게시글 조회
- 게시글 페이지 이동
- 메인페이지(index.html)에서 게시글을 클릭 시 게시글 페이지(article.html)으로 이동
- GET /articles/{articleId} 으로 요청

- 게시글 수정
- 게시글 수정 페이지 이동
- 게시글 페이지(article.html)에서 수정 버튼 누르기
- GET /articles/{articleId}/edit 으로 요청
- 게시글 수정 페이지(article-edit.html)로 이동
- 게시글 수정
- PUT /articles/{articleId} 으로 요청
- 게시글 페이지(article.html)로 이동
- 게시글 삭제
- 게시글 페이지(article.html)에서 삭제 버튼 누르기
- DELETE /articles/{articleId} 으로 요청
- 게시글 목록 조회 페이지(index.html)로 이동


### 제약조건
- HTML 중복제거
- 정적 파일 수정 시 재시작 하지 않고 변경사항 반영하기
- class 파일 수정 시 자동으로 재시작 하기
40 changes: 25 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
plugins {
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'java'
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'java'
}

apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.springframework.boot'

group = 'techcourse'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
targetCompatibility = 1.8

bootJar {
baseName = 'gs-accessing-data-mysql'
version = '0.1.0'
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
runtimeOnly 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'
implementation 'mysql:mysql-connector-java:8.0.16'
implementation 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit'
}
testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.projectlombok:lombok:1.18.8'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2'
implementation 'mysql:mysql-connector-java'
runtimeOnly 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit'
}
testImplementation 'org.springframework.boot:spring-boot-starter-webflux'
testImplementation("org.springframework.boot:spring-boot-devtools")

}
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Wed Jul 10 14:33:33 KST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
Binary file added src/.DS_Store
Binary file not shown.
Binary file added src/main/.DS_Store
Binary file not shown.
15 changes: 7 additions & 8 deletions src/main/java/techcourse/myblog/HelloWorldController.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package techcourse.myblog;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
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.RestController;

@Controller
@RestController
public class HelloWorldController {

@ResponseBody
@GetMapping("/helloworld")
public String helloWord(@RequestParam String blogName) {
public String temp(String blogName) {
return blogName;
}

@ResponseBody
@PostMapping("/helloworld")
public String helloWord2(@RequestBody String blogName) {
public String asd(@RequestBody String blogName) {
return blogName;
}
}
1 change: 0 additions & 1 deletion src/main/java/techcourse/myblog/MyblogApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

@SpringBootApplication
public class MyblogApplication {

public static void main(String[] args) {
SpringApplication.run(MyblogApplication.class, args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package techcourse.myblog.application.converter;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Converter<T, U> {

Choose a reason for hiding this comment

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

오오! 좋네요 ㅎㅎ 💯

private final Function<T, U> fromDto;
private final Function<U, T> fromEntity;

public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) {
this.fromDto = fromDto;
this.fromEntity = fromEntity;
}

public final U convertFromDto(final T dto) {
return fromDto.apply(dto);
}

public final T convertFromEntity(final U entity) {
return fromEntity.apply(entity);
}

public final List<U> createFromDtos(final Collection<T> dtos) {
return dtos.stream()
.map(this::convertFromDto)
.collect(Collectors.toList());
}


public final List<T> createFromEntities(final Collection<U> entities) {
return entities.stream()
.map(this::convertFromEntity)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package techcourse.myblog.application.converter;

import techcourse.myblog.application.dto.UserDto;
import techcourse.myblog.domain.User;

public class UserConverter extends Converter<UserDto, User> {

private UserConverter() {
super(userDto -> new User(
userDto.getEmail(),
userDto.getName(),
userDto.getPassword()),
UserDto::of);
}

public static UserConverter getInstance() {
return UserConverterHolder.INSTANCE;
}

private static class UserConverterHolder {
private static final UserConverter INSTANCE = new UserConverter();
}
}
32 changes: 32 additions & 0 deletions src/main/java/techcourse/myblog/application/dto/ArticleDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package techcourse.myblog.application.dto;

import lombok.Getter;
import lombok.Setter;
import techcourse.myblog.domain.Article;

@Getter
@Setter
public class ArticleDto {
private long id;
private String title;
private String coverUrl;
private String contents;

public ArticleDto() {
}

public ArticleDto(long id, String title, String coverUrl, String contents) {
this.id = id;
this.title = title;
this.coverUrl = coverUrl;
this.contents = contents;
}

public static ArticleDto of(Article article) {
return new ArticleDto(article.getId(),
article.getTitle(),
article.getCoverUrl(),
article.getContents()
);
}
}
28 changes: 28 additions & 0 deletions src/main/java/techcourse/myblog/application/dto/CommentDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package techcourse.myblog.application.dto;

import lombok.Getter;
import lombok.Setter;
import techcourse.myblog.domain.Comment;

import java.time.LocalDateTime;

@Getter
@Setter
public class CommentDto {
private long id;
private String contents;
private String userName;
private LocalDateTime createDateTime;
private LocalDateTime updateDateTime;

public static CommentDto of(Comment comment) {
CommentDto commentDto = new CommentDto();
commentDto.setId(comment.getId());
commentDto.setContents(comment.getContents());
commentDto.setUserName(comment.getUser().getName());
commentDto.setCreateDateTime(comment.getCreateDateTime());
commentDto.setUpdateDateTime(comment.getCreateDateTime());

return commentDto;
}
}
20 changes: 20 additions & 0 deletions src/main/java/techcourse/myblog/application/dto/LoginDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package techcourse.myblog.application.dto;

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

@Getter
@Setter
public class LoginDto {
@NotBlank(message = "이메일을 작성해주세요.")
@Email(message = "메일의 양식을 지켜주세요.")
private String email;

@NotBlank(message = "비밀번호를 작성해주세요.")
@Pattern(regexp = "^([a-zA-Z0-9!@#$%^&*]{8,})$", message = "8자리 이상의 글자, 숫자, 특수문자를 입력해야합니다.")
private String password;

Choose a reason for hiding this comment

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

현재는 8자리 이상이기만 하면 통과되네요.
email validator를 보시면 ConstraintValidator를 상속받아서 구현한 것을 볼 수 있습니다.
password의 경우에도 ConstraintValidator를 상속받아서 customValidator를 작성할 수 있어요.
그럼 도메인 모델의 구현코드에서 상당부분 해당 로직을 숨기고 비즈니스 로직만 표현이 가능해지겠네요.
참고만 해두세용 :)

https://www.baeldung.com/registration-password-strength-and-rules

}
Loading