-
Notifications
You must be signed in to change notification settings - Fork 46
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
[댓글 기능] 효오 미션 제출합니다. #154
Changes from all commits
908827b
e4de223
a9efec4
4788a14
d83cf82
a0db829
cc9d448
040d229
e4bb05f
85543e4
080acc2
394927a
cf8cfb6
c96f11d
47c5a9f
44545fe
4919e88
be72340
c70ad88
d500f11
ea40f45
1bcf940
cbea537
068dbd7
5a9e6e6
0bd10ed
d711281
a00d704
9e4fcab
b18424b
94c5acc
c4b9d50
1da775b
f9c330b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 파일 수정 시 자동으로 재시작 하기 |
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") | ||
|
||
} |
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 |
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; | ||
} | ||
} |
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> { | ||
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(); | ||
} | ||
} |
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() | ||
); | ||
} | ||
} |
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; | ||
} | ||
} |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재는 8자리 이상이기만 하면 통과되네요. https://www.baeldung.com/registration-password-strength-and-rules |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오! 좋네요 ㅎㅎ 💯