From 8f4025a77bf042b6815df4789498f649a0cdd546 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 15:38:06 +0900 Subject: [PATCH 001/102] #1 init: project --- .DS_Store | Bin 0 -> 6148 bytes .gradle/8.4/checksums/checksums.lock | Bin 0 -> 17 bytes .../dependencies-accessors.lock | Bin 0 -> 17 bytes .../8.4/dependencies-accessors/gc.properties | 0 .../8.4/executionHistory/executionHistory.bin | Bin 0 -> 41473 bytes .../executionHistory/executionHistory.lock | Bin 0 -> 17 bytes .gradle/8.4/fileChanges/last-build.bin | Bin 0 -> 1 bytes .gradle/8.4/fileHashes/fileHashes.bin | Bin 0 -> 18747 bytes .gradle/8.4/fileHashes/fileHashes.lock | Bin 0 -> 17 bytes .../8.4/fileHashes/resourceHashesCache.bin | Bin 0 -> 18531 bytes .gradle/8.4/gc.properties | 0 .../buildOutputCleanup.lock | Bin 0 -> 17 bytes .gradle/buildOutputCleanup/cache.properties | 2 + .gradle/buildOutputCleanup/outputFiles.bin | Bin 0 -> 18911 bytes .gradle/file-system.probe | Bin 0 -> 8 bytes .gradle/vcs-1/gc.properties | 0 .idea/.gitignore | 8 + .idea/.name | 1 + .idea/compiler.xml | 15 ++ .idea/dataSources.xml | 12 + .idea/gradle.xml | 17 ++ .idea/jarRepositories.xml | 20 ++ .idea/jpa-buddy.xml | 7 + .idea/misc.xml | 11 + .idea/modules.xml | 8 + .idea/modules/board.main.iml | 8 + .idea/vcs.xml | 6 + HELP.md | 28 ++ build.gradle | 40 +++ .../com/example/board/BoardApplication.class | Bin 0 -> 738 bytes build/resources/main/application.yaml | 1 + .../compileJava/previous-compilation-data.bin | Bin 0 -> 29841 bytes docker-compose.yml | 17 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ mysql/Dockerfile | 6 + mysql/init.sql | 26 ++ settings.gradle | 1 + src/.DS_Store | Bin 0 -> 6148 bytes src/main/.DS_Store | Bin 0 -> 6148 bytes .../com/example/board/BoardApplication.java | 13 + src/main/resources/application.yaml | 1 + .../example/board/BoardApplicationTests.java | 13 + 45 files changed, 609 insertions(+) create mode 100644 .DS_Store create mode 100644 .gradle/8.4/checksums/checksums.lock create mode 100644 .gradle/8.4/dependencies-accessors/dependencies-accessors.lock create mode 100644 .gradle/8.4/dependencies-accessors/gc.properties create mode 100644 .gradle/8.4/executionHistory/executionHistory.bin create mode 100644 .gradle/8.4/executionHistory/executionHistory.lock create mode 100644 .gradle/8.4/fileChanges/last-build.bin create mode 100644 .gradle/8.4/fileHashes/fileHashes.bin create mode 100644 .gradle/8.4/fileHashes/fileHashes.lock create mode 100644 .gradle/8.4/fileHashes/resourceHashesCache.bin create mode 100644 .gradle/8.4/gc.properties create mode 100644 .gradle/buildOutputCleanup/buildOutputCleanup.lock create mode 100644 .gradle/buildOutputCleanup/cache.properties create mode 100644 .gradle/buildOutputCleanup/outputFiles.bin create mode 100644 .gradle/file-system.probe create mode 100644 .gradle/vcs-1/gc.properties create mode 100644 .idea/.gitignore create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/dataSources.xml create mode 100644 .idea/gradle.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/jpa-buddy.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/modules/board.main.iml create mode 100644 .idea/vcs.xml create mode 100644 HELP.md create mode 100644 build.gradle create mode 100644 build/classes/java/main/com/example/board/BoardApplication.class create mode 100644 build/resources/main/application.yaml create mode 100644 build/tmp/compileJava/previous-compilation-data.bin create mode 100644 docker-compose.yml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 mysql/Dockerfile create mode 100644 mysql/init.sql create mode 100644 settings.gradle create mode 100644 src/.DS_Store create mode 100644 src/main/.DS_Store create mode 100644 src/main/java/com/example/board/BoardApplication.java create mode 100644 src/main/resources/application.yaml create mode 100644 src/test/java/com/example/board/BoardApplicationTests.java diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..cbd77902607888fc7b50641b9064381a2f963e87 GIT binary patch literal 6148 zcmeHKOHRWu5FOJBMe3$YHb@pXC?^OdoS+wgHmEETr4+HZSaA|Af;b5`V8@O(AKHdQ zVuMgYGm`!6`FNaH)ngM;nBJEYqA?L=XpDgeJ;Lf<(}6j&F~}MfU02J=Y|+%Ko`v>N z#d|-bC9Ua-O6u(Y{q?K8Zi-@FHFM~*RH9QtT}xnWY&a0-$c>saQ` z$BW*ip^xHDAy@R?8E^*L3=DKS=K6nuzfA8Vza8Q;XTTZwXAFd4F)OC{D66fV&*NGf x(Js+g*e~$_g0Ylhpp;@v_#8PBquL{nVb2YdBCo=F2?zQ`KnU^98TbVTJ^**8O0)m~ literal 0 HcmV?d00001 diff --git a/.gradle/8.4/checksums/checksums.lock b/.gradle/8.4/checksums/checksums.lock new file mode 100644 index 0000000000000000000000000000000000000000..53fe7d1415358ce522cbbee7b60f21b1bc5be88f GIT binary patch literal 17 TcmZP$)XU=j|2Ov@0~7!NF+>DE literal 0 HcmV?d00001 diff --git a/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock b/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock new file mode 100644 index 0000000000000000000000000000000000000000..7c175c2458be4070ef3f59e6ec11f3d61f7eb824 GIT binary patch literal 17 TcmZR6@p5vDluwf@0~7!NHzoub literal 0 HcmV?d00001 diff --git a/.gradle/8.4/dependencies-accessors/gc.properties b/.gradle/8.4/dependencies-accessors/gc.properties new file mode 100644 index 000000000..e69de29bb diff --git a/.gradle/8.4/executionHistory/executionHistory.bin b/.gradle/8.4/executionHistory/executionHistory.bin new file mode 100644 index 0000000000000000000000000000000000000000..15e2ea7cce2ad495cc825a61ece1091ed1bedf71 GIT binary patch literal 41473 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 literal 0 HcmV?d00001 diff --git a/.gradle/8.4/fileHashes/resourceHashesCache.bin b/.gradle/8.4/fileHashes/resourceHashesCache.bin new file mode 100644 index 0000000000000000000000000000000000000000..13fa9e928440556c1c879c09f6435c507dd91359 GIT binary patch literal 18531 zcmeI%F%CgN5CG7nP>2MD1A;AyS5WQN_7we4kJW97=6!AZB&R+g|_MUTox63!ifc#sK)q@=>G?G^*7^_`+}`@_ubLna3OE0a>Kd(cK4TSzjAzIch@tQ zL*@h8TO$J%l_P0mGJjfo`*8K1q~aSnGGCy*>p}EdbjRgJnU888z88Gi-TbLZ=EK@Y zOXf3*^8#O`KV9uB*V`tXuYHg4izI2E7?b@7isq<3m}e+iF&UT}w`=W5UNym_1YWZY|a-&O6|&kLtNbo8E*eo(vD zS9AAO+3XhSG3`eWIWphRr9|xRuhm|3zp?D_*}iu&Kcu~+B2YhdCaXnygZ9v3&(NdU zmQ?98+9T&LdfPoQx9p#-T|bRU-fMry6#)bgKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0;rZwc(SZoJ7llM-j*lJ-ln!arXA+%x~) I&g=TCpEPGiDF6Tf literal 0 HcmV?d00001 diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe new file mode 100644 index 0000000000000000000000000000000000000000..b78019120fe7d6f39d39ab42889f1bde93bd2606 GIT binary patch literal 8 PcmZQzVC=rOSs(=f2)Y7_ literal 0 HcmV?d00001 diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 000000000..e69de29bb diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 000000000..a692a299a --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +board \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 000000000..18d06625c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 000000000..1042223d1 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3308/board + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 000000000..3f9e50192 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 000000000..fdc392fe8 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jpa-buddy.xml b/.idea/jpa-buddy.xml new file mode 100644 index 000000000..898e07a67 --- /dev/null +++ b/.idea/jpa-buddy.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..8671517d8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..c66859a9d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules/board.main.iml b/.idea/modules/board.main.iml new file mode 100644 index 000000000..ec81b5385 --- /dev/null +++ b/.idea/modules/board.main.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/HELP.md b/HELP.md new file mode 100644 index 000000000..1f960cf95 --- /dev/null +++ b/HELP.md @@ -0,0 +1,28 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Gradle documentation](https://docs.gradle.org) +* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.1.5/gradle-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.1.5/gradle-plugin/reference/html/#build-image) +* [Spring Web](https://docs.spring.io/spring-boot/docs/3.1.5/reference/htmlsingle/index.html#web) +* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.1.5/reference/htmlsingle/index.html#data.sql.jpa-and-spring-data) + +### Guides + +The following guides illustrate how to use some features concretely: + +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) +* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/) +* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) + +### Additional Links + +These additional references should also help you: + +* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) + diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..295f98bce --- /dev/null +++ b/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.5' + id 'io.spring.dependency-management' version '1.1.3' +} + +group = 'com.example' +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-web' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' +} + +tasks.named('bootBuildImage') { + builder = 'paketobuildpacks/builder-jammy-base:latest' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/build/classes/java/main/com/example/board/BoardApplication.class b/build/classes/java/main/com/example/board/BoardApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..4904b413220b1bf5cf921d89f055fcd6b4753a5e GIT binary patch literal 738 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 literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..eedcd6b85 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" + +services: + mysql: + build: + context: ./mysql + dockerfile: Dockerfile + ports: + - "3308:3306" + volumes: + - "data_volume:/var/lib/mysql" + environment: + MYSQL_PASSWORD: "root" + MYSQL_ROOT_PASSWORD: "root" + MYSQL_DATABASE: "board" +volumes: + data_volume: \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7f93135c49b765f8051ef9d0a6055ff8e46073d8 GIT binary patch literal 63721 zcmb5Wb9gP!wgnp7wrv|bwr$&XvSZt}Z6`anZSUAlc9NHKf9JdJ;NJVr`=eI(_pMp0 zy1VAAG3FfAOI`{X1O)&90s;U4K;XLp008~hCjbEC_fbYfS%6kTR+JtXK>nW$ZR+`W ze|#J8f4A@M|F5BpfUJb5h>|j$jOe}0oE!`Zf6fM>CR?!y@zU(cL8NsKk`a z6tx5mAkdjD;J=LcJ;;Aw8p!v#ouk>mUDZF@ zK>yvw%+bKu+T{Nk@LZ;zkYy0HBKw06_IWcMHo*0HKpTsEFZhn5qCHH9j z)|XpN&{`!0a>Vl+PmdQc)Yg4A(AG-z!+@Q#eHr&g<9D?7E)_aEB?s_rx>UE9TUq|? z;(ggJt>9l?C|zoO@5)tu?EV0x_7T17q4fF-q3{yZ^ipUbKcRZ4Qftd!xO(#UGhb2y>?*@{xq%`(-`2T^vc=#< zx!+@4pRdk&*1ht2OWk^Z5IAQ0YTAXLkL{(D*$gENaD)7A%^XXrCchN&z2x+*>o2FwPFjWpeaL=!tzv#JOW#( z$B)Nel<+$bkH1KZv3&-}=SiG~w2sbDbAWarg%5>YbC|}*d9hBjBkR(@tyM0T)FO$# zPtRXukGPnOd)~z=?avu+4Co@wF}1T)-uh5jI<1$HLtyDrVak{gw`mcH@Q-@wg{v^c zRzu}hMKFHV<8w}o*yg6p@Sq%=gkd~;`_VGTS?L@yVu`xuGy+dH6YOwcP6ZE`_0rK% zAx5!FjDuss`FQ3eF|mhrWkjux(Pny^k$u_)dyCSEbAsecHsq#8B3n3kDU(zW5yE|( zgc>sFQywFj5}U*qtF9Y(bi*;>B7WJykcAXF86@)z|0-Vm@jt!EPoLA6>r)?@DIobIZ5Sx zsc@OC{b|3%vaMbyeM|O^UxEYlEMHK4r)V-{r)_yz`w1*xV0|lh-LQOP`OP`Pk1aW( z8DSlGN>Ts|n*xj+%If~+E_BxK)~5T#w6Q1WEKt{!Xtbd`J;`2a>8boRo;7u2M&iOop4qcy<)z023=oghSFV zST;?S;ye+dRQe>ygiJ6HCv4;~3DHtJ({fWeE~$H@mKn@Oh6Z(_sO>01JwH5oA4nvK zr5Sr^g+LC zLt(i&ecdmqsIJGNOSUyUpglvhhrY8lGkzO=0USEKNL%8zHshS>Qziu|`eyWP^5xL4 zRP122_dCJl>hZc~?58w~>`P_s18VoU|7(|Eit0-lZRgLTZKNq5{k zE?V=`7=R&ro(X%LTS*f+#H-mGo_j3dm@F_krAYegDLk6UV{`UKE;{YSsn$ z(yz{v1@p|p!0>g04!eRSrSVb>MQYPr8_MA|MpoGzqyd*$@4j|)cD_%^Hrd>SorF>@ zBX+V<@vEB5PRLGR(uP9&U&5=(HVc?6B58NJT_igiAH*q~Wb`dDZpJSKfy5#Aag4IX zj~uv74EQ_Q_1qaXWI!7Vf@ZrdUhZFE;L&P_Xr8l@GMkhc#=plV0+g(ki>+7fO%?Jb zl+bTy7q{w^pTb{>(Xf2q1BVdq?#f=!geqssXp z4pMu*q;iiHmA*IjOj4`4S&|8@gSw*^{|PT}Aw~}ZXU`6=vZB=GGeMm}V6W46|pU&58~P+?LUs%n@J}CSrICkeng6YJ^M? zS(W?K4nOtoBe4tvBXs@@`i?4G$S2W&;$z8VBSM;Mn9 zxcaEiQ9=vS|bIJ>*tf9AH~m&U%2+Dim<)E=}KORp+cZ^!@wI`h1NVBXu{@%hB2Cq(dXx_aQ9x3mr*fwL5!ZryQqi|KFJuzvP zK1)nrKZ7U+B{1ZmJub?4)Ln^J6k!i0t~VO#=q1{?T)%OV?MN}k5M{}vjyZu#M0_*u z8jwZKJ#Df~1jcLXZL7bnCEhB6IzQZ-GcoQJ!16I*39iazoVGugcKA{lhiHg4Ta2fD zk1Utyc5%QzZ$s3;p0N+N8VX{sd!~l*Ta3|t>lhI&G`sr6L~G5Lul`>m z{!^INm?J|&7X=;{XveF!(b*=?9NAp4y&r&N3(GKcW4rS(Ejk|Lzs1PrxPI_owB-`H zg3(Rruh^&)`TKA6+_!n>RdI6pw>Vt1_j&+bKIaMTYLiqhZ#y_=J8`TK{Jd<7l9&sY z^^`hmi7^14s16B6)1O;vJWOF$=$B5ONW;;2&|pUvJlmeUS&F;DbSHCrEb0QBDR|my zIs+pE0Y^`qJTyH-_mP=)Y+u^LHcuZhsM3+P||?+W#V!_6E-8boP#R-*na4!o-Q1 zVthtYhK{mDhF(&7Okzo9dTi03X(AE{8cH$JIg%MEQca`S zy@8{Fjft~~BdzWC(di#X{ny;!yYGK9b@=b|zcKZ{vv4D8i+`ilOPl;PJl{!&5-0!w z^fOl#|}vVg%=n)@_e1BrP)`A zKPgs`O0EO}Y2KWLuo`iGaKu1k#YR6BMySxQf2V++Wo{6EHmK>A~Q5o73yM z-RbxC7Qdh0Cz!nG+7BRZE>~FLI-?&W_rJUl-8FDIaXoNBL)@1hwKa^wOr1($*5h~T zF;%f^%<$p8Y_yu(JEg=c_O!aZ#)Gjh$n(hfJAp$C2he555W5zdrBqjFmo|VY+el;o z=*D_w|GXG|p0**hQ7~9-n|y5k%B}TAF0iarDM!q-jYbR^us(>&y;n^2l0C%@2B}KM zyeRT9)oMt97Agvc4sEKUEy%MpXr2vz*lb zh*L}}iG>-pqDRw7ud{=FvTD?}xjD)w{`KzjNom-$jS^;iw0+7nXSnt1R@G|VqoRhE%12nm+PH?9`(4rM0kfrZzIK9JU=^$YNyLvAIoxl#Q)xxDz!^0@zZ zSCs$nfcxK_vRYM34O<1}QHZ|hp4`ioX3x8(UV(FU$J@o%tw3t4k1QPmlEpZa2IujG&(roX_q*%e`Hq|);0;@k z0z=fZiFckp#JzW0p+2A+D$PC~IsakhJJkG(c;CqAgFfU0Z`u$PzG~-9I1oPHrCw&)@s^Dc~^)#HPW0Ra}J^=|h7Fs*<8|b13ZzG6MP*Q1dkoZ6&A^!}|hbjM{2HpqlSXv_UUg1U4gn z3Q)2VjU^ti1myodv+tjhSZp%D978m~p& z43uZUrraHs80Mq&vcetqfQpQP?m!CFj)44t8Z}k`E798wxg&~aCm+DBoI+nKq}&j^ zlPY3W$)K;KtEajks1`G?-@me7C>{PiiBu+41#yU_c(dITaqE?IQ(DBu+c^Ux!>pCj zLC|HJGU*v+!it1(;3e`6igkH(VA)-S+k(*yqxMgUah3$@C zz`7hEM47xr>j8^g`%*f=6S5n>z%Bt_Fg{Tvmr+MIsCx=0gsu_sF`q2hlkEmisz#Fy zj_0;zUWr;Gz}$BS%Y`meb(=$d%@Crs(OoJ|}m#<7=-A~PQbyN$x%2iXP2@e*nO0b7AwfH8cCUa*Wfu@b)D_>I*%uE4O3 z(lfnB`-Xf*LfC)E}e?%X2kK7DItK6Tf<+M^mX0Ijf_!IP>7c8IZX%8_#0060P{QMuV^B9i<^E`_Qf0pv9(P%_s8D`qvDE9LK9u-jB}J2S`(mCO&XHTS04Z5Ez*vl^T%!^$~EH8M-UdwhegL>3IQ*)(MtuH2Xt1p!fS4o~*rR?WLxlA!sjc2(O znjJn~wQ!Fp9s2e^IWP1C<4%sFF}T4omr}7+4asciyo3DntTgWIzhQpQirM$9{EbQd z3jz9vS@{aOqTQHI|l#aUV@2Q^Wko4T0T04Me4!2nsdrA8QY1%fnAYb~d2GDz@lAtfcHq(P7 zaMBAGo}+NcE-K*@9y;Vt3*(aCaMKXBB*BJcD_Qnxpt75r?GeAQ}*|>pYJE=uZb73 zC>sv)18)q#EGrTG6io*}JLuB_jP3AU1Uiu$D7r|2_zlIGb9 zjhst#ni)Y`$)!fc#reM*$~iaYoz~_Cy7J3ZTiPm)E?%`fbk`3Tu-F#`{i!l5pNEn5 zO-Tw-=TojYhzT{J=?SZj=Z8#|eoF>434b-DXiUsignxXNaR3 zm_}4iWU$gt2Mw5NvZ5(VpF`?X*f2UZDs1TEa1oZCif?Jdgr{>O~7}-$|BZ7I(IKW`{f;@|IZFX*R8&iT= zoWstN8&R;}@2Ka%d3vrLtR|O??ben;k8QbS-WB0VgiCz;<$pBmIZdN!aalyCSEm)crpS9dcD^Y@XT1a3+zpi-`D}e#HV<} z$Y(G&o~PvL-xSVD5D?JqF3?B9rxGWeb=oEGJ3vRp5xfBPlngh1O$yI95EL+T8{GC@ z98i1H9KhZGFl|;`)_=QpM6H?eDPpw~^(aFQWwyXZ8_EEE4#@QeT_URray*mEOGsGc z6|sdXtq!hVZo=d#+9^@lm&L5|q&-GDCyUx#YQiccq;spOBe3V+VKdjJA=IL=Zn%P} zNk=_8u}VhzFf{UYZV0`lUwcD&)9AFx0@Fc6LD9A6Rd1=ga>Mi0)_QxM2ddCVRmZ0d z+J=uXc(?5JLX3=)e)Jm$HS2yF`44IKhwRnm2*669_J=2LlwuF5$1tAo@ROSU@-y+;Foy2IEl2^V1N;fk~YR z?&EP8#t&m0B=?aJeuz~lHjAzRBX>&x=A;gIvb>MD{XEV zV%l-+9N-)i;YH%nKP?>f`=?#`>B(`*t`aiPLoQM(a6(qs4p5KFjDBN?8JGrf3z8>= zi7sD)c)Nm~x{e<^jy4nTx${P~cwz_*a>%0_;ULou3kHCAD7EYkw@l$8TN#LO9jC( z1BeFW`k+bu5e8Ns^a8dPcjEVHM;r6UX+cN=Uy7HU)j-myRU0wHd$A1fNI~`4;I~`zC)3ul#8#^rXVSO*m}Ag>c%_;nj=Nv$rCZ z*~L@C@OZg%Q^m)lc-kcX&a*a5`y&DaRxh6O*dfhLfF+fU5wKs(1v*!TkZidw*)YBP za@r`3+^IHRFeO%!ai%rxy;R;;V^Fr=OJlpBX;(b*3+SIw}7= zIq$*Thr(Zft-RlY)D3e8V;BmD&HOfX+E$H#Y@B3?UL5L~_fA-@*IB-!gItK7PIgG9 zgWuGZK_nuZjHVT_Fv(XxtU%)58;W39vzTI2n&)&4Dmq7&JX6G>XFaAR{7_3QB6zsT z?$L8c*WdN~nZGiscY%5KljQARN;`w$gho=p006z;n(qIQ*Zu<``TMO3n0{ARL@gYh zoRwS*|Niw~cR!?hE{m*y@F`1)vx-JRfqET=dJ5_(076st(=lFfjtKHoYg`k3oNmo_ zNbQEw8&sO5jAYmkD|Zaz_yUb0rC})U!rCHOl}JhbYIDLzLvrZVw0~JO`d*6f;X&?V=#T@ND*cv^I;`sFeq4 z##H5;gpZTb^0Hz@3C*~u0AqqNZ-r%rN3KD~%Gw`0XsIq$(^MEb<~H(2*5G^<2(*aI z%7}WB+TRlMIrEK#s0 z93xn*Ohb=kWFc)BNHG4I(~RPn-R8#0lqyBBz5OM6o5|>x9LK@%HaM}}Y5goCQRt2C z{j*2TtT4ne!Z}vh89mjwiSXG=%DURar~=kGNNaO_+Nkb+tRi~Rkf!7a$*QlavziD( z83s4GmQ^Wf*0Bd04f#0HX@ua_d8 z23~z*53ePD6@xwZ(vdl0DLc=>cPIOPOdca&MyR^jhhKrdQO?_jJh`xV3GKz&2lvP8 zEOwW6L*ufvK;TN{=S&R@pzV^U=QNk^Ec}5H z+2~JvEVA{`uMAr)?Kf|aW>33`)UL@bnfIUQc~L;TsTQ6>r-<^rB8uoNOJ>HWgqMI8 zSW}pZmp_;z_2O5_RD|fGyTxaxk53Hg_3Khc<8AUzV|ZeK{fp|Ne933=1&_^Dbv5^u zB9n=*)k*tjHDRJ@$bp9mrh}qFn*s}npMl5BMDC%Hs0M0g-hW~P*3CNG06G!MOPEQ_ zi}Qs-6M8aMt;sL$vlmVBR^+Ry<64jrm1EI1%#j?c?4b*7>)a{aDw#TfTYKq+SjEFA z(aJ&z_0?0JB83D-i3Vh+o|XV4UP+YJ$9Boid2^M2en@APw&wx7vU~t$r2V`F|7Qfo z>WKgI@eNBZ-+Og<{u2ZiG%>YvH2L3fNpV9J;WLJoBZda)01Rn;o@){01{7E#ke(7U zHK>S#qZ(N=aoae*4X!0A{)nu0R_sKpi1{)u>GVjC+b5Jyl6#AoQ-1_3UDovNSo`T> z?c-@7XX*2GMy?k?{g)7?Sv;SJkmxYPJPs!&QqB12ejq`Lee^-cDveVWL^CTUldb(G zjDGe(O4P=S{4fF=#~oAu>LG>wrU^z_?3yt24FOx>}{^lCGh8?vtvY$^hbZ)9I0E3r3NOlb9I?F-Yc=r$*~l`4N^xzlV~N zl~#oc>U)Yjl0BxV>O*Kr@lKT{Z09OXt2GlvE38nfs+DD7exl|&vT;)>VFXJVZp9Np zDK}aO;R3~ag$X*|hRVY3OPax|PG`@_ESc8E!mHRByJbZQRS38V2F__7MW~sgh!a>98Q2%lUNFO=^xU52|?D=IK#QjwBky-C>zOWlsiiM&1n z;!&1((Xn1$9K}xabq~222gYvx3hnZPg}VMF_GV~5ocE=-v>V=T&RsLBo&`)DOyIj* zLV{h)JU_y*7SdRtDajP_Y+rBkNN*1_TXiKwHH2&p51d(#zv~s#HwbNy?<+(=9WBvo zw2hkk2Dj%kTFhY+$T+W-b7@qD!bkfN#Z2ng@Pd=i3-i?xYfs5Z*1hO?kd7Sp^9`;Y zM2jeGg<-nJD1er@Pc_cSY7wo5dzQX44=%6rn}P_SRbpzsA{6B+!$3B0#;}qwO37G^ zL(V_5JK`XT?OHVk|{_$vQ|oNEpab*BO4F zUTNQ7RUhnRsU`TK#~`)$icsvKh~(pl=3p6m98@k3P#~upd=k*u20SNcb{l^1rUa)>qO997)pYRWMncC8A&&MHlbW?7i^7M`+B$hH~Y|J zd>FYOGQ;j>Zc2e7R{KK7)0>>nn_jYJy&o@sK!4G>-rLKM8Hv)f;hi1D2fAc$+six2 zyVZ@wZ6x|fJ!4KrpCJY=!Mq0;)X)OoS~{Lkh6u8J`eK%u0WtKh6B>GW_)PVc zl}-k`p09qwGtZ@VbYJC!>29V?Dr>>vk?)o(x?!z*9DJ||9qG-&G~#kXxbw{KKYy}J zQKa-dPt~M~E}V?PhW0R26xdA%1T*%ra6SguGu50YHngOTIv)@N|YttEXo#OZfgtP7;H?EeZZxo<}3YlYxtBq znJ!WFR^tmGf0Py}N?kZ(#=VtpC@%xJkDmfcCoBTxq zr_|5gP?u1@vJZbxPZ|G0AW4=tpb84gM2DpJU||(b8kMOV1S3|(yuwZJ&rIiFW(U;5 zUtAW`O6F6Zy+eZ1EDuP~AAHlSY-+A_eI5Gx)%*uro5tljy}kCZU*_d7)oJ>oQSZ3* zneTn`{gnNC&uJd)0aMBzAg021?YJ~b(fmkwZAd696a=0NzBAqBN54KuNDwa*no(^O z6p05bioXUR^uXjpTol*ppHp%1v9e)vkoUAUJyBx3lw0UO39b0?^{}yb!$yca(@DUn zCquRF?t=Zb9`Ed3AI6|L{eX~ijVH`VzSMheKoP7LSSf4g>md>`yi!TkoG5P>Ofp+n z(v~rW+(5L96L{vBb^g51B=(o)?%%xhvT*A5btOpw(TKh^g^4c zw>0%X!_0`{iN%RbVk+A^f{w-4-SSf*fu@FhruNL##F~sF24O~u zyYF<3el2b$$wZ_|uW#@Ak+VAGk#e|kS8nL1g>2B-SNMjMp^8;-FfeofY2fphFHO!{ z*!o4oTb{4e;S<|JEs<1_hPsmAlVNk?_5-Fp5KKU&d#FiNW~Y+pVFk@Cua1I{T+1|+ zHx6rFMor)7L)krbilqsWwy@T+g3DiH5MyVf8Wy}XbEaoFIDr~y;@r&I>FMW{ z?Q+(IgyebZ)-i4jNoXQhq4Muy9Fv+OxU;9_Jmn+<`mEC#%2Q_2bpcgzcinygNI!&^ z=V$)o2&Yz04~+&pPWWn`rrWxJ&}8khR)6B(--!9Q zubo}h+1T)>a@c)H^i``@<^j?|r4*{;tQf78(xn0g39IoZw0(CwY1f<%F>kEaJ zp9u|IeMY5mRdAlw*+gSN^5$Q)ShM<~E=(c8QM+T-Qk)FyKz#Sw0EJ*edYcuOtO#~Cx^(M7w5 z3)rl#L)rF|(Vun2LkFr!rg8Q@=r>9p>(t3Gf_auiJ2Xx9HmxYTa|=MH_SUlYL`mz9 zTTS$`%;D-|Jt}AP1&k7PcnfFNTH0A-*FmxstjBDiZX?}%u%Yq94$fUT&z6od+(Uk> zuqsld#G(b$G8tus=M!N#oPd|PVFX)?M?tCD0tS%2IGTfh}3YA3f&UM)W$_GNV8 zQo+a(ml2Km4o6O%gKTCSDNq+#zCTIQ1*`TIJh~k6Gp;htHBFnne))rlFdGqwC6dx2+La1&Mnko*352k0y z+tQcwndQlX`nc6nb$A9?<-o|r*%aWXV#=6PQic0Ok_D;q>wbv&j7cKc!w4~KF#-{6 z(S%6Za)WpGIWf7jZ3svNG5OLs0>vCL9{V7cgO%zevIVMH{WgP*^D9ws&OqA{yr|m| zKD4*07dGXshJHd#e%x%J+qmS^lS|0Bp?{drv;{@{l9ArPO&?Q5=?OO9=}h$oVe#3b z3Yofj&Cb}WC$PxmRRS)H%&$1-)z7jELS}!u!zQ?A^Y{Tv4QVt*vd@uj-^t2fYRzQj zfxGR>-q|o$3sGn^#VzZ!QQx?h9`njeJry}@x?|k0-GTTA4y3t2E`3DZ!A~D?GiJup z)8%PK2^9OVRlP(24P^4_<|D=H^7}WlWu#LgsdHzB%cPy|f8dD3|A^mh4WXxhLTVu_ z@abE{6Saz|Y{rXYPd4$tfPYo}ef(oQWZ=4Bct-=_9`#Qgp4ma$n$`tOwq#&E18$B; z@Bp)bn3&rEi0>fWWZ@7k5WazfoX`SCO4jQWwVuo+$PmSZn^Hz?O(-tW@*DGxuf)V1 zO_xm&;NVCaHD4dqt(-MlszI3F-p?0!-e$fbiCeuaw66h^TTDLWuaV<@C-`=Xe5WL) zwooG7h>4&*)p3pKMS3O!4>-4jQUN}iAMQ)2*70?hP~)TzzR?-f@?Aqy$$1Iy8VGG$ zMM?8;j!pUX7QQD$gRc_#+=raAS577ga-w?jd`vCiN5lu)dEUkkUPl9!?{$IJNxQys z*E4e$eF&n&+AMRQR2gcaFEjAy*r)G!s(P6D&TfoApMFC_*Ftx0|D0@E-=B7tezU@d zZ{hGiN;YLIoSeRS;9o%dEua4b%4R3;$SugDjP$x;Z!M!@QibuSBb)HY!3zJ7M;^jw zlx6AD50FD&p3JyP*>o+t9YWW8(7P2t!VQQ21pHJOcG_SXQD;(5aX#M6x##5H_Re>6lPyDCjxr*R(+HE%c&QN+b^tbT zXBJk?p)zhJj#I?&Y2n&~XiytG9!1ox;bw5Rbj~)7c(MFBb4>IiRATdhg zmiEFlj@S_hwYYI(ki{}&<;_7(Z0Qkfq>am z&LtL=2qc7rWguk3BtE4zL41@#S;NN*-jWw|7Kx7H7~_%7fPt;TIX}Ubo>;Rmj94V> zNB1=;-9AR7s`Pxn}t_6^3ahlq53e&!Lh85uG zec0vJY_6e`tg7LgfrJ3k!DjR)Bi#L@DHIrZ`sK=<5O0Ip!fxGf*OgGSpP@Hbbe&$9 z;ZI}8lEoC2_7;%L2=w?tb%1oL0V+=Z`7b=P&lNGY;yVBazXRYu;+cQDKvm*7NCxu&i;zub zAJh#11%?w>E2rf2e~C4+rAb-&$^vsdACs7 z@|Ra!OfVM(ke{vyiqh7puf&Yp6cd6{DptUteYfIRWG3pI+5< zBVBI_xkBAc<(pcb$!Y%dTW(b;B;2pOI-(QCsLv@U-D1XJ z(Gk8Q3l7Ws46Aktuj>|s{$6zA&xCPuXL-kB`CgYMs}4IeyG*P51IDwW?8UNQd+$i~ zlxOPtSi5L|gJcF@DwmJA5Ju8HEJ>o{{upwIpb!f{2(vLNBw`7xMbvcw<^{Fj@E~1( z?w`iIMieunS#>nXlmUcSMU+D3rX28f?s7z;X=se6bo8;5vM|O^(D6{A9*ChnGH!RG zP##3>LDC3jZPE4PH32AxrqPk|yIIrq~`aL-=}`okhNu9aT%q z1b)7iJ)CN=V#Ly84N_r7U^SH2FGdE5FpTO2 z630TF$P>GNMu8`rOytb(lB2};`;P4YNwW1<5d3Q~AX#P0aX}R2b2)`rgkp#zTxcGj zAV^cvFbhP|JgWrq_e`~exr~sIR$6p5V?o4Wym3kQ3HA+;Pr$bQ0(PmADVO%MKL!^q z?zAM8j1l4jrq|5X+V!8S*2Wl@=7*pPgciTVK6kS1Ge zMsd_u6DFK$jTnvVtE;qa+8(1sGBu~n&F%dh(&c(Zs4Fc#A=gG^^%^AyH}1^?|8quj zl@Z47h$){PlELJgYZCIHHL= z{U8O>Tw4x3<1{?$8>k-P<}1y9DmAZP_;(3Y*{Sk^H^A=_iSJ@+s5ktgwTXz_2$~W9>VVZsfwCm@s0sQ zeB50_yu@uS+e7QoPvdCwDz{prjo(AFwR%C?z`EL{1`|coJHQTk^nX=tvs1<0arUOJ z!^`*x&&BvTYmemyZ)2p~{%eYX=JVR?DYr(rNgqRMA5E1PR1Iw=prk=L2ldy3r3Vg@27IZx43+ywyzr-X*p*d@tZV+!U#~$-q=8c zgdSuh#r?b4GhEGNai)ayHQpk>5(%j5c@C1K3(W1pb~HeHpaqijJZa-e6vq_8t-^M^ zBJxq|MqZc?pjXPIH}70a5vt!IUh;l}<>VX<-Qcv^u@5(@@M2CHSe_hD$VG-eiV^V( zj7*9T0?di?P$FaD6oo?)<)QT>Npf6Og!GO^GmPV(Km0!=+dE&bk#SNI+C9RGQ|{~O*VC+tXK3!n`5 zHfl6>lwf_aEVV3`0T!aHNZLsj$paS$=LL(?b!Czaa5bbSuZ6#$_@LK<(7yrrl+80| z{tOFd=|ta2Z`^ssozD9BINn45NxUeCQis?-BKmU*Kt=FY-NJ+)8S1ecuFtN-M?&42 zl2$G>u!iNhAk*HoJ^4v^9#ORYp5t^wDj6|lx~5w45#E5wVqI1JQ~9l?nPp1YINf++ zMAdSif~_ETv@Er(EFBI^@L4BULFW>)NI+ejHFP*T}UhWNN`I)RRS8za? z*@`1>9ZB}An%aT5K=_2iQmfE;GcBVHLF!$`I99o5GO`O%O_zLr9AG18>&^HkG(;=V z%}c!OBQ~?MX(9h~tajX{=x)+!cbM7$YzTlmsPOdp2L-?GoW`@{lY9U3f;OUo*BwRB z8A+nv(br0-SH#VxGy#ZrgnGD(=@;HME;yd46EgWJ`EL%oXc&lFpc@Y}^>G(W>h_v_ zlN!`idhX+OjL+~T?19sroAFVGfa5tX-D49w$1g2g_-T|EpHL6}K_aX4$K=LTvwtlF zL*z}j{f+Uoe7{-px3_5iKPA<_7W=>Izkk)!l9ez2w%vi(?Y;i8AxRNLSOGDzNoqoI zP!1uAl}r=_871(G?y`i&)-7{u=%nxk7CZ_Qh#!|ITec zwQn`33GTUM`;D2POWnkqngqJhJRlM>CTONzTG}>^Q0wUunQyn|TAiHzyX2_%ATx%P z%7gW)%4rA9^)M<_%k@`Y?RbC<29sWU&5;@|9thf2#zf8z12$hRcZ!CSb>kUp=4N#y zl3hE#y6>kkA8VY2`W`g5Ip?2qC_BY$>R`iGQLhz2-S>x(RuWv)SPaGdl^)gGw7tjR zH@;jwk!jIaCgSg_*9iF|a);sRUTq30(8I(obh^|}S~}P4U^BIGYqcz;MPpC~Y@k_m zaw4WG1_vz2GdCAX!$_a%GHK**@IrHSkGoN>)e}>yzUTm52on`hYot7cB=oA-h1u|R ztH$11t?54Qg2L+i33FPFKKRm1aOjKST{l1*(nps`>sv%VqeVMWjl5+Gh+9);hIP8? zA@$?}Sc z3qIRpba+y5yf{R6G(u8Z^vkg0Fu&D-7?1s=QZU`Ub{-!Y`I?AGf1VNuc^L3v>)>i# z{DV9W$)>34wnzAXUiV^ZpYKw>UElrN_5Xj6{r_3| z$X5PK`e5$7>~9Dj7gK5ash(dvs`vwfk}&RD`>04;j62zoXESkFBklYaKm5seyiX(P zqQ-;XxlV*yg?Dhlx%xt!b0N3GHp@(p$A;8|%# zZ5m2KL|{on4nr>2_s9Yh=r5ScQ0;aMF)G$-9-Ca6%wA`Pa)i?NGFA|#Yi?{X-4ZO_ z^}%7%vkzvUHa$-^Y#aA+aiR5sa%S|Ebyn`EV<3Pc?ax_f>@sBZF1S;7y$CXd5t5=WGsTKBk8$OfH4v|0?0I=Yp}7c=WBSCg!{0n)XmiU;lfx)**zZaYqmDJelxk$)nZyx5`x$6R|fz(;u zEje5Dtm|a%zK!!tk3{i9$I2b{vXNFy%Bf{50X!x{98+BsDr_u9i>G5%*sqEX|06J0 z^IY{UcEbj6LDwuMh7cH`H@9sVt1l1#8kEQ(LyT@&+K}(ReE`ux8gb0r6L_#bDUo^P z3Ka2lRo52Hdtl_%+pwVs14=q`{d^L58PsU@AMf(hENumaxM{7iAT5sYmWh@hQCO^ zK&}ijo=`VqZ#a3vE?`7QW0ZREL17ZvDfdqKGD?0D4fg{7v%|Yj&_jcKJAB)>=*RS* zto8p6@k%;&^ZF>hvXm&$PCuEp{uqw3VPG$9VMdW5$w-fy2CNNT>E;>ejBgy-m_6`& z97L1p{%srn@O_JQgFpa_#f(_)eb#YS>o>q3(*uB;uZb605(iqM$=NK{nHY=+X2*G) zO3-_Xh%aG}fHWe*==58zBwp%&`mge<8uq8;xIxOd=P%9EK!34^E9sk|(Zq1QSz-JVeP12Fp)-`F|KY$LPwUE?rku zY@OJ)Z9A!ojfzfeyJ9;zv2EM7ZQB)AR5xGa-tMn^bl)FmoIiVyJ@!~@%{}qXXD&Ns zPnfe5U+&ohKefILu_1mPfLGuapX@btta5C#gPB2cjk5m4T}Nfi+Vfka!Yd(L?-c~5 z#ZK4VeQEXNPc4r$K00Fg>g#_W!YZ)cJ?JTS<&68_$#cZT-ME`}tcwqg3#``3M3UPvn+pi}(VNNx6y zFIMVb6OwYU(2`at$gHba*qrMVUl8xk5z-z~fb@Q3Y_+aXuEKH}L+>eW__!IAd@V}L zkw#s%H0v2k5-=vh$^vPCuAi22Luu3uKTf6fPo?*nvj$9(u)4$6tvF-%IM+3pt*cgs z_?wW}J7VAA{_~!?))?s6{M=KPpVhg4fNuU*|3THp@_(q!b*hdl{fjRVFWtu^1dV(f z6iOux9hi&+UK=|%M*~|aqFK{Urfl!TA}UWY#`w(0P!KMe1Si{8|o))Gy6d7;!JQYhgMYmXl?3FfOM2nQGN@~Ap6(G z3+d_5y@=nkpKAhRqf{qQ~k7Z$v&l&@m7Ppt#FSNzKPZM z8LhihcE6i=<(#87E|Wr~HKvVWhkll4iSK$^mUHaxgy8*K$_Zj;zJ`L$naPj+^3zTi z-3NTaaKnD5FPY-~?Tq6QHnmDDRxu0mh0D|zD~Y=vv_qig5r-cIbCpxlju&8Sya)@{ zsmv6XUSi)@(?PvItkiZEeN*)AE~I_?#+Ja-r8$(XiXei2d@Hi7Rx8+rZZb?ZLa{;@*EHeRQ-YDadz~M*YCM4&F-r;E#M+@CSJMJ0oU|PQ^ z=E!HBJDMQ2TN*Y(Ag(ynAL8%^v;=~q?s4plA_hig&5Z0x_^Oab!T)@6kRN$)qEJ6E zNuQjg|G7iwU(N8pI@_6==0CL;lRh1dQF#wePhmu@hADFd3B5KIH#dx(2A zp~K&;Xw}F_N6CU~0)QpQk7s$a+LcTOj1%=WXI(U=Dv!6 z{#<#-)2+gCyyv=Jw?Ab#PVkxPDeH|sAxyG`|Ys}A$PW4TdBv%zDz z^?lwrxWR<%Vzc8Sgt|?FL6ej_*e&rhqJZ3Y>k=X(^dytycR;XDU16}Pc9Vn0>_@H+ zQ;a`GSMEG64=JRAOg%~L)x*w{2re6DVprNp+FcNra4VdNjiaF0M^*>CdPkt(m150rCue?FVdL0nFL$V%5y6N z%eLr5%YN7D06k5ji5*p4v$UMM)G??Q%RB27IvH7vYr_^3>1D-M66#MN8tWGw>WED} z5AhlsanO=STFYFs)Il_0i)l)f<8qn|$DW7ZXhf5xI;m+7M5-%P63XFQrG9>DMqHc} zsgNU9nR`b}E^mL5=@7<1_R~j@q_2U^3h|+`7YH-?C=vme1C3m`Fe0HC>pjt6f_XMh zy~-i-8R46QNYneL4t@)<0VU7({aUO?aH`z4V2+kxgH5pYD5)wCh75JqQY)jIPN=U6 z+qi8cGiOtXG2tXm;_CfpH9ESCz#i5B(42}rBJJF$jh<1sbpj^8&L;gzGHb8M{of+} zzF^8VgML2O9nxBW7AvdEt90vp+#kZxWf@A)o9f9}vKJy9NDBjBW zSt=Hcs=YWCwnfY1UYx*+msp{g!w0HC<_SM!VL1(I2PE?CS}r(eh?{I)mQixmo5^p# zV?2R!R@3GV6hwTCrfHiK#3Orj>I!GS2kYhk1S;aFBD_}u2v;0HYFq}Iz1Z(I4oca4 zxquja8$+8JW_EagDHf$a1OTk5S97umGSDaj)gH=fLs9>_=XvVj^Xj9a#gLdk=&3tl zfmK9MNnIX9v{?%xdw7568 zNrZ|roYs(vC4pHB5RJ8>)^*OuyNC>x7ad)tB_}3SgQ96+-JT^Qi<`xi=)_=$Skwv~ zdqeT9Pa`LYvCAn&rMa2aCDV(TMI#PA5g#RtV|CWpgDYRA^|55LLN^uNh*gOU>Z=a06qJ;$C9z8;n-Pq=qZnc1zUwJ@t)L;&NN+E5m zRkQ(SeM8=l-aoAKGKD>!@?mWTW&~)uF2PYUJ;tB^my`r9n|Ly~0c%diYzqs9W#FTjy?h&X3TnH zXqA{QI82sdjPO->f=^K^f>N`+B`q9&rN0bOXO79S&a9XX8zund(kW7O76f4dcWhIu zER`XSMSFbSL>b;Rp#`CuGJ&p$s~G|76){d?xSA5wVg##_O0DrmyEYppyBr%fyWbbv zp`K84JwRNP$d-pJ!Qk|(RMr?*!wi1if-9G#0p>>1QXKXWFy)eB3ai)l3601q8!9JC zvU#ZWWDNKq9g6fYs?JQ)Q4C_cgTy3FhgKb8s&m)DdmL5zhNK#8wWg!J*7G7Qhe9VU zha?^AQTDpYcuN!B+#1dE*X{<#!M%zfUQbj=zLE{dW0XeQ7-oIsGY6RbkP2re@Q{}r_$iiH0xU%iN*ST`A)-EH6eaZB$GA#v)cLi z*MpA(3bYk$oBDKAzu^kJoSUsDd|856DApz={3u8sbQV@JnRkp2nC|)m;#T=DvIL-O zI4vh;g7824l}*`_p@MT4+d`JZ2%6NQh=N9bmgJ#q!hK@_<`HQq3}Z8Ij>3%~<*= zcv=!oT#5xmeGI92lqm9sGVE%#X$ls;St|F#u!?5Y7syhx6q#MVRa&lBmmn%$C0QzU z);*ldgwwCmzM3uglr}!Z2G+?& zf%Dpo&mD%2ZcNFiN-Z0f;c_Q;A%f@>26f?{d1kxIJD}LxsQkB47SAdwinfMILZdN3 zfj^HmTzS3Ku5BxY>ANutS8WPQ-G>v4^_Qndy==P3pDm+Xc?>rUHl-4+^%Sp5atOja z2oP}ftw-rqnb}+khR3CrRg^ibi6?QYk1*i^;kQGirQ=uB9Sd1NTfT-Rbv;hqnY4neE5H1YUrjS2m+2&@uXiAo- zrKUX|Ohg7(6F(AoP~tj;NZlV#xsfo-5reuQHB$&EIAhyZk;bL;k9ouDmJNBAun;H& zn;Of1z_Qj`x&M;5X;{s~iGzBQTY^kv-k{ksbE*Dl%Qf%N@hQCfY~iUw!=F-*$cpf2 z3wix|aLBV0b;W@z^%7S{>9Z^T^fLOI68_;l@+Qzaxo`nAI8emTV@rRhEKZ z?*z_{oGdI~R*#<2{bkz$G~^Qef}$*4OYTgtL$e9q!FY7EqxJ2`zk6SQc}M(k(_MaV zSLJnTXw&@djco1~a(vhBl^&w=$fa9{Sru>7g8SHahv$&Bl(D@(Zwxo_3r=;VH|uc5 zi1Ny)J!<(KN-EcQ(xlw%PNwK8U>4$9nVOhj(y0l9X^vP1TA>r_7WtSExIOsz`nDOP zs}d>Vxb2Vo2e5x8p(n~Y5ggAyvib>d)6?)|E@{FIz?G3PVGLf7-;BxaP;c?7ddH$z zA+{~k^V=bZuXafOv!RPsE1GrR3J2TH9uB=Z67gok+u`V#}BR86hB1xl}H4v`F+mRfr zYhortD%@IGfh!JB(NUNSDh+qDz?4ztEgCz&bIG-Wg7w-ua4ChgQR_c+z8dT3<1?uX z*G(DKy_LTl*Ea!%v!RhpCXW1WJO6F`bgS-SB;Xw9#! z<*K}=#wVu9$`Yo|e!z-CPYH!nj7s9dEPr-E`DXUBu0n!xX~&|%#G=BeM?X@shQQMf zMvr2!y7p_gD5-!Lnm|a@z8Of^EKboZsTMk%5VsJEm>VsJ4W7Kv{<|#4f-qDE$D-W>gWT%z-!qXnDHhOvLk=?^a1*|0j z{pW{M0{#1VcR5;F!!fIlLVNh_Gj zbnW(_j?0c2q$EHIi@fSMR{OUKBcLr{Y&$hrM8XhPByyZaXy|dd&{hYQRJ9@Fn%h3p7*VQolBIV@Eq`=y%5BU~3RPa^$a?ixp^cCg z+}Q*X+CW9~TL29@OOng(#OAOd!)e$d%sr}^KBJ-?-X&|4HTmtemxmp?cT3uA?md4% zT8yZ0U;6Rg6JHy3fJae{6TMGS?ZUX6+gGTT{Q{)SI85$5FD{g-eR%O0KMpWPY`4@O zx!hen1*8^E(*}{m^V_?}(b5k3hYo=T+$&M32+B`}81~KKZhY;2H{7O-M@vbCzuX0n zW-&HXeyr1%I3$@ns-V1~Lb@wIpkmx|8I~ob1Of7i6BTNysEwI}=!nU%q7(V_^+d*G z7G;07m(CRTJup!`cdYi93r^+LY+`M*>aMuHJm(A8_O8C#A*$!Xvddgpjx5)?_EB*q zgE8o5O>e~9IiSC@WtZpF{4Bj2J5eZ>uUzY%TgWF7wdDE!fSQIAWCP)V{;HsU3ap?4 znRsiiDbtN7i9hapO;(|Ew>Ip2TZSvK9Z^N21%J?OiA_&eP1{(Pu_=%JjKy|HOardq ze?zK^K zA%sjF64*Wufad%H<) z^|t>e*h+Z1#l=5wHexzt9HNDNXgM=-OPWKd^5p!~%SIl>Fo&7BvNpbf8{NXmH)o{r zO=aBJ;meX1^{O%q;kqdw*5k!Y7%t_30 zy{nGRVc&5qt?dBwLs+^Sfp;f`YVMSB#C>z^a9@fpZ!xb|b-JEz1LBX7ci)V@W+kvQ89KWA0T~Lj$aCcfW#nD5bt&Y_< z-q{4ZXDqVg?|0o)j1%l0^_it0WF*LCn-+)c!2y5yS7aZIN$>0LqNnkujV*YVes(v$ zY@_-!Q;!ZyJ}Bg|G-~w@or&u0RO?vlt5*9~yeoPV_UWrO2J54b4#{D(D>jF(R88u2 zo#B^@iF_%S>{iXSol8jpmsZuJ?+;epg>k=$d`?GSegAVp3n$`GVDvK${N*#L_1`44 z{w0fL{2%)0|E+qgZtjX}itZz^KJt4Y;*8uSK}Ft38+3>j|K(PxIXXR-t4VopXo#9# zt|F{LWr-?34y`$nLBVV_*UEgA6AUI65dYIbqpNq9cl&uLJ0~L}<=ESlOm?Y-S@L*d z<7vt}`)TW#f%Rp$Q}6@3=j$7Tze@_uZO@aMn<|si{?S}~maII`VTjs&?}jQ4_cut9$)PEqMukwoXobzaKx^MV z2fQwl+;LSZ$qy%Tys0oo^K=jOw$!YwCv^ei4NBVauL)tN%=wz9M{uf{IB(BxK|lT*pFkmNK_1tV`nb%jH=a0~VNq2RCKY(rG7jz!-D^k)Ec)yS%17pE#o6&eY+ z^qN(hQT$}5F(=4lgNQhlxj?nB4N6ntUY6(?+R#B?W3hY_a*)hnr4PA|vJ<6p`K3Z5Hy z{{8(|ux~NLUW=!?9Qe&WXMTAkQnLXg(g=I@(VG3{HE13OaUT|DljyWXPs2FE@?`iU z4GQlM&Q=T<4&v@Fe<+TuXiZQT3G~vZ&^POfmI1K2h6t4eD}Gk5XFGpbj1n_g*{qmD6Xy z`6Vv|lLZtLmrnv*{Q%xxtcWVj3K4M%$bdBk_a&ar{{GWyu#ljM;dII;*jP;QH z#+^o-A4np{@|Mz+LphTD0`FTyxYq#wY)*&Ls5o{0z9yg2K+K7ZN>j1>N&;r+Z`vI| zDzG1LJZ+sE?m?>x{5LJx^)g&pGEpY=fQ-4}{x=ru;}FL$inHemOg%|R*ZXPodU}Kh zFEd5#+8rGq$Y<_?k-}r5zgQ3jRV=ooHiF|@z_#D4pKVEmn5CGV(9VKCyG|sT9nc=U zEoT67R`C->KY8Wp-fEcjjFm^;Cg(ls|*ABVHq8clBE(;~K^b+S>6uj70g? z&{XQ5U&!Z$SO7zfP+y^8XBbiu*Cv-yJG|l-oe*!s5$@Lh_KpxYL2sx`B|V=dETN>5K+C+CU~a_3cI8{vbu$TNVdGf15*>D zz@f{zIlorkY>TRh7mKuAlN9A0>N>SV`X)+bEHms=mfYTMWt_AJtz_h+JMmrgH?mZt zm=lfdF`t^J*XLg7v+iS)XZROygK=CS@CvUaJo&w2W!Wb@aa?~Drtf`JV^cCMjngVZ zv&xaIBEo8EYWuML+vxCpjjY^s1-ahXJzAV6hTw%ZIy!FjI}aJ+{rE&u#>rs)vzuxz z+$5z=7W?zH2>Eb32dvgHYZtCAf!=OLY-pb4>Ae79rd68E2LkVPj-|jFeyqtBCCwiW zkB@kO_(3wFq)7qwV}bA=zD!*@UhT`geq}ITo%@O(Z5Y80nEX~;0-8kO{oB6|(4fQh z);73T!>3@{ZobPwRv*W?7m0Ml9GmJBCJd&6E?hdj9lV= z4flNfsc(J*DyPv?RCOx!MSvk(M952PJ-G|JeVxWVjN~SNS6n-_Ge3Q;TGE;EQvZg86%wZ`MB zSMQua(i*R8a75!6$QRO^(o7sGoomb+Y{OMy;m~Oa`;P9Yqo>?bJAhqXxLr7_3g_n>f#UVtxG!^F#1+y@os6x(sg z^28bsQ@8rw%Gxk-stAEPRbv^}5sLe=VMbkc@Jjimqjvmd!3E7+QnL>|(^3!R} zD-l1l7*Amu@j+PWLGHXXaFG0Ct2Q=}5YNUxEQHCAU7gA$sSC<5OGylNnQUa>>l%sM zyu}z6i&({U@x^hln**o6r2s-(C-L50tQvz|zHTqW!ir?w&V23tuYEDJVV#5pE|OJu z7^R!A$iM$YCe?8n67l*J-okwfZ+ZTkGvZ)tVPfR;|3gyFjF)8V zyXXN=!*bpyRg9#~Bg1+UDYCt0 ztp4&?t1X0q>uz;ann$OrZs{5*r`(oNvw=$7O#rD|Wuv*wIi)4b zGtq4%BX+kkagv3F9Id6~-c+1&?zny%w5j&nk9SQfo0k4LhdSU_kWGW7axkfpgR`8* z!?UTG*Zi_baA1^0eda8S|@&F z{)Rad0kiLjB|=}XFJhD(S3ssKlveFFmkN{Vl^_nb!o5M!RC=m)V&v2%e?ZoRC@h3> zJ(?pvToFd`*Zc@HFPL#=otWKwtuuQ_dT-Hr{S%pQX<6dqVJ8;f(o)4~VM_kEQkMR+ zs1SCVi~k>M`u1u2xc}>#D!V&6nOOh-E$O&SzYrjJdZpaDv1!R-QGA141WjQe2s0J~ zQ;AXG)F+K#K8_5HVqRoRM%^EduqOnS(j2)|ctA6Q^=|s_WJYU;Z%5bHp08HPL`YF2 zR)Ad1z{zh`=sDs^&V}J z%$Z$!jd7BY5AkT?j`eqMs%!Gm@T8)4w3GYEX~IwgE~`d|@T{WYHkudy(47brgHXx& zBL1yFG6!!!VOSmDxBpefy2{L_u5yTwja&HA!mYA#wg#bc-m%~8aRR|~AvMnind@zs zy>wkShe5&*un^zvSOdlVu%kHsEo>@puMQ`b1}(|)l~E{5)f7gC=E$fP(FC2=F<^|A zxeIm?{EE!3sO!Gr7e{w)Dx(uU#3WrFZ>ibmKSQ1tY?*-Nh1TDHLe+k*;{Rp!Bmd_m zb#^kh`Y*8l|9Cz2e{;RL%_lg{#^Ar+NH|3z*Zye>!alpt{z;4dFAw^^H!6ING*EFc z_yqhr8d!;%nHX9AKhFQZBGrSzfzYCi%C!(Q5*~hX>)0N`vbhZ@N|i;_972WSx*>LH z87?en(;2_`{_JHF`Sv6Wlps;dCcj+8IJ8ca6`DsOQCMb3n# z3)_w%FuJ3>fjeOOtWyq)ag|PmgQbC-s}KRHG~enBcIwqIiGW8R8jFeBNY9|YswRY5 zjGUxdGgUD26wOpwM#8a!Nuqg68*dG@VM~SbOroL_On0N6QdT9?)NeB3@0FCC?Z|E0 z6TPZj(AsPtwCw>*{eDEE}Gby>0q{*lI+g2e&(YQrsY&uGM{O~}(oM@YWmb*F zA0^rr5~UD^qmNljq$F#ARXRZ1igP`MQx4aS6*MS;Ot(1L5jF2NJ;de!NujUYg$dr# z=TEL_zTj2@>ZZN(NYCeVX2==~=aT)R30gETO{G&GM4XN<+!&W&(WcDP%oL8PyIVUC zs5AvMgh6qr-2?^unB@mXK*Dbil^y-GTC+>&N5HkzXtozVf93m~xOUHn8`HpX=$_v2 z61H;Z1qK9o;>->tb8y%#4H)765W4E>TQ1o0PFj)uTOPEvv&}%(_mG0ISmyhnQV33Z$#&yd{ zc{>8V8XK$3u8}04CmAQ#I@XvtmB*s4t8va?-IY4@CN>;)mLb_4!&P3XSw4pA_NzDb zORn!blT-aHk1%Jpi>T~oGLuh{DB)JIGZ9KOsciWs2N7mM1JWM+lna4vkDL?Q)z_Ct z`!mi0jtr+4*L&N7jk&LodVO#6?_qRGVaucqVB8*us6i3BTa^^EI0x%EREQSXV@f!lak6Wf1cNZ8>*artIJ(ADO*=<-an`3zB4d*oO*8D1K!f z*A@P1bZCNtU=p!742MrAj%&5v%Xp_dSX@4YCw%F|%Dk=u|1BOmo)HsVz)nD5USa zR~??e61sO(;PR)iaxK{M%QM_rIua9C^4ppVS$qCT9j2%?*em?`4Z;4@>I(c%M&#cH z>4}*;ej<4cKkbCAjjDsyKS8rIm90O)Jjgyxj5^venBx&7B!xLmzxW3jhj7sR(^3Fz z84EY|p1NauwXUr;FfZjdaAfh%ivyp+^!jBjJuAaKa!yCq=?T_)R!>16?{~p)FQ3LDoMyG%hL#pR!f@P%*;#90rs_y z@9}@r1BmM-SJ#DeuqCQk=J?ixDSwL*wh|G#us;dd{H}3*-Y7Tv5m=bQJMcH+_S`zVtf;!0kt*(zwJ zs+kedTm!A}cMiM!qv(c$o5K%}Yd0|nOd0iLjus&;s0Acvoi-PFrWm?+q9f^FslxGi z6ywB`QpL$rJzWDg(4)C4+!2cLE}UPCTBLa*_=c#*$b2PWrRN46$y~yST3a2$7hEH= zNjux+wna^AzQ=KEa_5#9Ph=G1{S0#hh1L3hQ`@HrVnCx{!fw_a0N5xV(iPdKZ-HOM za)LdgK}1ww*C_>V7hbQnTzjURJL`S%`6nTHcgS+dB6b_;PY1FsrdE8(2K6FN>37!62j_cBlui{jO^$dPkGHV>pXvW0EiOA zqW`YaSUBWg_v^Y5tPJfWLcLpsA8T zG)!x>pKMpt!lv3&KV!-um= zKCir6`bEL_LCFx4Z5bAFXW$g3Cq`?Q%)3q0r852XI*Der*JNuKUZ`C{cCuu8R8nkt z%pnF>R$uY8L+D!V{s^9>IC+bmt<05h**>49R*#vpM*4i0qRB2uPbg8{{s#9yC;Z18 zD7|4m<9qneQ84uX|J&f-g8a|nFKFt34@Bt{CU`v(SYbbn95Q67*)_Esl_;v291s=9 z+#2F2apZU4Tq=x+?V}CjwD(P=U~d<=mfEFuyPB`Ey82V9G#Sk8H_Ob_RnP3s?)S_3 zr%}Pb?;lt_)Nf>@zX~D~TBr;-LS<1I##8z`;0ZCvI_QbXNh8Iv)$LS=*gHr;}dgb=w5$3k2la1keIm|=7<-JD>)U%=Avl0Vj@+&vxn zt-)`vJxJr88D&!}2^{GPXc^nmRf#}nb$4MMkBA21GzB`-Or`-3lq^O^svO7Vs~FdM zv`NvzyG+0T!P8l_&8gH|pzE{N(gv_tgDU7SWeiI-iHC#0Ai%Ixn4&nt{5y3(GQs)i z&uA;~_0shP$0Wh0VooIeyC|lak__#KVJfxa7*mYmZ22@(<^W}FdKjd*U1CqSjNKW% z*z$5$=t^+;Ui=MoDW~A7;)Mj%ibX1_p4gu>RC}Z_pl`U*{_z@+HN?AF{_W z?M_X@o%w8fgFIJ$fIzBeK=v#*`mtY$HC3tqw7q^GCT!P$I%=2N4FY7j9nG8aIm$c9 zeKTxVKN!UJ{#W)zxW|Q^K!3s;(*7Gbn;e@pQBCDS(I|Y0euK#dSQ_W^)sv5pa%<^o zyu}3d?Lx`)3-n5Sy9r#`I{+t6x%I%G(iewGbvor&I^{lhu-!#}*Q3^itvY(^UWXgvthH52zLy&T+B)Pw;5>4D6>74 zO_EBS)>l!zLTVkX@NDqyN2cXTwsUVao7$HcqV2%t$YzdAC&T)dwzExa3*kt9d(}al zA~M}=%2NVNUjZiO7c>04YH)sRelXJYpWSn^aC$|Ji|E13a^-v2MB!Nc*b+=KY7MCm zqIteKfNkONq}uM;PB?vvgQvfKLPMB8u5+Am=d#>g+o&Ysb>dX9EC8q?D$pJH!MTAqa=DS5$cb+;hEvjwVfF{4;M{5U&^_+r zvZdu_rildI!*|*A$TzJ&apQWV@p{!W`=?t(o0{?9y&vM)V)ycGSlI3`;ps(vf2PUq zX745#`cmT*ra7XECC0gKkpu2eyhFEUb?;4@X7weEnLjXj_F~?OzL1U1L0|s6M+kIhmi%`n5vvDALMagi4`wMc=JV{XiO+^ z?s9i7;GgrRW{Mx)d7rj)?(;|b-`iBNPqdwtt%32se@?w4<^KU&585_kZ=`Wy^oLu9 z?DQAh5z%q;UkP48jgMFHTf#mj?#z|=w= z(q6~17Vn}P)J3M?O)x))%a5+>TFW3No~TgP;f}K$#icBh;rSS+R|}l鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..3fa8f862f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..1aa94a426 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mysql/Dockerfile b/mysql/Dockerfile new file mode 100644 index 000000000..24f50e8a8 --- /dev/null +++ b/mysql/Dockerfile @@ -0,0 +1,6 @@ +# Use the official MySQL 8 image as the base image +FROM mysql:8 +ENV MYSQL_ROOT_PASSWORD=root +ENV MYSQL_DATABASE=board +ENV MYSQL_ROOT_USER=root +COPY init.sql /docker-entrypoint-initdb.d/ \ No newline at end of file diff --git a/mysql/init.sql b/mysql/init.sql new file mode 100644 index 000000000..30193fb19 --- /dev/null +++ b/mysql/init.sql @@ -0,0 +1,26 @@ +CREATE DATABASE IF NOT EXISTS board; + +USE board; + +create table users +( + user_id bigint primary key auto_increment, + name varchar(20) not null , + age int not null, + hobby varchar(20) not null , + created_at datetime not null , + updated_at datetime not null +); + +create table posts +( + post_id bigint primary key auto_increment, + user_id bigint not null , + title varchar(30) not null, + contents blob not null , + created_at datetime not null , + updated_at datetime not null, + created_by varchar(20) not null, + + constraint post_user_fk FOREIGN key (user_id) references users(user_id) +); diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..5f717a7cc --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'board' diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f0b988e0d393c67c24b26759de6d507b9c5f857e GIT binary patch literal 6148 zcmeHLOHRWu5S@W6DzWJjcD_O-4q&Qqf?fbh5g;|BQp-N#FkA$&;RZbg5+~rzrzI8< zON3xXvY$O4Px7K<><|%89~Kj$F%gZ>1X%_HBI-fYfjf(lWsT*{WOmgytARy+t4glD zqJ}Q%j_%9rKRNH)nzpXz%XSXkXiKMW?~gBMrG4?OeRbRCCL2L3q%sM#W;Bg5!}fnXpQ*fSvChlD1W9Xmt) zbfD8C0I-5u1!LV~0+T3!*|9T(2ErB!v{3dV23t7v$^EipXK3NXni=bO=H20ieTU0Q z-HEed^ua(dP%?06)3MZloOO4vQ0Wx2U?3Ry(+qG`&*~{|(QE6Mo0VFd&@RwK#IKV9 p1Y<45KrO|X$Un7XHJUxi820Sg8Okc6*KlBL1eB2If`K1k-~%G7NLv5^ literal 0 HcmV?d00001 diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e91571673a2d66bf8db4ace391f069408af8f14f GIT binary patch literal 6148 zcmeHKOHKnZ40XyZrs}3kmbpT25USw-s289g1cHuI6}4<54#Pzd7h=JhXFD2ZG((pN zRkq|j%dr#Bna&63RFlqZttHXYI)-d$`l_7~A{VVjb&UFVY
    z>D z1wGP|js;wl+XX`-{1eO3^R=ZW8mBva7DGK=J=|7w)Vb{&sq Date: Thu, 16 Nov 2023 15:44:04 +0900 Subject: [PATCH 002/102] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9E=85?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cb154de0f..873168651 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,5 @@ Spring Boot JPA - Rest API를 강의를 듣고, 게시판 구현 미션을 수 1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) 2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 본인 username을 적어주세요 :) base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main` + +- 테스트 \ No newline at end of file From 34f4b7936cfdaef567e7f12b781a98ac52da147f Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 15:52:45 +0900 Subject: [PATCH 003/102] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B82=EC=9E=85?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 873168651..de2fb5e2b 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,4 @@ Spring Boot JPA - Rest API를 강의를 듣고, 게시판 구현 미션을 수 2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 본인 username을 적어주세요 :) base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main` -- 테스트 \ No newline at end of file +- 테스트2 \ No newline at end of file From a1738ddf780d023ab4c3f47a4b50cb2928c0da24 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 15:54:06 +0900 Subject: [PATCH 004/102] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B82=EC=9E=85?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de2fb5e2b..2b442fbf1 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,4 @@ Spring Boot JPA - Rest API를 강의를 듣고, 게시판 구현 미션을 수 2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 본인 username을 적어주세요 :) base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main` -- 테스트2 \ No newline at end of file +- 테스트3 \ No newline at end of file From 83d571490981cb18c6894793b3d936dd328ef531 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 16:26:20 +0900 Subject: [PATCH 005/102] =?UTF-8?q?#2=20feat:=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 223 ++++++++++++++++++ .../com/example/board/BoardApplication.java | 2 + .../com/example/board/model/BaseEntity.java | 21 ++ .../java/com/example/board/model/Post.java | 27 +++ .../java/com/example/board/model/User.java | 26 ++ 5 files changed, 299 insertions(+) create mode 100644 .gitignore create mode 100644 src/main/java/com/example/board/model/BaseEntity.java create mode 100644 src/main/java/com/example/board/model/Post.java create mode 100644 src/main/java/com/example/board/model/User.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ed4f0d700 --- /dev/null +++ b/.gitignore @@ -0,0 +1,223 @@ +# Created by https://www.toptal.com/developers/gitignore/api/gradle,java,macos,windows,visualstudiocode,intellij+all +# Edit at https://www.toptal.com/developers/gitignore?templates=gradle,java,macos,windows,visualstudiocode,intellij+all + +### Intellij+all ### +# 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 + +# 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+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### 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 + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### 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 + +# End of https://www.toptal.com/developers/gitignore/api/gradle,java,macos,windows,visualstudiocode,intellij+all \ No newline at end of file diff --git a/src/main/java/com/example/board/BoardApplication.java b/src/main/java/com/example/board/BoardApplication.java index 4c8cf857f..e1328bc56 100644 --- a/src/main/java/com/example/board/BoardApplication.java +++ b/src/main/java/com/example/board/BoardApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class BoardApplication { diff --git a/src/main/java/com/example/board/model/BaseEntity.java b/src/main/java/com/example/board/model/BaseEntity.java new file mode 100644 index 000000000..5ca802569 --- /dev/null +++ b/src/main/java/com/example/board/model/BaseEntity.java @@ -0,0 +1,21 @@ +package com.example.board.model; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public class BaseEntity { + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java new file mode 100644 index 000000000..4954b8192 --- /dev/null +++ b/src/main/java/com/example/board/model/Post.java @@ -0,0 +1,27 @@ +package com.example.board.model; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "posts") +public class Post extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "post_id") + private long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "title", length = 30) + private String title; + + @Lob + private String contents; +} diff --git a/src/main/java/com/example/board/model/User.java b/src/main/java/com/example/board/model/User.java new file mode 100644 index 000000000..e7b75d3a9 --- /dev/null +++ b/src/main/java/com/example/board/model/User.java @@ -0,0 +1,26 @@ +package com.example.board.model; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Table(name = "users") +public class User extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") + private long id; + + @Column(name = "name", length = 20) + private String name; + + @Column(name = "age") + private int age; + + @Column(name = "hobby", length = 40) + private String hobby; +} From 4e759bd21fcd78f1c033d7f05c4e61223e194ca5 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 16 Nov 2023 16:27:26 +0900 Subject: [PATCH 006/102] Delete build directory --- .../com/example/board/BoardApplication.class | Bin 738 -> 0 bytes build/resources/main/application.yaml | 1 - .../compileJava/previous-compilation-data.bin | Bin 29841 -> 0 bytes 3 files changed, 1 deletion(-) delete mode 100644 build/classes/java/main/com/example/board/BoardApplication.class delete mode 100644 build/resources/main/application.yaml delete mode 100644 build/tmp/compileJava/previous-compilation-data.bin diff --git a/build/classes/java/main/com/example/board/BoardApplication.class b/build/classes/java/main/com/example/board/BoardApplication.class deleted file mode 100644 index 4904b413220b1bf5cf921d89f055fcd6b4753a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 738 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 From 5ea2032555755ba50812ed8b7e8562bfb2f4d4e6 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 16 Nov 2023 16:27:39 +0900 Subject: [PATCH 007/102] Delete .gradle directory --- .gradle/8.4/checksums/checksums.lock | Bin 17 -> 0 bytes .../dependencies-accessors.lock | Bin 17 -> 0 bytes .../8.4/dependencies-accessors/gc.properties | 0 .../8.4/executionHistory/executionHistory.bin | Bin 41473 -> 0 bytes .../8.4/executionHistory/executionHistory.lock | Bin 17 -> 0 bytes .gradle/8.4/fileChanges/last-build.bin | Bin 1 -> 0 bytes .gradle/8.4/fileHashes/fileHashes.bin | Bin 18747 -> 0 bytes .gradle/8.4/fileHashes/fileHashes.lock | Bin 17 -> 0 bytes .gradle/8.4/fileHashes/resourceHashesCache.bin | Bin 18531 -> 0 bytes .gradle/8.4/gc.properties | 0 .../buildOutputCleanup/buildOutputCleanup.lock | Bin 17 -> 0 bytes .gradle/buildOutputCleanup/cache.properties | 2 -- .gradle/buildOutputCleanup/outputFiles.bin | Bin 18911 -> 0 bytes .gradle/file-system.probe | Bin 8 -> 0 bytes .gradle/vcs-1/gc.properties | 0 15 files changed, 2 deletions(-) delete mode 100644 .gradle/8.4/checksums/checksums.lock delete mode 100644 .gradle/8.4/dependencies-accessors/dependencies-accessors.lock delete mode 100644 .gradle/8.4/dependencies-accessors/gc.properties delete mode 100644 .gradle/8.4/executionHistory/executionHistory.bin delete mode 100644 .gradle/8.4/executionHistory/executionHistory.lock delete mode 100644 .gradle/8.4/fileChanges/last-build.bin delete mode 100644 .gradle/8.4/fileHashes/fileHashes.bin delete mode 100644 .gradle/8.4/fileHashes/fileHashes.lock delete mode 100644 .gradle/8.4/fileHashes/resourceHashesCache.bin delete mode 100644 .gradle/8.4/gc.properties delete mode 100644 .gradle/buildOutputCleanup/buildOutputCleanup.lock delete mode 100644 .gradle/buildOutputCleanup/cache.properties delete mode 100644 .gradle/buildOutputCleanup/outputFiles.bin delete mode 100644 .gradle/file-system.probe delete mode 100644 .gradle/vcs-1/gc.properties diff --git a/.gradle/8.4/checksums/checksums.lock b/.gradle/8.4/checksums/checksums.lock deleted file mode 100644 index 53fe7d1415358ce522cbbee7b60f21b1bc5be88f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZP$)XU=j|2Ov@0~7!NF+>DE diff --git a/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock b/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index 7c175c2458be4070ef3f59e6ec11f3d61f7eb824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZR6@p5vDluwf@0~7!NHzoub diff --git a/.gradle/8.4/dependencies-accessors/gc.properties b/.gradle/8.4/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/.gradle/8.4/executionHistory/executionHistory.bin b/.gradle/8.4/executionHistory/executionHistory.bin deleted file mode 100644 index 15e2ea7cce2ad495cc825a61ece1091ed1bedf71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41473 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 diff --git a/.gradle/8.4/fileHashes/resourceHashesCache.bin b/.gradle/8.4/fileHashes/resourceHashesCache.bin deleted file mode 100644 index 13fa9e928440556c1c879c09f6435c507dd91359..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18531 zcmeI%F%CgN5CG7nP>2MD1A;AyS5WQN_7we4kJW97=6!AZB&R+g|_MUTox63!ifc#sK)q@=>G?G^*7^_`+}`@_ubLna3OE0a>Kd(cK4TSzjAzIch@tQ zL*@h8TO$J%l_P0mGJjfo`*8K1q~aSnGGCy*>p}EdbjRgJnU888z88Gi-TbLZ=EK@Y zOXf3*^8#O`KV9uB*V`tXuYHg4izI2E7?b@7isq<3m}e+iF&UT}w`=W5UNym_1YWZY|a-&O6|&kLtNbo8E*eo(vD zS9AAO+3XhSG3`eWIWphRr9|xRuhm|3zp?D_*}iu&Kcu~+B2YhdCaXnygZ9v3&(NdU zmQ?98+9T&LdfPoQx9p#-T|bRU-fMry6#)bgKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0;rZwc(SZoJ7llM-j*lJ-ln!arXA+%x~) I&g=TCpEPGiDF6Tf diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe deleted file mode 100644 index b78019120fe7d6f39d39ab42889f1bde93bd2606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8 PcmZQzVC=rOSs(=f2)Y7_ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29bb..000000000 From b9daf3811033f8ad572f955b1ea9a904afee0d04 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 16 Nov 2023 16:27:48 +0900 Subject: [PATCH 008/102] Delete .idea directory --- .idea/.gitignore | 8 -------- .idea/.name | 1 - .idea/compiler.xml | 15 --------------- .idea/dataSources.xml | 12 ------------ .idea/gradle.xml | 17 ----------------- .idea/jarRepositories.xml | 20 -------------------- .idea/jpa-buddy.xml | 7 ------- .idea/misc.xml | 11 ----------- .idea/modules.xml | 8 -------- .idea/modules/board.main.iml | 8 -------- .idea/vcs.xml | 6 ------ 11 files changed, 113 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/.name delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/dataSources.xml delete mode 100644 .idea/gradle.xml delete mode 100644 .idea/jarRepositories.xml delete mode 100644 .idea/jpa-buddy.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/modules/board.main.iml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index a692a299a..000000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -board \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 18d06625c..000000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml deleted file mode 100644 index 1042223d1..000000000 --- a/.idea/dataSources.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - mysql.8 - true - com.mysql.cj.jdbc.Driver - jdbc:mysql://localhost:3308/board - $ProjectFileDir$ - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 3f9e50192..000000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392fe8..000000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/jpa-buddy.xml b/.idea/jpa-buddy.xml deleted file mode 100644 index 898e07a67..000000000 --- a/.idea/jpa-buddy.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 8671517d8..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index c66859a9d..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/modules/board.main.iml b/.idea/modules/board.main.iml deleted file mode 100644 index ec81b5385..000000000 --- a/.idea/modules/board.main.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 6c006f5f2da772dbdfbce9b4bb3987ed6e52314a Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 16 Nov 2023 16:27:59 +0900 Subject: [PATCH 009/102] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index cbd77902607888fc7b50641b9064381a2f963e87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKOHRWu5FOJBMe3$YHb@pXC?^OdoS+wgHmEETr4+HZSaA|Af;b5`V8@O(AKHdQ zVuMgYGm`!6`FNaH)ngM;nBJEYqA?L=XpDgeJ;Lf<(}6j&F~}MfU02J=Y|+%Ko`v>N z#d|-bC9Ua-O6u(Y{q?K8Zi-@FHFM~*RH9QtT}xnWY&a0-$c>saQ` z$BW*ip^xHDAy@R?8E^*L3=DKS=K6nuzfA8Vza8Q;XTTZwXAFd4F)OC{D66fV&*NGf x(Js+g*e~$_g0Ylhpp;@v_#8PBquL{nVb2YdBCo=F2?zQ`KnU^98TbVTJ^**8O0)m~ From e2d0a4dda11bbaee39a8d7a56064e52c9e02c779 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 16:32:53 +0900 Subject: [PATCH 010/102] =?UTF-8?q?chore:=20.gitignore=EC=97=90=20.idea/?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index ed4f0d700..35063751b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,14 +35,14 @@ # 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 + .idea/artifacts + .idea/compiler.xml + .idea/jarRepositories.xml + .idea/modules.xml + .idea/*.iml + .idea/modules + *.iml + *.ipr # CMake cmake-build-*/ @@ -220,4 +220,6 @@ gradle-app.setting # Java heap dump *.hprof -# End of https://www.toptal.com/developers/gitignore/api/gradle,java,macos,windows,visualstudiocode,intellij+all \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/gradle,java,macos,windows,visualstudiocode,intellij+all + +.idea/ \ No newline at end of file From ff82c0c579427b4813e32155275547ddd4b37c63 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 16:58:26 +0900 Subject: [PATCH 011/102] =?UTF-8?q?chore:=20db=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20default=20=EC=9C=A0=EC=A0=80=20=EB=84=A3=EB=8A=94?= =?UTF-8?q?=20sql=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/init.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mysql/init.sql b/mysql/init.sql index 30193fb19..c069fd518 100644 --- a/mysql/init.sql +++ b/mysql/init.sql @@ -24,3 +24,5 @@ create table posts constraint post_user_fk FOREIGN key (user_id) references users(user_id) ); + +insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); \ No newline at end of file From f31e85e349745524d07645f72c991cd4ea4deff1 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 16:58:58 +0900 Subject: [PATCH 012/102] =?UTF-8?q?#3=20feat:=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=ED=86=B5=EC=9D=BC=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20Response=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/response/Response.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/example/board/response/Response.java diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java new file mode 100644 index 000000000..d9436ad77 --- /dev/null +++ b/src/main/java/com/example/board/response/Response.java @@ -0,0 +1,29 @@ +package com.example.board.response; + +import com.example.exception.BaseException; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class Response { + + private T data; + private int code; + private String isSuccess; + + public static Response success(T data) { + return new Response<>(data, 200, "ok"); + } + + public static Response success() { + return new Response<>(null, 200, "ok"); + } + + public static Response fail(BaseException e) { + return new Response<>(e.getMessage(), e.getCode(), "fail"); + } + + public static Response fail(Exception e) { + return new Response<>(e.getMessage(), 500, "fail"); + } + +} From b709faa95b1df5b65394a1ce3bb44784ef58143e Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 16:59:34 +0900 Subject: [PATCH 013/102] =?UTF-8?q?#3=20feat:=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=83=81=20=EC=98=88=EC=99=B8=EB=A5=BC=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=EC=8B=9C=ED=82=A4=EA=B8=B0=20=EC=9C=84=ED=95=9C=20Custom=20Exc?= =?UTF-8?q?eption=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/exception/BaseException.java | 20 +++++++++++++++++++ .../com/example/exception/ErrorMessage.java | 15 ++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/main/java/com/example/exception/BaseException.java create mode 100644 src/main/java/com/example/exception/ErrorMessage.java diff --git a/src/main/java/com/example/exception/BaseException.java b/src/main/java/com/example/exception/BaseException.java new file mode 100644 index 000000000..df20f6ef2 --- /dev/null +++ b/src/main/java/com/example/exception/BaseException.java @@ -0,0 +1,20 @@ +package com.example.exception; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class BaseException extends RuntimeException{ + private int code; + private String message; + private HttpStatus httpStatus; + + public BaseException(ErrorMessage message){ + this.code = message.getCode(); + this.message = message.getMessage(); + this.httpStatus = message.getHttpStatus(); + } + +} diff --git a/src/main/java/com/example/exception/ErrorMessage.java b/src/main/java/com/example/exception/ErrorMessage.java new file mode 100644 index 000000000..b6d77d7ae --- /dev/null +++ b/src/main/java/com/example/exception/ErrorMessage.java @@ -0,0 +1,15 @@ +package com.example.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorMessage { + INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."); + + private int code; + private HttpStatus httpStatus; + private String message; +} From 6082617177a80aa3118fa0c07bfe19562b025d38 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 16:59:58 +0900 Subject: [PATCH 014/102] =?UTF-8?q?#3=20feat:=20=EC=A0=84=EC=97=AD=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20Exception=20Handler=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/com/example/exception/GlobalExceptionHandler.java diff --git a/src/main/java/com/example/exception/GlobalExceptionHandler.java b/src/main/java/com/example/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..2e6ce48a6 --- /dev/null +++ b/src/main/java/com/example/exception/GlobalExceptionHandler.java @@ -0,0 +1,30 @@ +package com.example.exception; + +import com.example.board.response.Response; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(BaseException.class) + public Response customExceptionHandle(BaseException e){ + return Response.fail(e); + } + + @ExceptionHandler(RuntimeException.class) + public Response customExceptionHandle(RuntimeException e){ + return Response.fail(e); + } + + @ExceptionHandler(Exception.class) + public Response customExceptionHandle(Exception e){ + return Response.fail(e); + } + + @ExceptionHandler(MismatchedInputException.class) + public Response customExceptionHandle(MismatchedInputException e){ + return Response.fail(e); + } +} From ee63df4e12af03686ca7ca096cde49fa44d985e3 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:07:56 +0900 Subject: [PATCH 015/102] =?UTF-8?q?refactor:=20exception=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=82=B4=EC=9A=A9=20board=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/{ => board}/exception/BaseException.java | 3 +-- .../java/com/example/{ => board}/exception/ErrorMessage.java | 2 +- .../example/{ => board}/exception/GlobalExceptionHandler.java | 2 +- src/main/java/com/example/board/response/Response.java | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) rename src/main/java/com/example/{ => board}/exception/BaseException.java (86%) rename src/main/java/com/example/{ => board}/exception/ErrorMessage.java (90%) rename src/main/java/com/example/{ => board}/exception/GlobalExceptionHandler.java (96%) diff --git a/src/main/java/com/example/exception/BaseException.java b/src/main/java/com/example/board/exception/BaseException.java similarity index 86% rename from src/main/java/com/example/exception/BaseException.java rename to src/main/java/com/example/board/exception/BaseException.java index df20f6ef2..eb189fb7a 100644 --- a/src/main/java/com/example/exception/BaseException.java +++ b/src/main/java/com/example/board/exception/BaseException.java @@ -1,7 +1,6 @@ -package com.example.exception; +package com.example.board.exception; -import lombok.AllArgsConstructor; import lombok.Getter; import org.springframework.http.HttpStatus; diff --git a/src/main/java/com/example/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java similarity index 90% rename from src/main/java/com/example/exception/ErrorMessage.java rename to src/main/java/com/example/board/exception/ErrorMessage.java index b6d77d7ae..42a94dc30 100644 --- a/src/main/java/com/example/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -1,4 +1,4 @@ -package com.example.exception; +package com.example.board.exception; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/com/example/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java similarity index 96% rename from src/main/java/com/example/exception/GlobalExceptionHandler.java rename to src/main/java/com/example/board/exception/GlobalExceptionHandler.java index 2e6ce48a6..e26f1e279 100644 --- a/src/main/java/com/example/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package com.example.exception; +package com.example.board.exception; import com.example.board.response.Response; import com.fasterxml.jackson.databind.exc.MismatchedInputException; diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index d9436ad77..4c61280b5 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -1,6 +1,6 @@ package com.example.board.response; -import com.example.exception.BaseException; +import com.example.board.exception.BaseException; import lombok.AllArgsConstructor; @AllArgsConstructor From a5c243a07916b16d9b0ce039f60cbfb1a5d701b0 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:44:33 +0900 Subject: [PATCH 016/102] =?UTF-8?q?chore:=20application.yaml=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=86=8C=EC=8A=A4,=20jpa=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 8b1378917..0d7170bf9 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1 +1,8 @@ - +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3308/board + username: root + password: root + jpa: + show-sql: true \ No newline at end of file From 348263393933eeca9ba2eb998b441c6bb4852e2d Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:45:51 +0900 Subject: [PATCH 017/102] =?UTF-8?q?#4=20feat:=20PostDto=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 --- src/main/java/com/example/board/dto/PostDto.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/example/board/dto/PostDto.java diff --git a/src/main/java/com/example/board/dto/PostDto.java b/src/main/java/com/example/board/dto/PostDto.java new file mode 100644 index 000000000..1217cc0fa --- /dev/null +++ b/src/main/java/com/example/board/dto/PostDto.java @@ -0,0 +1,8 @@ +package com.example.board.dto; + +public record PostDto( + long userId, + String title, + String contents +) { +} From 50f6f714467d6af4ff4c10ced6857c0f3f5e0784 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:46:09 +0900 Subject: [PATCH 018/102] =?UTF-8?q?#4=20feat:=20PostDto=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=84=ED=84=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/model/Post.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index 4954b8192..846fdb21f 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -1,5 +1,6 @@ package com.example.board.model; +import com.example.board.dto.PostDto; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -24,4 +25,18 @@ public class Post extends BaseEntity { @Lob private String contents; + + @Column(name = "created_by") + private String createdBy; + + private Post(User user, PostDto postDto) { + this.user = user; + this.title = postDto.title(); + this.contents = postDto.contents(); + this.createdBy = user.getName(); + } + + public static Post from(User user, PostDto postDto) { + return new Post(user, postDto); + } } From 8794784d621340f492d2e63ac360c67c059fe5ee Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:46:58 +0900 Subject: [PATCH 019/102] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=EA=B0=80=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20=EC=97=90=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/exception/ErrorMessage.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index 42a94dc30..b842290c8 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -7,7 +7,9 @@ @Getter @AllArgsConstructor public enum ErrorMessage { - INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."); + INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."), + // 1xxx user exception + USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."); private int code; private HttpStatus httpStatus; From 14621eef0ea89f62aeb0d940774e55c16ad5dffc Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:47:23 +0900 Subject: [PATCH 020/102] =?UTF-8?q?fix:=20Response=20@Getter=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/response/Response.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index 4c61280b5..ce3d35d9f 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -2,7 +2,9 @@ import com.example.board.exception.BaseException; import lombok.AllArgsConstructor; +import lombok.Getter; +@Getter @AllArgsConstructor public class Response { From 3b6267092be75863c9febe174c96d8278af94cfc Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:47:39 +0900 Subject: [PATCH 021/102] =?UTF-8?q?#4=20feat:=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 --- .../java/com/example/board/repository/UserRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/example/board/repository/UserRepository.java diff --git a/src/main/java/com/example/board/repository/UserRepository.java b/src/main/java/com/example/board/repository/UserRepository.java new file mode 100644 index 000000000..0df2c4b17 --- /dev/null +++ b/src/main/java/com/example/board/repository/UserRepository.java @@ -0,0 +1,7 @@ +package com.example.board.repository; + +import com.example.board.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserRepository extends JpaRepository { +} From e0252e3efb29b75bb7431d9e8562faeaaec56fee Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 16 Nov 2023 17:48:00 +0900 Subject: [PATCH 022/102] =?UTF-8?q?#4=20feat:=20PostRepository=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20PostService/PostController=20save=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 23 ++++++++++++++++ .../board/repository/PostRepository.java | 7 +++++ .../example/board/service/PostService.java | 27 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/main/java/com/example/board/controller/PostController.java create mode 100644 src/main/java/com/example/board/repository/PostRepository.java create mode 100644 src/main/java/com/example/board/service/PostService.java diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java new file mode 100644 index 000000000..778c06ede --- /dev/null +++ b/src/main/java/com/example/board/controller/PostController.java @@ -0,0 +1,23 @@ +package com.example.board.controller; + +import com.example.board.dto.PostDto; +import com.example.board.response.Response; +import com.example.board.service.PostService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RequestMapping("/api/v1/posts") +@RestController +public class PostController { + private final PostService postService; + + @PostMapping + public Response save(@RequestBody PostDto postDto) { + return Response.success(postService.save(postDto)); + } + +} diff --git a/src/main/java/com/example/board/repository/PostRepository.java b/src/main/java/com/example/board/repository/PostRepository.java new file mode 100644 index 000000000..65b1257c7 --- /dev/null +++ b/src/main/java/com/example/board/repository/PostRepository.java @@ -0,0 +1,7 @@ +package com.example.board.repository; + +import com.example.board.model.Post; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java new file mode 100644 index 000000000..67bdb30f7 --- /dev/null +++ b/src/main/java/com/example/board/service/PostService.java @@ -0,0 +1,27 @@ +package com.example.board.service; + +import com.example.board.dto.PostDto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; +import com.example.board.model.Post; +import com.example.board.model.User; +import com.example.board.repository.PostRepository; +import com.example.board.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class PostService { + private final PostRepository postRepository; + private final UserRepository userRepository; + + @Transactional + public Long save(PostDto postDto) { + User user = userRepository.findById(postDto.userId()).orElseThrow(() -> + new BaseException(ErrorMessage.USER_NOT_FOUND) + ); + return postRepository.save(Post.from(user, postDto)).getId(); + } +} From a67657a8cffe30222f1b9c5ea8ae45629991fd3e Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 20:31:41 +0900 Subject: [PATCH 023/102] Auto stash before merge of "main" and "origin/main" --- ...e.java => ErrorMessage.java~Stashed changes} | 0 .../ErrorMessage.java~Updated upstream | 17 +++++++++++++++++ 2 files changed, 17 insertions(+) rename src/main/java/com/example/board/exception/{ErrorMessage.java => ErrorMessage.java~Stashed changes} (100%) create mode 100644 src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java~Stashed changes similarity index 100% rename from src/main/java/com/example/board/exception/ErrorMessage.java rename to src/main/java/com/example/board/exception/ErrorMessage.java~Stashed changes diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream b/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream new file mode 100644 index 000000000..b842290c8 --- /dev/null +++ b/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream @@ -0,0 +1,17 @@ +package com.example.board.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorMessage { + INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."), + // 1xxx user exception + USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."); + + private int code; + private HttpStatus httpStatus; + private String message; +} From ff342369693e503239ec0724ef655eddf24c654e Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 20:32:35 +0900 Subject: [PATCH 024/102] =?UTF-8?q?conflict:=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20ErrorMessage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...e.java~Stashed changes => ErrorMessage.java} | 0 .../ErrorMessage.java~Updated upstream | 17 ----------------- 2 files changed, 17 deletions(-) rename src/main/java/com/example/board/exception/{ErrorMessage.java~Stashed changes => ErrorMessage.java} (100%) delete mode 100644 src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java~Stashed changes b/src/main/java/com/example/board/exception/ErrorMessage.java similarity index 100% rename from src/main/java/com/example/board/exception/ErrorMessage.java~Stashed changes rename to src/main/java/com/example/board/exception/ErrorMessage.java diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream b/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream deleted file mode 100644 index b842290c8..000000000 --- a/src/main/java/com/example/board/exception/ErrorMessage.java~Updated upstream +++ /dev/null @@ -1,17 +0,0 @@ -package com.example.board.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum ErrorMessage { - INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."), - // 1xxx user exception - USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."); - - private int code; - private HttpStatus httpStatus; - private String message; -} From 0887c1a43dc68459cc65dc4f9a76dbcdf9ca51ce Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:29:18 +0900 Subject: [PATCH 025/102] =?UTF-8?q?chore:=20test=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yaml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0d7170bf9..2cae5fb3e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -5,4 +5,18 @@ spring: username: root password: root jpa: - show-sql: true \ No newline at end of file + show-sql: true + +--- + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3308/board_test + username: root + password: root + jpa: + show-sql: true + config: + activate: + on-profile: test \ No newline at end of file From 4ea0c15127c9fa448c3259873648c99746bd77c1 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:29:45 +0900 Subject: [PATCH 026/102] =?UTF-8?q?#4=20test:=20=EC=A0=95=EC=83=81?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EA=B8=80=EC=9D=B4=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EC=98=A4=EB=8A=94=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostControllerTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/com/example/board/controller/PostControllerTest.java diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java new file mode 100644 index 000000000..de5feead4 --- /dev/null +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -0,0 +1,48 @@ +package com.example.board.controller; + +import com.example.board.dto.PostDto; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; + +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@SpringBootTest +@AutoConfigureMockMvc +class PostControllerTest { + + @MockBean + PostController postController; + + @Autowired + MockMvc mockMvc; + + @Autowired + ObjectMapper objectMapper; + + @Test + @DisplayName("정상적으로 게시글을 등록하는 상황") + void postSuccess() throws Exception { + //given + PostDto postDto = new PostDto(1, "test", "test Contents"); + + //when, then + mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isOk()) + .andDo(print()); + } + +} \ No newline at end of file From a6570dde9e41b71771f15ab9b14d3e4c7f02121a Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:29:56 +0900 Subject: [PATCH 027/102] =?UTF-8?q?#4=20test:=20=EC=A0=95=EC=83=81?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EA=B8=80=EC=9D=B4=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EC=98=A4=EB=8A=94=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/service/PostServiceTest.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/test/java/com/example/board/service/PostServiceTest.java diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java new file mode 100644 index 000000000..d03ebf194 --- /dev/null +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -0,0 +1,56 @@ +package com.example.board.service; + +import com.example.board.dto.PostDto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; +import com.example.board.model.Post; +import com.example.board.repository.PostRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +@ActiveProfiles("test") +@SpringBootTest +class PostServiceTest { + + @Autowired + PostService postService; + + @Autowired + PostRepository postRepository; + + @AfterEach + void clear(){ + postRepository.deleteAll(); + } + + @Test + @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") + void postSuccessInService(){ + PostDto postDto = new PostDto(1, "test", "test Contents"); + + Long save = postService.save(postDto); + Optional savedPosts = postRepository.findById(save); + + Assertions.assertThat(savedPosts).isNotEmpty(); + } + + @Test + @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") + void postFailWithAnonymousUser(){ + PostDto postDto = new PostDto(2, "test", "test Contents"); + + Assertions.assertThatThrownBy(() ->postService.save(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.USER_NOT_FOUND.getMessage()); + } + +} \ No newline at end of file From 77a14e25f399e770e339401fcca87ab42ecadf31 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:30:47 +0900 Subject: [PATCH 028/102] =?UTF-8?q?chore:=20test=20table=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EA=B2=80=EC=A6=9D=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 | 3 +++ mysql/init.sql | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/build.gradle b/build.gradle index 295f98bce..dc6b49062 100644 --- a/build.gradle +++ b/build.gradle @@ -27,8 +27,11 @@ dependencies { compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + implementation 'org.springframework.boot:spring-boot-starter-validation' + } tasks.named('bootBuildImage') { diff --git a/mysql/init.sql b/mysql/init.sql index c069fd518..391379570 100644 --- a/mysql/init.sql +++ b/mysql/init.sql @@ -25,4 +25,33 @@ create table posts constraint post_user_fk FOREIGN key (user_id) references users(user_id) ); +insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); + +CREATE DATABASE IF NOT EXISTS board_test; + +USE board_test; + +create table users +( + user_id bigint primary key auto_increment, + name varchar(20) not null , + age int not null, + hobby varchar(20) not null , + created_at datetime not null , + updated_at datetime not null +); + +create table posts +( + post_id bigint primary key auto_increment, + user_id bigint not null , + title varchar(30) not null, + contents blob not null , + created_at datetime not null , + updated_at datetime not null, + created_by varchar(20) not null, + + constraint post_user_fk FOREIGN key (user_id) references users(user_id) +); + insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); \ No newline at end of file From 052285e6f773ec43c71dede6a7453b7e9bf1bc6c Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:31:17 +0900 Subject: [PATCH 029/102] =?UTF-8?q?#4=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=9C=EC=83=9D=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20validation=20=ED=95=AD=EB=AA=A9=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/dto/PostDto.java | 11 ++++++++--- src/main/java/com/example/board/model/Post.java | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/board/dto/PostDto.java b/src/main/java/com/example/board/dto/PostDto.java index 1217cc0fa..3ec2b23fa 100644 --- a/src/main/java/com/example/board/dto/PostDto.java +++ b/src/main/java/com/example/board/dto/PostDto.java @@ -1,8 +1,13 @@ package com.example.board.dto; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import org.springframework.lang.NonNull; + +//TODO: dto 잘못된 값 검증해보기 public record PostDto( - long userId, - String title, - String contents + @NonNull long userId, + @Size(max = 20) String title, + @NotBlank String contents ) { } diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index 846fdb21f..38b0f7edb 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -2,6 +2,9 @@ import com.example.board.dto.PostDto; import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -16,13 +19,16 @@ public class Post extends BaseEntity { @Column(name = "post_id") private long id; + @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; + @Size(max = 20) @Column(name = "title", length = 30) private String title; + @NotBlank @Lob private String contents; From 6701616784268e09c4279fbcd96c19b576318ab2 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:31:45 +0900 Subject: [PATCH 030/102] =?UTF-8?q?#4=20feat:=20binding=20result=EC=97=90?= =?UTF-8?q?=20=EB=94=B0=EB=A5=B8=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index 778c06ede..a6e842983 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -1,14 +1,25 @@ package com.example.board.controller; import com.example.board.dto.PostDto; +import com.example.board.exception.BaseException; +import com.example.board.exception.BindingException; +import com.example.board.exception.ErrorMessage; import com.example.board.response.Response; import com.example.board.service.PostService; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RequiredArgsConstructor @RequestMapping("/api/v1/posts") @RestController @@ -16,8 +27,29 @@ public class PostController { private final PostService postService; @PostMapping - public Response save(@RequestBody PostDto postDto) { + public Response save(@RequestBody @Validated PostDto postDto, BindingResult bindingResult) { + bindChecking(bindingResult); return Response.success(postService.save(postDto)); } + private static void bindChecking(BindingResult bindingResult) { + if (bindingResult.hasErrors()) { + StringBuilder sb = new StringBuilder(); + bindingResult.getAllErrors().forEach(objectError -> { + + FieldError field = (FieldError) objectError; + String message = objectError.getDefaultMessage(); + + System.out.println("field :" + field.getField()); + System.out.println("message :" + message); + + sb.append("field :" + field.getField()); + sb.append("message :" + message); + + }); + + throw new BindingException(sb.toString()); + } + } + } From c28676a4655f06f323cf399f62feabed184ab445 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:32:07 +0900 Subject: [PATCH 031/102] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=20=EB=82=A8=EA=B8=B0?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/exception/GlobalExceptionHandler.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index e26f1e279..36311f40b 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -2,29 +2,36 @@ import com.example.board.response.Response; import com.fasterxml.jackson.databind.exc.MismatchedInputException; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +// TODO: httpstatus 상태 코드도 함께 전달하는 방법 찾기 +@Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BaseException.class) - public Response customExceptionHandle(BaseException e){ + public Response customExceptionHandle(BaseException e) { + log.info("error: {}", e.getMessage()); return Response.fail(e); } @ExceptionHandler(RuntimeException.class) - public Response customExceptionHandle(RuntimeException e){ + public Response customExceptionHandle(RuntimeException e) { + log.info("error: {}", e.getMessage()); return Response.fail(e); } @ExceptionHandler(Exception.class) - public Response customExceptionHandle(Exception e){ + public Response customExceptionHandle(Exception e) { + log.info("error: {}", e.getMessage()); return Response.fail(e); } @ExceptionHandler(MismatchedInputException.class) - public Response customExceptionHandle(MismatchedInputException e){ + public Response customExceptionHandle(MismatchedInputException e) { + log.info("error: {}", e.getMessage()); return Response.fail(e); } } From 60a0c9777347df6130513e4cba274fd870b7907b Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:32:26 +0900 Subject: [PATCH 032/102] =?UTF-8?q?chore:=20Exception=20=EB=A9=A4=EB=B2=84?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=EB=93=A4=20final=20=EB=A1=9C=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/exception/BaseException.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/board/exception/BaseException.java b/src/main/java/com/example/board/exception/BaseException.java index eb189fb7a..15fd32ed3 100644 --- a/src/main/java/com/example/board/exception/BaseException.java +++ b/src/main/java/com/example/board/exception/BaseException.java @@ -6,9 +6,9 @@ @Getter public class BaseException extends RuntimeException{ - private int code; - private String message; - private HttpStatus httpStatus; + private final int code; + private final String message; + private final HttpStatus httpStatus; public BaseException(ErrorMessage message){ this.code = message.getCode(); From ac06b823c21090f992cc40286b940a018dfa8dcf Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Thu, 16 Nov 2023 21:32:53 +0900 Subject: [PATCH 033/102] =?UTF-8?q?#4=20feat:=20binding=20result=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=97=90=EB=9F=AC=20=EC=82=AC=ED=95=AD=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 --- .../example/board/exception/BindingException.java | 13 +++++++++++++ .../java/com/example/board/response/Response.java | 3 +++ 2 files changed, 16 insertions(+) create mode 100644 src/main/java/com/example/board/exception/BindingException.java diff --git a/src/main/java/com/example/board/exception/BindingException.java b/src/main/java/com/example/board/exception/BindingException.java new file mode 100644 index 000000000..697fa640c --- /dev/null +++ b/src/main/java/com/example/board/exception/BindingException.java @@ -0,0 +1,13 @@ +package com.example.board.exception; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class BindingException extends RuntimeException{ + private String bindingMessages; + + @Override + public String getMessage() { + return this.bindingMessages; + } +} diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index ce3d35d9f..96e9d6a9c 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -28,4 +28,7 @@ public static Response fail(Exception e) { return new Response<>(e.getMessage(), 500, "fail"); } + public static Response fail(String bindingErrors) { + return new Response<>(bindingErrors, 400, "fail"); + } } From 973c558d1d8a25d62b39f60cd8151375c002b0b8 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 17 Nov 2023 17:34:45 +0900 Subject: [PATCH 034/102] =?UTF-8?q?#5=20feat:=20Post=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=9A=A9=20dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/dto/PostResponseDto.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/com/example/board/dto/PostResponseDto.java diff --git a/src/main/java/com/example/board/dto/PostResponseDto.java b/src/main/java/com/example/board/dto/PostResponseDto.java new file mode 100644 index 000000000..fb53ac060 --- /dev/null +++ b/src/main/java/com/example/board/dto/PostResponseDto.java @@ -0,0 +1,13 @@ +package com.example.board.dto; + +import com.example.board.model.Post; + +public record PostResponseDto( + String userName, + String title +) { + + public static PostResponseDto from(Post post) { + return new PostResponseDto(post.getTitle(), post.getUser().getName()); + } +} From 68f1e21b9a8631e47e7d42cf7913219e7f9e94af Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 17 Nov 2023 17:35:07 +0900 Subject: [PATCH 035/102] =?UTF-8?q?#5=20feat:=20Post=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EC=84=9C=EB=B9=84=EC=8A=A4=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 --- src/main/java/com/example/board/service/PostService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 67bdb30f7..922bfc108 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -1,6 +1,7 @@ package com.example.board.service; import com.example.board.dto.PostDto; +import com.example.board.dto.PostResponseDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; import com.example.board.model.Post; @@ -8,6 +9,8 @@ import com.example.board.repository.PostRepository; import com.example.board.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,4 +27,8 @@ public Long save(PostDto postDto) { ); return postRepository.save(Post.from(user, postDto)).getId(); } + + public Page readAllPost(Pageable pageable) { + return postRepository.findAll(pageable).map(PostResponseDto::from); + } } From e3603d7408143eb117d46c24362270996977728e Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 17 Nov 2023 17:35:14 +0900 Subject: [PATCH 036/102] =?UTF-8?q?#5=20feat:=20Post=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index a6e842983..e12118ce8 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -1,24 +1,18 @@ package com.example.board.controller; import com.example.board.dto.PostDto; -import com.example.board.exception.BaseException; +import com.example.board.dto.PostResponseDto; import com.example.board.exception.BindingException; -import com.example.board.exception.ErrorMessage; import com.example.board.response.Response; import com.example.board.service.PostService; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; -import org.springframework.validation.ObjectError; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; +import org.springframework.web.bind.annotation.*; @RequiredArgsConstructor @RequestMapping("/api/v1/posts") @@ -32,6 +26,13 @@ public Response save(@RequestBody @Validated PostDto postDto, BindingResul return Response.success(postService.save(postDto)); } + @GetMapping + public Response> readAllPost( + @PageableDefault(page = 0, size = 10) Pageable pageable + ){ + return Response.success(postService.readAllPost(pageable)); + } + private static void bindChecking(BindingResult bindingResult) { if (bindingResult.hasErrors()) { StringBuilder sb = new StringBuilder(); From 347ff1670ce688fc3ba8685b2683aaea716a6a01 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 17 Nov 2023 17:44:55 +0900 Subject: [PATCH 037/102] =?UTF-8?q?#9=20refactor:=20Post=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?N+1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/repository/PostRepository.java | 7 +++++++ src/main/java/com/example/board/service/PostService.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/repository/PostRepository.java b/src/main/java/com/example/board/repository/PostRepository.java index 65b1257c7..3719b22fa 100644 --- a/src/main/java/com/example/board/repository/PostRepository.java +++ b/src/main/java/com/example/board/repository/PostRepository.java @@ -1,7 +1,14 @@ package com.example.board.repository; import com.example.board.model.Post; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface PostRepository extends JpaRepository { + @EntityGraph(attributePaths = "user") + @Query("select p from Post p ") + Page findAllByEntityGraph(Pageable pageable); } diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 922bfc108..ed5b73546 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -29,6 +29,6 @@ public Long save(PostDto postDto) { } public Page readAllPost(Pageable pageable) { - return postRepository.findAll(pageable).map(PostResponseDto::from); + return postRepository.findAllByEntityGraph(pageable).map(PostResponseDto::from); } } From c815867a1ade20bece3997c46d1a770db19d546f Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:24:51 +0900 Subject: [PATCH 038/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EB=8B=A8=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20api=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/controller/PostController.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index e12118ce8..b3c77d265 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -1,5 +1,6 @@ package com.example.board.controller; +import com.example.board.dto.PostDetailResponseDto; import com.example.board.dto.PostDto; import com.example.board.dto.PostResponseDto; import com.example.board.exception.BindingException; @@ -28,11 +29,16 @@ public Response save(@RequestBody @Validated PostDto postDto, BindingResul @GetMapping public Response> readAllPost( - @PageableDefault(page = 0, size = 10) Pageable pageable + @PageableDefault Pageable pageable ){ return Response.success(postService.readAllPost(pageable)); } + @GetMapping("/{postId}") + public Response readDetailPost(@PathVariable Long postId){ + return Response.success(postService.readPostDetail(postId)); + } + private static void bindChecking(BindingResult bindingResult) { if (bindingResult.hasErrors()) { StringBuilder sb = new StringBuilder(); @@ -44,8 +50,8 @@ private static void bindChecking(BindingResult bindingResult) { System.out.println("field :" + field.getField()); System.out.println("message :" + message); - sb.append("field :" + field.getField()); - sb.append("message :" + message); + sb.append("field :").append(field.getField()); + sb.append("message :").append(message); }); From 17566f3a86047dcd39901d62e1db189ecbc1ee71 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:25:10 +0900 Subject: [PATCH 039/102] =?UTF-8?q?#6=20feat:=20=EB=8B=A8=EA=B1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EB=B0=98=ED=99=98=ED=95=A0=20?= =?UTF-8?q?dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/dto/PostResponseDto.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/example/board/dto/PostResponseDto.java b/src/main/java/com/example/board/dto/PostResponseDto.java index fb53ac060..a817d6eca 100644 --- a/src/main/java/com/example/board/dto/PostResponseDto.java +++ b/src/main/java/com/example/board/dto/PostResponseDto.java @@ -10,4 +10,6 @@ public record PostResponseDto( public static PostResponseDto from(Post post) { return new PostResponseDto(post.getTitle(), post.getUser().getName()); } + + } From 8036d3b9d48e8c36e508110fa6779b24f64255b5 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:25:40 +0900 Subject: [PATCH 040/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EB=8B=A8=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/service/PostService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index ed5b73546..5e134b160 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -1,5 +1,6 @@ package com.example.board.service; +import com.example.board.dto.PostDetailResponseDto; import com.example.board.dto.PostDto; import com.example.board.dto.PostResponseDto; import com.example.board.exception.BaseException; @@ -31,4 +32,9 @@ public Long save(PostDto postDto) { public Page readAllPost(Pageable pageable) { return postRepository.findAllByEntityGraph(pageable).map(PostResponseDto::from); } + + public PostDetailResponseDto readPostDetail(Long postId) { + return PostDetailResponseDto.from(postRepository.findById(postId) + .orElseThrow(() -> new BaseException(ErrorMessage.POST_NOT_FOUND))); + } } From a7a7ecab8557563c0af8f53c1e5c3e7230a82819 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:26:02 +0900 Subject: [PATCH 041/102] =?UTF-8?q?#6=20feat:=20=EB=8B=A8=EA=B1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EA=B2=8C=EC=8B=9C=EA=B8=80?= =?UTF-8?q?=EC=9D=B4=20=EC=A1=B4=EC=9E=AC=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=EC=9D=98=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/exception/ErrorMessage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index b842290c8..3c7e7fc38 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -9,7 +9,10 @@ public enum ErrorMessage { INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."), // 1xxx user exception - USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."); + USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."), + + //2xxx post exception + POST_NOT_FOUND(2000, HttpStatus.NOT_FOUND, "찾으시는 게시글이 없습니다."); private int code; private HttpStatus httpStatus; From eac94d7d30683e7f21332fbda3086e68d354f3a7 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:26:20 +0900 Subject: [PATCH 042/102] =?UTF-8?q?#6=20feat:=20=EB=8B=A8=EA=B1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?record=20dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/dto/PostDetailResponseDto.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/com/example/board/dto/PostDetailResponseDto.java diff --git a/src/main/java/com/example/board/dto/PostDetailResponseDto.java b/src/main/java/com/example/board/dto/PostDetailResponseDto.java new file mode 100644 index 000000000..cd3401d5b --- /dev/null +++ b/src/main/java/com/example/board/dto/PostDetailResponseDto.java @@ -0,0 +1,19 @@ +package com.example.board.dto; + +import com.example.board.model.Post; + +public record PostDetailResponseDto ( + Long postId, + String userName, + String title, + String contents +) { + + public static PostDetailResponseDto from(Post post) { + return new PostDetailResponseDto( + post.getId(), + post.getUser().getName(), + post.getTitle(), + post.getContents()); + } +} From 7d4dfa27ab807821cef547cc2438554389634d65 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 18 Nov 2023 15:30:34 +0900 Subject: [PATCH 043/102] =?UTF-8?q?#6=20refactor:=20=EB=8B=A8=EA=B1=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20user=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EA=B0=80=EC=A0=B8=EC=98=AC=20=EB=95=8C=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=EA=B0=80=20=EB=91=90=20=EB=B2=88=20=EB=82=98=EA=B0=80=EB=8A=94?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20EntityGraph=EB=A1=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/repository/PostRepository.java | 7 +++++++ src/main/java/com/example/board/service/PostService.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/repository/PostRepository.java b/src/main/java/com/example/board/repository/PostRepository.java index 3719b22fa..145bbf261 100644 --- a/src/main/java/com/example/board/repository/PostRepository.java +++ b/src/main/java/com/example/board/repository/PostRepository.java @@ -6,9 +6,16 @@ import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; public interface PostRepository extends JpaRepository { @EntityGraph(attributePaths = "user") @Query("select p from Post p ") Page findAllByEntityGraph(Pageable pageable); + + @EntityGraph(attributePaths = "user") + @Query("select p from Post p where p.id = :postId") + Optional findByIdEntityGraph(@Param("postId") Long postId); } diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 5e134b160..fbe8d474a 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -34,7 +34,7 @@ public Page readAllPost(Pageable pageable) { } public PostDetailResponseDto readPostDetail(Long postId) { - return PostDetailResponseDto.from(postRepository.findById(postId) + return PostDetailResponseDto.from(postRepository.findByIdEntityGraph(postId) .orElseThrow(() -> new BaseException(ErrorMessage.POST_NOT_FOUND))); } } From d44f3e6408bce047517ee889ddd9565a7130ba16 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:20:16 +0900 Subject: [PATCH 044/102] =?UTF-8?q?#7=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20dto=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/dto/PostUpdateDto.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/example/board/dto/PostUpdateDto.java diff --git a/src/main/java/com/example/board/dto/PostUpdateDto.java b/src/main/java/com/example/board/dto/PostUpdateDto.java new file mode 100644 index 000000000..213f9cf58 --- /dev/null +++ b/src/main/java/com/example/board/dto/PostUpdateDto.java @@ -0,0 +1,12 @@ +package com.example.board.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record PostUpdateDto( + @NotNull Long userId, + @Size(max = 20) String title, + @NotBlank String contents +) { +} From a9391aa6eec453357428cd9f80eff747f0447904 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:21:01 +0900 Subject: [PATCH 045/102] =?UTF-8?q?#6=20refactor:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EB=8B=A8=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=20dto?= =?UTF-8?q?=EC=97=90=20userId=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/dto/PostDetailResponseDto.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/dto/PostDetailResponseDto.java b/src/main/java/com/example/board/dto/PostDetailResponseDto.java index cd3401d5b..cf59637ea 100644 --- a/src/main/java/com/example/board/dto/PostDetailResponseDto.java +++ b/src/main/java/com/example/board/dto/PostDetailResponseDto.java @@ -2,8 +2,9 @@ import com.example.board.model.Post; -public record PostDetailResponseDto ( +public record PostDetailResponseDto( Long postId, + Long userID, String userName, String title, String contents @@ -12,6 +13,7 @@ public record PostDetailResponseDto ( public static PostDetailResponseDto from(Post post) { return new PostDetailResponseDto( post.getId(), + post.getUser().getId(), post.getUser().getName(), post.getTitle(), post.getContents()); From 2a6453023912a9b620416d108e837d8d40c27512 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:21:47 +0900 Subject: [PATCH 046/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EB=B6=88=EC=9D=BC=EC=B9=98=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/exception/ErrorMessage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index 3c7e7fc38..1bfbdbaf5 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -12,7 +12,8 @@ public enum ErrorMessage { USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."), //2xxx post exception - POST_NOT_FOUND(2000, HttpStatus.NOT_FOUND, "찾으시는 게시글이 없습니다."); + POST_NOT_FOUND(2000, HttpStatus.NOT_FOUND, "찾으시는 게시글이 없습니다."), + WRITER_NOT_MATCHED(2001, HttpStatus.BAD_REQUEST, "작성자가 일치하지 않습니다."); private int code; private HttpStatus httpStatus; From 95b97326b19ae583928daa3c10ba1a6bbdb16338 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:22:23 +0900 Subject: [PATCH 047/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=97=94=ED=8B=B0=ED=8B=B0=EC=97=90=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/model/Post.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index 38b0f7edb..dea44b46a 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -1,6 +1,7 @@ package com.example.board.model; import com.example.board.dto.PostDto; +import com.example.board.dto.PostUpdateDto; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -45,4 +46,10 @@ private Post(User user, PostDto postDto) { public static Post from(User user, PostDto postDto) { return new Post(user, postDto); } + + public Long update(PostUpdateDto postUpdateDto) { + this.title = postUpdateDto.title(); + this.contents = postUpdateDto.contents(); + return this.id; + } } From 8a6378a0da6c1f82288cf5d78956b33127c4b9c7 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:22:44 +0900 Subject: [PATCH 048/102] =?UTF-8?q?#6=20refactor:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=97=90=20EqualsAndHashCode=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 --- src/main/java/com/example/board/model/User.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/example/board/model/User.java b/src/main/java/com/example/board/model/User.java index e7b75d3a9..860c1e0e5 100644 --- a/src/main/java/com/example/board/model/User.java +++ b/src/main/java/com/example/board/model/User.java @@ -2,10 +2,12 @@ import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; @Getter +@EqualsAndHashCode(callSuper = false) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Table(name = "users") From ba4f41a8358f0d2b78b5a602436db4e6822a2722 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:23:12 +0900 Subject: [PATCH 049/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index b3c77d265..04f279f90 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -3,6 +3,7 @@ import com.example.board.dto.PostDetailResponseDto; import com.example.board.dto.PostDto; import com.example.board.dto.PostResponseDto; +import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BindingException; import com.example.board.response.Response; import com.example.board.service.PostService; @@ -21,24 +22,6 @@ public class PostController { private final PostService postService; - @PostMapping - public Response save(@RequestBody @Validated PostDto postDto, BindingResult bindingResult) { - bindChecking(bindingResult); - return Response.success(postService.save(postDto)); - } - - @GetMapping - public Response> readAllPost( - @PageableDefault Pageable pageable - ){ - return Response.success(postService.readAllPost(pageable)); - } - - @GetMapping("/{postId}") - public Response readDetailPost(@PathVariable Long postId){ - return Response.success(postService.readPostDetail(postId)); - } - private static void bindChecking(BindingResult bindingResult) { if (bindingResult.hasErrors()) { StringBuilder sb = new StringBuilder(); @@ -59,4 +42,30 @@ private static void bindChecking(BindingResult bindingResult) { } } + @PostMapping + public Response save(@RequestBody @Validated PostDto postDto, BindingResult bindingResult) { + bindChecking(bindingResult); + return Response.success(postService.save(postDto)); + } + + @GetMapping + public Response> readAllPost( + @PageableDefault Pageable pageable + ) { + return Response.success(postService.readAllPost(pageable)); + } + + @GetMapping("/{postId}") + public Response readDetailPost(@PathVariable Long postId) { + return Response.success(postService.readPostDetail(postId)); + } + + // TODO: 이후 로그인 정보로 유저 일치 확인하기(변경 예정) + @PatchMapping("/{postId}") + public Response update( + @PathVariable Long postId, + @RequestBody @Validated PostUpdateDto postUpdateDto + ) { + return Response.success(postService.update(postId, postUpdateDto, postUpdateDto.userId())); + } } From ae5fa996e71ec9da999ba57bdb1867b33ad8920b Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Sat, 18 Nov 2023 16:23:21 +0900 Subject: [PATCH 050/102] =?UTF-8?q?#6=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/service/PostService.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index fbe8d474a..37f22057d 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -3,6 +3,7 @@ import com.example.board.dto.PostDetailResponseDto; import com.example.board.dto.PostDto; import com.example.board.dto.PostResponseDto; +import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; import com.example.board.model.Post; @@ -37,4 +38,18 @@ public PostDetailResponseDto readPostDetail(Long postId) { return PostDetailResponseDto.from(postRepository.findByIdEntityGraph(postId) .orElseThrow(() -> new BaseException(ErrorMessage.POST_NOT_FOUND))); } + + @Transactional + public Long update(Long postId, PostUpdateDto postUpdateDto, Long userId) { + Post post = postRepository.findByIdEntityGraph(postId) + .orElseThrow(() -> new BaseException(ErrorMessage.POST_NOT_FOUND)); + User user = userRepository.findById(userId) + .orElseThrow(() -> new BaseException(ErrorMessage.USER_NOT_FOUND)); + + if (!post.getUser().equals(user)) { + throw new BaseException(ErrorMessage.WRITER_NOT_MATCHED); + } + return post.update(postUpdateDto); + + } } From 108c70f57f9437f6d870107feaf5080637b99516 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 14:41:41 +0900 Subject: [PATCH 051/102] =?UTF-8?q?#10=20refactor:=20ResponseEntity?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=B4=20status=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=8A=94=20=EB=B0=A9=EB=B2=95?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index 36311f40b..85aa9b11e 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -3,6 +3,8 @@ import com.example.board.response.Response; import com.fasterxml.jackson.databind.exc.MismatchedInputException; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -12,26 +14,26 @@ public class GlobalExceptionHandler { @ExceptionHandler(BaseException.class) - public Response customExceptionHandle(BaseException e) { + public ResponseEntity> customExceptionHandle(BaseException e) { log.info("error: {}", e.getMessage()); - return Response.fail(e); + return ResponseEntity.status(e.getHttpStatus()).body(Response.fail(e)); } @ExceptionHandler(RuntimeException.class) - public Response customExceptionHandle(RuntimeException e) { + public ResponseEntity> customExceptionHandle(RuntimeException e) { log.info("error: {}", e.getMessage()); - return Response.fail(e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } @ExceptionHandler(Exception.class) - public Response customExceptionHandle(Exception e) { + public ResponseEntity> customExceptionHandle(Exception e) { log.info("error: {}", e.getMessage()); - return Response.fail(e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } @ExceptionHandler(MismatchedInputException.class) - public Response customExceptionHandle(MismatchedInputException e) { + public ResponseEntity> customExceptionHandle(MismatchedInputException e) { log.info("error: {}", e.getMessage()); - return Response.fail(e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.fail(e)); } } From 9a12053e3e8c54e1d2987cfac1e23a0cb671ac77 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 16:53:22 +0900 Subject: [PATCH 052/102] =?UTF-8?q?#8=20chore:=20rest-docs=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20build=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dc6b49062..742c0d59f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.1.5' id 'io.spring.dependency-management' version '1.1.3' + id "org.asciidoctor.jvm.convert" version "3.3.2" } group = 'com.example' @@ -15,6 +16,8 @@ configurations { compileOnly { extendsFrom annotationProcessor } + asciidoctorExt + } repositories { @@ -29,9 +32,17 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' implementation 'org.springframework.boot:spring-boot-starter-validation' + // ascii-docs + asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + +} + +ext { + snippetsDir = file('build/generated-snippets') + asciidocDir = file('src/docs/asciidoc') } tasks.named('bootBuildImage') { @@ -41,3 +52,10 @@ tasks.named('bootBuildImage') { tasks.named('test') { useJUnitPlatform() } + +asciidoctor { + inputs.dir snippetsDir + configurations 'asciidoctorExt' + dependsOn test + outputs.dir asciidocDir // 추가된 부분 +} \ No newline at end of file From d2692bf46d367bbbd8d56f0c08ab4d04e152d1ee Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 16:53:52 +0900 Subject: [PATCH 053/102] =?UTF-8?q?#8=20chore:=20rest-docs=20html=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EB=B3=80=ED=99=98=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20adoc=20=ED=8C=8C=EC=9D=BC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/docs/asciidoc/index.adoc diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc new file mode 100644 index 000000000..e6dc022a1 --- /dev/null +++ b/src/docs/asciidoc/index.adoc @@ -0,0 +1,81 @@ +:hardbreaks: +ifndef::snippets[] +:snippets: ../../../build/generated-snippets +endif::[] + +== 게시글 +=== 게시글 등록 +==== /api/v1/posts + +.Request + +include::{snippets}/post-save/http-request.adoc[] + +.Request Fields + +include::{snippets}/post-save/request-fields.adoc[] + +.Response + +include::{snippets}/post-save/http-response.adoc[] + +.Response Fields + +include::{snippets}/post-save/response-fields.adoc[] + +=== 게시글 전체조회 +==== /api/v1/posts + +.Request + +include::{snippets}/post-getAll/http-request.adoc[] + +.Request Fields + +include::{snippets}/post-getAll/request-fields.adoc[] + +.Response + +include::{snippets}/post-getAll/http-response.adoc[] + +.Response Fields + +include::{snippets}/post-getAll/response-fields.adoc[] + +=== 게시글 단건 조회 +==== /api/v1/posts/{postId} + +.Request + +include::{snippets}/post-getById/http-request.adoc[] + +.Request Fields + +include::{snippets}/post-getById/request-fields.adoc[] + +.Response + +include::{snippets}/post-getById/http-response.adoc[] + +.Response Fields + +include::{snippets}/post-getById/response-fields.adoc[] + +=== 게시글 수정 +==== /api/v1/posts/{postId} + +.Request + +include::{snippets}/post-update/http-request.adoc[] + +.Request Fields + +include::{snippets}/post-update/request-fields.adoc[] + +.Response + +include::{snippets}/post-update/http-response.adoc[] + +.Response Fields + +include::{snippets}/post-update/response-fields.adoc[] \ No newline at end of file From 2fa505ebd03b4ff43067be6237e239207aca84df Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 16:54:24 +0900 Subject: [PATCH 054/102] =?UTF-8?q?#8=20test:=20rest-docs=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EA=B0=81=20controller=20=EC=A0=95?= =?UTF-8?q?=EC=83=81=20=EA=B5=AC=EB=8F=99=20api=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostControllerTest.java | 141 ++++++++++++++++-- 1 file changed, 132 insertions(+), 9 deletions(-) diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index de5feead4..0e0b6c675 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -1,30 +1,40 @@ package com.example.board.controller; import com.example.board.dto.PostDto; +import com.example.board.dto.PostUpdateDto; +import com.example.board.service.PostService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +import org.springframework.transaction.annotation.Transactional; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @SpringBootTest +@AutoConfigureRestDocs @AutoConfigureMockMvc +@ActiveProfiles("test") class PostControllerTest { - @MockBean + @Autowired PostController postController; + @Autowired + PostService postService; + @Autowired MockMvc mockMvc; @@ -32,17 +42,130 @@ class PostControllerTest { ObjectMapper objectMapper; @Test - @DisplayName("정상적으로 게시글을 등록하는 상황") + @DisplayName("정상적으로 게시글을 등록된다.") + @Transactional void postSuccess() throws Exception { //given - PostDto postDto = new PostDto(1, "test", "test Contents"); + PostDto postDto = new PostDto(1, "test2", "test Contents2"); //when, then mockMvc.perform(post("http://localhost:8080/api/v1/posts") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsBytes(postDto))) .andExpect(status().isOk()) - .andDo(print()); + .andDo(document("post-save", + requestFields( + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("contents").type(JsonFieldType.STRING).description("contents") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("정상적으로 모든 게시물들을 받아온다.") + @Transactional + void getSuccess() throws Exception { + //given + postService.save(new PostDto(1, "testTitle1", "hihihihihihihih1")); + postService.save(new PostDto(1, "testTitle2", "hihihihihihihih2")); + + //when, then + mockMvc.perform(get("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("post-getAll", + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), + fieldWithPath("data.content[]").type(JsonFieldType.ARRAY).description("contents-list"), + fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.pageable").type(JsonFieldType.OBJECT).description("pageable"), + fieldWithPath("data.pageable.pageNumber").type(JsonFieldType.NUMBER).description("pageNumber"), + fieldWithPath("data.pageable.pageSize").type(JsonFieldType.NUMBER).description("pageSize"), + fieldWithPath("data.pageable.sort").type(JsonFieldType.OBJECT).description("sort"), + fieldWithPath("data.pageable.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), + fieldWithPath("data.pageable.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), + fieldWithPath("data.pageable.sort.sorted").type(JsonFieldType.BOOLEAN ).description("sorted"), + fieldWithPath("data.pageable.offset").type(JsonFieldType.NUMBER).description("offset"), + fieldWithPath("data.pageable.paged").type(JsonFieldType.BOOLEAN).description("paged"), + fieldWithPath("data.pageable.unpaged").type(JsonFieldType.BOOLEAN).description("unpaged"), + fieldWithPath("data.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), + fieldWithPath("data.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), + fieldWithPath("data.last").type(JsonFieldType.BOOLEAN).description("last"), + fieldWithPath("data.size").type(JsonFieldType.NUMBER).description("size"), + fieldWithPath("data.number").type(JsonFieldType.NUMBER).description("number"), + fieldWithPath("data.sort").type(JsonFieldType.OBJECT).description("sort"), + fieldWithPath("data.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), + fieldWithPath("data.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), + fieldWithPath("data.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), + fieldWithPath("data.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements"), + fieldWithPath("data.first").type(JsonFieldType.BOOLEAN).description("first"), + fieldWithPath("data.empty").type(JsonFieldType.BOOLEAN).description("unpaged"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("정상적으로 특정 게시물 하나를 받아온다..") + @Transactional + void getPostByIdSuccess() throws Exception { + //given + Long savedContentsId = postService.save(new PostDto(1, "testTitle", "hihihihihihihih")); + + //when, then + mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + savedContentsId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("post-getById", + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), + fieldWithPath("data.userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.postId").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("data.userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("data.title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.contents").type(JsonFieldType.STRING).description("contents"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("정상적으로 게시글을 수정한다.") + @Transactional + void updatePostSuccess() throws Exception { + //given + Long savedContents = postService.save(new PostDto(1, "testTitle", "hihihihihihihih")); + + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "test2", "test Contents2"); + + //when, then + mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postUpdateDto))) + .andExpect(status().isOk()) + .andDo(document("post-update", + requestFields( + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("title").type(JsonFieldType.STRING).description("title update"), + fieldWithPath("contents").type(JsonFieldType.STRING).description("contents update") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); } } \ No newline at end of file From 49a4958a9f5a88e45e7d8cd768d38b78412930e1 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 16:55:07 +0900 Subject: [PATCH 055/102] =?UTF-8?q?#8=20refactor:=20rest-docs=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=A4=91=20coding=20convention=20field=20=EB=AA=85?= =?UTF-8?q?=20camel=20case=20=EB=AF=B8=EC=8B=A4=EC=8B=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/dto/PostDetailResponseDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/dto/PostDetailResponseDto.java b/src/main/java/com/example/board/dto/PostDetailResponseDto.java index cf59637ea..8f762974c 100644 --- a/src/main/java/com/example/board/dto/PostDetailResponseDto.java +++ b/src/main/java/com/example/board/dto/PostDetailResponseDto.java @@ -4,7 +4,7 @@ public record PostDetailResponseDto( Long postId, - Long userID, + Long userId, String userName, String title, String contents From 46557fc0ae8a8272fb415374e9d490578669b92f Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Tue, 21 Nov 2023 16:55:54 +0900 Subject: [PATCH 056/102] =?UTF-8?q?chore:=20=ED=86=B5=EC=9D=BC=20=EC=95=88?= =?UTF-8?q?=EB=90=9C=20Entity=20=EB=B0=8F=20Dto=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=EC=9D=98=20ID=20=EB=B3=80=EC=88=98=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC=20=ED=95=84=EC=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/dto/PostUpdateDto.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/example/board/dto/PostUpdateDto.java b/src/main/java/com/example/board/dto/PostUpdateDto.java index 213f9cf58..517d50bb2 100644 --- a/src/main/java/com/example/board/dto/PostUpdateDto.java +++ b/src/main/java/com/example/board/dto/PostUpdateDto.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +//TODO: userId 타입 통일 public record PostUpdateDto( @NotNull Long userId, @Size(max = 20) String title, From 1f061a94c076c5802fb5f38012a93ee54c4a4b16 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:47:03 +0900 Subject: [PATCH 057/102] =?UTF-8?q?#11=20feat:=20Post=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/model/Post.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index dea44b46a..cddd99c9b 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -2,10 +2,9 @@ import com.example.board.dto.PostDto; import com.example.board.dto.PostUpdateDto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,18 +17,15 @@ public class Post extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "post_id") - private long id; + private Long id; - @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - @Size(max = 20) @Column(name = "title", length = 30) private String title; - @NotBlank @Lob private String contents; @@ -37,12 +33,28 @@ public class Post extends BaseEntity { private String createdBy; private Post(User user, PostDto postDto) { + postSaveValidate(user, postDto); this.user = user; this.title = postDto.title(); this.contents = postDto.contents(); this.createdBy = user.getName(); } + private static void postSaveValidate(User user, PostDto postDto) { + if (user == null) { + throw new BaseException(ErrorMessage.USER_NOT_FOUND); + } + if (postDto.title().isBlank() || (postDto.title().length() > 20)) { + throw new BaseException(ErrorMessage.WRONG_TITLE_VALUE); + } + if (postDto.contents().isBlank()) { + throw new BaseException(ErrorMessage.WRONG_CONTENTS_VALUE); + } + if (user.getName().isBlank()) { + throw new BaseException(ErrorMessage.WRONG_USER_NAME); + } + } + public static Post from(User user, PostDto postDto) { return new Post(user, postDto); } From 13ad54ed876c327f73cb90bdee35ee485ad8a18b Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:47:30 +0900 Subject: [PATCH 058/102] =?UTF-8?q?#11=20feat:=20PostDto(=EC=83=9D?= =?UTF-8?q?=EC=84=B1)=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/dto/PostDto.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/board/dto/PostDto.java b/src/main/java/com/example/board/dto/PostDto.java index 3ec2b23fa..d221ed2d3 100644 --- a/src/main/java/com/example/board/dto/PostDto.java +++ b/src/main/java/com/example/board/dto/PostDto.java @@ -1,13 +1,19 @@ package com.example.board.dto; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import org.springframework.lang.NonNull; -//TODO: dto 잘못된 값 검증해보기 +/** + * 추후 게시글 입력 항목 확장을 고려해 PostUpdateDto와 필드는 동일하지만 구분하여 사용할 예정입니다. + */ public record PostDto( - @NonNull long userId, - @Size(max = 20) String title, - @NotBlank String contents + @NotNull(message = "사용자 id를 입력해주세요.") + Long userId, + @NotBlank(message = "게시글 제목을 입력해주세요.") + @Size(max = 20, message = "제목은 최대 20자까지만 입력해주세요.") + String title, + @NotBlank(message = "게시글 내용을 입력해주세요.") + String contents ) { } From 2ca6a2c6ac06a5e39e47b70d37ffb9c260382f76 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:47:47 +0900 Subject: [PATCH 059/102] =?UTF-8?q?#11=20feat:=20PostUpdateDto(=EC=88=98?= =?UTF-8?q?=EC=A0=95)=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/dto/PostUpdateDto.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/example/board/dto/PostUpdateDto.java b/src/main/java/com/example/board/dto/PostUpdateDto.java index 517d50bb2..de131ac11 100644 --- a/src/main/java/com/example/board/dto/PostUpdateDto.java +++ b/src/main/java/com/example/board/dto/PostUpdateDto.java @@ -4,10 +4,13 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -//TODO: userId 타입 통일 public record PostUpdateDto( - @NotNull Long userId, - @Size(max = 20) String title, - @NotBlank String contents + @NotNull(message = "사용자 id를 입력해주세요.") + Long userId, + @NotBlank(message = "게시글 제목을 입력해주세요.") + @Size(max = 20, message = "제목은 최대 20자까지만 입력해주세요.") + String title, + @NotBlank(message = "게시글 내용을 입력해주세요.") + String contents ) { } From bd83f36a7571deabd0c1123577e5b2bc12411cb2 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:48:09 +0900 Subject: [PATCH 060/102] =?UTF-8?q?#11=20feat:=20PostDetailResponseDto=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/dto/PostDetailResponseDto.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/com/example/board/dto/PostDetailResponseDto.java b/src/main/java/com/example/board/dto/PostDetailResponseDto.java index 8f762974c..a6dfb442d 100644 --- a/src/main/java/com/example/board/dto/PostDetailResponseDto.java +++ b/src/main/java/com/example/board/dto/PostDetailResponseDto.java @@ -1,5 +1,7 @@ package com.example.board.dto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; import com.example.board.model.Post; public record PostDetailResponseDto( @@ -11,6 +13,7 @@ public record PostDetailResponseDto( ) { public static PostDetailResponseDto from(Post post) { + postDetailResponseValidate(post); return new PostDetailResponseDto( post.getId(), post.getUser().getId(), @@ -18,4 +21,25 @@ public static PostDetailResponseDto from(Post post) { post.getTitle(), post.getContents()); } + + private static void postDetailResponseValidate(Post post) { + if (post.getId() == null) { + throw new BaseException(ErrorMessage.POST_NOT_FOUND); + } + if (post.getUser() == null) { + throw new BaseException(ErrorMessage.USER_NOT_FOUND); + } + if (post.getUser().getId() == null) { + throw new BaseException(ErrorMessage.WRONG_USER_ID); + } + if (post.getTitle().isBlank() || (post.getTitle().length() > 20)) { + throw new BaseException(ErrorMessage.WRONG_TITLE_VALUE); + } + if (post.getContents().isBlank()) { + throw new BaseException(ErrorMessage.WRONG_CONTENTS_VALUE); + } + if (post.getUser().getName().isBlank()) { + throw new BaseException(ErrorMessage.WRONG_USER_NAME); + } + } } From e962e4442875fc3dd0c7a2c883b8fedd5b944a04 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:48:20 +0900 Subject: [PATCH 061/102] =?UTF-8?q?#11=20feat:=20PostResponseDto=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/board/dto/PostResponseDto.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/board/dto/PostResponseDto.java b/src/main/java/com/example/board/dto/PostResponseDto.java index a817d6eca..3f5fcb514 100644 --- a/src/main/java/com/example/board/dto/PostResponseDto.java +++ b/src/main/java/com/example/board/dto/PostResponseDto.java @@ -1,15 +1,31 @@ package com.example.board.dto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; import com.example.board.model.Post; public record PostResponseDto( + Long postId, String userName, String title ) { - public static PostResponseDto from(Post post) { - return new PostResponseDto(post.getTitle(), post.getUser().getName()); + postResponseValidate(post); + return new PostResponseDto(post.getId(), post.getTitle(), post.getUser().getName()); } - + private static void postResponseValidate(Post post) { + if (post.getId() == null) { + throw new BaseException(ErrorMessage.POST_NOT_FOUND); + } + if (post.getTitle().isBlank() || (post.getTitle().length() > 20)) { + throw new BaseException(ErrorMessage.WRONG_TITLE_VALUE); + } + if (post.getUser() == null) { + throw new BaseException(ErrorMessage.USER_NOT_FOUND); + } + if (post.getUser().getName().isBlank()) { + throw new BaseException(ErrorMessage.WRONG_USER_NAME); + } + } } From 157dc0983c03c6a2c4ddc0fcd9ecdcdd4ba18dec Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:50:18 +0900 Subject: [PATCH 062/102] =?UTF-8?q?#11=20refactor:=20binding=20result=20er?= =?UTF-8?q?ror=20=EA=B2=80=EC=82=AC=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 31 ++++++------------- .../com/example/board/util/Validation.java | 18 +++++++++++ .../board/controller/PostControllerTest.java | 24 +++++++------- 3 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/example/board/util/Validation.java diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index 04f279f90..149e09c7f 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -11,40 +11,27 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; +import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; -import org.springframework.validation.FieldError; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + +import static com.example.board.util.Validation.bindChecking; + @RequiredArgsConstructor @RequestMapping("/api/v1/posts") @RestController public class PostController { private final PostService postService; - private static void bindChecking(BindingResult bindingResult) { - if (bindingResult.hasErrors()) { - StringBuilder sb = new StringBuilder(); - bindingResult.getAllErrors().forEach(objectError -> { - - FieldError field = (FieldError) objectError; - String message = objectError.getDefaultMessage(); - - System.out.println("field :" + field.getField()); - System.out.println("message :" + message); - - sb.append("field :").append(field.getField()); - sb.append("message :").append(message); - - }); - - throw new BindingException(sb.toString()); - } - } - @PostMapping public Response save(@RequestBody @Validated PostDto postDto, BindingResult bindingResult) { - bindChecking(bindingResult); + List bindingErrors = bindChecking(bindingResult); + if (!bindingErrors.isEmpty()) { + throw new BindingException(bindingErrors); + } return Response.success(postService.save(postDto)); } diff --git a/src/main/java/com/example/board/util/Validation.java b/src/main/java/com/example/board/util/Validation.java new file mode 100644 index 000000000..64c2e36b4 --- /dev/null +++ b/src/main/java/com/example/board/util/Validation.java @@ -0,0 +1,18 @@ +package com.example.board.util; + +import org.springframework.validation.BindingResult; + +import java.util.ArrayList; +import java.util.List; + +public class Validation { + public static List bindChecking(BindingResult bindingResult) { + List messages = new ArrayList<>(); + if (bindingResult.hasErrors()) { + bindingResult.getAllErrors().forEach(objectError -> { + messages.add(objectError.getDefaultMessage()); + }); + } + return messages; + } +} diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 0e0b6c675..203f35f08 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -12,7 +12,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; @@ -20,8 +19,7 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; - -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @AutoConfigureRestDocs @@ -46,12 +44,12 @@ class PostControllerTest { @Transactional void postSuccess() throws Exception { //given - PostDto postDto = new PostDto(1, "test2", "test Contents2"); + PostDto postDto = new PostDto(1L, "test2", "test Contents2"); //when, then mockMvc.perform(post("http://localhost:8080/api/v1/posts") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsBytes(postDto))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) .andExpect(status().isOk()) .andDo(document("post-save", requestFields( @@ -72,8 +70,8 @@ void postSuccess() throws Exception { @Transactional void getSuccess() throws Exception { //given - postService.save(new PostDto(1, "testTitle1", "hihihihihihihih1")); - postService.save(new PostDto(1, "testTitle2", "hihihihihihihih2")); + postService.save(new PostDto(1L, "testTitle1", "hihihihihihihih1")); + postService.save(new PostDto(1L, "testTitle2", "hihihihihihihih2")); //when, then mockMvc.perform(get("http://localhost:8080/api/v1/posts") @@ -84,8 +82,10 @@ void getSuccess() throws Exception { fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), fieldWithPath("data.content[]").type(JsonFieldType.ARRAY).description("contents-list"), + fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), fieldWithPath("data.pageable").type(JsonFieldType.OBJECT).description("pageable"), @@ -94,7 +94,7 @@ void getSuccess() throws Exception { fieldWithPath("data.pageable.sort").type(JsonFieldType.OBJECT).description("sort"), fieldWithPath("data.pageable.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), fieldWithPath("data.pageable.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), - fieldWithPath("data.pageable.sort.sorted").type(JsonFieldType.BOOLEAN ).description("sorted"), + fieldWithPath("data.pageable.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), fieldWithPath("data.pageable.offset").type(JsonFieldType.NUMBER).description("offset"), fieldWithPath("data.pageable.paged").type(JsonFieldType.BOOLEAN).description("paged"), fieldWithPath("data.pageable.unpaged").type(JsonFieldType.BOOLEAN).description("unpaged"), @@ -111,7 +111,7 @@ void getSuccess() throws Exception { fieldWithPath("data.first").type(JsonFieldType.BOOLEAN).description("first"), fieldWithPath("data.empty").type(JsonFieldType.BOOLEAN).description("unpaged"), fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") - ) + ) )); } @@ -120,7 +120,7 @@ void getSuccess() throws Exception { @Transactional void getPostByIdSuccess() throws Exception { //given - Long savedContentsId = postService.save(new PostDto(1, "testTitle", "hihihihihihihih")); + Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); //when, then mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + savedContentsId) @@ -145,7 +145,7 @@ void getPostByIdSuccess() throws Exception { @Transactional void updatePostSuccess() throws Exception { //given - Long savedContents = postService.save(new PostDto(1, "testTitle", "hihihihihihihih")); + Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "test2", "test Contents2"); From 7b69b9d13216233e7d16e7f93a58c543fb6f5472 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:51:38 +0900 Subject: [PATCH 063/102] =?UTF-8?q?#11=20refactor:=20BindingException=20ge?= =?UTF-8?q?tMessage=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/exception/BindingException.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/example/board/exception/BindingException.java b/src/main/java/com/example/board/exception/BindingException.java index 697fa640c..45019d3f4 100644 --- a/src/main/java/com/example/board/exception/BindingException.java +++ b/src/main/java/com/example/board/exception/BindingException.java @@ -1,13 +1,21 @@ package com.example.board.exception; -import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; -@AllArgsConstructor -public class BindingException extends RuntimeException{ - private String bindingMessages; +import java.util.List; + +@RequiredArgsConstructor +@Getter +public class BindingException extends RuntimeException { + private final List bindingMessages; @Override public String getMessage() { - return this.bindingMessages; + String errorMessages = ""; + for (String message : bindingMessages) { + errorMessages += (message + System.lineSeparator()); + } + return errorMessages; } } From 66ec7699953dc4b3d1c7d9096ab13b4207a8dcca Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:52:13 +0900 Subject: [PATCH 064/102] =?UTF-8?q?#11=20feat:=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EA=B4=80=EB=A0=A8=20ErrorMess?= =?UTF-8?q?age=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/exception/ErrorMessage.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index 1bfbdbaf5..2d850e0d0 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -10,10 +10,13 @@ public enum ErrorMessage { INTERNAL_SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 장애가 발생했습니다."), // 1xxx user exception USER_NOT_FOUND(1000, HttpStatus.NOT_FOUND, "찾으시는 사용자가 없습니다."), - + WRONG_USER_NAME(1001, HttpStatus.SERVICE_UNAVAILABLE, "사용자 이름이 존재하지 않습니다."), + WRONG_USER_ID(1002, HttpStatus.SERVICE_UNAVAILABLE, "사용자 id 값이 존재하지 않습니다."), //2xxx post exception POST_NOT_FOUND(2000, HttpStatus.NOT_FOUND, "찾으시는 게시글이 없습니다."), - WRITER_NOT_MATCHED(2001, HttpStatus.BAD_REQUEST, "작성자가 일치하지 않습니다."); + WRITER_NOT_MATCHED(2001, HttpStatus.BAD_REQUEST, "작성자가 일치하지 않습니다."), + WRONG_TITLE_VALUE(2002, HttpStatus.BAD_REQUEST, "게시글 제목은 1자~20자 이내로 작성해주십시오."), + WRONG_CONTENTS_VALUE(2003, HttpStatus.BAD_REQUEST, "게시글 내용을 작성해주십시오."); private int code; private HttpStatus httpStatus; From f75c0f9b9f4aef8bb72b6ccdd0b78ad5921899bc Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:52:49 +0900 Subject: [PATCH 065/102] =?UTF-8?q?#11=20feat:=20BindingException=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/exception/GlobalExceptionHandler.java | 6 ++++++ src/main/java/com/example/board/response/Response.java | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index 85aa9b11e..fcd6780f5 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -25,6 +25,12 @@ public ResponseEntity> customExceptionHandle(RuntimeException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } + @ExceptionHandler(BindingException.class) + public ResponseEntity> bindingExceptionHandle(BindingException e) { + log.info("error: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.fail(e.getBindingMessages())); + } + @ExceptionHandler(Exception.class) public ResponseEntity> customExceptionHandle(Exception e) { log.info("error: {}", e.getMessage()); diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index 96e9d6a9c..13636dcf2 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.List; + @Getter @AllArgsConstructor public class Response { @@ -28,7 +30,7 @@ public static Response fail(Exception e) { return new Response<>(e.getMessage(), 500, "fail"); } - public static Response fail(String bindingErrors) { + public static Response> fail(List bindingErrors) { return new Response<>(bindingErrors, 400, "fail"); } } From 13ff128557f3cb6bd437c73c48bd5754aca5ebfa Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Wed, 22 Nov 2023 15:53:30 +0900 Subject: [PATCH 066/102] =?UTF-8?q?#11=20refactor:=20long=EC=9D=84=20wappe?= =?UTF-8?q?r=20=ED=81=B4=EB=9E=98=EC=8A=A4(Long)=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/model/User.java | 2 +- src/test/java/com/example/board/service/PostServiceTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/example/board/model/User.java b/src/main/java/com/example/board/model/User.java index 860c1e0e5..3735a0634 100644 --- a/src/main/java/com/example/board/model/User.java +++ b/src/main/java/com/example/board/model/User.java @@ -15,7 +15,7 @@ public class User extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") - private long id; + private Long id; @Column(name = "name", length = 20) private String name; diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java index d03ebf194..867030828 100644 --- a/src/test/java/com/example/board/service/PostServiceTest.java +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -35,7 +35,7 @@ void clear(){ @Test @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") void postSuccessInService(){ - PostDto postDto = new PostDto(1, "test", "test Contents"); + PostDto postDto = new PostDto(1L, "test", "test Contents"); Long save = postService.save(postDto); Optional savedPosts = postRepository.findById(save); @@ -46,7 +46,7 @@ void postSuccessInService(){ @Test @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") void postFailWithAnonymousUser(){ - PostDto postDto = new PostDto(2, "test", "test Contents"); + PostDto postDto = new PostDto(2L, "test", "test Contents"); Assertions.assertThatThrownBy(() ->postService.save(postDto)) .isInstanceOf(BaseException.class) From 69acf0aa4721299bf3c36efa22b24152c885e79d Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Wed, 22 Nov 2023 16:07:40 +0900 Subject: [PATCH 067/102] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EB=A6=AC(=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import,=EB=93=B1=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/controller/PostController.java | 1 - .../java/com/example/board/exception/BaseException.java | 4 ++-- src/main/java/com/example/board/exception/ErrorMessage.java | 6 +++--- src/main/java/com/example/board/response/Response.java | 4 ---- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index 149e09c7f..cb211df65 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -11,7 +11,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; -import org.springframework.validation.BindException; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/example/board/exception/BaseException.java b/src/main/java/com/example/board/exception/BaseException.java index 15fd32ed3..6f787d7eb 100644 --- a/src/main/java/com/example/board/exception/BaseException.java +++ b/src/main/java/com/example/board/exception/BaseException.java @@ -5,12 +5,12 @@ import org.springframework.http.HttpStatus; @Getter -public class BaseException extends RuntimeException{ +public class BaseException extends RuntimeException { private final int code; private final String message; private final HttpStatus httpStatus; - public BaseException(ErrorMessage message){ + public BaseException(ErrorMessage message) { this.code = message.getCode(); this.message = message.getMessage(); this.httpStatus = message.getHttpStatus(); diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index 2d850e0d0..5eabda30c 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -18,7 +18,7 @@ public enum ErrorMessage { WRONG_TITLE_VALUE(2002, HttpStatus.BAD_REQUEST, "게시글 제목은 1자~20자 이내로 작성해주십시오."), WRONG_CONTENTS_VALUE(2003, HttpStatus.BAD_REQUEST, "게시글 내용을 작성해주십시오."); - private int code; - private HttpStatus httpStatus; - private String message; + private final int code; + private final HttpStatus httpStatus; + private final String message; } diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index 13636dcf2..f448e7ae3 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -18,10 +18,6 @@ public static Response success(T data) { return new Response<>(data, 200, "ok"); } - public static Response success() { - return new Response<>(null, 200, "ok"); - } - public static Response fail(BaseException e) { return new Response<>(e.getMessage(), e.getCode(), "fail"); } From 26ebf0dac88f0949c034ab99abb6cd169eb350cc Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Wed, 22 Nov 2023 16:52:31 +0900 Subject: [PATCH 068/102] =?UTF-8?q?refactor:=20BindingResult=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20binding=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=8B=9C=20=EC=98=88=EC=99=B8=20=EB=B0=9C=EC=83=9D=EC=8B=9C?= =?UTF-8?q?=ED=82=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20update=20api=EC=97=90?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/controller/PostController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index cb211df65..547cbd13d 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -50,8 +50,13 @@ public Response readDetailPost(@PathVariable Long postId) @PatchMapping("/{postId}") public Response update( @PathVariable Long postId, - @RequestBody @Validated PostUpdateDto postUpdateDto + @RequestBody @Validated PostUpdateDto postUpdateDto, + BindingResult bindingResult ) { + List bindingErrors = bindChecking(bindingResult); + if (!bindingErrors.isEmpty()) { + throw new BindingException(bindingErrors); + } return Response.success(postService.update(postId, postUpdateDto, postUpdateDto.userId())); } } From 2510704346c611baab9f5c3f06a086a166339993 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Wed, 22 Nov 2023 16:53:39 +0900 Subject: [PATCH 069/102] =?UTF-8?q?#13=20test:=20api=20controller=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?edge=20case=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20api=20=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20@Nested=20=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostControllerTest.java | 476 +++++++++++++----- 1 file changed, 350 insertions(+), 126 deletions(-) diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 203f35f08..2d42ec5d6 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -2,10 +2,15 @@ import com.example.board.dto.PostDto; import com.example.board.dto.PostUpdateDto; +import com.example.board.response.Response; import com.example.board.service.PostService; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -16,9 +21,14 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -29,143 +39,357 @@ class PostControllerTest { @Autowired PostController postController; - + @Autowired + ObjectMapper objectMapper; @Autowired PostService postService; - @Autowired MockMvc mockMvc; - @Autowired - ObjectMapper objectMapper; - @Test - @DisplayName("정상적으로 게시글을 등록된다.") - @Transactional - void postSuccess() throws Exception { - //given - PostDto postDto = new PostDto(1L, "test2", "test Contents2"); - - //when, then - mockMvc.perform(post("http://localhost:8080/api/v1/posts") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsBytes(postDto))) - .andExpect(status().isOk()) - .andDo(document("post-save", - requestFields( - fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), - fieldWithPath("title").type(JsonFieldType.STRING).description("title"), - fieldWithPath("contents").type(JsonFieldType.STRING).description("contents") - ), - responseFields( - fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), - fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), - fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") - ) - )); - } + @Nested + @DisplayName("게시글 등록") + class PostNew { + @Test + @DisplayName("정상적으로 게시글을 등록된다.") + @Transactional + void postSuccess() throws Exception { + //given + PostDto postDto = new PostDto(1L, "test2", "test Contents2"); + + //when, then + mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isOk()) + .andDo(document("post-save", + requestFields( + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("contents").type(JsonFieldType.STRING).description("contents") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("userId를 포함하지 않고 보낼 경우 예외가 발생한다.") + @Transactional + void postFailWithAnonymousUser() throws Exception { + //given + PostDto postDto = new PostDto(null, "test2", "test Contents2"); + + //when, then + String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("사용자 id를 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } + + @Test + @DisplayName("존재하지 않는 userId를 보낼 경우 예외가 발생한다.") + @Transactional + void postFailWithWrongUser() throws Exception { + //given + PostDto postDto = new PostDto(0L, "test2", "test Contents2"); + + //when, then + String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isNotFound()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getCode()).isEqualTo(1000); + } + + @Test + @DisplayName("제목이 20자 이상인 경우 예외를 발생시킨다.") + @Transactional + void postFailWithLongTitle() throws Exception { + //given + PostDto postDto = new PostDto(1L, "test222222222222222222222222", "test Contents2"); + + //when, then + String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } + + + @ParameterizedTest + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("제목이 없는 경우 예외를 발생시킨다.") + @Transactional + void postFailWithNoTitle(String input) throws Exception { + //given + PostDto postDto = new PostDto(1L, input, "test Contents2"); + + //when, then + String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("게시글 제목을 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } - @Test - @DisplayName("정상적으로 모든 게시물들을 받아온다.") - @Transactional - void getSuccess() throws Exception { - //given - postService.save(new PostDto(1L, "testTitle1", "hihihihihihihih1")); - postService.save(new PostDto(1L, "testTitle2", "hihihihihihihih2")); - - //when, then - mockMvc.perform(get("http://localhost:8080/api/v1/posts") - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("post-getAll", - responseFields( - fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), - fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), - fieldWithPath("data.content[]").type(JsonFieldType.ARRAY).description("contents-list"), - fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), - fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), - fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), - fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), - fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), - fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), - fieldWithPath("data.pageable").type(JsonFieldType.OBJECT).description("pageable"), - fieldWithPath("data.pageable.pageNumber").type(JsonFieldType.NUMBER).description("pageNumber"), - fieldWithPath("data.pageable.pageSize").type(JsonFieldType.NUMBER).description("pageSize"), - fieldWithPath("data.pageable.sort").type(JsonFieldType.OBJECT).description("sort"), - fieldWithPath("data.pageable.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), - fieldWithPath("data.pageable.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), - fieldWithPath("data.pageable.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), - fieldWithPath("data.pageable.offset").type(JsonFieldType.NUMBER).description("offset"), - fieldWithPath("data.pageable.paged").type(JsonFieldType.BOOLEAN).description("paged"), - fieldWithPath("data.pageable.unpaged").type(JsonFieldType.BOOLEAN).description("unpaged"), - fieldWithPath("data.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), - fieldWithPath("data.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), - fieldWithPath("data.last").type(JsonFieldType.BOOLEAN).description("last"), - fieldWithPath("data.size").type(JsonFieldType.NUMBER).description("size"), - fieldWithPath("data.number").type(JsonFieldType.NUMBER).description("number"), - fieldWithPath("data.sort").type(JsonFieldType.OBJECT).description("sort"), - fieldWithPath("data.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), - fieldWithPath("data.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), - fieldWithPath("data.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), - fieldWithPath("data.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements"), - fieldWithPath("data.first").type(JsonFieldType.BOOLEAN).description("first"), - fieldWithPath("data.empty").type(JsonFieldType.BOOLEAN).description("unpaged"), - fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") - ) - )); + @ParameterizedTest + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("내용이 없는 경우 예외를 발생시킨다.") + @Transactional + void postFailWithNoContents(String input) throws Exception { + //given + PostDto postDto = new PostDto(1L, "title", input); + + //when, then + String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("게시글 내용을 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } } - @Test - @DisplayName("정상적으로 특정 게시물 하나를 받아온다..") - @Transactional - void getPostByIdSuccess() throws Exception { - //given - Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); - - //when, then - mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + savedContentsId) - .contentType(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andDo(document("post-getById", - responseFields( - fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), - fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), - fieldWithPath("data.userName").type(JsonFieldType.STRING).description("username"), - fieldWithPath("data.postId").type(JsonFieldType.NUMBER).description("postId"), - fieldWithPath("data.userId").type(JsonFieldType.NUMBER).description("userId"), - fieldWithPath("data.title").type(JsonFieldType.STRING).description("title"), - fieldWithPath("data.contents").type(JsonFieldType.STRING).description("contents"), - fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") - ) - )); + @Nested + @DisplayName("게시글 전체 조회") + class PostGetAll { + @Test + @DisplayName("정상적으로 모든 게시물들을 받아온다.") + @Transactional + void getSuccess() throws Exception { + //given + postService.save(new PostDto(1L, "testTitle1", "hihihihihihihih1")); + postService.save(new PostDto(1L, "testTitle2", "hihihihihihihih2")); + + //when, then + mockMvc.perform(get("http://localhost:8080/api/v1/posts") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("post-getAll", + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), + fieldWithPath("data.content[]").type(JsonFieldType.ARRAY).description("contents-list"), + fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.content[].postId").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("data.content[].userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.content[].title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.pageable").type(JsonFieldType.OBJECT).description("pageable"), + fieldWithPath("data.pageable.pageNumber").type(JsonFieldType.NUMBER).description("pageNumber"), + fieldWithPath("data.pageable.pageSize").type(JsonFieldType.NUMBER).description("pageSize"), + fieldWithPath("data.pageable.sort").type(JsonFieldType.OBJECT).description("sort"), + fieldWithPath("data.pageable.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), + fieldWithPath("data.pageable.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), + fieldWithPath("data.pageable.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), + fieldWithPath("data.pageable.offset").type(JsonFieldType.NUMBER).description("offset"), + fieldWithPath("data.pageable.paged").type(JsonFieldType.BOOLEAN).description("paged"), + fieldWithPath("data.pageable.unpaged").type(JsonFieldType.BOOLEAN).description("unpaged"), + fieldWithPath("data.totalPages").type(JsonFieldType.NUMBER).description("totalPages"), + fieldWithPath("data.totalElements").type(JsonFieldType.NUMBER).description("totalElements"), + fieldWithPath("data.last").type(JsonFieldType.BOOLEAN).description("last"), + fieldWithPath("data.size").type(JsonFieldType.NUMBER).description("size"), + fieldWithPath("data.number").type(JsonFieldType.NUMBER).description("number"), + fieldWithPath("data.sort").type(JsonFieldType.OBJECT).description("sort"), + fieldWithPath("data.sort.empty").type(JsonFieldType.BOOLEAN).description("empty"), + fieldWithPath("data.sort.sorted").type(JsonFieldType.BOOLEAN).description("sorted"), + fieldWithPath("data.sort.unsorted").type(JsonFieldType.BOOLEAN).description("unsorted"), + fieldWithPath("data.numberOfElements").type(JsonFieldType.NUMBER).description("numberOfElements"), + fieldWithPath("data.first").type(JsonFieldType.BOOLEAN).description("first"), + fieldWithPath("data.empty").type(JsonFieldType.BOOLEAN).description("unpaged"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } } - @Test - @DisplayName("정상적으로 게시글을 수정한다.") - @Transactional - void updatePostSuccess() throws Exception { - //given - Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); - - PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "test2", "test Contents2"); - - //when, then - mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsBytes(postUpdateDto))) - .andExpect(status().isOk()) - .andDo(document("post-update", - requestFields( - fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), - fieldWithPath("title").type(JsonFieldType.STRING).description("title update"), - fieldWithPath("contents").type(JsonFieldType.STRING).description("contents update") - ), - responseFields( - fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), - fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), - fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") - ) - )); + @Nested + @DisplayName("게시글 단건 조회") + class PostGetOne { + @Test + @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") + @Transactional + void getPostByIdSuccess() throws Exception { + //given + Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + + //when, then + mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + savedContentsId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("post-getById", + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.OBJECT).description("contents-list"), + fieldWithPath("data.userName").type(JsonFieldType.STRING).description("username"), + fieldWithPath("data.postId").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("data.userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("data.title").type(JsonFieldType.STRING).description("title"), + fieldWithPath("data.contents").type(JsonFieldType.STRING).description("contents"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("존재하지 않는 게시물을 호출하여 예외가 발생하였다.") + @Transactional + void getPostByIdNotFoundFail() throws Exception { + //given + + //when + String response = mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + "0") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response exception = objectMapper.readValue(response, Response.class); + + //then + assertThat(exception.getCode()).isEqualTo(2000); + + } } + @Nested + @DisplayName("게시글 수정") + class PostUpdate { + @Test + @DisplayName("정상적으로 게시글을 수정한다.") + @Transactional + void updatePostSuccess() throws Exception { + //given + Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "test2", "test Contents2"); + + //when, then + mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postUpdateDto))) + .andExpect(status().isOk()) + .andDo(document("post-update", + requestFields( + fieldWithPath("userId").type(JsonFieldType.NUMBER).description("userId"), + fieldWithPath("title").type(JsonFieldType.STRING).description("title update"), + fieldWithPath("contents").type(JsonFieldType.STRING).description("contents update") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("statusCode"), + fieldWithPath("data").type(JsonFieldType.NUMBER).description("postId"), + fieldWithPath("isSuccess").type(JsonFieldType.STRING).description("isSuccess") + ) + )); + } + + @Test + @DisplayName("수정 시 제목이 20자 이상인 경우 예외를 발생시킨다.") + @Transactional + void updateFailWithLongTitle() throws Exception { + //given + Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + PostUpdateDto postDto = new PostUpdateDto(1L, "test222222222222222222222222", "test Contents2"); + + //when, then + String response = mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } + + + @ParameterizedTest + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("수정 시 제목이 없는 경우 예외를 발생시킨다.") + @Transactional + void updateFailWithNoTitle(String input) throws Exception { + //given + Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + PostDto postDto = new PostDto(1L, input, "test Contents2"); + + //when, then + String response = mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("게시글 제목을 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } + + @ParameterizedTest + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("수정 시 내용이 없는 경우 예외를 발생시킨다.") + @Transactional + void updateFailWithNoContents(String input) throws Exception { + //given + Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + PostDto postDto = new PostDto(1L, "title", input); + + //when, then + String response = mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsBytes(postDto))) + .andExpect(status().isBadRequest()) + .andDo(print()) + .andReturn() + .getResponse() + .getContentAsString(StandardCharsets.UTF_8); + Response> exception = objectMapper.readValue(response, Response.class); + assertThat(exception.getData().get(0)).isEqualTo("게시글 내용을 입력해주세요."); + assertThat(exception.getCode()).isEqualTo(400); + } + } } \ No newline at end of file From 1ba412ca05cf515ffff521dfb1bf753b904eb067 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 23 Nov 2023 16:24:45 +0900 Subject: [PATCH 070/102] =?UTF-8?q?#13=20refactor:=20Post=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=ED=95=A0=20=EB=95=8C=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20=EB=A1=9C=EC=A7=81=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 --- .../java/com/example/board/model/Post.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index cddd99c9b..a7fec5fe1 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -4,6 +4,7 @@ import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; +import io.micrometer.common.util.StringUtils; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; @@ -40,28 +41,38 @@ private Post(User user, PostDto postDto) { this.createdBy = user.getName(); } + public static Post from(User user, PostDto postDto) { + return new Post(user, postDto); + } + + public Long update(PostUpdateDto postUpdateDto) { + postUpdateValidate(postUpdateDto); + this.title = postUpdateDto.title(); + this.contents = postUpdateDto.contents(); + return this.id; + } + private static void postSaveValidate(User user, PostDto postDto) { if (user == null) { throw new BaseException(ErrorMessage.USER_NOT_FOUND); } - if (postDto.title().isBlank() || (postDto.title().length() > 20)) { + if (StringUtils.isBlank(postDto.title()) || (postDto.title().length() > 20)) { throw new BaseException(ErrorMessage.WRONG_TITLE_VALUE); } - if (postDto.contents().isBlank()) { + if (StringUtils.isBlank(postDto.contents())) { throw new BaseException(ErrorMessage.WRONG_CONTENTS_VALUE); } - if (user.getName().isBlank()) { + if (StringUtils.isBlank(user.getName())) { throw new BaseException(ErrorMessage.WRONG_USER_NAME); } } - public static Post from(User user, PostDto postDto) { - return new Post(user, postDto); - } - - public Long update(PostUpdateDto postUpdateDto) { - this.title = postUpdateDto.title(); - this.contents = postUpdateDto.contents(); - return this.id; + private static void postUpdateValidate(PostUpdateDto postUpdateDto) { + if (StringUtils.isBlank(postUpdateDto.title()) || (postUpdateDto.title().length() > 20)) { + throw new BaseException(ErrorMessage.WRONG_TITLE_VALUE); + } + if (StringUtils.isBlank(postUpdateDto.contents())) { + throw new BaseException(ErrorMessage.WRONG_CONTENTS_VALUE); + } } } From f59424aa3f7306dc83d1d5a1495c63fca5b4f6c1 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 23 Nov 2023 16:25:19 +0900 Subject: [PATCH 071/102] =?UTF-8?q?#13=20test:=20PostService=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20CRUD=20=ED=85=8C=EC=8A=A4=ED=8A=B8=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 --- .../board/service/PostServiceTest.java | 240 ++++++++++++++++-- 1 file changed, 218 insertions(+), 22 deletions(-) diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java index 867030828..d99b8c8d9 100644 --- a/src/test/java/com/example/board/service/PostServiceTest.java +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -1,24 +1,36 @@ package com.example.board.service; +import com.example.board.dto.PostDetailResponseDto; import com.example.board.dto.PostDto; +import com.example.board.dto.PostResponseDto; +import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; import com.example.board.model.Post; import com.example.board.repository.PostRepository; -import org.assertj.core.api.Assertions; -import org.junit.jupiter.api.AfterEach; +import com.example.board.repository.UserRepository; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @ActiveProfiles("test") @SpringBootTest +@Transactional class PostServiceTest { @Autowired @@ -27,30 +39,214 @@ class PostServiceTest { @Autowired PostRepository postRepository; - @AfterEach - void clear(){ - postRepository.deleteAll(); - } + @Autowired + UserRepository userRepository; + + // TODO: user 생성 후 그 id 적용해보기, userName 유효성 검사 + @Nested + @DisplayName("게시글 생성") + class PostNew { + @Test + @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") + void postSuccessInService() { + PostDto postDto = new PostDto(1L, "test", "test Contents"); + + Long save = postService.save(postDto); + Optional savedPosts = postRepository.findById(save); + + assertThat(savedPosts).isNotEmpty(); + } + + @Test + @DisplayName("제목이 20자 이상인 경우 실패한다.") + void postFailByTitleLength() { + // given + PostDto postDto = new PostDto(1L, "testtesttesttesttesttesttest", "test Contents"); + + // when, then + assertThatThrownBy(() -> postService.save(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " "}) + @NullAndEmptySource + @DisplayName("제목이 null이거나 빈 공백인 경우 실패한다.") + void postFailByTitleNullOrBlank(String input) { + // given + PostDto postDto = new PostDto(1L, input, "test Contents"); - @Test - @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") - void postSuccessInService(){ - PostDto postDto = new PostDto(1L, "test", "test Contents"); + // when, then + assertThatThrownBy(() -> postService.save(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } - Long save = postService.save(postDto); - Optional savedPosts = postRepository.findById(save); + @ParameterizedTest + @ValueSource(strings = {" ", " "}) + @NullAndEmptySource + @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") + void postFailByContents(String input) { + // given + PostDto postDto = new PostDto(1L, "title", input); - Assertions.assertThat(savedPosts).isNotEmpty(); + // when, then + assertThatThrownBy(() -> postService.save(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_CONTENTS_VALUE.getMessage()); + } + + @Test + @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") + void postFailWithAnonymousUser() { + PostDto postDto = new PostDto(0L, "test", "test Contents"); + + assertThatThrownBy(() -> postService.save(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.USER_NOT_FOUND.getMessage()); + } + } + + @Nested + @DisplayName("게시글 전체 조회") + class PostGetAll { + @Test + @DisplayName("정상적으로 모든 게시물들을 받아온다.") + void readAllSuccess() { + for (int i = 0; i < 15; i++) { + PostDto postDto = new PostDto(1L, "title" + i, "contents" + i); + postService.save(postDto); + } + // TODO: pageable 직접 설정하는 것이 괜찮을까? + Pageable pageable = PageRequest.of(0, 10); + Page postPage = postService.readAllPost(pageable); + assertThat(postPage.getContent()).hasSize(10); + assertThat(postPage.getTotalPages()).isEqualTo(2); + + Pageable pageable2 = PageRequest.of(1, 10); + Page postPage2 = postService.readAllPost(pageable2); + assertThat(postPage2.getContent()).hasSize(5); + } } - @Test - @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") - void postFailWithAnonymousUser(){ - PostDto postDto = new PostDto(2L, "test", "test Contents"); + @Nested + @DisplayName("게시글 단건 조회") + class PostGetOne { + @Test + @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") + void getPostByIdSuccess() { + PostDto postDto = new PostDto(1L, "title", "contents"); + Long savedId = postService.save(postDto); + + PostDetailResponseDto postDetailResponseDto = postService.readPostDetail(savedId); + assertThat(postDetailResponseDto.postId()).isEqualTo(savedId); + } - Assertions.assertThatThrownBy(() ->postService.save(postDto)) - .isInstanceOf(BaseException.class) - .hasMessage(ErrorMessage.USER_NOT_FOUND.getMessage()); + @Test + @DisplayName("존재하지 않는 게시물을 호출하여 단건 조회에 실패한다.") + void getPostByIdNotFoundFail() { + assertThatThrownBy(() -> postService.readPostDetail(0L)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.POST_NOT_FOUND.getMessage()); + } } -} \ No newline at end of file + @Nested + @DisplayName("게시글 수정") + class PostUpdate { + @Test + @DisplayName("정상적으로 PostDto를 받아 글 수정을 완료하는 경우") + void updateSuccessInService() { + PostDto postDto = new PostDto(1L, "test", "test Contents"); + Long savedId = postService.save(postDto); + + String updatedTitle = "test update"; + String updatedContents = "test Contents update"; + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, updatedTitle, updatedContents); + + Long userId = postUpdateDto.userId(); + postService.update(savedId, postUpdateDto, userId); + PostDetailResponseDto postDetailResponseDto = postService.readPostDetail(savedId); + + assertThat(postDetailResponseDto.title()).isEqualTo(updatedTitle); + assertThat(postDetailResponseDto.contents()).isEqualTo(updatedContents); + } + + @Test + @DisplayName("존재하지 않는 게시물을 호출하여 수정에 실패한다.") + void updateByIdNotFoundFail() { + Long userId = 1L; + String updatedTitle = "test update"; + String updatedContents = "test Contents update"; + PostUpdateDto postUpdateDto = new PostUpdateDto(userId, updatedTitle, updatedContents); + assertThatThrownBy(() -> postService.update(0L, postUpdateDto, postUpdateDto.userId())) + .isInstanceOf(BaseException.class).hasMessage(ErrorMessage.POST_NOT_FOUND.getMessage()); + } + + @Test + @DisplayName("제목이 20자 이상인 경우 수정에 실패한다.") + void updateFailByTitleLength() { + // given + PostDto postDto = new PostDto(1L, "test", "test Contents"); + Long savedId = postService.save(postDto); + + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "testtesttesttesttesttesttest", "test Contents"); + + // when, then + assertThatThrownBy(() -> postService.update(savedId, postUpdateDto, postUpdateDto.userId())) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " "}) + @NullAndEmptySource + @DisplayName("제목이 null이거나 빈 공백인 경우 수정에 실패한다.") + void updateFailByTitleNullOrBlank(String input) { + // given + PostDto postDto = new PostDto(1L, "test", "test Contents"); + Long savedId = postService.save(postDto); + + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, input, "test Contents"); + + // when, then + assertThatThrownBy(() -> postService.update(savedId, postUpdateDto, postUpdateDto.userId())) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " "}) + @NullAndEmptySource + @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") + void updateFailByContents(String input) { + // given + PostDto postDto = new PostDto(1L, "test", "test Contents"); + Long savedId = postService.save(postDto); + + PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "title", input); + + // when, then + assertThatThrownBy(() -> postService.update(savedId, postUpdateDto, postUpdateDto.userId())) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_CONTENTS_VALUE.getMessage()); + } + + @Test + @DisplayName("존재하지 않는 유저가 글을 수정하는 경우 예외를 발생시킨다.") + void updateFailWithAnonymousUser() { + // given + PostDto postDto = new PostDto(1L, "test", "test Contents"); + Long savedId = postService.save(postDto); + + PostUpdateDto postUpdateDto = new PostUpdateDto(0L, "title update", "test Contents update"); + + + // when, then + assertThatThrownBy(() -> postService.update(savedId, postUpdateDto, postUpdateDto.userId())) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.USER_NOT_FOUND.getMessage()); + } + } +} From d3eda17170f4b5a4c1bf14b3f96383fdffec64b1 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Thu, 23 Nov 2023 16:26:01 +0900 Subject: [PATCH 072/102] =?UTF-8?q?#13=20refactor:=20PostControllerTest=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EC=97=90=20@Transactional=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostControllerTest.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 2d42ec5d6..4f0bf8926 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -24,7 +24,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -35,6 +35,7 @@ @AutoConfigureRestDocs @AutoConfigureMockMvc @ActiveProfiles("test") +@Transactional class PostControllerTest { @Autowired @@ -52,7 +53,6 @@ class PostControllerTest { class PostNew { @Test @DisplayName("정상적으로 게시글을 등록된다.") - @Transactional void postSuccess() throws Exception { //given PostDto postDto = new PostDto(1L, "test2", "test Contents2"); @@ -78,7 +78,6 @@ void postSuccess() throws Exception { @Test @DisplayName("userId를 포함하지 않고 보낼 경우 예외가 발생한다.") - @Transactional void postFailWithAnonymousUser() throws Exception { //given PostDto postDto = new PostDto(null, "test2", "test Contents2"); @@ -99,7 +98,6 @@ void postFailWithAnonymousUser() throws Exception { @Test @DisplayName("존재하지 않는 userId를 보낼 경우 예외가 발생한다.") - @Transactional void postFailWithWrongUser() throws Exception { //given PostDto postDto = new PostDto(0L, "test2", "test Contents2"); @@ -119,7 +117,6 @@ void postFailWithWrongUser() throws Exception { @Test @DisplayName("제목이 20자 이상인 경우 예외를 발생시킨다.") - @Transactional void postFailWithLongTitle() throws Exception { //given PostDto postDto = new PostDto(1L, "test222222222222222222222222", "test Contents2"); @@ -143,7 +140,6 @@ void postFailWithLongTitle() throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("제목이 없는 경우 예외를 발생시킨다.") - @Transactional void postFailWithNoTitle(String input) throws Exception { //given PostDto postDto = new PostDto(1L, input, "test Contents2"); @@ -166,7 +162,6 @@ void postFailWithNoTitle(String input) throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("내용이 없는 경우 예외를 발생시킨다.") - @Transactional void postFailWithNoContents(String input) throws Exception { //given PostDto postDto = new PostDto(1L, "title", input); @@ -191,7 +186,6 @@ void postFailWithNoContents(String input) throws Exception { class PostGetAll { @Test @DisplayName("정상적으로 모든 게시물들을 받아온다.") - @Transactional void getSuccess() throws Exception { //given postService.save(new PostDto(1L, "testTitle1", "hihihihihihihih1")); @@ -245,7 +239,6 @@ void getSuccess() throws Exception { class PostGetOne { @Test @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") - @Transactional void getPostByIdSuccess() throws Exception { //given Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -270,7 +263,6 @@ void getPostByIdSuccess() throws Exception { @Test @DisplayName("존재하지 않는 게시물을 호출하여 예외가 발생하였다.") - @Transactional void getPostByIdNotFoundFail() throws Exception { //given @@ -295,7 +287,6 @@ void getPostByIdNotFoundFail() throws Exception { class PostUpdate { @Test @DisplayName("정상적으로 게시글을 수정한다.") - @Transactional void updatePostSuccess() throws Exception { //given Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -323,7 +314,6 @@ void updatePostSuccess() throws Exception { @Test @DisplayName("수정 시 제목이 20자 이상인 경우 예외를 발생시킨다.") - @Transactional void updateFailWithLongTitle() throws Exception { //given Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -348,7 +338,6 @@ void updateFailWithLongTitle() throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("수정 시 제목이 없는 경우 예외를 발생시킨다.") - @Transactional void updateFailWithNoTitle(String input) throws Exception { //given Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -372,7 +361,6 @@ void updateFailWithNoTitle(String input) throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("수정 시 내용이 없는 경우 예외를 발생시킨다.") - @Transactional void updateFailWithNoContents(String input) throws Exception { //given Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -392,4 +380,4 @@ void updateFailWithNoContents(String input) throws Exception { assertThat(exception.getCode()).isEqualTo(400); } } -} \ No newline at end of file +} From 34eef467fcc50c4c96ac213a575f090296fc05fc Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 24 Nov 2023 10:54:43 +0900 Subject: [PATCH 073/102] =?UTF-8?q?refactor:=20parameterIzedTest=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=91=9C=EA=B8=B0=20=EB=AA=85=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/service/PostServiceTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java index d99b8c8d9..99a6d8f61 100644 --- a/src/test/java/com/example/board/service/PostServiceTest.java +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -69,7 +69,7 @@ void postFailByTitleLength() { .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); } - @ParameterizedTest + @ParameterizedTest(name = "title이 `{0}`인 경우 저장 로직에서 실패한다.") @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("제목이 null이거나 빈 공백인 경우 실패한다.") @@ -83,7 +83,7 @@ void postFailByTitleNullOrBlank(String input) { .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); } - @ParameterizedTest + @ParameterizedTest(name = "contents가 `{0}`인 경우 저장 로직에서 실패한다.") @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") @@ -199,7 +199,7 @@ void updateFailByTitleLength() { .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); } - @ParameterizedTest + @ParameterizedTest(name = "title이 `{0}`인 경우 수정 로직에서 실패한다.") @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("제목이 null이거나 빈 공백인 경우 수정에 실패한다.") @@ -216,7 +216,7 @@ void updateFailByTitleNullOrBlank(String input) { .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); } - @ParameterizedTest + @ParameterizedTest(name = "contents가 `{0}`인 경우 수정 로직에서 실패한다.") @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") From c42fa9444c8467d3764ef1dc1420bc1efbe8e799 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 24 Nov 2023 10:55:04 +0900 Subject: [PATCH 074/102] =?UTF-8?q?refactor:=20parameterIzedTest=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=91=9C=EA=B8=B0=20=EB=AA=85=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/controller/PostControllerTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 4f0bf8926..1cabc002a 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -136,7 +136,7 @@ void postFailWithLongTitle() throws Exception { } - @ParameterizedTest + @ParameterizedTest(name = "title이 `{0}`인 경우 저장 요청에 실패한다.") @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("제목이 없는 경우 예외를 발생시킨다.") @@ -158,7 +158,7 @@ void postFailWithNoTitle(String input) throws Exception { assertThat(exception.getCode()).isEqualTo(400); } - @ParameterizedTest + @ParameterizedTest(name = "contents가 `{0}`인 경우 저장 요청에 실패한다.") @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("내용이 없는 경우 예외를 발생시킨다.") @@ -334,7 +334,7 @@ void updateFailWithLongTitle() throws Exception { } - @ParameterizedTest + @ParameterizedTest(name = "title이 `{0}`인 경우 수정 요청에 실패한다.") @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("수정 시 제목이 없는 경우 예외를 발생시킨다.") @@ -357,7 +357,7 @@ void updateFailWithNoTitle(String input) throws Exception { assertThat(exception.getCode()).isEqualTo(400); } - @ParameterizedTest + @ParameterizedTest(name = "contents가 `{0}`인 경우 수정 요청에 실패한다.") @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("수정 시 내용이 없는 경우 예외를 발생시킨다.") From 05826c79db4a7faa30e9c38f50af5e83b391a86b Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 24 Nov 2023 10:56:07 +0900 Subject: [PATCH 075/102] =?UTF-8?q?#13=20test:=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 게시글 저장 시 이용되는 test 케이스 작성 1. 게시글 수정 시 이용되는 test 케이스 작성 --- .../com/example/board/model/PostTest.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/test/java/com/example/board/model/PostTest.java diff --git a/src/test/java/com/example/board/model/PostTest.java b/src/test/java/com/example/board/model/PostTest.java new file mode 100644 index 000000000..74831a55a --- /dev/null +++ b/src/test/java/com/example/board/model/PostTest.java @@ -0,0 +1,182 @@ +package com.example.board.model; + +import com.example.board.dto.PostDto; +import com.example.board.dto.PostUpdateDto; +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith(SpringExtension.class) +class PostTest { + + @Mock + User mockUser; + + static Post featurePost; + @BeforeEach + void setup(){ + when(mockUser.getId()).thenReturn(1L); + when(mockUser.getName()).thenReturn("mockUser"); + featurePost = Post.from(mockUser, new PostDto(mockUser.getId(), "title", "contents")); + } + @Nested + @DisplayName("Post save 시 생성하는 객체 유효성 검사 테스트") + class PostCreate { + private static List invalidTitle() { + String string = "asdf"; + return List.of(string.repeat(6)); + } + + @Test + @DisplayName("post 생성 시 정상적으로 저장을 위한 Post 객체가 생성되는 경우") + void saveEntitySuccess() { + // given + PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + + //when + Post post = Post.from(mockUser, postDto); + + //then + assertThat(post.getTitle()).isEqualTo(postDto.title()); + assertThat(post.getContents()).isEqualTo(postDto.contents()); + } + + //TODO: 이 테스트를 유지하는 것이 맞는가, 너무 억지스럽게 된 것 같다. + @Test + @DisplayName("post 생성 시 User 객체가 null 값이라 불러올 수 없는 경우") + void saveEntityFailWithNullUser() { + PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + mockUser = null; + + //when + assertThatThrownBy(() ->Post.from(mockUser, postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.USER_NOT_FOUND.getMessage()); + + } + + @ParameterizedTest(name = "title이 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") + @ValueSource(strings = {" "}) + @MethodSource("invalidTitle") + @NullAndEmptySource + @DisplayName("post 생성 시 제목이 null, 빈 공백, 20자 이상인 경우 post 객체 생성에 실패한다.") + void saveEntityFailWithInvalidTitle(String input) { + // given + PostDto postDto = new PostDto(1L, input, "test Contents"); + + // when, then + assertThatThrownBy(() -> Post.from(mockUser, postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } + + @ParameterizedTest(name = "contents가 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("post 생성 시 본문 내용이 null, 빈 공백인 경우 post 객체 생성에 실패한다.") + void saveEntityFailWithInvalidContents(String input) { + // given + PostDto postDto = new PostDto(1L, "title", input); + + // when, then + assertThatThrownBy(() -> Post.from(mockUser, postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_CONTENTS_VALUE.getMessage()); + } + + @ParameterizedTest(name = "user의 이름이 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("post 생성 시 본문 수정 시 저장하는 user의 이름이 null, 빈 공백인 경우 post 객체 생성에 실패한다.") + void saveEntityFailWithInvalidUserName(String input) { + // given + when(mockUser.getName()).thenReturn(input); + PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + + //when + assertThatThrownBy(() -> Post.from(mockUser, postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_USER_NAME.getMessage()); + } + } + + @Nested + @DisplayName("Post update 시 생성하는 객체 유효성 검사 테스트") + class PostUpdate { + private static List invalidTitle() { + String string = "asdf"; + return List.of(string.repeat(6)); + } + + + + + + + @Test + @DisplayName("post 수정 시 정상적으로 수정을 위한 Post 객체가 생성되는 경우") + void saveEntitySuccess() { + // given + when(mockUser.getId()).thenReturn(1L); + when(mockUser.getName()).thenReturn("mock user"); + + PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + + //when + Post post = Post.from(mockUser, postDto); + + PostUpdateDto updateDto = new PostUpdateDto(mockUser.getId(), "title", "contents"); + + //when + post.update(updateDto); + + //then + assertThat(post.getTitle()).isEqualTo(updateDto.title()); + assertThat(post.getContents()).isEqualTo(updateDto.contents()); + } + + + @ParameterizedTest(name = "title이 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") + @ValueSource(strings = {" "}) + @MethodSource("invalidTitle") + @NullAndEmptySource + @DisplayName("post 수정 시 제목이 null, 빈 공백, 20자 이상인 경우 post 객체 생성에 실패한다.") + void saveEntityFailWithInvalidTitle(String input) { + // given + PostUpdateDto updateDto = new PostUpdateDto(mockUser.getId(), input, "test Contents"); + + // when, then + assertThatThrownBy(() -> featurePost.update(updateDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_TITLE_VALUE.getMessage()); + } + + @ParameterizedTest(name = "contents가 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") + @ValueSource(strings = {" "}) + @NullAndEmptySource + @DisplayName("본문 수정 시 내용이 null, 빈 공백인 경우 post 객체 생성에 실패한다.") + void updateEntityFailWithInvalidContents(String input) { + // given + PostUpdateDto postDto = new PostUpdateDto(1L, "title", input); + + // when, then + assertThatThrownBy(() -> featurePost.update(postDto)) + .isInstanceOf(BaseException.class) + .hasMessage(ErrorMessage.WRONG_CONTENTS_VALUE.getMessage()); + } + + } + +} \ No newline at end of file From 661ba81a5d9d5af825ca7e340993a32ae39275e6 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 24 Nov 2023 11:26:13 +0900 Subject: [PATCH 076/102] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EA=B5=AC=EC=B2=B4=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 6 +++--- .../board/controller/PostControllerTest.java | 18 +++++++++--------- .../example/board/service/PostServiceTest.java | 13 ++++++------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index fcd6780f5..d9e727d94 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -14,13 +14,13 @@ public class GlobalExceptionHandler { @ExceptionHandler(BaseException.class) - public ResponseEntity> customExceptionHandle(BaseException e) { + public ResponseEntity> baseExceptionHandle(BaseException e) { log.info("error: {}", e.getMessage()); return ResponseEntity.status(e.getHttpStatus()).body(Response.fail(e)); } @ExceptionHandler(RuntimeException.class) - public ResponseEntity> customExceptionHandle(RuntimeException e) { + public ResponseEntity> runtimeExceptionHandle(RuntimeException e) { log.info("error: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } @@ -32,7 +32,7 @@ public ResponseEntity> bindingExceptionHandle(BindingException e) { } @ExceptionHandler(Exception.class) - public ResponseEntity> customExceptionHandle(Exception e) { + public ResponseEntity> exceptionHandle(Exception e) { log.info("error: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 1cabc002a..7f43e84e4 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -53,7 +53,7 @@ class PostControllerTest { class PostNew { @Test @DisplayName("정상적으로 게시글을 등록된다.") - void postSuccess() throws Exception { + void saveSuccess() throws Exception { //given PostDto postDto = new PostDto(1L, "test2", "test Contents2"); @@ -78,7 +78,7 @@ void postSuccess() throws Exception { @Test @DisplayName("userId를 포함하지 않고 보낼 경우 예외가 발생한다.") - void postFailWithAnonymousUser() throws Exception { + void saveFailWithAnonymousUser() throws Exception { //given PostDto postDto = new PostDto(null, "test2", "test Contents2"); @@ -98,7 +98,7 @@ void postFailWithAnonymousUser() throws Exception { @Test @DisplayName("존재하지 않는 userId를 보낼 경우 예외가 발생한다.") - void postFailWithWrongUser() throws Exception { + void saveFailWithWrongUser() throws Exception { //given PostDto postDto = new PostDto(0L, "test2", "test Contents2"); @@ -117,7 +117,7 @@ void postFailWithWrongUser() throws Exception { @Test @DisplayName("제목이 20자 이상인 경우 예외를 발생시킨다.") - void postFailWithLongTitle() throws Exception { + void saveFailWithLongTitle() throws Exception { //given PostDto postDto = new PostDto(1L, "test222222222222222222222222", "test Contents2"); @@ -140,7 +140,7 @@ void postFailWithLongTitle() throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("제목이 없는 경우 예외를 발생시킨다.") - void postFailWithNoTitle(String input) throws Exception { + void saveFailWithNoTitle(String input) throws Exception { //given PostDto postDto = new PostDto(1L, input, "test Contents2"); @@ -162,7 +162,7 @@ void postFailWithNoTitle(String input) throws Exception { @ValueSource(strings = {" "}) @NullAndEmptySource @DisplayName("내용이 없는 경우 예외를 발생시킨다.") - void postFailWithNoContents(String input) throws Exception { + void saveFailWithNoContents(String input) throws Exception { //given PostDto postDto = new PostDto(1L, "title", input); @@ -239,7 +239,7 @@ void getSuccess() throws Exception { class PostGetOne { @Test @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") - void getPostByIdSuccess() throws Exception { + void getByIdSuccess() throws Exception { //given Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); @@ -263,7 +263,7 @@ void getPostByIdSuccess() throws Exception { @Test @DisplayName("존재하지 않는 게시물을 호출하여 예외가 발생하였다.") - void getPostByIdNotFoundFail() throws Exception { + void getByIdNotFoundFail() throws Exception { //given //when @@ -287,7 +287,7 @@ void getPostByIdNotFoundFail() throws Exception { class PostUpdate { @Test @DisplayName("정상적으로 게시글을 수정한다.") - void updatePostSuccess() throws Exception { + void updateSuccess() throws Exception { //given Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java index 99a6d8f61..396be8e84 100644 --- a/src/test/java/com/example/board/service/PostServiceTest.java +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -42,13 +42,12 @@ class PostServiceTest { @Autowired UserRepository userRepository; - // TODO: user 생성 후 그 id 적용해보기, userName 유효성 검사 @Nested @DisplayName("게시글 생성") class PostNew { @Test @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") - void postSuccessInService() { + void saveSuccessInService() { PostDto postDto = new PostDto(1L, "test", "test Contents"); Long save = postService.save(postDto); @@ -59,7 +58,7 @@ void postSuccessInService() { @Test @DisplayName("제목이 20자 이상인 경우 실패한다.") - void postFailByTitleLength() { + void saveFailByTitleLength() { // given PostDto postDto = new PostDto(1L, "testtesttesttesttesttesttest", "test Contents"); @@ -73,7 +72,7 @@ void postFailByTitleLength() { @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("제목이 null이거나 빈 공백인 경우 실패한다.") - void postFailByTitleNullOrBlank(String input) { + void saveFailByTitleNullOrBlank(String input) { // given PostDto postDto = new PostDto(1L, input, "test Contents"); @@ -87,7 +86,7 @@ void postFailByTitleNullOrBlank(String input) { @ValueSource(strings = {" ", " "}) @NullAndEmptySource @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") - void postFailByContents(String input) { + void saveFailByContents(String input) { // given PostDto postDto = new PostDto(1L, "title", input); @@ -99,7 +98,7 @@ void postFailByContents(String input) { @Test @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") - void postFailWithAnonymousUser() { + void saveFailWithAnonymousUser() { PostDto postDto = new PostDto(0L, "test", "test Contents"); assertThatThrownBy(() -> postService.save(postDto)) @@ -113,7 +112,7 @@ void postFailWithAnonymousUser() { class PostGetAll { @Test @DisplayName("정상적으로 모든 게시물들을 받아온다.") - void readAllSuccess() { + void getAllSuccess() { for (int i = 0; i < 15; i++) { PostDto postDto = new PostDto(1L, "title" + i, "contents" + i); postService.save(postDto); From 478bca4699eaea3202d842851a1a7081f42600a4 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 24 Nov 2023 11:26:32 +0900 Subject: [PATCH 077/102] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=98=88=EC=99=B8=20=ED=95=B8=EB=93=A4=EB=9F=AC=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 --- .../example/board/exception/GlobalExceptionHandler.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index d9e727d94..6b3335940 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -1,14 +1,12 @@ package com.example.board.exception; import com.example.board.response.Response; -import com.fasterxml.jackson.databind.exc.MismatchedInputException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -// TODO: httpstatus 상태 코드도 함께 전달하는 방법 찾기 @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @@ -36,10 +34,4 @@ public ResponseEntity> exceptionHandle(Exception e) { log.info("error: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Response.fail(e)); } - - @ExceptionHandler(MismatchedInputException.class) - public ResponseEntity> customExceptionHandle(MismatchedInputException e) { - log.info("error: {}", e.getMessage()); - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Response.fail(e)); - } } From 88523f3cfe2d8e43d94e0bd72eb90f5e7200d0a8 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 24 Nov 2023 11:27:23 +0900 Subject: [PATCH 078/102] =?UTF-8?q?refactor:=20BindingException=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20Validation=20?= =?UTF-8?q?=EC=95=88=EC=9C=BC=EB=A1=9C=20=EC=B2=98=EB=A6=AC=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 19 +++++-------------- .../com/example/board/util/Validation.java | 7 +++++-- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index 547cbd13d..b4ea615d3 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -4,19 +4,16 @@ import com.example.board.dto.PostDto; import com.example.board.dto.PostResponseDto; import com.example.board.dto.PostUpdateDto; -import com.example.board.exception.BindingException; import com.example.board.response.Response; import com.example.board.service.PostService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.util.List; - import static com.example.board.util.Validation.bindChecking; @RequiredArgsConstructor @@ -26,11 +23,8 @@ public class PostController { private final PostService postService; @PostMapping - public Response save(@RequestBody @Validated PostDto postDto, BindingResult bindingResult) { - List bindingErrors = bindChecking(bindingResult); - if (!bindingErrors.isEmpty()) { - throw new BindingException(bindingErrors); - } + public Response save(@RequestBody @Valid PostDto postDto, BindingResult bindingResult) { + bindChecking(bindingResult); return Response.success(postService.save(postDto)); } @@ -50,13 +44,10 @@ public Response readDetailPost(@PathVariable Long postId) @PatchMapping("/{postId}") public Response update( @PathVariable Long postId, - @RequestBody @Validated PostUpdateDto postUpdateDto, + @RequestBody @Valid PostUpdateDto postUpdateDto, BindingResult bindingResult ) { - List bindingErrors = bindChecking(bindingResult); - if (!bindingErrors.isEmpty()) { - throw new BindingException(bindingErrors); - } + bindChecking(bindingResult); return Response.success(postService.update(postId, postUpdateDto, postUpdateDto.userId())); } } diff --git a/src/main/java/com/example/board/util/Validation.java b/src/main/java/com/example/board/util/Validation.java index 64c2e36b4..684f3219f 100644 --- a/src/main/java/com/example/board/util/Validation.java +++ b/src/main/java/com/example/board/util/Validation.java @@ -1,18 +1,21 @@ package com.example.board.util; +import com.example.board.exception.BindingException; import org.springframework.validation.BindingResult; import java.util.ArrayList; import java.util.List; public class Validation { - public static List bindChecking(BindingResult bindingResult) { + public static void bindChecking(BindingResult bindingResult) { List messages = new ArrayList<>(); if (bindingResult.hasErrors()) { bindingResult.getAllErrors().forEach(objectError -> { messages.add(objectError.getDefaultMessage()); }); } - return messages; + if (!messages.isEmpty()) { + throw new BindingException(messages); + } } } From e8e7829deca6a2d66673e5f24f1a707ca8202fe0 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 1 Dec 2023 10:07:27 +0900 Subject: [PATCH 079/102] Update build.gradle Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 742c0d59f..927abb3c7 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,6 @@ configurations { extendsFrom annotationProcessor } asciidoctorExt - } repositories { From 3ff9bc53eeb3c823edbd275f5c4afed2a861bc47 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 10:45:00 +0900 Subject: [PATCH 080/102] =?UTF-8?q?refactor:=20=EC=8A=A4=ED=82=A4=EB=A7=88?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1=EA=B3=BC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=82=BD=EC=9E=85=20sql=20=EB=B6=84=EB=A6=AC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/{init.sql => 01_schema.sql} | 6 +----- mysql/02_data.sql | 5 +++++ mysql/Dockerfile | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) rename mysql/{init.sql => 01_schema.sql} (85%) create mode 100644 mysql/02_data.sql diff --git a/mysql/init.sql b/mysql/01_schema.sql similarity index 85% rename from mysql/init.sql rename to mysql/01_schema.sql index 391379570..5034d76d1 100644 --- a/mysql/init.sql +++ b/mysql/01_schema.sql @@ -25,8 +25,6 @@ create table posts constraint post_user_fk FOREIGN key (user_id) references users(user_id) ); -insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); - CREATE DATABASE IF NOT EXISTS board_test; USE board_test; @@ -52,6 +50,4 @@ create table posts created_by varchar(20) not null, constraint post_user_fk FOREIGN key (user_id) references users(user_id) -); - -insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); \ No newline at end of file +); \ No newline at end of file diff --git a/mysql/02_data.sql b/mysql/02_data.sql new file mode 100644 index 000000000..945c31ab7 --- /dev/null +++ b/mysql/02_data.sql @@ -0,0 +1,5 @@ +USE board; +insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); + +USE board_test; +insert into users (name, age, hobby, created_at, updated_at) values ('test', 20, 'coding', now(), now()); \ No newline at end of file diff --git a/mysql/Dockerfile b/mysql/Dockerfile index 24f50e8a8..d20d95749 100644 --- a/mysql/Dockerfile +++ b/mysql/Dockerfile @@ -3,4 +3,4 @@ FROM mysql:8 ENV MYSQL_ROOT_PASSWORD=root ENV MYSQL_DATABASE=board ENV MYSQL_ROOT_USER=root -COPY init.sql /docker-entrypoint-initdb.d/ \ No newline at end of file +COPY ./ /docker-entrypoint-initdb.d/ \ No newline at end of file From 61d22fcc71d7d7c49762462d2bd15856efdbbd9d Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 10:45:27 +0900 Subject: [PATCH 081/102] =?UTF-8?q?refactor:=20.gitignore=EC=97=90=20HELP.?= =?UTF-8?q?md=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 35063751b..9c5e52c27 100644 --- a/.gitignore +++ b/.gitignore @@ -222,4 +222,5 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/gradle,java,macos,windows,visualstudiocode,intellij+all -.idea/ \ No newline at end of file +.idea/ +HELP.md \ No newline at end of file From 6fa10aa08d9f4eb774e24af79aa57d66b6ad146d Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 10:45:47 +0900 Subject: [PATCH 082/102] =?UTF-8?q?refactor:=20lombok=20testImplementation?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 742c0d59f..e27e6e098 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,6 @@ dependencies { compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.springframework.boot:spring-boot-starter-validation' From 1095fbd3bdca936803e1638ebb0f98244ef7e7e1 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 10:46:11 +0900 Subject: [PATCH 083/102] =?UTF-8?q?docs:=20README.md=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EB=AC=B8=EA=B5=AC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 2b442fbf1..94a596782 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,4 @@ Spring Boot JPA - Rest API를 강의를 듣고, 게시판 구현 미션을 수 ## Branch 명명 규칙 1. 여러분 repo는 알아서 해주시고 😀(본인 레포니 main으로 하셔두 되져) 2. prgrms-be-devcourse/spring-board 레포로 PR시 branch는 본인 username을 적어주세요 :) -base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main` - -- 테스트3 \ No newline at end of file +base repo : `여기repo` base : `username` ← head repo : `여러분repo` compare : `main` \ No newline at end of file From 1470348612a2155631c178f4f2fec7cf3cd1c047 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 11:01:14 +0900 Subject: [PATCH 084/102] =?UTF-8?q?refactor:=20Post=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=ED=9B=84=20status=20201=20=EB=B0=98=ED=99=98=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20ResponseEntity=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/controller/PostController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index b4ea615d3..ad20a428e 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -23,9 +23,10 @@ public class PostController { private final PostService postService; @PostMapping - public Response save(@RequestBody @Valid PostDto postDto, BindingResult bindingResult) { + public ResponseEntity> save(@RequestBody @Valid PostCreateDto postDto, BindingResult bindingResult) { bindChecking(bindingResult); - return Response.success(postService.save(postDto)); + return ResponseEntity.status(HttpStatus.CREATED) + .body(Response.success(postService.save(postDto))); } @GetMapping From e70a179e1c82a0f8f62e7dbb0555602b46758269 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 11:01:55 +0900 Subject: [PATCH 085/102] =?UTF-8?q?refactor:=20PostDto=EB=A5=BC=20PostCrea?= =?UTF-8?q?teDto=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/controller/PostController.java | 4 ++- .../dto/{PostDto.java => PostCreateDto.java} | 2 +- .../java/com/example/board/model/Post.java | 8 ++--- .../example/board/service/PostService.java | 4 +-- .../board/controller/PostControllerTest.java | 32 +++++++++---------- .../com/example/board/model/PostTest.java | 16 +++++----- .../board/service/PostServiceTest.java | 26 +++++++-------- 7 files changed, 47 insertions(+), 45 deletions(-) rename src/main/java/com/example/board/dto/{PostDto.java => PostCreateDto.java} (95%) diff --git a/src/main/java/com/example/board/controller/PostController.java b/src/main/java/com/example/board/controller/PostController.java index ad20a428e..533da75c8 100644 --- a/src/main/java/com/example/board/controller/PostController.java +++ b/src/main/java/com/example/board/controller/PostController.java @@ -1,7 +1,7 @@ package com.example.board.controller; import com.example.board.dto.PostDetailResponseDto; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostResponseDto; import com.example.board.dto.PostUpdateDto; import com.example.board.response.Response; @@ -11,6 +11,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/example/board/dto/PostDto.java b/src/main/java/com/example/board/dto/PostCreateDto.java similarity index 95% rename from src/main/java/com/example/board/dto/PostDto.java rename to src/main/java/com/example/board/dto/PostCreateDto.java index d221ed2d3..639b5104f 100644 --- a/src/main/java/com/example/board/dto/PostDto.java +++ b/src/main/java/com/example/board/dto/PostCreateDto.java @@ -7,7 +7,7 @@ /** * 추후 게시글 입력 항목 확장을 고려해 PostUpdateDto와 필드는 동일하지만 구분하여 사용할 예정입니다. */ -public record PostDto( +public record PostCreateDto( @NotNull(message = "사용자 id를 입력해주세요.") Long userId, @NotBlank(message = "게시글 제목을 입력해주세요.") diff --git a/src/main/java/com/example/board/model/Post.java b/src/main/java/com/example/board/model/Post.java index a7fec5fe1..ee1e31e8d 100644 --- a/src/main/java/com/example/board/model/Post.java +++ b/src/main/java/com/example/board/model/Post.java @@ -1,6 +1,6 @@ package com.example.board.model; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; @@ -33,7 +33,7 @@ public class Post extends BaseEntity { @Column(name = "created_by") private String createdBy; - private Post(User user, PostDto postDto) { + private Post(User user, PostCreateDto postDto) { postSaveValidate(user, postDto); this.user = user; this.title = postDto.title(); @@ -41,7 +41,7 @@ private Post(User user, PostDto postDto) { this.createdBy = user.getName(); } - public static Post from(User user, PostDto postDto) { + public static Post from(User user, PostCreateDto postDto) { return new Post(user, postDto); } @@ -52,7 +52,7 @@ public Long update(PostUpdateDto postUpdateDto) { return this.id; } - private static void postSaveValidate(User user, PostDto postDto) { + private static void postSaveValidate(User user, PostCreateDto postDto) { if (user == null) { throw new BaseException(ErrorMessage.USER_NOT_FOUND); } diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 37f22057d..550f45b17 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -1,7 +1,7 @@ package com.example.board.service; import com.example.board.dto.PostDetailResponseDto; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostResponseDto; import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; @@ -23,7 +23,7 @@ public class PostService { private final UserRepository userRepository; @Transactional - public Long save(PostDto postDto) { + public Long save(PostCreateDto postDto) { User user = userRepository.findById(postDto.userId()).orElseThrow(() -> new BaseException(ErrorMessage.USER_NOT_FOUND) ); diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index 7f43e84e4..a5cdc8180 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -1,6 +1,6 @@ package com.example.board.controller; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostUpdateDto; import com.example.board.response.Response; import com.example.board.service.PostService; @@ -55,7 +55,7 @@ class PostNew { @DisplayName("정상적으로 게시글을 등록된다.") void saveSuccess() throws Exception { //given - PostDto postDto = new PostDto(1L, "test2", "test Contents2"); + PostCreateDto postDto = new PostCreateDto(1L, "test2", "test Contents2"); //when, then mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -80,7 +80,7 @@ void saveSuccess() throws Exception { @DisplayName("userId를 포함하지 않고 보낼 경우 예외가 발생한다.") void saveFailWithAnonymousUser() throws Exception { //given - PostDto postDto = new PostDto(null, "test2", "test Contents2"); + PostCreateDto postDto = new PostCreateDto(null, "test2", "test Contents2"); //when, then String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -100,7 +100,7 @@ void saveFailWithAnonymousUser() throws Exception { @DisplayName("존재하지 않는 userId를 보낼 경우 예외가 발생한다.") void saveFailWithWrongUser() throws Exception { //given - PostDto postDto = new PostDto(0L, "test2", "test Contents2"); + PostCreateDto postDto = new PostCreateDto(0L, "test2", "test Contents2"); //when, then String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -119,7 +119,7 @@ void saveFailWithWrongUser() throws Exception { @DisplayName("제목이 20자 이상인 경우 예외를 발생시킨다.") void saveFailWithLongTitle() throws Exception { //given - PostDto postDto = new PostDto(1L, "test222222222222222222222222", "test Contents2"); + PostCreateDto postDto = new PostCreateDto(1L, "test222222222222222222222222", "test Contents2"); //when, then String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -142,7 +142,7 @@ void saveFailWithLongTitle() throws Exception { @DisplayName("제목이 없는 경우 예외를 발생시킨다.") void saveFailWithNoTitle(String input) throws Exception { //given - PostDto postDto = new PostDto(1L, input, "test Contents2"); + PostCreateDto postDto = new PostCreateDto(1L, input, "test Contents2"); //when, then String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -164,7 +164,7 @@ void saveFailWithNoTitle(String input) throws Exception { @DisplayName("내용이 없는 경우 예외를 발생시킨다.") void saveFailWithNoContents(String input) throws Exception { //given - PostDto postDto = new PostDto(1L, "title", input); + PostCreateDto postDto = new PostCreateDto(1L, "title", input); //when, then String response = mockMvc.perform(post("http://localhost:8080/api/v1/posts") @@ -188,8 +188,8 @@ class PostGetAll { @DisplayName("정상적으로 모든 게시물들을 받아온다.") void getSuccess() throws Exception { //given - postService.save(new PostDto(1L, "testTitle1", "hihihihihihihih1")); - postService.save(new PostDto(1L, "testTitle2", "hihihihihihihih2")); + postService.save(new PostCreateDto(1L, "testTitle1", "hihihihihihihih1")); + postService.save(new PostCreateDto(1L, "testTitle2", "hihihihihihihih2")); //when, then mockMvc.perform(get("http://localhost:8080/api/v1/posts") @@ -241,7 +241,7 @@ class PostGetOne { @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") void getByIdSuccess() throws Exception { //given - Long savedContentsId = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + Long savedContentsId = postService.save(new PostCreateDto(1L, "testTitle", "hihihihihihihih")); //when, then mockMvc.perform(get("http://localhost:8080/api/v1/posts/" + savedContentsId) @@ -289,7 +289,7 @@ class PostUpdate { @DisplayName("정상적으로 게시글을 수정한다.") void updateSuccess() throws Exception { //given - Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + Long savedContents = postService.save(new PostCreateDto(1L, "testTitle", "hihihihihihihih")); PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "test2", "test Contents2"); @@ -316,7 +316,7 @@ void updateSuccess() throws Exception { @DisplayName("수정 시 제목이 20자 이상인 경우 예외를 발생시킨다.") void updateFailWithLongTitle() throws Exception { //given - Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); + Long savedContents = postService.save(new PostCreateDto(1L, "testTitle", "hihihihihihihih")); PostUpdateDto postDto = new PostUpdateDto(1L, "test222222222222222222222222", "test Contents2"); //when, then @@ -340,8 +340,8 @@ void updateFailWithLongTitle() throws Exception { @DisplayName("수정 시 제목이 없는 경우 예외를 발생시킨다.") void updateFailWithNoTitle(String input) throws Exception { //given - Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); - PostDto postDto = new PostDto(1L, input, "test Contents2"); + Long savedContents = postService.save(new PostCreateDto(1L, "testTitle", "hihihihihihihih")); + PostCreateDto postDto = new PostCreateDto(1L, input, "test Contents2"); //when, then String response = mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) @@ -363,8 +363,8 @@ void updateFailWithNoTitle(String input) throws Exception { @DisplayName("수정 시 내용이 없는 경우 예외를 발생시킨다.") void updateFailWithNoContents(String input) throws Exception { //given - Long savedContents = postService.save(new PostDto(1L, "testTitle", "hihihihihihihih")); - PostDto postDto = new PostDto(1L, "title", input); + Long savedContents = postService.save(new PostCreateDto(1L, "testTitle", "hihihihihihihih")); + PostCreateDto postDto = new PostCreateDto(1L, "title", input); //when, then String response = mockMvc.perform(patch("http://localhost:8080/api/v1/posts/" + savedContents) diff --git a/src/test/java/com/example/board/model/PostTest.java b/src/test/java/com/example/board/model/PostTest.java index 74831a55a..8455b8172 100644 --- a/src/test/java/com/example/board/model/PostTest.java +++ b/src/test/java/com/example/board/model/PostTest.java @@ -1,6 +1,6 @@ package com.example.board.model; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; import com.example.board.exception.ErrorMessage; @@ -29,7 +29,7 @@ class PostTest { void setup(){ when(mockUser.getId()).thenReturn(1L); when(mockUser.getName()).thenReturn("mockUser"); - featurePost = Post.from(mockUser, new PostDto(mockUser.getId(), "title", "contents")); + featurePost = Post.from(mockUser, new PostCreateDto(mockUser.getId(), "title", "contents")); } @Nested @DisplayName("Post save 시 생성하는 객체 유효성 검사 테스트") @@ -43,7 +43,7 @@ private static List invalidTitle() { @DisplayName("post 생성 시 정상적으로 저장을 위한 Post 객체가 생성되는 경우") void saveEntitySuccess() { // given - PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + PostCreateDto postDto = new PostCreateDto(mockUser.getId(), "title", "contents"); //when Post post = Post.from(mockUser, postDto); @@ -57,7 +57,7 @@ void saveEntitySuccess() { @Test @DisplayName("post 생성 시 User 객체가 null 값이라 불러올 수 없는 경우") void saveEntityFailWithNullUser() { - PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + PostCreateDto postDto = new PostCreateDto(mockUser.getId(), "title", "contents"); mockUser = null; //when @@ -74,7 +74,7 @@ void saveEntityFailWithNullUser() { @DisplayName("post 생성 시 제목이 null, 빈 공백, 20자 이상인 경우 post 객체 생성에 실패한다.") void saveEntityFailWithInvalidTitle(String input) { // given - PostDto postDto = new PostDto(1L, input, "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, input, "test Contents"); // when, then assertThatThrownBy(() -> Post.from(mockUser, postDto)) @@ -88,7 +88,7 @@ void saveEntityFailWithInvalidTitle(String input) { @DisplayName("post 생성 시 본문 내용이 null, 빈 공백인 경우 post 객체 생성에 실패한다.") void saveEntityFailWithInvalidContents(String input) { // given - PostDto postDto = new PostDto(1L, "title", input); + PostCreateDto postDto = new PostCreateDto(1L, "title", input); // when, then assertThatThrownBy(() -> Post.from(mockUser, postDto)) @@ -103,7 +103,7 @@ void saveEntityFailWithInvalidContents(String input) { void saveEntityFailWithInvalidUserName(String input) { // given when(mockUser.getName()).thenReturn(input); - PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + PostCreateDto postDto = new PostCreateDto(mockUser.getId(), "title", "contents"); //when assertThatThrownBy(() -> Post.from(mockUser, postDto)) @@ -132,7 +132,7 @@ void saveEntitySuccess() { when(mockUser.getId()).thenReturn(1L); when(mockUser.getName()).thenReturn("mock user"); - PostDto postDto = new PostDto(mockUser.getId(), "title", "contents"); + PostCreateDto postDto = new PostCreateDto(mockUser.getId(), "title", "contents"); //when Post post = Post.from(mockUser, postDto); diff --git a/src/test/java/com/example/board/service/PostServiceTest.java b/src/test/java/com/example/board/service/PostServiceTest.java index 396be8e84..3829841f5 100644 --- a/src/test/java/com/example/board/service/PostServiceTest.java +++ b/src/test/java/com/example/board/service/PostServiceTest.java @@ -1,7 +1,7 @@ package com.example.board.service; import com.example.board.dto.PostDetailResponseDto; -import com.example.board.dto.PostDto; +import com.example.board.dto.PostCreateDto; import com.example.board.dto.PostResponseDto; import com.example.board.dto.PostUpdateDto; import com.example.board.exception.BaseException; @@ -48,7 +48,7 @@ class PostNew { @Test @DisplayName("정상적으로 PostDto를 받아 글 작성을 완료하는 경우") void saveSuccessInService() { - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long save = postService.save(postDto); Optional savedPosts = postRepository.findById(save); @@ -60,7 +60,7 @@ void saveSuccessInService() { @DisplayName("제목이 20자 이상인 경우 실패한다.") void saveFailByTitleLength() { // given - PostDto postDto = new PostDto(1L, "testtesttesttesttesttesttest", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "testtesttesttesttesttesttest", "test Contents"); // when, then assertThatThrownBy(() -> postService.save(postDto)) @@ -74,7 +74,7 @@ void saveFailByTitleLength() { @DisplayName("제목이 null이거나 빈 공백인 경우 실패한다.") void saveFailByTitleNullOrBlank(String input) { // given - PostDto postDto = new PostDto(1L, input, "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, input, "test Contents"); // when, then assertThatThrownBy(() -> postService.save(postDto)) @@ -88,7 +88,7 @@ void saveFailByTitleNullOrBlank(String input) { @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") void saveFailByContents(String input) { // given - PostDto postDto = new PostDto(1L, "title", input); + PostCreateDto postDto = new PostCreateDto(1L, "title", input); // when, then assertThatThrownBy(() -> postService.save(postDto)) @@ -99,7 +99,7 @@ void saveFailByContents(String input) { @Test @DisplayName("존재하지 않는 유저가 글을 작성하는 경우 예외를 발생시킨다.") void saveFailWithAnonymousUser() { - PostDto postDto = new PostDto(0L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(0L, "test", "test Contents"); assertThatThrownBy(() -> postService.save(postDto)) .isInstanceOf(BaseException.class) @@ -114,7 +114,7 @@ class PostGetAll { @DisplayName("정상적으로 모든 게시물들을 받아온다.") void getAllSuccess() { for (int i = 0; i < 15; i++) { - PostDto postDto = new PostDto(1L, "title" + i, "contents" + i); + PostCreateDto postDto = new PostCreateDto(1L, "title" + i, "contents" + i); postService.save(postDto); } // TODO: pageable 직접 설정하는 것이 괜찮을까? @@ -135,7 +135,7 @@ class PostGetOne { @Test @DisplayName("정상적으로 특정 게시물 하나를 받아온다.") void getPostByIdSuccess() { - PostDto postDto = new PostDto(1L, "title", "contents"); + PostCreateDto postDto = new PostCreateDto(1L, "title", "contents"); Long savedId = postService.save(postDto); PostDetailResponseDto postDetailResponseDto = postService.readPostDetail(savedId); @@ -157,7 +157,7 @@ class PostUpdate { @Test @DisplayName("정상적으로 PostDto를 받아 글 수정을 완료하는 경우") void updateSuccessInService() { - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long savedId = postService.save(postDto); String updatedTitle = "test update"; @@ -187,7 +187,7 @@ void updateByIdNotFoundFail() { @DisplayName("제목이 20자 이상인 경우 수정에 실패한다.") void updateFailByTitleLength() { // given - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long savedId = postService.save(postDto); PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "testtesttesttesttesttesttest", "test Contents"); @@ -204,7 +204,7 @@ void updateFailByTitleLength() { @DisplayName("제목이 null이거나 빈 공백인 경우 수정에 실패한다.") void updateFailByTitleNullOrBlank(String input) { // given - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long savedId = postService.save(postDto); PostUpdateDto postUpdateDto = new PostUpdateDto(1L, input, "test Contents"); @@ -221,7 +221,7 @@ void updateFailByTitleNullOrBlank(String input) { @DisplayName("내용이 null이거나 빈 공백인 경우 실패한다.") void updateFailByContents(String input) { // given - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long savedId = postService.save(postDto); PostUpdateDto postUpdateDto = new PostUpdateDto(1L, "title", input); @@ -236,7 +236,7 @@ void updateFailByContents(String input) { @DisplayName("존재하지 않는 유저가 글을 수정하는 경우 예외를 발생시킨다.") void updateFailWithAnonymousUser() { // given - PostDto postDto = new PostDto(1L, "test", "test Contents"); + PostCreateDto postDto = new PostCreateDto(1L, "test", "test Contents"); Long savedId = postService.save(postDto); PostUpdateDto postUpdateDto = new PostUpdateDto(0L, "title update", "test Contents update"); From 2c112ec9a2bc92d02581bf8d9c2750c26f7e6f6e Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 1 Dec 2023 11:03:09 +0900 Subject: [PATCH 086/102] Update src/main/java/com/example/board/dto/PostResponseDto.java Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- src/main/java/com/example/board/dto/PostResponseDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/dto/PostResponseDto.java b/src/main/java/com/example/board/dto/PostResponseDto.java index 3f5fcb514..d7ab27d59 100644 --- a/src/main/java/com/example/board/dto/PostResponseDto.java +++ b/src/main/java/com/example/board/dto/PostResponseDto.java @@ -11,7 +11,7 @@ public record PostResponseDto( ) { public static PostResponseDto from(Post post) { postResponseValidate(post); - return new PostResponseDto(post.getId(), post.getTitle(), post.getUser().getName()); + return new PostResponseDto(post.getId(), post.getUser().getName(), post.getTitle()); } private static void postResponseValidate(Post post) { From cc326c977e5fac88a2544e4a5c903a884e20bb63 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 1 Dec 2023 11:04:07 +0900 Subject: [PATCH 087/102] Update src/main/java/com/example/board/exception/BaseException.java Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- src/main/java/com/example/board/exception/BaseException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/BaseException.java b/src/main/java/com/example/board/exception/BaseException.java index 6f787d7eb..b53364fb1 100644 --- a/src/main/java/com/example/board/exception/BaseException.java +++ b/src/main/java/com/example/board/exception/BaseException.java @@ -15,5 +15,4 @@ public BaseException(ErrorMessage message) { this.message = message.getMessage(); this.httpStatus = message.getHttpStatus(); } - } From 3cf530f56a51425b35a6b907771e548f639fff23 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 1 Dec 2023 11:30:03 +0900 Subject: [PATCH 088/102] Update src/test/java/com/example/board/controller/PostControllerTest.java Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- .../java/com/example/board/controller/PostControllerTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index a5cdc8180..ace476a20 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -47,7 +47,6 @@ class PostControllerTest { @Autowired MockMvc mockMvc; - @Nested @DisplayName("게시글 등록") class PostNew { From 0a1d87d677286ec88e42163681412074d3496a2b Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 1 Dec 2023 11:38:44 +0900 Subject: [PATCH 089/102] =?UTF-8?q?refactor:=20Response.java=20=EB=A5=BC?= =?UTF-8?q?=20record=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/board/response/Response.java | 13 +------- .../board/controller/PostControllerTest.java | 32 +++++++++---------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/example/board/response/Response.java b/src/main/java/com/example/board/response/Response.java index f448e7ae3..a129ab8c0 100644 --- a/src/main/java/com/example/board/response/Response.java +++ b/src/main/java/com/example/board/response/Response.java @@ -1,19 +1,8 @@ package com.example.board.response; - import com.example.board.exception.BaseException; -import lombok.AllArgsConstructor; -import lombok.Getter; - import java.util.List; -@Getter -@AllArgsConstructor -public class Response { - - private T data; - private int code; - private String isSuccess; - +public record Response(T data, int code, String isSuccess) { public static Response success(T data) { return new Response<>(data, 200, "ok"); } diff --git a/src/test/java/com/example/board/controller/PostControllerTest.java b/src/test/java/com/example/board/controller/PostControllerTest.java index ace476a20..72b6dc4ef 100644 --- a/src/test/java/com/example/board/controller/PostControllerTest.java +++ b/src/test/java/com/example/board/controller/PostControllerTest.java @@ -91,8 +91,8 @@ void saveFailWithAnonymousUser() throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("사용자 id를 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("사용자 id를 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } @Test @@ -111,7 +111,7 @@ void saveFailWithWrongUser() throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getCode()).isEqualTo(1000); + assertThat(exception.code()).isEqualTo(1000); } @Test @@ -130,8 +130,8 @@ void saveFailWithLongTitle() throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } @@ -153,8 +153,8 @@ void saveFailWithNoTitle(String input) throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("게시글 제목을 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("게시글 제목을 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } @ParameterizedTest(name = "contents가 `{0}`인 경우 저장 요청에 실패한다.") @@ -175,8 +175,8 @@ void saveFailWithNoContents(String input) throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("게시글 내용을 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("게시글 내용을 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } } @@ -276,7 +276,7 @@ void getByIdNotFoundFail() throws Exception { Response exception = objectMapper.readValue(response, Response.class); //then - assertThat(exception.getCode()).isEqualTo(2000); + assertThat(exception.code()).isEqualTo(2000); } } @@ -328,8 +328,8 @@ void updateFailWithLongTitle() throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("제목은 최대 20자까지만 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } @@ -352,8 +352,8 @@ void updateFailWithNoTitle(String input) throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("게시글 제목을 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("게시글 제목을 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } @ParameterizedTest(name = "contents가 `{0}`인 경우 수정 요청에 실패한다.") @@ -375,8 +375,8 @@ void updateFailWithNoContents(String input) throws Exception { .getResponse() .getContentAsString(StandardCharsets.UTF_8); Response> exception = objectMapper.readValue(response, Response.class); - assertThat(exception.getData().get(0)).isEqualTo("게시글 내용을 입력해주세요."); - assertThat(exception.getCode()).isEqualTo(400); + assertThat(exception.data().get(0)).isEqualTo("게시글 내용을 입력해주세요."); + assertThat(exception.code()).isEqualTo(400); } } } From ac3890a20f3bede815c73c58e938dd564ac13b77 Mon Sep 17 00:00:00 2001 From: LEE JUNGWON <85065626+shoeone96@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:45:21 +0900 Subject: [PATCH 090/102] Update src/main/java/com/example/board/service/PostService.java Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- src/main/java/com/example/board/service/PostService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 550f45b17..2f27ec586 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -50,6 +50,5 @@ public Long update(Long postId, PostUpdateDto postUpdateDto, Long userId) { throw new BaseException(ErrorMessage.WRITER_NOT_MATCHED); } return post.update(postUpdateDto); - } } From 85f75ee68f850071f70b6940f7bf02e07b9e6b27 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 1 Dec 2023 11:47:36 +0900 Subject: [PATCH 091/102] =?UTF-8?q?refactor:=20Util=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/util/Validation.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/util/Validation.java b/src/main/java/com/example/board/util/Validation.java index 684f3219f..8ebbcf338 100644 --- a/src/main/java/com/example/board/util/Validation.java +++ b/src/main/java/com/example/board/util/Validation.java @@ -1,12 +1,15 @@ package com.example.board.util; import com.example.board.exception.BindingException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.springframework.validation.BindingResult; import java.util.ArrayList; import java.util.List; -public class Validation { +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class Validation { public static void bindChecking(BindingResult bindingResult) { List messages = new ArrayList<>(); if (bindingResult.hasErrors()) { From 1ad12bc30e4199e3c67c7715586ebe0992eb3e63 Mon Sep 17 00:00:00 2001 From: LEE JUNGWON <85065626+shoeone96@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:51:19 +0900 Subject: [PATCH 092/102] Update src/test/java/com/example/board/model/PostTest.java Co-authored-by: Injae Song <110002292+IjjS@users.noreply.github.com> --- src/test/java/com/example/board/model/PostTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/com/example/board/model/PostTest.java b/src/test/java/com/example/board/model/PostTest.java index 8455b8172..3b877ed8e 100644 --- a/src/test/java/com/example/board/model/PostTest.java +++ b/src/test/java/com/example/board/model/PostTest.java @@ -31,6 +31,7 @@ void setup(){ when(mockUser.getName()).thenReturn("mockUser"); featurePost = Post.from(mockUser, new PostCreateDto(mockUser.getId(), "title", "contents")); } + @Nested @DisplayName("Post save 시 생성하는 객체 유효성 검사 테스트") class PostCreate { From 943f4840359ac43bf7dcdf356f31da22f0cf8f45 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Fri, 1 Dec 2023 11:51:48 +0900 Subject: [PATCH 093/102] =?UTF-8?q?refactor:=20test=20=EA=B0=9C=ED=96=89?= =?UTF-8?q?=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/example/board/model/PostTest.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/test/java/com/example/board/model/PostTest.java b/src/test/java/com/example/board/model/PostTest.java index 8455b8172..f5868d294 100644 --- a/src/test/java/com/example/board/model/PostTest.java +++ b/src/test/java/com/example/board/model/PostTest.java @@ -25,12 +25,14 @@ class PostTest { User mockUser; static Post featurePost; + @BeforeEach void setup(){ when(mockUser.getId()).thenReturn(1L); when(mockUser.getName()).thenReturn("mockUser"); featurePost = Post.from(mockUser, new PostCreateDto(mockUser.getId(), "title", "contents")); } + @Nested @DisplayName("Post save 시 생성하는 객체 유효성 검사 테스트") class PostCreate { @@ -120,11 +122,6 @@ private static List invalidTitle() { return List.of(string.repeat(6)); } - - - - - @Test @DisplayName("post 수정 시 정상적으로 수정을 위한 Post 객체가 생성되는 경우") void saveEntitySuccess() { @@ -134,7 +131,6 @@ void saveEntitySuccess() { PostCreateDto postDto = new PostCreateDto(mockUser.getId(), "title", "contents"); - //when Post post = Post.from(mockUser, postDto); PostUpdateDto updateDto = new PostUpdateDto(mockUser.getId(), "title", "contents"); @@ -147,7 +143,6 @@ void saveEntitySuccess() { assertThat(post.getContents()).isEqualTo(updateDto.contents()); } - @ParameterizedTest(name = "title이 `{0}`인 경우 저장을 위한 객체 생성이 실패한다.") @ValueSource(strings = {" "}) @MethodSource("invalidTitle") From 7a0f39bf9eafa9e0d73e4592d942bfd8b3258828 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 11:57:10 +0900 Subject: [PATCH 094/102] =?UTF-8?q?refactor:=20GlobalExceptionHandler=20?= =?UTF-8?q?=EA=B0=9C=ED=96=89=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/board/exception/GlobalExceptionHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java index 6b3335940..8dd1eda1c 100644 --- a/src/main/java/com/example/board/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/example/board/exception/GlobalExceptionHandler.java @@ -10,7 +10,6 @@ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { - @ExceptionHandler(BaseException.class) public ResponseEntity> baseExceptionHandle(BaseException e) { log.info("error: {}", e.getMessage()); From 95be154bf715d0248950878987da328f77264f97 Mon Sep 17 00:00:00 2001 From: Sehee-Lee-01 Date: Fri, 1 Dec 2023 11:57:43 +0900 Subject: [PATCH 095/102] =?UTF-8?q?refactor:=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20EqualsAndHashCode=20callSuper=3Dtrue=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/model/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/model/User.java b/src/main/java/com/example/board/model/User.java index 3735a0634..be51db1e6 100644 --- a/src/main/java/com/example/board/model/User.java +++ b/src/main/java/com/example/board/model/User.java @@ -7,7 +7,7 @@ import lombok.NoArgsConstructor; @Getter -@EqualsAndHashCode(callSuper = false) +@EqualsAndHashCode(callSuper = true) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Table(name = "users") From 3ec250a5a0c61a6a34d04c06a03dfb17d08d5023 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:29:26 +0900 Subject: [PATCH 096/102] =?UTF-8?q?#16=20chore:docker=20redis=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ docker-compose.yml | 5 +++++ src/main/resources/application.yaml | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/build.gradle b/build.gradle index 29e836f5b..4b8a44950 100644 --- a/build.gradle +++ b/build.gradle @@ -36,6 +36,9 @@ dependencies { asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + // redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + } ext { diff --git a/docker-compose.yml b/docker-compose.yml index eedcd6b85..dbf0782ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,5 +13,10 @@ services: MYSQL_PASSWORD: "root" MYSQL_ROOT_PASSWORD: "root" MYSQL_DATABASE: "board" + redis: + image: "redis:latest" + ports: + - "6830:6379" + volumes: data_volume: \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2cae5fb3e..c586848bf 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -6,6 +6,11 @@ spring: password: root jpa: show-sql: true + data: + redis: + host: localhost + port: 6830 + --- From b49186f3d2e52b10e76efe94030007f091dc4a0c Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:30:40 +0900 Subject: [PATCH 097/102] =?UTF-8?q?#16=20chore:=20Redis=20Template=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/RedisConfiguration.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/com/example/board/configuration/RedisConfiguration.java diff --git a/src/main/java/com/example/board/configuration/RedisConfiguration.java b/src/main/java/com/example/board/configuration/RedisConfiguration.java new file mode 100644 index 000000000..d7ff3558e --- /dev/null +++ b/src/main/java/com/example/board/configuration/RedisConfiguration.java @@ -0,0 +1,36 @@ +package com.example.board.configuration; + +import com.example.board.model.PostCounter; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@RequiredArgsConstructor +@Configuration +@EnableRedisRepositories +public class RedisConfiguration { + + private final RedisProperties redisProperties; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new Jackson2JsonRedisSerializer<>(Long.class)); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(PostCounter.class)); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + return redisTemplate; + } +} From dff1bc61c0ce9f02eb526169398d354f6d0d4dcf Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:32:29 +0900 Subject: [PATCH 098/102] =?UTF-8?q?#16=20feat:=20PostCounter=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 유저별 postCount를 Redis에 저장할 클래스 생성 - 유저가 글을 작성하는 상황별 로직 추가 * LocalDateTime 직렬화를 위한 @JsonSerialIze, @JsonDeserialize 도입 --- .../com/example/board/model/PostCounter.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/main/java/com/example/board/model/PostCounter.java diff --git a/src/main/java/com/example/board/model/PostCounter.java b/src/main/java/com/example/board/model/PostCounter.java new file mode 100644 index 000000000..d92a2ba58 --- /dev/null +++ b/src/main/java/com/example/board/model/PostCounter.java @@ -0,0 +1,71 @@ +package com.example.board.model; + +import com.example.board.exception.BaseException; +import com.example.board.exception.ErrorMessage; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class PostCounter { + private Long userId; + private int totalWriteInDay; + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime lastPostedAt; + private int totalWritePerMit; + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + private LocalDateTime lastPostedInMin; + + private PostCounter(Long userId) { + this.userId = userId; + this.totalWriteInDay = 1; + this.lastPostedAt = LocalDateTime.now(); + this.totalWritePerMit = 1; + this.lastPostedInMin = LocalDateTime.now(); + } + + public static PostCounter firstPostInDay(Long userId) { + return new PostCounter(userId); + } + + public void update() { + if (totalWriteInDay >= 9) { + throw new BaseException(ErrorMessage.OVER_MAX_POST_PER_DAY); + } + if (checkWritesInMinuteOverCountFour()) return; + if (lastPostedAt.plusMinutes(1).isAfter(LocalDateTime.now())) { + totalWritePerMit++; + totalWriteInDay++; + lastPostedAt = LocalDateTime.now(); + return; + } + writeTimeOverMinute(); + + } + + private boolean checkWritesInMinuteOverCountFour() { + if (totalWritePerMit >= 4) { + if (writeTimeOverMinute()) return true; + throw new BaseException(ErrorMessage.OVER_MAX_POST_PER_MINUTE); + } + return false; + } + + private boolean writeTimeOverMinute() { + if (lastPostedAt.plusMinutes(1).isBefore(LocalDateTime.now())) { + totalWritePerMit = 1; + totalWriteInDay++; + lastPostedAt = LocalDateTime.now(); + lastPostedInMin = lastPostedAt; + return true; + } + ; + return false; + } +} From 6c1dcdc6051f36730082f60a7a1235e4af4183d5 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:33:26 +0900 Subject: [PATCH 099/102] =?UTF-8?q?#16=20feat:=20Redis=EB=A5=BC=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20PostCountRepository=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 RedisTemplate을 이용한 PostCounter 저장 및 탐색 메서드 추가 --- .../PostCounterRedisRepository.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/example/board/repository/PostCounterRedisRepository.java diff --git a/src/main/java/com/example/board/repository/PostCounterRedisRepository.java b/src/main/java/com/example/board/repository/PostCounterRedisRepository.java new file mode 100644 index 000000000..adef64a2f --- /dev/null +++ b/src/main/java/com/example/board/repository/PostCounterRedisRepository.java @@ -0,0 +1,24 @@ +package com.example.board.repository; + +import com.example.board.model.PostCounter; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class PostCounterRedisRepository { + + private final RedisTemplate redisTemplate; + + public Optional getPostCounter(Long userId){ + PostCounter postCounter = redisTemplate.opsForValue().get(userId); + return Optional.ofNullable(postCounter); + } + + public void savePostCounter(Long userId, PostCounter postCounter){ + redisTemplate.opsForValue().set(userId, postCounter); + } +} From 5e95e7d7fcabd9ec4290373838d78270fe990159 Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:34:01 +0900 Subject: [PATCH 100/102] =?UTF-8?q?#16=20feat:=20=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EA=B0=80=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20=EC=8B=9C=20Redis?= =?UTF-8?q?=EC=97=90=20=EA=B0=92=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=ED=95=A0=20Service=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/board/service/RedisService.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/java/com/example/board/service/RedisService.java diff --git a/src/main/java/com/example/board/service/RedisService.java b/src/main/java/com/example/board/service/RedisService.java new file mode 100644 index 000000000..63c31f70a --- /dev/null +++ b/src/main/java/com/example/board/service/RedisService.java @@ -0,0 +1,29 @@ +package com.example.board.service; + +import com.example.board.model.PostCounter; +import com.example.board.model.User; +import com.example.board.repository.PostCounterRedisRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RedisService { + private final PostCounterRedisRepository redisRepository; + + public void postInfoChecking(User user){ + redisRepository.getPostCounter(user.getId()) + .ifPresentOrElse( + postCounter -> updateSavedData(postCounter, user.getId()), + () -> redisRepository.savePostCounter( + user.getId(), + PostCounter.firstPostInDay(user.getId()))); + } + + private void updateSavedData(PostCounter postCounter, Long userId){ + postCounter.update(); + redisRepository.savePostCounter(userId, postCounter); + } + + +} From 6eaf5b2bc50be4a475f0a09d8912c7aa6513ab5e Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:34:38 +0900 Subject: [PATCH 101/102] =?UTF-8?q?#16=20feat:=20=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=A1=9C=EC=A7=81=EC=97=90=EC=84=9C=20=EC=83=81?= =?UTF-8?q?=ED=99=A9=20=EB=B3=84=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=ED=95=9C=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=EB=B0=9C=EC=83=9D=ED=95=A0=20Exception=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/exception/ErrorMessage.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/example/board/exception/ErrorMessage.java b/src/main/java/com/example/board/exception/ErrorMessage.java index 5eabda30c..6185fe677 100644 --- a/src/main/java/com/example/board/exception/ErrorMessage.java +++ b/src/main/java/com/example/board/exception/ErrorMessage.java @@ -16,7 +16,9 @@ public enum ErrorMessage { POST_NOT_FOUND(2000, HttpStatus.NOT_FOUND, "찾으시는 게시글이 없습니다."), WRITER_NOT_MATCHED(2001, HttpStatus.BAD_REQUEST, "작성자가 일치하지 않습니다."), WRONG_TITLE_VALUE(2002, HttpStatus.BAD_REQUEST, "게시글 제목은 1자~20자 이내로 작성해주십시오."), - WRONG_CONTENTS_VALUE(2003, HttpStatus.BAD_REQUEST, "게시글 내용을 작성해주십시오."); + WRONG_CONTENTS_VALUE(2003, HttpStatus.BAD_REQUEST, "게시글 내용을 작성해주십시오."), + OVER_MAX_POST_PER_DAY(2004, HttpStatus.BAD_REQUEST, "하루에 작성할 수 있는 최대 게시글 개수를 초과하였습니다."), + OVER_MAX_POST_PER_MINUTE(2005, HttpStatus.BAD_REQUEST, "1분 간 5개 이상의 글을 작성할 수 없습니다."); private final int code; private final HttpStatus httpStatus; From 5c2b46ae84ee7144e6cc7c07f1d68556e6cb709f Mon Sep 17 00:00:00 2001 From: shoeone96 Date: Sat, 2 Dec 2023 21:35:14 +0900 Subject: [PATCH 102/102] =?UTF-8?q?#16=20refactor:=20post=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20user?= =?UTF-8?q?=EB=B3=84=20post=20count=EB=A5=BC=20=EC=B2=B4=ED=81=AC=ED=95=A0?= =?UTF-8?q?=20redis=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/board/service/PostService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/example/board/service/PostService.java b/src/main/java/com/example/board/service/PostService.java index 2f27ec586..8fd83e7a8 100644 --- a/src/main/java/com/example/board/service/PostService.java +++ b/src/main/java/com/example/board/service/PostService.java @@ -21,12 +21,14 @@ public class PostService { private final PostRepository postRepository; private final UserRepository userRepository; + private final RedisService redisService; @Transactional public Long save(PostCreateDto postDto) { User user = userRepository.findById(postDto.userId()).orElseThrow(() -> new BaseException(ErrorMessage.USER_NOT_FOUND) ); + redisService.postInfoChecking(user); return postRepository.save(Post.from(user, postDto)).getId(); }