From 88ba28d84de3266d093e1998efdfc0b509a12e1d Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Wed, 2 Aug 2023 21:27:48 +0900 Subject: [PATCH 01/47] =?UTF-8?q?[Feat]=20:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 213 ++++++++++++++++++ build.gradle | 37 +++ settings.gradle | 1 + .../SpringbootBoardJpaApplication.java | 13 ++ src/main/resources/application.yml | 0 .../SpringbootBoardJpaApplicationTests.java | 13 ++ 6 files changed, 277 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 settings.gradle create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplication.java create mode 100644 src/main/resources/application.yml create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..57d7cbfa6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,213 @@ +# Created by https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle,intellij,macos + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +gradle/ +gradlew +gradlew.bat +HELP.md + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +###jar 파일 생성 관련 META-INF### +META-INF/* + +.idea/* + +# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij,macos diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..1555b9ac2 --- /dev/null +++ b/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.2' + id 'io.spring.dependency-management' version '1.1.2' +} + +group = 'com.blackdog' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..7a7db22cc --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'springbootBoardJpa' diff --git a/src/main/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplication.java b/src/main/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplication.java new file mode 100644 index 000000000..ea43f9cc5 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplication.java @@ -0,0 +1,13 @@ +package com.blackdog.springbootBoardJpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringbootBoardJpaApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringbootBoardJpaApplication.class, args); + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplicationTests.java b/src/test/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplicationTests.java new file mode 100644 index 000000000..41904e0e5 --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/SpringbootBoardJpaApplicationTests.java @@ -0,0 +1,13 @@ +package com.blackdog.springbootBoardJpa; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringbootBoardJpaApplicationTests { + + @Test + void contextLoads() { + } + +} From 4f57ecfa0acdcb7877c9ee6a3bded059eac810cd Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:43:30 +0900 Subject: [PATCH 02/47] =?UTF-8?q?[Feat]=20:=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 70 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 1555b9ac2..94aa1f17b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,37 +1,73 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.1.2' - id 'io.spring.dependency-management' version '1.1.2' + id 'java' + id 'org.springframework.boot' version '3.1.2' + id 'io.spring.dependency-management' version '1.1.2' + id 'org.asciidoctor.jvm.convert' version '3.3.2' } group = 'com.blackdog' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '17' + sourceCompatibility = '17' } configurations { - compileOnly { - extendsFrom annotationProcessor - } + compileOnly { + extendsFrom annotationProcessor + } } repositories { - mavenCentral() + mavenCentral() +} + +ext { + snippetsDir = file('build/generated-snippets') } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + runtimeOnly 'com.mysql:mysql-connector-j' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() + outputs.dir snippetsDir +} + +asciidoctor { + dependsOn test + inputs.dir snippetsDir +} + +//기존에 존재하는 docs 삭제 +asciidoctor.doFirst { + delete file('src/main/resources/static/docs') +} + +//bootJar 설정을 통해 html 파일을 BOOT-INF/classes/static/docs로 복사 +bootJar { + dependsOn asciidoctor + copy { + from "${asciidoctor.outputDir}" + into 'BOOT-INF/classes/static/docs' + } +} + +// build/docs/asciidocs 파일을 src/main/resources/static/docs로 복사 +task copyDocument(type: Copy) { + dependsOn asciidoctor + from file("build/docs/asciidoc") + into file("src/main/resources/static/doc") +} + +build { + dependsOn copyDocument } From 1cd3687b8df92c73042a41a874a401afed5475e5 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:43:52 +0900 Subject: [PATCH 03/47] =?UTF-8?q?[Feat]=20:=20yml=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 25 +++++++++++++++++++++++++ src/test/resources/application-test.yml | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src/test/resources/application-test.yml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e69de29bb..bfc9da538 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -0,0 +1,25 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/jpa_board_mission + username: root + password: root1234! + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: create + properties: + hibernate: + show_sql: true + format_sql: true + dialect: org.hibernate.dialect.MySQLDialect + open-in-view: false + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + + logging: + level: + org: + hibernate: + type: + descriptor: + sql: trace diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml new file mode 100644 index 000000000..e219b0b57 --- /dev/null +++ b/src/test/resources/application-test.yml @@ -0,0 +1,25 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/jpa_board_mission + username: root + password: root1234! + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: update + properties: + hibernate: + show_sql: true + format_sql: true + dialect: org.hibernate.dialect.MySQLDialect + open-in-view: false + database-platform: org.hibernate.dialect.MySQL5InnoDBDialect + + logging: + level: + org: + hibernate: + type: + descriptor: + sql: trace From 4a0852f42a59d4c1895871756af7861e3a9c7e96 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:44:14 +0900 Subject: [PATCH 04/47] =?UTF-8?q?[Feat]=20:=20BaseEntity=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/JpaConfig.java | 9 ++++++ .../global/entity/BaseEntity.java | 32 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/config/JpaConfig.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/entity/BaseEntity.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/config/JpaConfig.java b/src/main/java/com/blackdog/springbootBoardJpa/global/config/JpaConfig.java new file mode 100644 index 000000000..f8c1db801 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/config/JpaConfig.java @@ -0,0 +1,9 @@ +package com.blackdog.springbootBoardJpa.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class JpaConfig { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/entity/BaseEntity.java b/src/main/java/com/blackdog/springbootBoardJpa/global/entity/BaseEntity.java new file mode 100644 index 000000000..722008ebd --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/entity/BaseEntity.java @@ -0,0 +1,32 @@ +package com.blackdog.springbootBoardJpa.global.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class BaseEntity { + + @CreatedDate + @Column(updatable = false, name = "created_at", columnDefinition = "DATETIME") + protected LocalDateTime createdAt; + + @LastModifiedDate + @Column(updatable = false, name = "updated_at", columnDefinition = "DATETIME") + protected LocalDateTime updatedAt; + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } + +} From 386cc4bdd57ffbe45fa3ccc6ac54acee73bf89cc Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:44:43 +0900 Subject: [PATCH 05/47] =?UTF-8?q?[Feat]=20:=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=20=EB=8B=A4=EC=9D=B4=EC=96=B4=EA=B7=B8=EB=9E=A8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- board.mermaid | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 board.mermaid diff --git a/board.mermaid b/board.mermaid new file mode 100644 index 000000000..4d68980a8 --- /dev/null +++ b/board.mermaid @@ -0,0 +1,19 @@ +classDiagram + + class Post { + Long id + String title; + String content; + } + + class User { + Long id + Name name + Age age + String hobby + } + + class BaseEntity { + LocalDateTime createdAt + Long createdBy + } From cea7c3f1c50b75dd37e95acb8d3ed2a635e5244e Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:46:24 +0900 Subject: [PATCH 06/47] =?UTF-8?q?[Feat]=20:=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/model/User.java | 80 +++++++++++++++++++ .../domain/user/model/vo/Age.java | 25 ++++++ .../domain/user/model/vo/Name.java | 25 ++++++ 3 files changed, 130 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/User.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/User.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/User.java new file mode 100644 index 000000000..56daa912c --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/User.java @@ -0,0 +1,80 @@ +package com.blackdog.springbootBoardJpa.domain.user.model; + +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.blackdog.springbootBoardJpa.global.entity.BaseEntity; +import jakarta.persistence.*; + +@Entity +@Table(name = "users") +public class User extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Embedded + private Name name; + + @Embedded + private Age age; + + @Column + private String hobby; + + protected User() { + } + + public static User builder() { + return new User(); + } + + public User name(final Name name) { + this.name = name; + return this; + } + + public User age(final Age age) { + this.age = age; + return this; + } + + public User hobby(final String hobby) { + this.hobby = hobby; + return this; + } + + public User build() { + return new User( + this.name, + this.age, + this.hobby + ); + } + + public User( + Name name, + Age age, + String hobby + ) { + this.name = name; + this.age = age; + this.hobby = hobby; + } + + public Long getId() { + return id; + } + + public Name getName() { + return name; + } + + public Age getAge() { + return age; + } + + public String getHobby() { + return hobby; + } + +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java new file mode 100644 index 000000000..e50c09d55 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java @@ -0,0 +1,25 @@ +package com.blackdog.springbootBoardJpa.domain.user.model.vo; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.validation.constraints.Positive; + +@Embeddable +public class Age { + + @Column(nullable = false) + @Positive + private int ageValue; + + protected Age() { + } + + public Age(final int ageValue) { + this.ageValue = ageValue; + } + + public int getAgeValue() { + return ageValue; + } + +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java new file mode 100644 index 000000000..f6c3d2b35 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java @@ -0,0 +1,25 @@ +package com.blackdog.springbootBoardJpa.domain.user.model.vo; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.validation.constraints.NotBlank; + +@Embeddable +public class Name { + + @NotBlank + @Column(nullable = false) + private String nameValue; + + protected Name() { + } + + public Name(final String nameValue) { + this.nameValue = nameValue; + } + + public String getNameValue() { + return nameValue; + } + +} From 21e480327e5adc3fbeb00bdf5281bbd4cf70b22f Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:47:21 +0900 Subject: [PATCH 07/47] =?UTF-8?q?[Feat]=20:=20User=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java new file mode 100644 index 000000000..65d580200 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java @@ -0,0 +1,57 @@ +package com.blackdog.springbootBoardJpa.domain.user.controller; + +import com.blackdog.springbootBoardJpa.domain.user.controller.converter.UserControllerConverter; +import com.blackdog.springbootBoardJpa.domain.user.controller.dto.UserCreateDto; +import com.blackdog.springbootBoardJpa.domain.user.service.UserService; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import jakarta.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/users") +public class UserController { + + private final UserService service; + private final UserControllerConverter converter; + + public UserController( + final UserService service, + final UserControllerConverter converter + ) { + this.service = service; + this.converter = converter; + } + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity saveUser(@Valid @RequestBody UserCreateDto createDto) { + return ResponseEntity + .status(HttpStatus.CREATED) + .body(service.saveUser(converter.toRequest(createDto))); + } + + @DeleteMapping(value = "/{userId}") + public ResponseEntity deleteUser(@PathVariable long userId) { + service.deleteUserById(userId); + return ResponseEntity.noContent().build(); + } + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getAllUsers(Pageable pageable) { + return ResponseEntity + .status(HttpStatus.OK) + .body(service.findAllUsers(pageable)); + } + + @GetMapping(value = "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getUser(@PathVariable Long userId) { + return ResponseEntity + .status(HttpStatus.OK) + .body(service.findUserById(userId)); + } + +} From 5810a42aec8c4c97b5a8b17003f153ff2c48d07b Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:48:02 +0900 Subject: [PATCH 08/47] =?UTF-8?q?[Feat]=20:=20User=20Controller=20Dto=20?= =?UTF-8?q?=EB=B0=8F=20Converter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/UserControllerConverter.java | 20 +++++++++++++++++++ .../user/controller/dto/UserCreateDto.java | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/converter/UserControllerConverter.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/converter/UserControllerConverter.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/converter/UserControllerConverter.java new file mode 100644 index 000000000..0d78e64ac --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/converter/UserControllerConverter.java @@ -0,0 +1,20 @@ +package com.blackdog.springbootBoardJpa.domain.user.controller.converter; + +import com.blackdog.springbootBoardJpa.domain.user.controller.dto.UserCreateDto; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; +import org.springframework.stereotype.Component; + +@Component +public class UserControllerConverter { + + public UserCreateRequest toRequest(UserCreateDto createDto) { + return new UserCreateRequest( + new Name(createDto.name()), + new Age(createDto.age()), + createDto.hobby() + ); + } + +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java new file mode 100644 index 000000000..37ecd7710 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java @@ -0,0 +1,16 @@ +package com.blackdog.springbootBoardJpa.domain.user.controller.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public record UserCreateDto( + @NotBlank + String name, + + @Min(0) + int age, + + @NotBlank + String hobby +) { +} From 08da48db01d1a95689d3a096258b6c328273ceed Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:48:20 +0900 Subject: [PATCH 09/47] =?UTF-8?q?[Feat]=20:=20User=20Service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/service/UserService.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java new file mode 100644 index 000000000..e58e9101a --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java @@ -0,0 +1,51 @@ +package com.blackdog.springbootBoardJpa.domain.user.service; + +import com.blackdog.springbootBoardJpa.domain.user.repository.UserRepository; +import com.blackdog.springbootBoardJpa.domain.user.service.converter.UserServiceConverter; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import com.blackdog.springbootBoardJpa.global.exception.UserNotFoundException; +import jakarta.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static com.blackdog.springbootBoardJpa.global.response.ErrorCode.NOT_FOUND_USER; + +@Service +@Transactional(readOnly = true) +public class UserService { + + private final UserRepository repository; + private final UserServiceConverter converter; + + public UserService( + final UserRepository repository, + final UserServiceConverter converter + ) { + this.repository = repository; + this.converter = converter; + } + + @Transactional + public UserResponse saveUser(@Valid UserCreateRequest dto) { + return converter.toResponse(repository.save(converter.toEntity(dto))); + } + + @Transactional + public void deleteUserById(Long id) { + repository.deleteById(id); + } + + public UserResponses findAllUsers(Pageable pageable) { + return converter.toResponses(repository.findAll(pageable)); + } + + public UserResponse findUserById(Long id) { + return converter.toResponse( + repository.findById(id) + .orElseThrow(() -> new UserNotFoundException(NOT_FOUND_USER))); + } + +} From 5a59911ad692f32b2e2814ea846d4e6a147f1ae0 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:49:56 +0900 Subject: [PATCH 10/47] =?UTF-8?q?[Feat]=20:=20User=20Service=20Dto=20?= =?UTF-8?q?=EB=B0=8F=20Converter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/UserServiceConverter.java | 36 +++++++++++++++++++ .../user/service/dto/UserCreateRequest.java | 18 ++++++++++ .../domain/user/service/dto/UserResponse.java | 30 ++++++++++++++++ .../user/service/dto/UserResponses.java | 8 +++++ 4 files changed, 92 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/converter/UserServiceConverter.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserCreateRequest.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponses.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/converter/UserServiceConverter.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/converter/UserServiceConverter.java new file mode 100644 index 000000000..1fa03bf98 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/converter/UserServiceConverter.java @@ -0,0 +1,36 @@ +package com.blackdog.springbootBoardJpa.domain.user.service.converter; + +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Component; + +@Component +public class UserServiceConverter { + + public User toEntity(UserCreateRequest dto) { + return User.builder() + .name(dto.name()) + .age(dto.age()) + .hobby(dto.hobby()) + .build(); + } + + public UserResponse toResponse(User user) { + return new UserResponse( + user.getId(), + user.getName().getNameValue(), + user.getAge().getAgeValue(), + user.getHobby(), + user.getCreatedAt(), + user.getUpdatedAt() + ); + } + + public UserResponses toResponses(Page users) { + return new UserResponses(users.map(this::toResponse)); + } + +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserCreateRequest.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserCreateRequest.java new file mode 100644 index 000000000..c8b63c2b9 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserCreateRequest.java @@ -0,0 +1,18 @@ +package com.blackdog.springbootBoardJpa.domain.user.service.dto; + +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record UserCreateRequest( + @NotNull + Name name, + + @NotNull + Age age, + + @NotBlank + String hobby +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java new file mode 100644 index 000000000..f007c7132 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java @@ -0,0 +1,30 @@ +package com.blackdog.springbootBoardJpa.domain.user.service.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.time.LocalDateTime; + +public record UserResponse( + @NotNull + Long id, + + @NotBlank + String name, + + @NotNull + int age, + + @NotBlank + String hobby, + + @NotNull + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + LocalDateTime createdAt, + + @NotNull + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + LocalDateTime updatedAt +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponses.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponses.java new file mode 100644 index 000000000..5cf787757 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponses.java @@ -0,0 +1,8 @@ +package com.blackdog.springbootBoardJpa.domain.user.service.dto; + +import org.springframework.data.domain.Page; + +public record UserResponses( + Page userResponses +) { +} From 2698eb3d70539b4082e529242d11aefd822cb05f Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:50:46 +0900 Subject: [PATCH 11/47] =?UTF-8?q?[Feat]=20:=20UserNotFoundException=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/UserNotFoundException.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/exception/UserNotFoundException.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/UserNotFoundException.java b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/UserNotFoundException.java new file mode 100644 index 000000000..908445564 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/UserNotFoundException.java @@ -0,0 +1,12 @@ +package com.blackdog.springbootBoardJpa.global.exception; + +import com.blackdog.springbootBoardJpa.global.response.ErrorCode; + +public class UserNotFoundException extends RuntimeException { + private final ErrorCode errorCode; + + public UserNotFoundException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} From 0235e6dfaaab921e4f3589d02c7f39d5f7336895 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:51:36 +0900 Subject: [PATCH 12/47] =?UTF-8?q?[Feat]=20:=20ErrorCode=20=EB=B0=8F=20Glob?= =?UTF-8?q?alExceptionHandler=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 81 +++++++++++++++++++ .../global/response/ErrorCode.java | 41 ++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorCode.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..a19f8394b --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java @@ -0,0 +1,81 @@ +package com.blackdog.springbootBoardJpa.global.exception; + +import com.blackdog.springbootBoardJpa.global.response.ErrorCode; +import com.blackdog.springbootBoardJpa.global.response.ErrorResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import static com.blackdog.springbootBoardJpa.global.response.ErrorCode.*; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + private Logger log = LoggerFactory.getLogger(getClass()); + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { + ErrorResponse errorResponse = ErrorResponse.of(INVALID_METHOD_ERROR); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + e.printStackTrace(); + return ResponseEntity + .status(HttpStatus.METHOD_NOT_ALLOWED) + .body(errorResponse); + } + + @ExceptionHandler(PermissionDeniedException.class) + public ResponseEntity permissionDeniedExceptionHandler(PermissionDeniedException e) { + ErrorResponse errorResponse = ErrorResponse.of(ErrorCode.PERMISSION_DENIED); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + return ResponseEntity + .status(HttpStatus.NON_AUTHORITATIVE_INFORMATION) + .body(errorResponse); + } + + @ExceptionHandler(PostNotFoundException.class) + public ResponseEntity postNotFoundExceptionHandler(PostNotFoundException e) { + ErrorResponse errorResponse = ErrorResponse.of(NOT_FOUND_POST); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(errorResponse); + } + + @ExceptionHandler(UserNotFoundException.class) + public ResponseEntity userNotFoundExceptionHandler(UserNotFoundException e) { + ErrorResponse errorResponse = ErrorResponse.of(NOT_FOUND_USER); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + return ResponseEntity + .status(HttpStatus.NOT_FOUND) + .body(errorResponse); + } + + @ExceptionHandler(RuntimeException.class) + public ResponseEntity handleRuntimeException(RuntimeException e) { + ErrorResponse errorResponse = ErrorResponse.of(INTERNAL_SERVER_ERROR); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleAllException(Exception e) { + ErrorResponse errorResponse = ErrorResponse.of(INTERNAL_SERVER_ERROR); + log.warn("{}", errorResponse); + log.warn("{}", e.getCause()); + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorCode.java b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorCode.java new file mode 100644 index 000000000..f39d02f9c --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorCode.java @@ -0,0 +1,41 @@ +package com.blackdog.springbootBoardJpa.global.response; + +import org.springframework.http.HttpStatus; + +public enum ErrorCode { + //global + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "G001", "Internal Server Error"), + INVALID_METHOD_ERROR(HttpStatus.METHOD_NOT_ALLOWED, "G002", "Method Argument가 적절하지 않습니다."), + + //user + NOT_FOUND_USER(HttpStatus.NOT_FOUND, "U001", "존재하지 않는 유저입니다."), + + //post + NOT_FOUND_POST(HttpStatus.NOT_FOUND, "P001", "존재하지 않는 게시글입니다."), + PERMISSION_DENIED(HttpStatus.NON_AUTHORITATIVE_INFORMATION, "P002", "권한 없는 유저입니다."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + public HttpStatus getHttpStatus() { + return httpStatus; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + ErrorCode(final HttpStatus httpStatus, + final String code, + final String message) { + this.httpStatus = httpStatus; + this.code = code; + this.message = message; + } + +} From 952e6aca079de42e63dba6aa5b427062bd571519 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:51:55 +0900 Subject: [PATCH 13/47] =?UTF-8?q?[Feat]=20:=20ErrorResponse=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/response/ErrorResponse.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java new file mode 100644 index 000000000..772dee27a --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java @@ -0,0 +1,31 @@ +package com.blackdog.springbootBoardJpa.global.response; + +public class ErrorResponse { + + private final String code; + private final String message; + + public ErrorResponse( + final String code, + final String message + ) { + this.code = code; + this.message = message; + } + + public static ErrorResponse of(ErrorCode errorCode) { + return new ErrorResponse( + errorCode.getCode(), + errorCode.getMessage() + ); + } + + @Override + public String toString() { + return "ErrorResponse{" + + "code='" + code + '\'' + + ", message='" + message + '\'' + + '}'; + } + +} From 3494d20b89d6cbc98664b2f8ee27801b82838a3b Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:52:17 +0900 Subject: [PATCH 14/47] =?UTF-8?q?[Feat]=20:=20SuccessCode=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/response/SuccessCode.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessCode.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessCode.java b/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessCode.java new file mode 100644 index 000000000..353014e82 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessCode.java @@ -0,0 +1,23 @@ +package com.blackdog.springbootBoardJpa.global.response; + +public enum SuccessCode { + /** + * Post - 게시글 관련 Code + */ + POST_DELETE_SUCCESS("게시글이 성공적으로 삭제되었습니다."), + + /** + * User - 유저 관련 성공 Code + */ + USER_DELETE_SUCCESS("유저가 성공적으로 삭제되었습니다."); + + private final String message; + + SuccessCode(final String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} From 798c2a4ce3c95c2f51b3dc293564f4ce537623d9 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:52:49 +0900 Subject: [PATCH 15/47] =?UTF-8?q?[Feat]=20:=20Post=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/model/Post.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java new file mode 100644 index 000000000..280e94e30 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java @@ -0,0 +1,93 @@ +package com.blackdog.springbootBoardJpa.domain.post.model; + +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.global.entity.BaseEntity; +import jakarta.persistence.*; + +@Entity +@Table(name = "posts") +public class Post extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String title; + + @Column(nullable = false) + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "users_id", referencedColumnName = "id", nullable = false) + private User user; + + protected Post() { + } + + public static Post builder() { + return new Post(); + } + + public Post title(String title) { + this.title = title; + return this; + } + + public Post content(String content) { + this.content = content; + return this; + } + + public Post user(User user) { + this.user = user; + return this; + } + + public Post build() { + return new Post( + this.title, + this.content, + this.user + ); + } + + private Post( + String title, + String content, + User user + ) { + this.title = title; + this.content = content; + this.user = user; + } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getContent() { + return content; + } + + public User getUser() { + return user; + } + + private void changeTitle(String title) { + this.title = title; + } + + private void changeContent(String content) { + this.content = content; + } + + public void changePost(PostUpdateRequest post) { + changeTitle(post.title()); + changeContent(post.content()); + } +} From 43329b2adaac9cd573751cea2c93470fd30f51a8 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:53:11 +0900 Subject: [PATCH 16/47] =?UTF-8?q?[Feat]=20:=20Post=20Controller=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostController.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java new file mode 100644 index 000000000..2dfb93420 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java @@ -0,0 +1,72 @@ +package com.blackdog.springbootBoardJpa.domain.post.controller; + +import com.blackdog.springbootBoardJpa.domain.post.controller.converter.PostControllerConverter; +import com.blackdog.springbootBoardJpa.domain.post.controller.dto.PostCreateDto; +import com.blackdog.springbootBoardJpa.domain.post.controller.dto.PostUpdateDto; +import com.blackdog.springbootBoardJpa.domain.post.service.PostService; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import jakarta.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/posts") +public class PostController { + + private final PostService postService; + private final PostControllerConverter controllerConverter; + + public PostController( + final PostService postService, + final PostControllerConverter controllerConverter + ) { + this.postService = postService; + this.controllerConverter = controllerConverter; + } + + @PostMapping(value = "/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity savePost(@PathVariable Long userId, @Valid @RequestBody PostCreateDto createDto) { + return ResponseEntity + .status(HttpStatus.CREATED) + .body(postService.savePost(userId, controllerConverter.toCreateRequest(createDto))); + } + + @PatchMapping(value = "/{postId}/user/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity updatePost(@PathVariable Long postId, @PathVariable Long userId, @Valid @RequestBody PostUpdateDto updateDto) { + return ResponseEntity + .status(HttpStatus.OK) + .body(postService.updatePost(postId, userId, controllerConverter.toUpdateRequest(updateDto))); + } + + @DeleteMapping(path = "/{postId}/user/{userId}") + public ResponseEntity deletePost(@PathVariable Long postId, @PathVariable Long userId) { + postService.deletePostById(userId, postId); + return ResponseEntity.noContent().build(); + } + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getAllPosts(Pageable pageable) { + return ResponseEntity + .status(HttpStatus.OK) + .body(postService.findAllPosts(pageable)); + } + + @GetMapping(value = "/{postId}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getPostById(@PathVariable Long postId) { + return ResponseEntity + .status(HttpStatus.OK) + .body(postService.findPostById(postId)); + } + + @GetMapping(value = "/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getPostsByUserId(@PathVariable Long userId, Pageable pageable) { + return ResponseEntity + .status(HttpStatus.OK) + .body(postService.findPostsByUserId(userId, pageable)); + } + +} From 8e435e58a6a8aaa81555231d73bd9b42de0db9df Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:54:04 +0900 Subject: [PATCH 17/47] =?UTF-8?q?[Feat]=20:=20Post=20Controller=20Dto=20?= =?UTF-8?q?=EB=B0=8F=20Converter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/PostControllerConverter.java | 25 +++++++++++++++++++ .../post/controller/dto/PostCreateDto.java | 15 +++++++++++ .../post/controller/dto/PostUpdateDto.java | 15 +++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/converter/PostControllerConverter.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/converter/PostControllerConverter.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/converter/PostControllerConverter.java new file mode 100644 index 000000000..8237ba242 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/converter/PostControllerConverter.java @@ -0,0 +1,25 @@ +package com.blackdog.springbootBoardJpa.domain.post.controller.converter; + +import com.blackdog.springbootBoardJpa.domain.post.controller.dto.PostCreateDto; +import com.blackdog.springbootBoardJpa.domain.post.controller.dto.PostUpdateDto; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostCreateRequest; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; +import org.springframework.stereotype.Component; + +@Component +public class PostControllerConverter { + + public PostCreateRequest toCreateRequest(PostCreateDto dto) { + return new PostCreateRequest( + dto.title(), + dto.content() + ); + } + + public PostUpdateRequest toUpdateRequest(PostUpdateDto dto) { + return new PostUpdateRequest( + dto.title(), + dto.content() + ); + } +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java new file mode 100644 index 000000000..2ec0345c3 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java @@ -0,0 +1,15 @@ +package com.blackdog.springbootBoardJpa.domain.post.controller.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record PostCreateDto( + @NotBlank + String title, + + @NotNull + @Size(max = 255) + String content +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java new file mode 100644 index 000000000..b89adfe94 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java @@ -0,0 +1,15 @@ +package com.blackdog.springbootBoardJpa.domain.post.controller.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record PostUpdateDto( + @NotBlank + String title, + + @NotNull + @Size(max = 255) + String content +) { +} From 0a0843e875d89754a0898e8f8a38624a592fe909 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:54:20 +0900 Subject: [PATCH 18/47] =?UTF-8?q?[Feat]=20:=20Post=20Service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/service/PostService.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java new file mode 100644 index 000000000..028659454 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java @@ -0,0 +1,86 @@ +package com.blackdog.springbootBoardJpa.domain.post.service; + +import com.blackdog.springbootBoardJpa.domain.post.model.Post; +import com.blackdog.springbootBoardJpa.domain.post.repository.PostRepository; +import com.blackdog.springbootBoardJpa.domain.post.service.converter.PostServiceConverter; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostCreateRequest; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.domain.user.repository.UserRepository; +import com.blackdog.springbootBoardJpa.global.exception.PermissionDeniedException; +import com.blackdog.springbootBoardJpa.global.exception.PostNotFoundException; +import com.blackdog.springbootBoardJpa.global.exception.UserNotFoundException; +import jakarta.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Objects; + +import static com.blackdog.springbootBoardJpa.global.response.ErrorCode.*; + +@Service +@Transactional(readOnly = true) +public class PostService { + + private final PostRepository postRepository; + private final UserRepository userRepository; + private final PostServiceConverter converter; + + public PostService( + final PostRepository postRepository, + final UserRepository userRepository, + final PostServiceConverter converter + ) { + this.postRepository = postRepository; + this.userRepository = userRepository; + this.converter = converter; + } + + @Transactional + public PostResponse savePost(Long userId, @Valid PostCreateRequest request) { + User user = userRepository + .findById(userId) + .orElseThrow(() -> new UserNotFoundException(NOT_FOUND_USER)); + + Post post = converter.toEntity(request, user); + + return converter.toResponse(postRepository.save(post)); + } + + @Transactional + public PostResponse updatePost(Long userId, Long postId, @Valid PostUpdateRequest dto) { + Post targetPost = postRepository + .findById(postId) + .orElseThrow(() -> new PostNotFoundException(NOT_FOUND_POST)); + + if (!Objects.equals(targetPost.getUser().getId(), userId)) { + throw new PermissionDeniedException(PERMISSION_DENIED); + } + + targetPost.changePost(dto); + return converter.toResponse(targetPost); + } + + @Transactional + public void deletePostById(Long userId, Long id) { + postRepository.deleteByIdAndUserId(id, userId); + } + + public PostResponses findAllPosts(Pageable pageable) { + return converter.toResponses(postRepository.findAll(pageable)); + } + + public PostResponse findPostById(Long id) { + return converter.toResponse( + postRepository.findById(id) + .orElseThrow(() -> new PostNotFoundException(NOT_FOUND_POST))); + } + + public PostResponses findPostsByUserId(Long userId, Pageable pageable) { + return converter.toResponses(postRepository.findPostsByUserId(userId, pageable)); + } + +} From 4b77c1e77a3c727e08652886b5f68741d12691f8 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:55:01 +0900 Subject: [PATCH 19/47] =?UTF-8?q?[Feat]=20:=20Post=20Service=20Dto=20?= =?UTF-8?q?=EB=B0=8F=20Converter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/PostServiceConverter.java | 36 +++++++++++++++++++ .../post/service/dto/PostCreateRequest.java | 15 ++++++++ .../domain/post/service/dto/PostResponse.java | 32 +++++++++++++++++ .../post/service/dto/PostResponses.java | 8 +++++ .../post/service/dto/PostUpdateRequest.java | 15 ++++++++ 5 files changed, 106 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/converter/PostServiceConverter.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostCreateRequest.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponses.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostUpdateRequest.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/converter/PostServiceConverter.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/converter/PostServiceConverter.java new file mode 100644 index 000000000..21f4db8f4 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/converter/PostServiceConverter.java @@ -0,0 +1,36 @@ +package com.blackdog.springbootBoardJpa.domain.post.service.converter; + +import com.blackdog.springbootBoardJpa.domain.post.model.Post; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostCreateRequest; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Component; + +@Component +public class PostServiceConverter { + + public Post toEntity(PostCreateRequest dto, User user) { + return Post.builder() + .title(dto.title()) + .content(dto.content()) + .user(user) + .build(); + } + + public PostResponse toResponse(Post post) { + return new PostResponse( + post.getId(), + post.getTitle(), + post.getContent(), + post.getUser().getName().getNameValue(), + post.getCreatedAt(), + post.getUpdatedAt() + ); + } + + public PostResponses toResponses(Page posts) { + return new PostResponses(posts.map(this::toResponse)); + } +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostCreateRequest.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostCreateRequest.java new file mode 100644 index 000000000..b8ff3bd58 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostCreateRequest.java @@ -0,0 +1,15 @@ +package com.blackdog.springbootBoardJpa.domain.post.service.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record PostCreateRequest( + @NotBlank + String title, + + @NotNull + @Size(max = 255) + String content +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java new file mode 100644 index 000000000..3a54b8ded --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java @@ -0,0 +1,32 @@ +package com.blackdog.springbootBoardJpa.domain.post.service.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +import java.time.LocalDateTime; + +public record PostResponse( + @NotNull + Long id, + + @NotBlank + String title, + + @NotNull + @Size(max = 255) + String content, + + @NotBlank + String name, + + @NotNull + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + LocalDateTime createdAt, + + @NotNull + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + LocalDateTime updatedAt +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponses.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponses.java new file mode 100644 index 000000000..edaa5dea8 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponses.java @@ -0,0 +1,8 @@ +package com.blackdog.springbootBoardJpa.domain.post.service.dto; + +import org.springframework.data.domain.Page; + +public record PostResponses( + Page postResponses +) { +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostUpdateRequest.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostUpdateRequest.java new file mode 100644 index 000000000..7248fa8cc --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostUpdateRequest.java @@ -0,0 +1,15 @@ +package com.blackdog.springbootBoardJpa.domain.post.service.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record PostUpdateRequest( + @NotBlank + String title, + + @NotNull + @Size(max = 255) + String content +) { +} From 46db2d2e9475bb6eb6fea5b75e51d78faa23736a Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:56:33 +0900 Subject: [PATCH 20/47] =?UTF-8?q?[Feat]=20:=20Custom=20Exception=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20PermissionDeniedException=20-=20PostNo?= =?UTF-8?q?tFoundException?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/PermissionDeniedException.java | 12 ++++++++++++ .../global/exception/PostNotFoundException.java | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/exception/PermissionDeniedException.java create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/exception/PostNotFoundException.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PermissionDeniedException.java b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PermissionDeniedException.java new file mode 100644 index 000000000..4a8d75500 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PermissionDeniedException.java @@ -0,0 +1,12 @@ +package com.blackdog.springbootBoardJpa.global.exception; + +import com.blackdog.springbootBoardJpa.global.response.ErrorCode; + +public class PermissionDeniedException extends RuntimeException { + private final ErrorCode errorCode; + + public PermissionDeniedException(final ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PostNotFoundException.java b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PostNotFoundException.java new file mode 100644 index 000000000..08d445562 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/PostNotFoundException.java @@ -0,0 +1,12 @@ +package com.blackdog.springbootBoardJpa.global.exception; + +import com.blackdog.springbootBoardJpa.global.response.ErrorCode; + +public class PostNotFoundException extends RuntimeException { + private final ErrorCode errorCode; + + public PostNotFoundException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} From 7d99660e69bfb515cadb5d81bbc683f36e840fe6 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:57:23 +0900 Subject: [PATCH 21/47] =?UTF-8?q?[Feat]=20:=20PostRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/repository/PostRepository.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/post/repository/PostRepository.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/repository/PostRepository.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/repository/PostRepository.java new file mode 100644 index 000000000..5636fa20c --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/repository/PostRepository.java @@ -0,0 +1,14 @@ +package com.blackdog.springbootBoardJpa.domain.post.repository; + +import com.blackdog.springbootBoardJpa.domain.post.model.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { + + Page findPostsByUserId(Long userId, Pageable pageable); + + void deleteByIdAndUserId(Long id, Long userId); + +} From 9c5c72a8a0dcad0b3eab452db4c1cb1d5930e774 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:57:46 +0900 Subject: [PATCH 22/47] =?UTF-8?q?[Feat]=20:=20UserRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/repository/UserRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/domain/user/repository/UserRepository.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/repository/UserRepository.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/repository/UserRepository.java new file mode 100644 index 000000000..cc07e767c --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/repository/UserRepository.java @@ -0,0 +1,7 @@ +package com.blackdog.springbootBoardJpa.domain.user.repository; + +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { +} From 43804102f3c811c196cb64634e9068d714d8725d Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:58:18 +0900 Subject: [PATCH 23/47] =?UTF-8?q?[Test]=20:=20User=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20-=20Contr?= =?UTF-8?q?oller=20&=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserControllerTest.java | 197 ++++++++++++++++++ .../user/service/UserServiceTest.java | 142 +++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/user/service/UserServiceTest.java diff --git a/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java new file mode 100644 index 000000000..d41fc5266 --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java @@ -0,0 +1,197 @@ +package com.blackdog.springbootBoardJpa.user.controller; + +import com.blackdog.springbootBoardJpa.domain.user.controller.UserController; +import com.blackdog.springbootBoardJpa.domain.user.controller.converter.UserControllerConverter; +import com.blackdog.springbootBoardJpa.domain.user.controller.dto.UserCreateDto; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.blackdog.springbootBoardJpa.domain.user.service.UserService; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.MockMvc; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@AutoConfigureRestDocs +@WebMvcTest(UserController.class) +class UserControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserControllerConverter controllerConverter; + + @MockBean + private UserService userService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + @DisplayName("유저를 생성한다.") + void saveUser_Dto_ReturnResponse() throws Exception { + //given + UserCreateDto createDto = new UserCreateDto("Kim", 23, "축구"); + UserResponse response = new UserResponse(1L, "Kim", 23, "축구", LocalDateTime.now(), LocalDateTime.now()); + given(userService.saveUser(any())).willReturn(response); + + //when & then + mockMvc.perform(post("/users") + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isCreated()) + .andDo(print()) + .andDo(document("user-save", + requestFields( + fieldWithPath("name").type(JsonFieldType.STRING).description("회원 이름"), + fieldWithPath("age").type(JsonFieldType.NUMBER).description("나이"), + fieldWithPath("hobby").type(JsonFieldType.STRING).description("취미")), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("회원 ID"), + fieldWithPath("name").type(JsonFieldType.STRING).description("회원 이름"), + fieldWithPath("age").type(JsonFieldType.NUMBER).description("나이"), + fieldWithPath("hobby").type(JsonFieldType.STRING).description("취미"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("회원 가입 시간"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("회원 수정 시간") + ) + )); + } + + @Test + @DisplayName("유저를 삭제한다.") + void deleteUser_Id_ReturnMessage() throws Exception { + //given + doNothing().when(userService).deleteUserById(anyLong()); + + //when & then + mockMvc.perform(RestDocumentationRequestBuilders.delete("/users/{userId}", 1L)) + .andExpect(status().isNoContent()) + .andDo(print()) + .andDo(document("user-delete", + pathParameters( + parameterWithName("userId").description("회원 ID") + ))); + } + + @Test + @DisplayName("유저를 단건 조회한다.") + void getUser_Id_ReturnResponse() throws Exception { + //given + UserResponse response = new UserResponse(1L, "Park", 26, "여행", LocalDateTime.now(), LocalDateTime.now()); + given(userService.findUserById(anyLong())).willReturn(response); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.get("/users/{userId}", 1L) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(print()) + .andDo(document("user-get", + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("userId").description("회원 ID") + ), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("유저 아이디"), + fieldWithPath("name").type(JsonFieldType.STRING).description("유저 이름"), + fieldWithPath("age").type(JsonFieldType.NUMBER).description("유저 나이"), + fieldWithPath("hobby").type(JsonFieldType.STRING).description("유저 취미"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("유저 생성일"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("유저 갱신일") + ) + )); + + // then + verify(userService, times(1)).findUserById(anyLong()); + } + + @ParameterizedTest + @DisplayName("유저를 전체 조회한다.") + @MethodSource("userCreateRequest_Data") + void getAllUsers_Pageable_ReturnResponses(List requests) throws Exception { + //given + UserResponse response1 = new UserResponse(1L, "Park", 26, "여행", LocalDateTime.now(), LocalDateTime.now()); + UserResponse response2 = new UserResponse(2L, "Park", 26, "여행", LocalDateTime.now(), LocalDateTime.now()); + UserResponses responses = new UserResponses(new PageImpl<>(List.of(response1, response2))); + given(userService.findAllUsers(PageRequest.of(0, 5))).willReturn(responses); + + //when & then + mockMvc.perform(get("/users") + .param("page", "0") + .param("size", "5")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.userResponses.content", Matchers.hasSize(2))) + .andDo(print()) + .andDo(document("users-get", + preprocessResponse(prettyPrint()), + queryParameters( + parameterWithName("page").description("페이지"), + parameterWithName("size").description("사이즈") + ), + responseFields( + fieldWithPath("userResponses").type(JsonFieldType.OBJECT).description("유저 응답"), + fieldWithPath("userResponses.content[]").type(JsonFieldType.ARRAY).description("유저 정보 배열"), + fieldWithPath("userResponses.content[].id").type(JsonFieldType.NUMBER).description("유저 아이디"), + fieldWithPath("userResponses.content[].name").type(JsonFieldType.STRING).description("유저 이름"), + fieldWithPath("userResponses.content[].age").type(JsonFieldType.NUMBER).description("유저 나이"), + fieldWithPath("userResponses.content[].hobby").type(JsonFieldType.STRING).description("유저 취미"), + fieldWithPath("userResponses.content[].createdAt").type(JsonFieldType.STRING).description("유저 생성일"), + fieldWithPath("userResponses.content[].updatedAt").type(JsonFieldType.STRING).description("유저 갱신일"), + fieldWithPath("userResponses.pageable").type(JsonFieldType.OBJECT).description("pageable").ignored(), + fieldWithPath("userResponses.last").type(JsonFieldType.BOOLEAN).description("last").ignored(), + fieldWithPath("userResponses.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), + fieldWithPath("userResponses.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), + fieldWithPath("userResponses.size").type(JsonFieldType.NUMBER).description("size").ignored(), + fieldWithPath("userResponses.number").type(JsonFieldType.NUMBER).description("number").ignored(), + fieldWithPath("userResponses.sort.empty").type(JsonFieldType.BOOLEAN).description("sort.empty").ignored(), + fieldWithPath("userResponses.sort.sorted").type(JsonFieldType.BOOLEAN).description("sort.sorted").ignored(), + fieldWithPath("userResponses.sort.unsorted").type(JsonFieldType.BOOLEAN).description("sort.unsorted").ignored(), + fieldWithPath("userResponses.first").type(JsonFieldType.BOOLEAN).description("first").ignored(), + fieldWithPath("userResponses.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements").ignored(), + fieldWithPath("userResponses.empty").type(JsonFieldType.BOOLEAN).description("empty").ignored() + ) + )); + } + + private static Stream> userCreateRequest_Data() { + UserCreateRequest request1 = new UserCreateRequest(new Name("Kim"), new Age(23), "축구"); + UserCreateRequest request2 = new UserCreateRequest(new Name("Park"), new Age(44), "배구"); + return Stream.of(List.of(request1, request2)); + } +} diff --git a/src/test/java/com/blackdog/springbootBoardJpa/user/service/UserServiceTest.java b/src/test/java/com/blackdog/springbootBoardJpa/user/service/UserServiceTest.java new file mode 100644 index 000000000..eb7fd6286 --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/user/service/UserServiceTest.java @@ -0,0 +1,142 @@ +package com.blackdog.springbootBoardJpa.user.service; + +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.blackdog.springbootBoardJpa.domain.user.repository.UserRepository; +import com.blackdog.springbootBoardJpa.domain.user.service.UserService; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; +import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import com.blackdog.springbootBoardJpa.global.exception.UserNotFoundException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.blackdog.springbootBoardJpa.global.response.ErrorCode.NOT_FOUND_USER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SpringBootTest +@Transactional +public class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + private User user; + + @BeforeEach + void setUp() { + user = User.builder() + .name(new Name("park")) + .age(new Age(12)) + .hobby("야구") + .build(); + } + + @Test + @DisplayName("유저를 생성한다.") + void saveUser_Dto_ReturnUserResponse() { + //given + UserCreateRequest request = new UserCreateRequest( + new Name("park"), + new Age(26), + "축구" + ); + + //when + UserResponse response = userService.saveUser(request); + + //then + User savedUser = userRepository.findById(response.id()).get(); + assertThat(savedUser.getId()).isEqualTo(savedUser.getId()); + } + + @Test + @DisplayName("ID를 이용해 유저를 삭제한다.") + void deleteUserById_Id_Success() { + //given + User savedUser = userRepository.save(user); + + //when + userService.deleteUserById(savedUser.getId()); + + //then + Optional optionalUser = userRepository.findById(savedUser.getId()); + assertThat(optionalUser).isEmpty(); + } + + @Test + @DisplayName("ID를 이용하여 특정 유저를 조회한다.") + void findUserById_Id_ReturnUserResponse() { + //given + User savedUser = userRepository.save(user); + + //when + UserResponse userResponse = userService.findUserById(savedUser.getId()); + + //then + assertThat(userResponse).isNotNull(); + assertThat(userResponse.age()).isEqualTo(savedUser.getAge().getAgeValue()); + assertThat(userResponse.name()).isEqualTo(savedUser.getName().getNameValue()); + assertThat(userResponse.hobby()).isEqualTo(savedUser.getHobby()); + } + + @Test + @DisplayName("유효하지 않은 Id로 단건 조회시 조회에 실패한다.") + void findUserById_Id_ThrowUserNotFoundException() { + //given + User savedUser = userRepository.save(user); + + //when & then + assertThatThrownBy(() -> userService.findUserById(1000L)) + .isInstanceOf(UserNotFoundException.class) + .hasMessage(NOT_FOUND_USER.getMessage()); + } + + + @ParameterizedTest + @DisplayName("페이징을 이용하여 유저를 전체 조회한다.") + @MethodSource("user_Data") + void findAllUsers_Pageable_UserResponses(List users) { + //given + users.forEach(user -> userRepository.save(user)); + + //when + UserResponses responses = userService.findAllUsers(PageRequest.of(0, 2)); + + //then + assertThat(responses).isNotNull(); + assertThat(responses.userResponses()).isNotEmpty(); + assertThat(responses.userResponses().getSize()).isEqualTo(2); + } + + private static Stream> user_Data() { + User user1 = User.builder() + .name(new Name("Park")) + .age(new Age(12)) + .hobby("야구") + .build(); + User user2 = User.builder() + .name(new Name("Kim")) + .age(new Age(21)) + .hobby("농구") + .build(); + return Stream.of(List.of(user1, user2)); + } + +} From d088b35b76637e3d00d2071459407bb16525e80b Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:58:34 +0900 Subject: [PATCH 24/47] =?UTF-8?q?[Test]=20:=20Post=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20-=20Contr?= =?UTF-8?q?oller=20&=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostControllerTest.java | 328 ++++++++++++++++++ .../post/service/PostServiceTest.java | 177 ++++++++++ 2 files changed, 505 insertions(+) create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java diff --git a/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java b/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java new file mode 100644 index 000000000..65ce5e783 --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java @@ -0,0 +1,328 @@ +package com.blackdog.springbootBoardJpa.post.controller; + +import com.blackdog.springbootBoardJpa.domain.post.controller.PostController; +import com.blackdog.springbootBoardJpa.domain.post.controller.converter.PostControllerConverter; +import com.blackdog.springbootBoardJpa.domain.post.controller.dto.PostCreateDto; +import com.blackdog.springbootBoardJpa.domain.post.service.PostService; +import com.blackdog.springbootBoardJpa.domain.post.service.converter.PostServiceConverter; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostCreateRequest; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.MockMvc; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@AutoConfigureRestDocs +@WebMvcTest(PostController.class) +@Import({PostServiceConverter.class, PostControllerConverter.class, PostService.class}) +class PostControllerTest { + + @Autowired + MockMvc mockMvc; + + @Autowired + PostControllerConverter ControllerConverter; + + @Autowired + PostServiceConverter serviceConverter; + + @Autowired + ObjectMapper objectMapper; + + @MockBean + PostService service; + + @ParameterizedTest + @DisplayName("존재하는 유저로 게시글을 생성하면 성공한다.") + @MethodSource("provideTestData") + void savePost_Dto_SaveReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { + // given + given(service.savePost(any(Long.class), any(PostCreateRequest.class))) + .willReturn(response); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.post("/posts/{userId}", 1L) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(dto))) + .andExpect(status().isCreated()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.title").value(dto.title())) + .andDo(document("post-save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("userId").description("유저 아이디") + ), + requestFields( + fieldWithPath("title").type(JsonFieldType.STRING).description("게시물 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시물 내용")), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시물 ID"), + fieldWithPath("title").type(JsonFieldType.STRING).description("게시물 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시물 내용"), + fieldWithPath("name").type(JsonFieldType.STRING).description("작성자 이름"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시물 작성 시간"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("게시물 수정 시간") + ) + )); + + // then + verify(service, times(1)).savePost(any(), any()); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 수정하면 성공한다.") + @MethodSource("provideTestData") + void updatePost_Dto_UpdateReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { + // given + given(service.updatePost(any(Long.class), any(Long.class), any(PostUpdateRequest.class))) + .willReturn(response); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.patch("/posts/{postId}/user/{userId}", 1L, 1L) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(dto))) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.title").value(dto.title())) + .andDo(document("post-update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("postId").description("게시글 아이디"), + parameterWithName("userId").description("유저 아이디") + ), + requestFields( + fieldWithPath("title").type(JsonFieldType.STRING).description("게시물 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시물 내용")), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시물 ID"), + fieldWithPath("title").type(JsonFieldType.STRING).description("게시물 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시물 내용"), + fieldWithPath("name").type(JsonFieldType.STRING).description("작성자 이름"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시물 작성 시간"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("게시물 수정 시간") + ) + )); + + // then + verify(service, times(1)).updatePost(any(), any(), any()); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 삭제하면 성공한다.") + @MethodSource("provideTestData") + void deletePostById_Dto_Delete() throws Exception { + // given + doNothing().when(service) + .deletePostById(any(Long.class), any(Long.class)); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.delete("/posts/{postId}/user/{userId}", 1L, 1L)) + .andExpect(status().isNoContent()) + .andDo(document("post - delete", + pathParameters( + parameterWithName("postId").description("게시글 아이디"), + parameterWithName("userId").description("회원 아이디") + ) + )); + + // then + verify(service, times(1)).deletePostById(any(), any()); + } + + @ParameterizedTest + @DisplayName("모든 게시글을 조회하면 성공한다.") + @MethodSource("provideTestData") + void getAllPosts_Void_ReturnResponses() throws Exception { + // given + given(service.findAllPosts(pageable)).willReturn(responses); + + // when + mockMvc.perform(get("/posts") + .param("page", "0") + .param("size", "5")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.postResponses.content", Matchers.hasSize(2))) + .andDo(print()) + .andDo(document("posts - get", preprocessResponse(prettyPrint()), + responseFields( + + fieldWithPath("postResponses").type(JsonFieldType.OBJECT).description("게시물 응답"), + fieldWithPath("postResponses.content").type(JsonFieldType.ARRAY).description("게시물 정보 배열"), + fieldWithPath("postResponses.content[].id").type(JsonFieldType.NUMBER).description("게시물 ID"), + fieldWithPath("postResponses.content[].title").type(JsonFieldType.STRING).description("게시물 제목"), + fieldWithPath("postResponses.content[].content").type(JsonFieldType.STRING).description("게시물 내용"), + fieldWithPath("postResponses.content[].name").type(JsonFieldType.STRING).description("게시물 작성자 이름"), + fieldWithPath("postResponses.content[].createdAt").type(JsonFieldType.STRING).description("게시물 생성일"), + fieldWithPath("postResponses.content[].updatedAt").type(JsonFieldType.STRING).description("게시물 갱신일"), + + fieldWithPath("postResponses.pageable").type(JsonFieldType.OBJECT).description("pageable").ignored(), + fieldWithPath("postResponses.last").type(JsonFieldType.BOOLEAN).description("last").ignored(), + fieldWithPath("postResponses.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), + fieldWithPath("postResponses.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), + fieldWithPath("postResponses.size").type(JsonFieldType.NUMBER).description("size").ignored(), + fieldWithPath("postResponses.number").type(JsonFieldType.NUMBER).description("number").ignored(), + fieldWithPath("postResponses.sort.empty").type(JsonFieldType.BOOLEAN).description("sort.empty").ignored(), + fieldWithPath("postResponses.sort.sorted").type(JsonFieldType.BOOLEAN).description("sort.sorted").ignored(), + fieldWithPath("postResponses.sort.unsorted").type(JsonFieldType.BOOLEAN).description("sort.unsorted").ignored(), + fieldWithPath("postResponses.first").type(JsonFieldType.BOOLEAN).description("first").ignored(), + fieldWithPath("postResponses.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements").ignored(), + fieldWithPath("postResponses.empty").type(JsonFieldType.BOOLEAN).description("empty").ignored() + ))); + + // then + verify(service, times(1)).findAllPosts(pageable); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 조회하면 성공한다.") + @MethodSource("provideTestData") + void getPostById_id_ReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { + // given + given(service.findPostById(any(Long.class))).willReturn(response); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}", 1L) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.title").value(dto.title())) + .andDo(print()) + .andDo(document("post-get", + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("postId").description("게시글 아이디") + ), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 아이디"), + fieldWithPath("title").type(JsonFieldType.STRING).description("게시글 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시글 본문"), + fieldWithPath("name").type(JsonFieldType.STRING).description("게시글 작성자 이름"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시글 작성일"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("게시글 갱신일") + ) + )); + + // then + verify(service, times(1)).findPostById(any()); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 유저로 조회하면 성공한다.") + @MethodSource("provideTestData") + void getPostsByUserId_id_ReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { + // given + given(service.findPostsByUserId(any(), any())).willReturn(responses); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/user/{userId}", 1L) + .param("page", "0") + .param("size", "5") + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.postResponses.content", Matchers.hasSize(2))) + .andDo(print()) + .andDo(document("post-get-by-user", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + queryParameters( + parameterWithName("page").description("페이지"), + parameterWithName("size").description("사이즈") + ), + pathParameters( + parameterWithName("userId").description("회원 아이디") + ), + responseFields( + fieldWithPath("postResponses").type(JsonFieldType.OBJECT).description("게시글 응답"), + fieldWithPath("postResponses.content").type(JsonFieldType.ARRAY).description("게시글 정보 배열"), + fieldWithPath("postResponses.content[].id").type(JsonFieldType.NUMBER).description("게시글 아이디"), + fieldWithPath("postResponses.content[].title").type(JsonFieldType.STRING).description("게시글 이름"), + fieldWithPath("postResponses.content[].content").type(JsonFieldType.STRING).description("게시글 나이"), + fieldWithPath("postResponses.content[].name").type(JsonFieldType.STRING).description("게시글 작성자 이름"), + fieldWithPath("postResponses.content[].createdAt").type(JsonFieldType.STRING).description("게시글 생성일"), + fieldWithPath("postResponses.content[].updatedAt").type(JsonFieldType.STRING).description("게시글 갱신일"), + + fieldWithPath("postResponses.pageable").type(JsonFieldType.OBJECT).description("pageable").ignored(), + fieldWithPath("postResponses.last").type(JsonFieldType.BOOLEAN).description("last").ignored(), + fieldWithPath("postResponses.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), + fieldWithPath("postResponses.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), + fieldWithPath("postResponses.size").type(JsonFieldType.NUMBER).description("size").ignored(), + fieldWithPath("postResponses.number").type(JsonFieldType.NUMBER).description("number").ignored(), + fieldWithPath("postResponses.sort.empty").type(JsonFieldType.BOOLEAN).description("sort.empty").ignored(), + fieldWithPath("postResponses.sort.sorted").type(JsonFieldType.BOOLEAN).description("sort.sorted").ignored(), + fieldWithPath("postResponses.sort.unsorted").type(JsonFieldType.BOOLEAN).description("sort.unsorted").ignored(), + fieldWithPath("postResponses.first").type(JsonFieldType.BOOLEAN).description("first").ignored(), + fieldWithPath("postResponses.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements").ignored(), + fieldWithPath("postResponses.empty").type(JsonFieldType.BOOLEAN).description("empty").ignored() + ) + )); + + // then + verify(service, times(1)).findPostsByUserId(any(), any()); + } + + static User user = User.builder() + .name(new Name("둘리")) + .age(new Age(1200)) + .hobby("고길동 등골 빼먹기") + .build(); + + static List postCreateDto = List.of( + new PostCreateDto("subject1", "content1"), + new PostCreateDto("subject2", "content2") + ); + + static List postResponses = List.of( + new PostResponse(1L, "subject1", "content1", user.getName().getNameValue(), LocalDateTime.now(), LocalDateTime.now()), + new PostResponse(2L, "subject2", "content2", user.getName().getNameValue(), LocalDateTime.now(), LocalDateTime.now()) + ); + + static Pageable pageable = PageRequest.of(0, 5); + + static PostResponses responses = new PostResponses( + new PageImpl<>(postResponses) + ); + + static Stream provideTestData() { + return IntStream.range(0, 2) + .mapToObj(i -> Arguments.of(postCreateDto.get(i), postResponses.get(i))); + } +} diff --git a/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java b/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java new file mode 100644 index 000000000..511febafd --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java @@ -0,0 +1,177 @@ +package com.blackdog.springbootBoardJpa.post.service; + +import com.blackdog.springbootBoardJpa.domain.post.service.PostService; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostCreateRequest; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; +import com.blackdog.springbootBoardJpa.domain.user.model.User; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; +import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; +import com.blackdog.springbootBoardJpa.domain.user.repository.UserRepository; +import com.blackdog.springbootBoardJpa.global.exception.PostNotFoundException; +import com.blackdog.springbootBoardJpa.global.exception.UserNotFoundException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.*; + +@SpringBootTest +@Transactional +class PostServiceTest { + + @Autowired + PostService postService; + + @Autowired + UserRepository userRepository; + + static User savedUser; + + @BeforeEach + void setup() { + User user = User.builder() + .name(new Name("홍길동")) + .age(new Age(1200)) + .hobby("동에번쩍하는거") + .build(); + savedUser = userRepository.save(user); + } + + @ParameterizedTest + @DisplayName("존재하는 유저로 게시글을 생성하면 성공한다.") + @MethodSource("providePostCreateRequest") + void savePost_Dto_SaveReturnResponse(PostCreateRequest request) { + // when + PostResponse savedPost = postService.savePost(savedUser.getId(), request); + + // then + PostResponse result = postService.findPostById(savedPost.id()); + assertThat(result.title()).isEqualTo(savedPost.title()); + } + + @ParameterizedTest + @DisplayName("존재하지 않는 유저로 게시글을 생성하면 실패한다.") + @MethodSource("providePostCreateRequest") + void savePost_Dto_Exception(PostCreateRequest request) { + // given + Long userId = Long.MAX_VALUE; + + // when + Exception exception = catchException(() -> postService.savePost(userId, request)); + + // then + assertThat(exception).isInstanceOf(UserNotFoundException.class); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 수정하면 성공한다.") + @MethodSource("providePostCreateRequest") + void updatePost_Dto_UpdateReturnResponse(PostCreateRequest request) { + // given + PostResponse savedPost = postService.savePost(savedUser.getId(), request); + PostUpdateRequest updateRequest = new PostUpdateRequest("수정제목", "수정본문"); + + // when + PostResponse updatePost = postService.updatePost(savedUser.getId(), savedPost.id(), updateRequest); + + // then + PostResponse result = postService.findPostById(updatePost.id()); + assertThat(result.title()).isEqualTo(updatePost.title()); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 삭제하면 성공한다.") + @MethodSource("providePostCreateRequest") + void deletePostById_Dto_Delete(PostCreateRequest request) { + // given + PostResponse savedPost = postService.savePost(savedUser.getId(), request); + + // when + postService.deletePostById(savedUser.getId(), savedPost.id()); + + // then + assertThatThrownBy(() -> postService.findPostById(savedPost.id())) + .isInstanceOf(PostNotFoundException.class); + } + + @ParameterizedTest + @DisplayName("모든 게시글을 조회하면 성공한다.") + @MethodSource("providePostCreateRequest") + void findAllPosts_Void_ReturnResponses(PostCreateRequest request) { + // given + postService.savePost(savedUser.getId(), request); + Pageable pageable = PageRequest.of(0, 10); + + // when + PostResponses result = postService.findAllPosts(pageable); + + // then + assertThat(result).isNotNull(); + assertThat(result.postResponses()).isNotEmpty(); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 조회하면 성공한다.") + @MethodSource("providePostCreateRequest") + void findPostById_id_ReturnResponse(PostCreateRequest request) { + // given + PostResponse savedPost = postService.savePost(savedUser.getId(), request); + + // when + PostResponse result = postService.findPostById(savedPost.id()); + + // then + assertThat(result.title()).isEqualTo(savedPost.title()); + } + + @ParameterizedTest + @DisplayName("존재하지 않는 게시글을 조회하면 실패한다.") + @MethodSource("providePostCreateRequest") + void findPostById_id_Exception(PostCreateRequest request) { + + // when + Exception exception = catchException(() -> postService.findPostById(100L)); + + // then + assertThat(exception).isInstanceOf(PostNotFoundException.class); + } + + @ParameterizedTest + @DisplayName("존재하는 게시글을 조회하면 성공한다.") + @MethodSource("providePostCreateRequest") + void findPostsByUserId_id_ReturnResponse(PostCreateRequest request) { + // given + PostResponse savedPost = postService.savePost(savedUser.getId(), request); + Pageable pageable = PageRequest.of(0, 10); + + // when + PostResponses result = postService.findPostsByUserId(savedUser.getId(), pageable); + + // then + assertThat(result).isNotNull(); + assertThat(result.postResponses()).isNotEmpty(); + } + + static List postCreateRequests = List.of( + new PostCreateRequest("subject1", "content1"), + new PostCreateRequest("subject2", "content2") + ); + + static Stream providePostCreateRequest() { + return postCreateRequests.stream() + .map(Arguments::of); + } + +} From 33e57f3184bc7587c395f049b83e76e0367ad956 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 15:59:40 +0900 Subject: [PATCH 25/47] =?UTF-8?q?[Test]=20:=20RestDocs=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 9 ++++++ src/docs/asciidoc/post.adoc | 54 ++++++++++++++++++++++++++++++++++ src/docs/asciidoc/user.adoc | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/docs/asciidoc/index.adoc create mode 100644 src/docs/asciidoc/post.adoc create mode 100644 src/docs/asciidoc/user.adoc diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc new file mode 100644 index 000000000..89347b248 --- /dev/null +++ b/src/docs/asciidoc/index.adoc @@ -0,0 +1,9 @@ += Spring Boot JPA Board + +== Post API docs + +xref:post.adoc[Post API] + +== User API docs + +xref:user.adoc[User API] diff --git a/src/docs/asciidoc/post.adoc b/src/docs/asciidoc/post.adoc new file mode 100644 index 000000000..95a65dd6b --- /dev/null +++ b/src/docs/asciidoc/post.adoc @@ -0,0 +1,54 @@ +:hardbreaks: +ifndef::snippets[] +:snippets: ./build/generated-snippets +endif::[] + += API docs +:doctype: book +:source-highlighter:: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + +== Post + +=== 게시물 생성 + +.Request +include::{snippets}/post-save/http-request.adoc[] +include::{snippets}/post-save/request-fields.adoc[] + +.Response +include::{snippets}/post-save/http-response.adoc[] +include::{snippets}/post-save/response-fields.adoc[] + +--- + +=== 게시물 전체 조회 + +.Request +include::{snippets}/posts - get/http-request.adoc[] + +.Response +include::{snippets}/posts - get/http-response.adoc[] +include::{snippets}/posts - get/response-fields.adoc[] + +--- + +=== 게시글 단건 조회 + +.Request +include::{snippets}/post-get/http-request.adoc[] + +.Response +include::{snippets}/post-get/http-response.adoc[] +include::{snippets}/post-get/response-fields.adoc[] + +=== 게시물 작성자로 조회 + +.Request +include::{snippets}/post-get-by-user/http-request.adoc[] + +.Response +include::{snippets}/post-get-by-user/http-response.adoc[] +include::{snippets}/post-get-by-user/response-fields.adoc[] diff --git a/src/docs/asciidoc/user.adoc b/src/docs/asciidoc/user.adoc new file mode 100644 index 000000000..1c4c31d04 --- /dev/null +++ b/src/docs/asciidoc/user.adoc @@ -0,0 +1,56 @@ +:hardbreaks: +ifndef::snippets[] +:snippets: ./build/generated-snippets +endif::[] + += API docs +:doctype: book +:source-highlighter:: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + +== 유저 + +--- +=== 유저 생성 + +.Request +include::{snippets}/user-save/http-request.adoc[] +include::{snippets}/user-save/request-fields.adoc[] + +.Response +include::{snippets}/user-save/http-response.adoc[] +include::{snippets}/user-save/response-fields.adoc[] + +--- + +=== 유저 삭제 + +.Request +include::{snippets}/user-delete/http-request.adoc[] + +.Response +include::{snippets}/user-delete/http-response.adoc[] + +--- + +=== 유저 단건 조회 + +.Request +include::{snippets}/user-get/http-request.adoc[] + +.Response +include::{snippets}/user-get/http-response.adoc[] +include::{snippets}/user-get/response-fields.adoc[] + +--- + +=== 유저 전체 조회 + +.Request +include::{snippets}/users-get/http-request.adoc[] + +.Response +include::{snippets}/users-get/http-response.adoc[] +include::{snippets}/users-get/response-fields.adoc[] From cbd4c7915a08241e52423f9185a6c701b46cae92 Mon Sep 17 00:00:00 2001 From: KimMinheee <99alsgmlalsl@naver.com> Date: Tue, 8 Aug 2023 16:02:12 +0900 Subject: [PATCH 26/47] =?UTF-8?q?[Style]=20:=20RestDocs=20html=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BOOT-INF/classes/static/docs/index.html | 471 ++++++++++++ BOOT-INF/classes/static/docs/post.html | 894 +++++++++++++++++++++++ BOOT-INF/classes/static/docs/user.html | 790 ++++++++++++++++++++ src/main/resources/static/doc/index.html | 471 ++++++++++++ src/main/resources/static/doc/post.html | 894 +++++++++++++++++++++++ src/main/resources/static/doc/user.html | 790 ++++++++++++++++++++ 6 files changed, 4310 insertions(+) create mode 100644 BOOT-INF/classes/static/docs/index.html create mode 100644 BOOT-INF/classes/static/docs/post.html create mode 100644 BOOT-INF/classes/static/docs/user.html create mode 100644 src/main/resources/static/doc/index.html create mode 100644 src/main/resources/static/doc/post.html create mode 100644 src/main/resources/static/doc/user.html diff --git a/BOOT-INF/classes/static/docs/index.html b/BOOT-INF/classes/static/docs/index.html new file mode 100644 index 000000000..e2e1e0273 --- /dev/null +++ b/BOOT-INF/classes/static/docs/index.html @@ -0,0 +1,471 @@ + + + + + + + +Spring Boot JPA Board + + + + + +
+
+

Post API docs

+
+ +
+
+
+

User API docs

+
+ +
+
+
+ + + \ No newline at end of file diff --git a/BOOT-INF/classes/static/docs/post.html b/BOOT-INF/classes/static/docs/post.html new file mode 100644 index 000000000..68c47f8c2 --- /dev/null +++ b/BOOT-INF/classes/static/docs/post.html @@ -0,0 +1,894 @@ + + + + + + + + +API docs + + + + + +
+
+

Post

+
+
+

게시물 생성

+
+
Request
+
+
POST /posts/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 52
+Host: localhost:8080
+
+{
+  "title" : "subject2",
+  "content" : "content2"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

title

String

게시물 제목

content

String

게시물 내용

+
+
Response
+
+
HTTP/1.1 201 Created
+Content-Type: application/json
+Content-Length: 163
+
+{
+  "id" : 2,
+  "title" : "subject2",
+  "content" : "content2",
+  "name" : "둘리",
+  "createdAt" : "2023-08-08 15:26:14",
+  "updatedAt" : "2023-08-08 15:26:14"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

게시물 ID

title

String

게시물 제목

content

String

게시물 내용

name

String

작성자 이름

createdAt

String

게시물 작성 시간

updatedAt

String

게시물 수정 시간

+
+
+
+

게시물 전체 조회

+
+
Request
+
+
GET /posts?page=0&size=5 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 724
+
+{
+  "postResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "title" : "subject1",
+      "content" : "content1",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    }, {
+      "id" : 2,
+      "title" : "subject2",
+      "content" : "content2",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

postResponses

Object

게시물 응답

postResponses.content

Array

게시물 정보 배열

postResponses.content[].id

Number

게시물 ID

postResponses.content[].title

String

게시물 제목

postResponses.content[].content

String

게시물 내용

postResponses.content[].name

String

게시물 작성자 이름

postResponses.content[].createdAt

String

게시물 생성일

postResponses.content[].updatedAt

String

게시물 갱신일

postResponses.totalElements

Number

totalElements

postResponses.totalPages

Number

totalPages

+
+
+
+

게시글 단건 조회

+
+
Request
+
+
GET /posts/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 163
+
+{
+  "id" : 2,
+  "title" : "subject2",
+  "content" : "content2",
+  "name" : "둘리",
+  "createdAt" : "2023-08-08 15:26:14",
+  "updatedAt" : "2023-08-08 15:26:14"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

게시글 아이디

title

String

게시글 제목

content

String

게시글 본문

name

String

게시글 작성자 이름

createdAt

String

게시글 작성일

updatedAt

String

게시글 갱신일

+
+
+

게시물 작성자로 조회

+
+
Request
+
+
GET /posts/user/1?page=0&size=5 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 724
+
+{
+  "postResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "title" : "subject1",
+      "content" : "content1",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    }, {
+      "id" : 2,
+      "title" : "subject2",
+      "content" : "content2",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

postResponses

Object

게시글 응답

postResponses.content

Array

게시글 정보 배열

postResponses.content[].id

Number

게시글 아이디

postResponses.content[].title

String

게시글 이름

postResponses.content[].content

String

게시글 나이

postResponses.content[].name

String

게시글 작성자 이름

postResponses.content[].createdAt

String

게시글 생성일

postResponses.content[].updatedAt

String

게시글 갱신일

postResponses.totalElements

Number

totalElements

postResponses.totalPages

Number

totalPages

+
+
+
+
+ + + \ No newline at end of file diff --git a/BOOT-INF/classes/static/docs/user.html b/BOOT-INF/classes/static/docs/user.html new file mode 100644 index 000000000..7119d04e3 --- /dev/null +++ b/BOOT-INF/classes/static/docs/user.html @@ -0,0 +1,790 @@ + + + + + + + + +API docs + + + + + +
+
+

유저

+
+
+
+

유저 생성

+
+
Request
+
+
POST /users HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Accept: application/json
+Content-Length: 40
+Host: localhost:8080
+
+{"name":"Kim","age":23,"hobby":"축구"}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

name

String

회원 이름

age

Number

나이

hobby

String

취미

+
+
Response
+
+
HTTP/1.1 201 Created
+Content-Type: application/json
+Content-Length: 115
+
+{"id":1,"name":"Kim","age":23,"hobby":"축구","createdAt":"2023-08-08 15:26:15","updatedAt":"2023-08-08 15:26:15"}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

회원 ID

name

String

회원 이름

age

Number

나이

hobby

String

취미

createdAt

String

회원 가입 시간

updatedAt

String

회원 수정 시간

+
+
+
+

유저 삭제

+
+
Request
+
+
DELETE /users/1 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 204 No Content
+
+
+
+
+
+

유저 단건 조회

+
+
Request
+
+
GET /users/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Accept: application/json
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 147
+
+{
+  "id" : 1,
+  "name" : "Park",
+  "age" : 26,
+  "hobby" : "여행",
+  "createdAt" : "2023-08-08 15:26:15",
+  "updatedAt" : "2023-08-08 15:26:15"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

유저 아이디

name

String

유저 이름

age

Number

유저 나이

hobby

String

유저 취미

createdAt

String

유저 생성일

updatedAt

String

유저 갱신일

+
+
+
+

유저 전체 조회

+
+
Request
+
+
GET /users?page=0&size=5 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 692
+
+{
+  "userResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "name" : "Park",
+      "age" : 26,
+      "hobby" : "여행",
+      "createdAt" : "2023-08-08 15:26:15",
+      "updatedAt" : "2023-08-08 15:26:15"
+    }, {
+      "id" : 2,
+      "name" : "Park",
+      "age" : 26,
+      "hobby" : "여행",
+      "createdAt" : "2023-08-08 15:26:15",
+      "updatedAt" : "2023-08-08 15:26:15"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

userResponses

Object

유저 응답

userResponses.content[]

Array

유저 정보 배열

userResponses.content[].id

Number

유저 아이디

userResponses.content[].name

String

유저 이름

userResponses.content[].age

Number

유저 나이

userResponses.content[].hobby

String

유저 취미

userResponses.content[].createdAt

String

유저 생성일

userResponses.content[].updatedAt

String

유저 갱신일

userResponses.totalElements

Number

totalElements

userResponses.totalPages

Number

totalPages

+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/static/doc/index.html b/src/main/resources/static/doc/index.html new file mode 100644 index 000000000..e2e1e0273 --- /dev/null +++ b/src/main/resources/static/doc/index.html @@ -0,0 +1,471 @@ + + + + + + + +Spring Boot JPA Board + + + + + +
+
+

Post API docs

+
+ +
+
+
+

User API docs

+
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/static/doc/post.html b/src/main/resources/static/doc/post.html new file mode 100644 index 000000000..68c47f8c2 --- /dev/null +++ b/src/main/resources/static/doc/post.html @@ -0,0 +1,894 @@ + + + + + + + + +API docs + + + + + +
+
+

Post

+
+
+

게시물 생성

+
+
Request
+
+
POST /posts/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 52
+Host: localhost:8080
+
+{
+  "title" : "subject2",
+  "content" : "content2"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

title

String

게시물 제목

content

String

게시물 내용

+
+
Response
+
+
HTTP/1.1 201 Created
+Content-Type: application/json
+Content-Length: 163
+
+{
+  "id" : 2,
+  "title" : "subject2",
+  "content" : "content2",
+  "name" : "둘리",
+  "createdAt" : "2023-08-08 15:26:14",
+  "updatedAt" : "2023-08-08 15:26:14"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

게시물 ID

title

String

게시물 제목

content

String

게시물 내용

name

String

작성자 이름

createdAt

String

게시물 작성 시간

updatedAt

String

게시물 수정 시간

+
+
+
+

게시물 전체 조회

+
+
Request
+
+
GET /posts?page=0&size=5 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 724
+
+{
+  "postResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "title" : "subject1",
+      "content" : "content1",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    }, {
+      "id" : 2,
+      "title" : "subject2",
+      "content" : "content2",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

postResponses

Object

게시물 응답

postResponses.content

Array

게시물 정보 배열

postResponses.content[].id

Number

게시물 ID

postResponses.content[].title

String

게시물 제목

postResponses.content[].content

String

게시물 내용

postResponses.content[].name

String

게시물 작성자 이름

postResponses.content[].createdAt

String

게시물 생성일

postResponses.content[].updatedAt

String

게시물 갱신일

postResponses.totalElements

Number

totalElements

postResponses.totalPages

Number

totalPages

+
+
+
+

게시글 단건 조회

+
+
Request
+
+
GET /posts/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 163
+
+{
+  "id" : 2,
+  "title" : "subject2",
+  "content" : "content2",
+  "name" : "둘리",
+  "createdAt" : "2023-08-08 15:26:14",
+  "updatedAt" : "2023-08-08 15:26:14"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

게시글 아이디

title

String

게시글 제목

content

String

게시글 본문

name

String

게시글 작성자 이름

createdAt

String

게시글 작성일

updatedAt

String

게시글 갱신일

+
+
+

게시물 작성자로 조회

+
+
Request
+
+
GET /posts/user/1?page=0&size=5 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 724
+
+{
+  "postResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "title" : "subject1",
+      "content" : "content1",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    }, {
+      "id" : 2,
+      "title" : "subject2",
+      "content" : "content2",
+      "name" : "둘리",
+      "createdAt" : "2023-08-08 15:26:14",
+      "updatedAt" : "2023-08-08 15:26:14"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

postResponses

Object

게시글 응답

postResponses.content

Array

게시글 정보 배열

postResponses.content[].id

Number

게시글 아이디

postResponses.content[].title

String

게시글 이름

postResponses.content[].content

String

게시글 나이

postResponses.content[].name

String

게시글 작성자 이름

postResponses.content[].createdAt

String

게시글 생성일

postResponses.content[].updatedAt

String

게시글 갱신일

postResponses.totalElements

Number

totalElements

postResponses.totalPages

Number

totalPages

+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/static/doc/user.html b/src/main/resources/static/doc/user.html new file mode 100644 index 000000000..7119d04e3 --- /dev/null +++ b/src/main/resources/static/doc/user.html @@ -0,0 +1,790 @@ + + + + + + + + +API docs + + + + + +
+
+

유저

+
+
+
+

유저 생성

+
+
Request
+
+
POST /users HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Accept: application/json
+Content-Length: 40
+Host: localhost:8080
+
+{"name":"Kim","age":23,"hobby":"축구"}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

name

String

회원 이름

age

Number

나이

hobby

String

취미

+
+
Response
+
+
HTTP/1.1 201 Created
+Content-Type: application/json
+Content-Length: 115
+
+{"id":1,"name":"Kim","age":23,"hobby":"축구","createdAt":"2023-08-08 15:26:15","updatedAt":"2023-08-08 15:26:15"}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

회원 ID

name

String

회원 이름

age

Number

나이

hobby

String

취미

createdAt

String

회원 가입 시간

updatedAt

String

회원 수정 시간

+
+
+
+

유저 삭제

+
+
Request
+
+
DELETE /users/1 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 204 No Content
+
+
+
+
+
+

유저 단건 조회

+
+
Request
+
+
GET /users/1 HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Accept: application/json
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 147
+
+{
+  "id" : 1,
+  "name" : "Park",
+  "age" : 26,
+  "hobby" : "여행",
+  "createdAt" : "2023-08-08 15:26:15",
+  "updatedAt" : "2023-08-08 15:26:15"
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

id

Number

유저 아이디

name

String

유저 이름

age

Number

유저 나이

hobby

String

유저 취미

createdAt

String

유저 생성일

updatedAt

String

유저 갱신일

+
+
+
+

유저 전체 조회

+
+
Request
+
+
GET /users?page=0&size=5 HTTP/1.1
+Host: localhost:8080
+
+
+
+
Response
+
+
HTTP/1.1 200 OK
+Content-Type: application/json
+Content-Length: 692
+
+{
+  "userResponses" : {
+    "content" : [ {
+      "id" : 1,
+      "name" : "Park",
+      "age" : 26,
+      "hobby" : "여행",
+      "createdAt" : "2023-08-08 15:26:15",
+      "updatedAt" : "2023-08-08 15:26:15"
+    }, {
+      "id" : 2,
+      "name" : "Park",
+      "age" : 26,
+      "hobby" : "여행",
+      "createdAt" : "2023-08-08 15:26:15",
+      "updatedAt" : "2023-08-08 15:26:15"
+    } ],
+    "pageable" : "INSTANCE",
+    "totalPages" : 1,
+    "totalElements" : 2,
+    "last" : true,
+    "size" : 2,
+    "number" : 0,
+    "sort" : {
+      "empty" : true,
+      "sorted" : false,
+      "unsorted" : true
+    },
+    "numberOfElements" : 2,
+    "first" : true,
+    "empty" : false
+  }
+}
+
+
+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

userResponses

Object

유저 응답

userResponses.content[]

Array

유저 정보 배열

userResponses.content[].id

Number

유저 아이디

userResponses.content[].name

String

유저 이름

userResponses.content[].age

Number

유저 나이

userResponses.content[].hobby

String

유저 취미

userResponses.content[].createdAt

String

유저 생성일

userResponses.content[].updatedAt

String

유저 갱신일

userResponses.totalElements

Number

totalElements

userResponses.totalPages

Number

totalPages

+
+
+
+
+ + + \ No newline at end of file From 189ba50783c6f4343192f7d7bd088d0e8f889774 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:26:10 +0900 Subject: [PATCH 27/47] =?UTF-8?q?[Refactor]=20:=20Dto=20=EB=82=B4=20messag?= =?UTF-8?q?e=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/controller/dto/PostCreateDto.java | 4 ++-- .../domain/post/controller/dto/PostUpdateDto.java | 4 ++-- .../domain/user/controller/dto/UserCreateDto.java | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java index 2ec0345c3..191d34dab 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostCreateDto.java @@ -5,11 +5,11 @@ import jakarta.validation.constraints.Size; public record PostCreateDto( - @NotBlank + @NotBlank(message = "title은 공백일 수 없습니다.") String title, @NotNull - @Size(max = 255) + @Size(max = 255, message = "내용은 최대 255자 입니다.") String content ) { } diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java index b89adfe94..34d1d82a8 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/dto/PostUpdateDto.java @@ -5,11 +5,11 @@ import jakarta.validation.constraints.Size; public record PostUpdateDto( - @NotBlank + @NotBlank(message = "title은 공백일 수 없습니다.") String title, @NotNull - @Size(max = 255) + @Size(max = 255, message = "내용은 최대 255자 입니다.") String content ) { } diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java index 37ecd7710..c62450928 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/dto/UserCreateDto.java @@ -4,13 +4,13 @@ import jakarta.validation.constraints.NotBlank; public record UserCreateDto( - @NotBlank + @NotBlank(message = "이름은 공백일 수 없습니다.") String name, - @Min(0) + @Min(value = 0, message = "나이는 최소 0세 이상이어야 합니다") int age, - @NotBlank + @NotBlank(message = "이름은 공백일 수 없습니다.") String hobby ) { } From 4fef77754a639cbd08dd9af32134ee4a96420541 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:27:15 +0900 Subject: [PATCH 28/47] =?UTF-8?q?[Refactor]=20:=20PostController=20@Reques?= =?UTF-8?q?tMapping=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20api=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostController.java | 74 +++++++++++++++---- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java index 2dfb93420..ae42124f9 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/controller/PostController.java @@ -6,6 +6,7 @@ import com.blackdog.springbootBoardJpa.domain.post.service.PostService; import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponse; import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostResponses; +import com.blackdog.springbootBoardJpa.global.response.SuccessResponse; import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; @@ -13,8 +14,10 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import static com.blackdog.springbootBoardJpa.global.response.SuccessCode.POST_DELETE_SUCCESS; + @RestController -@RequestMapping("/posts") +@RequestMapping(path = "/posts", produces = MediaType.APPLICATION_JSON_VALUE) public class PostController { private final PostService postService; @@ -28,45 +31,84 @@ public PostController( this.controllerConverter = controllerConverter; } - @PostMapping(value = "/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) + /** + * [게시글 저장 API] + * + * @param userId + * @param createDto + * @return ResponseEntity + */ + @PostMapping(path = "/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity savePost(@PathVariable Long userId, @Valid @RequestBody PostCreateDto createDto) { return ResponseEntity .status(HttpStatus.CREATED) - .body(postService.savePost(userId, controllerConverter.toCreateRequest(createDto))); + .body(postService.savePost( + userId, + controllerConverter.toCreateRequest(createDto))); } - @PatchMapping(value = "/{postId}/user/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) + /** + * [게시글 수정 API] + * + * @param postId + * @param userId + * @param updateDto + * @return ResponseEntity + */ + @PatchMapping(path = "/{postId}/user/{userId}", consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity updatePost(@PathVariable Long postId, @PathVariable Long userId, @Valid @RequestBody PostUpdateDto updateDto) { return ResponseEntity .status(HttpStatus.OK) - .body(postService.updatePost(postId, userId, controllerConverter.toUpdateRequest(updateDto))); + .body(postService.updatePost( + postId, + userId, + controllerConverter.toUpdateRequest(updateDto))); } + /** + * [게시글 삭제 API] + * + * @param postId + * @param userId + * @return ResponseEntity + */ @DeleteMapping(path = "/{postId}/user/{userId}") - public ResponseEntity deletePost(@PathVariable Long postId, @PathVariable Long userId) { + public ResponseEntity deletePost(@PathVariable Long postId, @PathVariable Long userId) { postService.deletePostById(userId, postId); - return ResponseEntity.noContent().build(); - } - - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getAllPosts(Pageable pageable) { return ResponseEntity .status(HttpStatus.OK) - .body(postService.findAllPosts(pageable)); + .body(SuccessResponse.of(POST_DELETE_SUCCESS)); } - @GetMapping(value = "/{postId}", produces = MediaType.APPLICATION_JSON_VALUE) + /** + * [게시글 상세 조회 API] + * + * @param postId + * @return ResponseEntity + */ + @GetMapping(path = "/{postId}") public ResponseEntity getPostById(@PathVariable Long postId) { return ResponseEntity .status(HttpStatus.OK) .body(postService.findPostById(postId)); } - @GetMapping(value = "/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getPostsByUserId(@PathVariable Long userId, Pageable pageable) { + /** + * [게시글 전체 조회 API] + * + * @param userId + * @param pageable + * @return ResponseEntity + */ + @GetMapping + public ResponseEntity getPosts(@RequestParam(defaultValue = "-1") Long userId, Pageable pageable) { + PostResponses postResponses = (userId == -1) + ? postService.findAllPosts(pageable) + : postService.findPostsByUserId(userId, pageable); + return ResponseEntity .status(HttpStatus.OK) - .body(postService.findPostsByUserId(userId, pageable)); + .body(postResponses); } } From d7caca498ff1c49f3e999799c53e892c25d96455 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:28:10 +0900 Subject: [PATCH 29/47] =?UTF-8?q?[Refactor]=20:=20Post=20entity=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=82=B4=20@Getter=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EB=82=B4=EB=B6=80=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=B6=94=E3=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/model/Post.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java index 280e94e30..af1067d7f 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/model/Post.java @@ -1,10 +1,12 @@ package com.blackdog.springbootBoardJpa.domain.post.model; -import com.blackdog.springbootBoardJpa.domain.post.service.dto.PostUpdateRequest; import com.blackdog.springbootBoardJpa.domain.user.model.User; import com.blackdog.springbootBoardJpa.global.entity.BaseEntity; import jakarta.persistence.*; +import lombok.Getter; +import org.springframework.util.Assert; +@Getter @Entity @Table(name = "posts") public class Post extends BaseEntity { @@ -43,7 +45,7 @@ public Post user(User user) { this.user = user; return this; } - + public Post build() { return new Post( this.title, @@ -57,6 +59,9 @@ private Post( String content, User user ) { + Assert.notNull(title, "title은 공백일 수 없습니다."); + Assert.notNull(content, "content는 공백일 수 없습니다."); + this.title = title; this.content = content; this.user = user; @@ -86,8 +91,9 @@ private void changeContent(String content) { this.content = content; } - public void changePost(PostUpdateRequest post) { - changeTitle(post.title()); - changeContent(post.content()); + public void changePost(String title, String content) { + changeTitle(title); + changeContent(content); } + } From 66fa377295403b4abdfaaf258c53ca912b88f438 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:29:42 +0900 Subject: [PATCH 30/47] =?UTF-8?q?[Refactor]=20:=20Response=20Dto=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=82=B4=20@Valid=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/service/dto/PostResponse.java | 7 ------- .../domain/user/service/dto/UserResponse.java | 8 -------- 2 files changed, 15 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java index 3a54b8ded..73a602985 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/dto/PostResponse.java @@ -8,24 +8,17 @@ import java.time.LocalDateTime; public record PostResponse( - @NotNull Long id, - @NotBlank String title, - @NotNull - @Size(max = 255) String content, - @NotBlank String name, - @NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime createdAt, - @NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime updatedAt ) { diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java index f007c7132..0f760ff8c 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/dto/UserResponse.java @@ -1,29 +1,21 @@ package com.blackdog.springbootBoardJpa.domain.user.service.dto; import com.fasterxml.jackson.annotation.JsonFormat; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.time.LocalDateTime; public record UserResponse( - @NotNull Long id, - @NotBlank String name, - @NotNull int age, - @NotBlank String hobby, - @NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime createdAt, - @NotNull @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime updatedAt ) { From 9826104a9e83b7d8591f9f5e363448cb93443493 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:30:33 +0900 Subject: [PATCH 31/47] =?UTF-8?q?[Feat]=20:=20SuccessResponse=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EC=8B=9C=20=EC=82=AC=EC=9A=A9=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/controller/UserController.java | 7 ++++++- .../global/response/SuccessResponse.java | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessResponse.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java index 65d580200..680b2ea87 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java @@ -5,6 +5,7 @@ import com.blackdog.springbootBoardJpa.domain.user.service.UserService; import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; +import com.blackdog.springbootBoardJpa.global.response.SuccessResponse; import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; @@ -12,6 +13,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import static com.blackdog.springbootBoardJpa.global.response.SuccessCode.USER_DELETE_SUCCESS; + @RestController @RequestMapping("/users") public class UserController { @@ -37,7 +40,9 @@ public ResponseEntity saveUser(@Valid @RequestBody UserCreateDto c @DeleteMapping(value = "/{userId}") public ResponseEntity deleteUser(@PathVariable long userId) { service.deleteUserById(userId); - return ResponseEntity.noContent().build(); + return ResponseEntity + .status(HttpStatus.OK) + .body(SuccessResponse.of(USER_DELETE_SUCCESS)); } @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessResponse.java new file mode 100644 index 000000000..933f983b0 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/response/SuccessResponse.java @@ -0,0 +1,17 @@ +package com.blackdog.springbootBoardJpa.global.response; + +public class SuccessResponse { + private String message; + + private SuccessResponse(String message) { + this.message = message; + } + + public static SuccessResponse of(SuccessCode code) { + return new SuccessResponse(code.getMessage()); + } + + public String getMessage() { + return message; + } +} From 5a7bf8a51038bde981216077dbacc1e926e1951c Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:32:31 +0900 Subject: [PATCH 32/47] =?UTF-8?q?[Refactor]=20:=20GlobalExceptionHandler?= =?UTF-8?q?=20=EB=82=B4=20=EC=BD=94=EB=93=9C=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20-=20final=20=ED=82=A4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20PermissionDeniedHandler=20http=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/GlobalExceptionHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java index a19f8394b..6ecab357d 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/exception/GlobalExceptionHandler.java @@ -15,7 +15,7 @@ @RestControllerAdvice public class GlobalExceptionHandler { - private Logger log = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { @@ -34,7 +34,7 @@ public ResponseEntity permissionDeniedExceptionHandler(Permission log.warn("{}", errorResponse); log.warn("{}", e.getCause()); return ResponseEntity - .status(HttpStatus.NON_AUTHORITATIVE_INFORMATION) + .status(HttpStatus.FORBIDDEN) .body(errorResponse); } From b0b633b46dd7a4beb5b9f16a638536a318100358 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:33:07 +0900 Subject: [PATCH 33/47] =?UTF-8?q?[Refactor]=20:=20ErrorResponse=20?= =?UTF-8?q?=EB=82=B4=20get=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20&=20=EA=B0=9C=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springbootBoardJpa/global/response/ErrorResponse.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java index 772dee27a..eebfdaff4 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/response/ErrorResponse.java @@ -1,7 +1,6 @@ package com.blackdog.springbootBoardJpa.global.response; public class ErrorResponse { - private final String code; private final String message; @@ -28,4 +27,11 @@ public String toString() { '}'; } + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } } From aa1eb85c34b64df597a4b872c378a81a4395e3da Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:34:53 +0900 Subject: [PATCH 34/47] =?UTF-8?q?[Refactor]=20:=20PostService=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95=20-=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=ED=95=98?= =?UTF-8?q?=EB=8A=94=20boolean=20=EB=B3=80=EC=88=98=20=EC=B6=94=EC=B6=9C?= =?UTF-8?q?=20-=20=EA=B0=9C=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springbootBoardJpa/domain/post/service/PostService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java index 028659454..7b426333c 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java @@ -56,7 +56,8 @@ public PostResponse updatePost(Long userId, Long postId, @Valid PostUpdateReques .findById(postId) .orElseThrow(() -> new PostNotFoundException(NOT_FOUND_POST)); - if (!Objects.equals(targetPost.getUser().getId(), userId)) { + boolean isOwner = Objects.equals(targetPost.getUser().getId(), userId); + if (!isOwner) { throw new PermissionDeniedException(PERMISSION_DENIED); } @@ -70,7 +71,8 @@ public void deletePostById(Long userId, Long id) { } public PostResponses findAllPosts(Pageable pageable) { - return converter.toResponses(postRepository.findAll(pageable)); + return converter.toResponses( + postRepository.findAll(pageable)); } public PostResponse findPostById(Long id) { From 281491a5213538ec683f13e1963f72d55ba982f0 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:36:47 +0900 Subject: [PATCH 35/47] =?UTF-8?q?[Feat]=20:=20JasyptTest=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/JasyptConfig.java | 80 +++++++++++++++++++ .../resources/jasypt-encryptor-password.txt | 1 + .../springbootBoardJpa/global/JasyptTest.java | 43 ++++++++++ 3 files changed, 124 insertions(+) create mode 100644 src/main/java/com/blackdog/springbootBoardJpa/global/config/JasyptConfig.java create mode 100644 src/main/resources/jasypt-encryptor-password.txt create mode 100644 src/test/java/com/blackdog/springbootBoardJpa/global/JasyptTest.java diff --git a/src/main/java/com/blackdog/springbootBoardJpa/global/config/JasyptConfig.java b/src/main/java/com/blackdog/springbootBoardJpa/global/config/JasyptConfig.java new file mode 100644 index 000000000..bf4db2ba9 --- /dev/null +++ b/src/main/java/com/blackdog/springbootBoardJpa/global/config/JasyptConfig.java @@ -0,0 +1,80 @@ +package com.blackdog.springbootBoardJpa.global.config; + +import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; +import org.jasypt.encryption.StringEncryptor; +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Configuration +@ConfigurationProperties("jasypt.encryptor") +@EnableEncryptableProperties +public class JasyptConfig { + + private String algorithm; + private int poolSize; + private String stringOutputType; + private int keyObtentionIterations; + + @Bean("jasyptStringEncryptor") + public StringEncryptor jasyptStringEncryptor() { + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + SimpleStringPBEConfig configuration = new SimpleStringPBEConfig(); + configuration.setAlgorithm(algorithm); + configuration.setPoolSize(poolSize); + configuration.setStringOutputType(stringOutputType); + configuration.setKeyObtentionIterations(keyObtentionIterations); + configuration.setPassword(getJasyptEncryptorPassword()); + encryptor.setConfig(configuration); + return encryptor; + } + + private String getJasyptEncryptorPassword() { + try { + ClassPathResource resource = new ClassPathResource("src/main/resources/jasypt-encryptor-password.txt"); + return String.join("", Files.readAllLines(Paths.get(resource.getPath()))); + } catch (IOException e) { + throw new RuntimeException("jasypt password file not found"); + } + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public int getPoolSize() { + return poolSize; + } + + public void setPoolSize(int poolSize) { + this.poolSize = poolSize; + } + + public String getStringOutputType() { + return stringOutputType; + } + + public void setStringOutputType(String stringOutputType) { + this.stringOutputType = stringOutputType; + } + + public int getKeyObtentionIterations() { + return keyObtentionIterations; + } + + public void setKeyObtentionIterations(int keyObtentionIterations) { + this.keyObtentionIterations = keyObtentionIterations; + } +} + diff --git a/src/main/resources/jasypt-encryptor-password.txt b/src/main/resources/jasypt-encryptor-password.txt new file mode 100644 index 000000000..a3f3f49b3 --- /dev/null +++ b/src/main/resources/jasypt-encryptor-password.txt @@ -0,0 +1 @@ +key1234! diff --git a/src/test/java/com/blackdog/springbootBoardJpa/global/JasyptTest.java b/src/test/java/com/blackdog/springbootBoardJpa/global/JasyptTest.java new file mode 100644 index 000000000..81b01982f --- /dev/null +++ b/src/test/java/com/blackdog/springbootBoardJpa/global/JasyptTest.java @@ -0,0 +1,43 @@ +package com.blackdog.springbootBoardJpa.global; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class JasyptTest { + + @Test + void jasypt() { + String url = "jdbc:mysql://localhost:3306/jpa_board_mission"; + String username = "root"; + String password = "root1234!"; + + String encryptUrl = jasyptEncrypt(url); + String encryptUsername = jasyptEncrypt(username); + String encryptPassword = jasyptEncrypt(password); + + System.out.println("encrypt url : " + encryptUrl); + System.out.println("encrypt username: " + encryptUsername); + System.out.println("encrypt password: " + encryptPassword); + + assertThat(url).isEqualTo(jasyptDecrypt(encryptUrl)); + } + + private String jasyptEncrypt(String input) { + String key = "key1234!"; + StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + encryptor.setAlgorithm("PBEWithMD5AndDES"); + encryptor.setPassword(key); + return encryptor.encrypt(input); + } + + private String jasyptDecrypt(String input) { + String key = "key1234!"; + StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); + encryptor.setAlgorithm("PBEWithMD5AndDES"); + encryptor.setPassword(key); + return encryptor.decrypt(input); + } + +} From 054756def3768b18f79c4bc4c936eaff176e16a7 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:37:35 +0900 Subject: [PATCH 36/47] =?UTF-8?q?[Test]=20:=20PostControllerTest=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/PostControllerTest.java | 103 +++++++++++------- 1 file changed, 63 insertions(+), 40 deletions(-) diff --git a/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java b/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java index 65ce5e783..6498b97f1 100644 --- a/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java +++ b/src/test/java/com/blackdog/springbootBoardJpa/post/controller/PostControllerTest.java @@ -13,8 +13,10 @@ import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.Assertions; import org.hamcrest.Matchers; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -30,7 +32,10 @@ import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.List; import java.util.stream.IntStream; @@ -44,6 +49,7 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.restdocs.request.RequestDocumentation.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -105,6 +111,25 @@ void savePost_Dto_SaveReturnResponse(PostCreateDto dto, PostResponse response) t verify(service, times(1)).savePost(any(), any()); } + @Test + @DisplayName("Dto의 필드가 valid하지 않다면 유효성 검사에 실패한다.") + void savePost_Dto_ThrowMethodArgumentNotValidException() throws Exception { + PostCreateDto dto = new PostCreateDto("", "내용"); + MockHttpServletRequestBuilder builder = post("/posts/{userId}", 1L) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(dto)); + + MvcResult result = mockMvc.perform(builder) + .andDo(print()) + .andReturn(); + + String message = result.getResponse().getContentAsString(StandardCharsets.UTF_8); + + Assertions.assertThat(message).contains("Method Argument가 적절하지 않습니다."); + + } + + @ParameterizedTest @DisplayName("존재하는 게시글을 수정하면 성공한다.") @MethodSource("provideTestData") @@ -154,7 +179,7 @@ void deletePostById_Dto_Delete() throws Exception { // when mockMvc.perform(RestDocumentationRequestBuilders.delete("/posts/{postId}/user/{userId}", 1L, 1L)) - .andExpect(status().isNoContent()) + .andExpect(status().isOk()) .andDo(document("post - delete", pathParameters( parameterWithName("postId").description("게시글 아이디"), @@ -166,6 +191,39 @@ void deletePostById_Dto_Delete() throws Exception { verify(service, times(1)).deletePostById(any(), any()); } + @ParameterizedTest + @DisplayName("존재하는 게시글을 조회하면 성공한다.") + @MethodSource("provideTestData") + void getPostById_id_ReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { + // given + given(service.findPostById(any(Long.class))).willReturn(response); + + // when + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}", 1L) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.title").value(dto.title())) + .andDo(print()) + .andDo(document("post-get", + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("postId").description("게시글 아이디") + ), + responseFields( + fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 아이디"), + fieldWithPath("title").type(JsonFieldType.STRING).description("게시글 제목"), + fieldWithPath("content").type(JsonFieldType.STRING).description("게시글 본문"), + fieldWithPath("name").type(JsonFieldType.STRING).description("게시글 작성자 이름"), + fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시글 작성일"), + fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("게시글 갱신일") + ) + )); + + // then + verify(service, times(1)).findPostById(any()); + } + @ParameterizedTest @DisplayName("모든 게시글을 조회하면 성공한다.") @MethodSource("provideTestData") @@ -183,7 +241,6 @@ void getAllPosts_Void_ReturnResponses() throws Exception { .andDo(print()) .andDo(document("posts - get", preprocessResponse(prettyPrint()), responseFields( - fieldWithPath("postResponses").type(JsonFieldType.OBJECT).description("게시물 응답"), fieldWithPath("postResponses.content").type(JsonFieldType.ARRAY).description("게시물 정보 배열"), fieldWithPath("postResponses.content[].id").type(JsonFieldType.NUMBER).description("게시물 ID"), @@ -211,39 +268,6 @@ void getAllPosts_Void_ReturnResponses() throws Exception { verify(service, times(1)).findAllPosts(pageable); } - @ParameterizedTest - @DisplayName("존재하는 게시글을 조회하면 성공한다.") - @MethodSource("provideTestData") - void getPostById_id_ReturnResponse(PostCreateDto dto, PostResponse response) throws Exception { - // given - given(service.findPostById(any(Long.class))).willReturn(response); - - // when - mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/{postId}", 1L) - .contentType(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) - .andExpect(jsonPath("$.title").value(dto.title())) - .andDo(print()) - .andDo(document("post-get", - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("postId").description("게시글 아이디") - ), - responseFields( - fieldWithPath("id").type(JsonFieldType.NUMBER).description("게시글 아이디"), - fieldWithPath("title").type(JsonFieldType.STRING).description("게시글 제목"), - fieldWithPath("content").type(JsonFieldType.STRING).description("게시글 본문"), - fieldWithPath("name").type(JsonFieldType.STRING).description("게시글 작성자 이름"), - fieldWithPath("createdAt").type(JsonFieldType.STRING).description("게시글 작성일"), - fieldWithPath("updatedAt").type(JsonFieldType.STRING).description("게시글 갱신일") - ) - )); - - // then - verify(service, times(1)).findPostById(any()); - } - @ParameterizedTest @DisplayName("존재하는 게시글을 유저로 조회하면 성공한다.") @MethodSource("provideTestData") @@ -252,9 +276,10 @@ void getPostsByUserId_id_ReturnResponse(PostCreateDto dto, PostResponse response given(service.findPostsByUserId(any(), any())).willReturn(responses); // when - mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/user/{userId}", 1L) + mockMvc.perform(get("/posts") .param("page", "0") .param("size", "5") + .param("userId", "1") .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON_VALUE)) @@ -265,10 +290,8 @@ void getPostsByUserId_id_ReturnResponse(PostCreateDto dto, PostResponse response preprocessResponse(prettyPrint()), queryParameters( parameterWithName("page").description("페이지"), - parameterWithName("size").description("사이즈") - ), - pathParameters( - parameterWithName("userId").description("회원 아이디") + parameterWithName("size").description("사이즈"), + parameterWithName("userId").description("회원ID") ), responseFields( fieldWithPath("postResponses").type(JsonFieldType.OBJECT).description("게시글 응답"), From dc1636e73049b9ce960f7f39964ed2ba7fcffe62 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:38:00 +0900 Subject: [PATCH 37/47] =?UTF-8?q?[Test]=20:=20PostServiceTest=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/service/PostServiceTest.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java b/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java index 511febafd..64c97a7b8 100644 --- a/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java +++ b/src/test/java/com/blackdog/springbootBoardJpa/post/service/PostServiceTest.java @@ -13,9 +13,11 @@ import com.blackdog.springbootBoardJpa.global.exception.UserNotFoundException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.PageRequest; @@ -51,7 +53,7 @@ void setup() { @ParameterizedTest @DisplayName("존재하는 유저로 게시글을 생성하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void savePost_Dto_SaveReturnResponse(PostCreateRequest request) { // when PostResponse savedPost = postService.savePost(savedUser.getId(), request); @@ -63,7 +65,7 @@ void savePost_Dto_SaveReturnResponse(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하지 않는 유저로 게시글을 생성하면 실패한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void savePost_Dto_Exception(PostCreateRequest request) { // given Long userId = Long.MAX_VALUE; @@ -77,7 +79,7 @@ void savePost_Dto_Exception(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하는 게시글을 수정하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void updatePost_Dto_UpdateReturnResponse(PostCreateRequest request) { // given PostResponse savedPost = postService.savePost(savedUser.getId(), request); @@ -93,7 +95,7 @@ void updatePost_Dto_UpdateReturnResponse(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하는 게시글을 삭제하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void deletePostById_Dto_Delete(PostCreateRequest request) { // given PostResponse savedPost = postService.savePost(savedUser.getId(), request); @@ -108,7 +110,7 @@ void deletePostById_Dto_Delete(PostCreateRequest request) { @ParameterizedTest @DisplayName("모든 게시글을 조회하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void findAllPosts_Void_ReturnResponses(PostCreateRequest request) { // given postService.savePost(savedUser.getId(), request); @@ -124,7 +126,7 @@ void findAllPosts_Void_ReturnResponses(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하는 게시글을 조회하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void findPostById_id_ReturnResponse(PostCreateRequest request) { // given PostResponse savedPost = postService.savePost(savedUser.getId(), request); @@ -138,7 +140,7 @@ void findPostById_id_ReturnResponse(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하지 않는 게시글을 조회하면 실패한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void findPostById_id_Exception(PostCreateRequest request) { // when @@ -150,7 +152,7 @@ void findPostById_id_Exception(PostCreateRequest request) { @ParameterizedTest @DisplayName("존재하는 게시글을 조회하면 성공한다.") - @MethodSource("providePostCreateRequest") + @ArgumentsSource(value = PostCreateRequestDataProvider.class) void findPostsByUserId_id_ReturnResponse(PostCreateRequest request) { // given PostResponse savedPost = postService.savePost(savedUser.getId(), request); @@ -164,14 +166,19 @@ void findPostsByUserId_id_ReturnResponse(PostCreateRequest request) { assertThat(result.postResponses()).isNotEmpty(); } - static List postCreateRequests = List.of( - new PostCreateRequest("subject1", "content1"), - new PostCreateRequest("subject2", "content2") - ); + static class PostCreateRequestDataProvider implements ArgumentsProvider { + + static List postCreateRequests = List.of( + new PostCreateRequest("subject1", "content1"), + new PostCreateRequest("subject2", "content2") + ); + + @Override + public Stream provideArguments(ExtensionContext context) throws Exception { + return postCreateRequests.stream() + .map(Arguments::of); + } - static Stream providePostCreateRequest() { - return postCreateRequests.stream() - .map(Arguments::of); } } From 7ba24e2265a285d82638dd95bab344a86e5ce29e Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:38:24 +0900 Subject: [PATCH 38/47] =?UTF-8?q?[Test]=20:=20UserControllerTest=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserControllerTest.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java index d41fc5266..e6da23e47 100644 --- a/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java +++ b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java @@ -3,18 +3,13 @@ import com.blackdog.springbootBoardJpa.domain.user.controller.UserController; import com.blackdog.springbootBoardJpa.domain.user.controller.converter.UserControllerConverter; import com.blackdog.springbootBoardJpa.domain.user.controller.dto.UserCreateDto; -import com.blackdog.springbootBoardJpa.domain.user.model.vo.Age; -import com.blackdog.springbootBoardJpa.domain.user.model.vo.Name; import com.blackdog.springbootBoardJpa.domain.user.service.UserService; -import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserCreateRequest; import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponse; import com.blackdog.springbootBoardJpa.domain.user.service.dto.UserResponses; import com.fasterxml.jackson.databind.ObjectMapper; import org.hamcrest.Matchers; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -28,7 +23,6 @@ import java.time.LocalDateTime; import java.util.List; -import java.util.stream.Stream; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; @@ -141,10 +135,9 @@ void getUser_Id_ReturnResponse() throws Exception { verify(userService, times(1)).findUserById(anyLong()); } - @ParameterizedTest + @Test @DisplayName("유저를 전체 조회한다.") - @MethodSource("userCreateRequest_Data") - void getAllUsers_Pageable_ReturnResponses(List requests) throws Exception { + void getAllUsers_Pageable_ReturnResponses() throws Exception { //given UserResponse response1 = new UserResponse(1L, "Park", 26, "여행", LocalDateTime.now(), LocalDateTime.now()); UserResponse response2 = new UserResponse(2L, "Park", 26, "여행", LocalDateTime.now(), LocalDateTime.now()); @@ -189,9 +182,4 @@ void getAllUsers_Pageable_ReturnResponses(List requests) thro )); } - private static Stream> userCreateRequest_Data() { - UserCreateRequest request1 = new UserCreateRequest(new Name("Kim"), new Age(23), "축구"); - UserCreateRequest request2 = new UserCreateRequest(new Name("Park"), new Age(44), "배구"); - return Stream.of(List.of(request1, request2)); - } } From 92e3699cd40af39e3866bc9dfcdb955e1293a1d9 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:38:48 +0900 Subject: [PATCH 39/47] =?UTF-8?q?[Feat]=20:=20application-test.yml=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application-test.yml | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/test/resources/application-test.yml diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml deleted file mode 100644 index e219b0b57..000000000 --- a/src/test/resources/application-test.yml +++ /dev/null @@ -1,25 +0,0 @@ -spring: - datasource: - url: jdbc:mysql://localhost:3306/jpa_board_mission - username: root - password: root1234! - driver-class-name: com.mysql.cj.jdbc.Driver - - jpa: - hibernate: - ddl-auto: update - properties: - hibernate: - show_sql: true - format_sql: true - dialect: org.hibernate.dialect.MySQLDialect - open-in-view: false - database-platform: org.hibernate.dialect.MySQL5InnoDBDialect - - logging: - level: - org: - hibernate: - type: - descriptor: - sql: trace From 4ab0ba7443f569ff3a20744e5c8eb9a94cac36cb Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:40:34 +0900 Subject: [PATCH 40/47] =?UTF-8?q?[Feat]=20:=20jascypt=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index 94aa1f17b..f8759bb1b 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + + // jasypt + implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.4' + + // lombok + compileOnly 'org.projectlombok:lombok:1.18.28' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' runtimeOnly 'com.mysql:mysql-connector-j' testImplementation 'org.springframework.boot:spring-boot-starter-test' From 9f5c9a58095dc88663adc4e746753a8f35e6b526 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:41:17 +0900 Subject: [PATCH 41/47] =?UTF-8?q?[Feat]=20:=20gitignore=EC=97=90=20jasycpt?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 57d7cbfa6..527389dfe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ gradlew gradlew.bat HELP.md +src/test/java/com/blackdog/springbootBoardJpa/global/JasyptTest.java +src/main/resources/jasypt-encryptor-password.txt + # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml From c20d65be6f92e616e7d281fc0511139f43879fda Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:43:12 +0900 Subject: [PATCH 42/47] =?UTF-8?q?[Test]=20:=20UserControllerTest=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserControllerTest.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java index e6da23e47..7ceb9fcce 100644 --- a/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java +++ b/src/test/java/com/blackdog/springbootBoardJpa/user/controller/UserControllerTest.java @@ -33,7 +33,6 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.restdocs.request.RequestDocumentation.*; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -87,6 +86,23 @@ void saveUser_Dto_ReturnResponse() throws Exception { )); } + @Test + @DisplayName("유효하지 않은 유저는 생성 못한다.") + void saveUser_Dto_ReturnFailResponse() throws Exception { + //given + UserCreateDto createDto = new UserCreateDto("", -1, ""); + UserResponse response = new UserResponse(1L, "", -1, "", LocalDateTime.now(), LocalDateTime.now()); + given(userService.saveUser(any())).willReturn(response); + + //when & then + mockMvc.perform(post("/users") + .accept(MediaType.APPLICATION_JSON_VALUE) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().is4xxClientError()) + .andDo(print()); + } + @Test @DisplayName("유저를 삭제한다.") void deleteUser_Id_ReturnMessage() throws Exception { @@ -95,7 +111,7 @@ void deleteUser_Id_ReturnMessage() throws Exception { //when & then mockMvc.perform(RestDocumentationRequestBuilders.delete("/users/{userId}", 1L)) - .andExpect(status().isNoContent()) + .andExpect(status().isOk()) .andDo(print()) .andDo(document("user-delete", pathParameters( From 7aa2c200f14887055b42e7dd890bdde2717f5e2d Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:47:13 +0900 Subject: [PATCH 43/47] =?UTF-8?q?[Feat]=20:=20jascypt=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20application.yml=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bfc9da538..f428a2590 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,17 @@ +jasypt: + encryptor: + bean: jasyptStringEncryptor + algorithm: PBEWithMD5AndDES + provider-name: SunJCE + pool-size: 2 + key-obtention-iterations: 1000 + string-output-type: base64 + spring: datasource: - url: jdbc:mysql://localhost:3306/jpa_board_mission - username: root - password: root1234! + url: ENC(NG45wSV57jwBic94bKgq+HcjwGxbDE7Mht8GAr5SI4byEIZ6GT+ieKinVbZk2FC5keTWRJVqsFM=) + username: ENC(VzS/i/YnWH1aKINeKZElTw==) + password: ENC(4ZgFCYri6aT5EMvyXPdAwWPXDkrYIZHO) driver-class-name: com.mysql.cj.jdbc.Driver jpa: From d06196c04b78f0418d93f495345dc95c9e0d42e6 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:47:51 +0900 Subject: [PATCH 44/47] =?UTF-8?q?[Refactor]=20:=20PostService=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/post/service/PostService.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java index 7b426333c..48b6c655a 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/post/service/PostService.java @@ -47,7 +47,8 @@ public PostResponse savePost(Long userId, @Valid PostCreateRequest request) { Post post = converter.toEntity(request, user); - return converter.toResponse(postRepository.save(post)); + return converter.toResponse( + postRepository.save(post)); } @Transactional @@ -61,7 +62,7 @@ public PostResponse updatePost(Long userId, Long postId, @Valid PostUpdateReques throw new PermissionDeniedException(PERMISSION_DENIED); } - targetPost.changePost(dto); + targetPost.changePost(dto.title(), dto.content()); return converter.toResponse(targetPost); } @@ -82,7 +83,8 @@ public PostResponse findPostById(Long id) { } public PostResponses findPostsByUserId(Long userId, Pageable pageable) { - return converter.toResponses(postRepository.findPostsByUserId(userId, pageable)); + return converter.toResponses( + postRepository.findPostsByUserId(userId, pageable)); } } From ded9b9e71853c400abb0acaf2dc6b2715750a9d5 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:48:40 +0900 Subject: [PATCH 45/47] =?UTF-8?q?[Style]=20:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B0=9C=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springbootBoardJpa/domain/user/service/UserService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java index e58e9101a..5d100f13d 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/service/UserService.java @@ -30,7 +30,8 @@ public UserService( @Transactional public UserResponse saveUser(@Valid UserCreateRequest dto) { - return converter.toResponse(repository.save(converter.toEntity(dto))); + return converter.toResponse( + repository.save(converter.toEntity(dto))); } @Transactional From 46e48eff33631ffd4b71c8052d2efaa7a0c564de Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:49:14 +0900 Subject: [PATCH 46/47] =?UTF-8?q?[Refactor]=20:=20UserController=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java index 680b2ea87..cdc7de337 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/controller/UserController.java @@ -16,7 +16,7 @@ import static com.blackdog.springbootBoardJpa.global.response.SuccessCode.USER_DELETE_SUCCESS; @RestController -@RequestMapping("/users") +@RequestMapping(path = "/users", produces = MediaType.APPLICATION_JSON_VALUE) public class UserController { private final UserService service; @@ -30,29 +30,58 @@ public UserController( this.converter = converter; } + /** + * register or update user data + * + * @param createDto + * @return ResponseEntity + * HttpStatus 201 + */ @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity saveUser(@Valid @RequestBody UserCreateDto createDto) { return ResponseEntity .status(HttpStatus.CREATED) - .body(service.saveUser(converter.toRequest(createDto))); + .body(service.saveUser( + converter.toRequest(createDto))); } - @DeleteMapping(value = "/{userId}") - public ResponseEntity deleteUser(@PathVariable long userId) { + /** + * delete user by userId + * + * @param userId + * @return ResponseEntity + * HttpStatus 200 + */ + @DeleteMapping(path = "/{userId}") + public ResponseEntity deleteUser(@PathVariable long userId) { service.deleteUserById(userId); return ResponseEntity .status(HttpStatus.OK) .body(SuccessResponse.of(USER_DELETE_SUCCESS)); } - @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + /** + * search all users with pagination + * + * @param pageable + * @return ResponseEntity + * HttpStatus 200 + */ + @GetMapping public ResponseEntity getAllUsers(Pageable pageable) { return ResponseEntity .status(HttpStatus.OK) .body(service.findAllUsers(pageable)); } - @GetMapping(value = "/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) + /** + * search user by userId + * + * @param userId + * @return ResponseEntity + * HttpStatus 200 + */ + @GetMapping(path = "/{userId}") public ResponseEntity getUser(@PathVariable Long userId) { return ResponseEntity .status(HttpStatus.OK) From 1d35fc392260ec41397107874a5195a7ca613cc2 Mon Sep 17 00:00:00 2001 From: KimMinhee <99alsgmlalsl@naver.com> Date: Fri, 29 Sep 2023 19:49:56 +0900 Subject: [PATCH 47/47] =?UTF-8?q?[Refactor]=20:=20(Age,=20Name)=20vo=20?= =?UTF-8?q?=EB=82=B4=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blackdog/springbootBoardJpa/domain/user/model/vo/Age.java | 2 ++ .../blackdog/springbootBoardJpa/domain/user/model/vo/Name.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java index e50c09d55..36484aa96 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Age.java @@ -3,6 +3,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.validation.constraints.Positive; +import org.springframework.util.Assert; @Embeddable public class Age { @@ -15,6 +16,7 @@ protected Age() { } public Age(final int ageValue) { + Assert.isTrue(ageValue > 0, "나이는 1살 이상이어야 합니다."); this.ageValue = ageValue; } diff --git a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java index f6c3d2b35..8d3f673d2 100644 --- a/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java +++ b/src/main/java/com/blackdog/springbootBoardJpa/domain/user/model/vo/Name.java @@ -1,8 +1,10 @@ package com.blackdog.springbootBoardJpa.domain.user.model.vo; +import io.micrometer.common.util.StringUtils; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.validation.constraints.NotBlank; +import org.springframework.util.Assert; @Embeddable public class Name { @@ -15,6 +17,7 @@ protected Name() { } public Name(final String nameValue) { + Assert.isTrue(StringUtils.isNotBlank(nameValue), "name must be given"); this.nameValue = nameValue; }