From 48817043488a7a89e5b33fad18d3f1ef96171da2 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Thu, 5 Sep 2024 23:15:53 +0900 Subject: [PATCH 01/42] =?UTF-8?q?feat:=20JASYPT=20=ED=82=A4=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20GitHub=20Action=20Sec?= =?UTF-8?q?rets=EC=97=90=EC=84=9C=20Vault=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 --- .github/workflows/dev-aws-CI-CD.yml | 3 --- .github/workflows/gradle.yml | 2 -- 2 files changed, 5 deletions(-) diff --git a/.github/workflows/dev-aws-CI-CD.yml b/.github/workflows/dev-aws-CI-CD.yml index 686ae70..11784b8 100644 --- a/.github/workflows/dev-aws-CI-CD.yml +++ b/.github/workflows/dev-aws-CI-CD.yml @@ -40,8 +40,6 @@ jobs: run: chmod +x gradlew - name: Build With Gradle - env: - JASYPT_ENCRYPTOR_PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }} run: ./gradlew build -x test --info - name: List All Files for Debugging @@ -143,7 +141,6 @@ jobs: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} export DOCKER_REPOSITORY=${{ secrets.DOCKER_REPOSITORY }} export DOCKER_REPOSITORY_NGINX=${{ secrets.DOCKER_REPOSITORY_NGINX }} - export JASYPT_ENCRYPTOR_PASSWORD=${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }} # Pull 최신 이미지 docker-compose -f /home/ubuntu/docker-compose.yml pull # 새 컨테이너 실행 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 4f4d063..0dc2e3c 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -30,8 +30,6 @@ jobs: run: | ./gradlew build ./gradlew --info test - env: - JASYPT_ENCRYPTOR_PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }} - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v1 From 2469a1ef100bc5116b89701004a5ef458a0a6a82 Mon Sep 17 00:00:00 2001 From: subin Date: Thu, 22 Aug 2024 01:09:57 +0900 Subject: [PATCH 02/42] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80,=20=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=EC=BD=94=EB=93=9C=20=EB=B0=9C=EA=B8=89=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 --- build.gradle.kts | 4 +++ .../common/config/SecurityConfiguration.kt | 2 ++ .../controller/KakaoCallbackController.kt | 14 ++++++++++ .../controller/KakaoLoginPageController.kt | 26 ++++++++++++++++++ src/main/resources/application.yml | 5 ++-- .../static/kakao_login_medium_narrow.png | Bin 0 -> 2946 bytes src/main/resources/templates/kakaoLogin.html | 17 ++++++++++++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt create mode 100644 src/main/resources/static/kakao_login_medium_narrow.png create mode 100644 src/main/resources/templates/kakaoLogin.html diff --git a/build.gradle.kts b/build.gradle.kts index 7e52fdc..7a175d2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,6 +32,10 @@ dependencies { //web implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-thymeleaf") + implementation("org.springframework.boot:spring-boot-starter-webflux") + + //thymeleaf + implementation("org.springframework.boot:spring-boot-starter-thymeleaf") // logging implementation("io.github.oshai:kotlin-logging-jvm:5.1.4") diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index b38b59a..406fc45 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -67,6 +67,8 @@ class SecurityConfiguration( "/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list", "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", + "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", + "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "config-service/**" ).permitAll() .requestMatchers(HttpMethod.POST, "/v1/members").permitAll() diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt new file mode 100644 index 0000000..09e5d8b --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt @@ -0,0 +1,14 @@ +package org.store.clothstar.kakaoLogin.controller + +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +class KakaoCallbackController { + @GetMapping("/auth/kakao/callback") + fun kakaoCallback(@RequestParam code: String): ResponseEntity { + return ResponseEntity.ok("Authorization code: $code") + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt new file mode 100644 index 0000000..65130ee --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt @@ -0,0 +1,26 @@ +package org.store.clothstar.kakaoLogin.controller + +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Controller +class KakaoLoginPageController { + + @Value("\${kakao.client_id}") + private lateinit var clientId: String + + @Value("\${kakao.redirect_uri}") + private lateinit var redirectUri: String + + @GetMapping("/kakaoLogin/page") + fun loginPage(model: Model): String { + val location = "https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri" + model.addAttribute("location", location) + + return "kakaoLogin" + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 13c11e5..16e0f67 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,5 +43,6 @@ springdoc: #path: "swagger.html" # http://localhost:8080/swagger.html? ?? ?? path: "/" # http://localhost:8080? ?? ?? - - +kakao: + client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) + redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) \ No newline at end of file diff --git a/src/main/resources/static/kakao_login_medium_narrow.png b/src/main/resources/static/kakao_login_medium_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..09bb358843a08576d0029ff8427120c9f5f5c36d GIT binary patch literal 2946 zcmV-|3w`v7P)Px=JV``BRCodHT?=$n#Tou~^I8JtArcY-;n7xG;YdSyzvLw#RsjQ?LV-plh_pbV z7&Y>!6{SGyQ7PIA1q-OyLJ?6xkSGWUhC(S+KzRr@5E4Up1d{9~+0K7&=I-8o-o1M_ z>K zJfyi-#pdme(05ddSGi|FWMQ#bu-~&sqw*H#dF*pK(aop^c{`RCutpXZ-Vqb+{@?~PST%axaN|9sl5JgJIH%c4~IU>To?lhB0+0XCxP3iNAxKi{VC zjvQKnb(`Xq{N3HFP+286-lkOzX3S4Srw$HekGZbU1WWi7JQ!0$sJB|v8L$zYDQlkR?lC+CKkVmPTkqT#7o4e%f+nkhKWm0nES@$uNi!c2 zssx`jE+k%wc<(zU3e%*=la=(SN1GyRa#JNw%RavJkZar|{jF zIh9v8Swf}bW|5sHAS=!LW)Ya=RHl5htyRnfvZt&O-$U^1P~<)4O#w*c5zssi&Rq<2 z=r2E%7C|2ZUJESuX4v?S^iJ+2-sy0*FeW|j+Fx0*j9y6a&uMW@dlNWz;7OTq#^{GU^9##0; zKN=%%Q3CGmUj>qbR=6vclzG3b0VUCa+Sq#_P_10IYaeiTkKlB^D84)sjm-9T96Kqm zeu?J(hi*eclUg{cEx1q|B>_OI zUQw03yRDIwC$PWyyomMf`gBkc8kh;F;k=)&!+`(EO2UiKw>z-zRV5p8qPEt8R$Wu^ z)u$H`-$>rj;owbqDjCU%wOIJ13=jd%HJo?EX?fl=e)gpV%$;%7v)FHb{rmA4b)WnV zw)GPmhW4xUOB+swCctwS0E_2@(~UOX8x>Xse0L7|<5;b(VZ*)7Y-vq^o9(3sC zDGm80%p{xXK9S7?uBWWhmnIdmV~89Uy}*`%O+1N2K0E~29!fcDVHN{>xy4zC9c(HZ z5MGmlI~W3Nw?AJ@mcdXmuwG1*27(!^DFJpcgbb_~U>gW#u%-mq!4NXAUK7|yjsZi) z0vYT$mg1ww4V2(J)Kt*b2mxGO?Ttt@+ebWC;9ah_giI)+qI~a%kg3HjY=^ME0&Vd& za@*21s^5TM_ZkkIIp@Etwi&>lP5$cx)dbql_O}O4?Jauc7QDPt{`Q)gQ;yk>UQ>0` zx_g=`KW@YgX-9F}P8)XZSDTjbt=J#+PsO!z3l489M$_aPJeHS?SD!b2uRGvAcOanY zje^yy-r6J6vvU>bp};LSI05{ z;03wQ$u>BH@2&(rJaqkO3Krx7Il0L=_HmI^9BeC>^XiQ?joEq|55z|nvSS`=iauE> zig^qj9FT=9*#XU%pTvd^*RVo|4YoKZ&KJ2XXwP4alFHos$>^2_GAZYezkTlFQn{U8 zo$Ywzr==VQEdZ#>L&K+RtBGB9Df{~oAbZ+K(rB?%BZJ>g;F@>1zoY}fi? z(5;O_TU{qHn^%1+ksUu3gCF!s1s&%tefA1krl|p{sR2e!ah=4(v17LFZ@LV=cwdgX z9|PGzUAi2_9t-l{_NL?XM#KTZ9@qzX_jTZbZ11kNtOYu@_b%63!cS;C&jH;}bu1Q_ zX7hH34X8jyi)u;hTenmLO*H)IY}mxx@WE>N55#%h;}bhCD%sBWrQq})*YVWIAr(rU zk%@p{Q%85+E_XLR3z#<(Xwul2?*F{d_XI>IG#oI`o;Gfk&k}k0!K3n-CfUJL*s#07h{YA+22b9Ituf5 zeCDsMhFTsJ?1n118RfcZs}VDT6|T$z>~IkbfnR_f0$Aaa3b4aPFa&-posES+FGzJF zKsZ}1=@`gPfG5|T0_$u_u=zuPaFy^SoQcR;f;8B%mVK`c@W&*|1p;P3KpLrIG2YS) z7wD>R4%^px%?yGI83h7b5s*f2rrg(r@tIc01f5wBpyi2GD7IPtTtd!J5Fou4pYEX` zCTLBCfKYBz{kep&p&%fXI~248t*H<&v6=ry{E%>`XFc`?43bbn-oK{c> z1j0cetdaY(1G1-w-XpXrHVH~U0HP-k5yamR1Kq19<4bausWk-SA$ys^qG%jC8Y>wR zIi1?u)+$5zbh3=TJlv6Y3$pq(Q_wwf?=4`qjg$4iD*Kv67P|$zp6#&rWVZPA8OV3? zC!eX!xL0q8$DrPom_O6E;|D!F%%c|`{!Oyv6L0!M<&sO3%no*Z{Y^A;jS`QSmeESRmr;YoPhVE5$jZ9laIBK|Oe}wkc{O0b&*tqme7G|Xr*;>q`~;EH$87dw4Xcn`at88|HUC?Qlp|;8G#rze~<1hSmUA*K0hS#rQKkP0Y9hm;GtnB@I)L>T5HObW{%{J+& z-nPV?w;{*6m77doSFQt2pVR+(>8ZdYWA*dvItYv2%TiX%(B^O}BDK zv1=>4*=@458-ORDXaA!l#M^bh&jTio=J3{k{2{wxmbWMoZP^HV^3x*e3W}!`gZoyh z%JuNf`}I}OL*WQ>+1qnOGwj(|0y6S{@wcv}glFG8Pb2X9zm($hf6L1Z^7MA}L=2Y9 zaq&j6FQ=a{FLPSL?t)mvuzLpGJGs+4@w?eq*bKnD)OEG1E{$nvv!T)$W-~vEt)G2) zwj&pQEt&&eGl8)qz^2Qa55<}djhVYahu28WC8LkS2{V#$X0J=>L*?1>u}$*g&D*57 z{5rdm`9%y3y4u*=Q|~OznE5VR7CS#etkBt=aO1D0lvDVEi<;1HzUWf1D4=W1!i9aF^GY za=X|2O6%eA)y~Z9PV7be0)}6*3A0LPTJe swC^J>2!{xpAaAe;Fon)X-38(Q1DqPh + + + + + KakaoLogin + + +
+

카카오 로그인

+ > + + + +
+ + From 0acad647ea8fd07597171a7226fa33b600d5b1f1 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Fri, 23 Aug 2024 23:47:13 +0900 Subject: [PATCH 03/42] =?UTF-8?q?fix:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80,=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=83=9C=EA=B7=B8=20src=20?= =?UTF-8?q?=EA=B2=BD=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 --- .../clothstar/common/config/WebConfig.kt | 20 ++++++++++++++++++ .../kakao_login_medium_narrow.png | Bin src/main/resources/templates/kakaoLogin.html | 9 ++++---- 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/common/config/WebConfig.kt rename src/main/resources/static/{ => images}/kakao_login_medium_narrow.png (100%) diff --git a/src/main/kotlin/org/store/clothstar/common/config/WebConfig.kt b/src/main/kotlin/org/store/clothstar/common/config/WebConfig.kt new file mode 100644 index 0000000..beb2acf --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/common/config/WebConfig.kt @@ -0,0 +1,20 @@ +package org.store.clothstar.common.config + +import org.springframework.context.annotation.Configuration +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer + +@Configuration +class WebConfig : WebMvcConfigurer { + + override fun addResourceHandlers(registry: ResourceHandlerRegistry) { + registry.addResourceHandler("/favicon.ico") + .addResourceLocations("classpath:/static/") + registry.addResourceHandler("/js/**") + .addResourceLocations("classpath:/static/js/") + registry.addResourceHandler("/css/**") + .addResourceLocations("classpath:/static/css/") + registry.addResourceHandler("/images/**") + .addResourceLocations("classpath:/static/images/") + } +} \ No newline at end of file diff --git a/src/main/resources/static/kakao_login_medium_narrow.png b/src/main/resources/static/images/kakao_login_medium_narrow.png similarity index 100% rename from src/main/resources/static/kakao_login_medium_narrow.png rename to src/main/resources/static/images/kakao_login_medium_narrow.png diff --git a/src/main/resources/templates/kakaoLogin.html b/src/main/resources/templates/kakaoLogin.html index 8172ab9..17d6350 100644 --- a/src/main/resources/templates/kakaoLogin.html +++ b/src/main/resources/templates/kakaoLogin.html @@ -1,16 +1,17 @@ - + KakaoLogin -
+

카카오 로그인

> - - + +
From cd2c7debd569e9b6fb64ff1d00e7282f5c804188 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 24 Aug 2024 16:24:44 +0900 Subject: [PATCH 04/42] =?UTF-8?q?refactor:=20favicon=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/kakaoLogin.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/templates/kakaoLogin.html b/src/main/resources/templates/kakaoLogin.html index 17d6350..c679d04 100644 --- a/src/main/resources/templates/kakaoLogin.html +++ b/src/main/resources/templates/kakaoLogin.html @@ -3,6 +3,7 @@ + KakaoLogin From 904003cc9029e6de9b0d7db2c5b3b8ee5dbc207f Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 24 Aug 2024 17:34:02 +0900 Subject: [PATCH 05/42] =?UTF-8?q?refactor:=20client=5Fsecret,=20admin=5Fke?= =?UTF-8?q?y=EC=9D=84=20yml=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kakaoLogin/controller/KakaoCallbackController.kt | 4 ++-- src/main/resources/application.yml | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt index 09e5d8b..32912fc 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.RestController @RestController class KakaoCallbackController { @GetMapping("/auth/kakao/callback") - fun kakaoCallback(@RequestParam code: String): ResponseEntity { - return ResponseEntity.ok("Authorization code: $code") + fun kakaoCallback(@RequestParam code: String): ResponseEntity { + return ResponseEntity.ok().build() } } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 16e0f67..b1e9a68 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -45,4 +45,6 @@ springdoc: kakao: client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) - redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) \ No newline at end of file + redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) + client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) + admin_key: ENC(BmWFmOXxaXogq1sWkCEZu/OM2qCKiOfPfZSUCG5Xwokpm4XKKmLJMSud7+1vbP8M) \ No newline at end of file From bb5fdb3722e1cf7895d9f0efbd7f08f4bb056f3a Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 24 Aug 2024 18:47:45 +0900 Subject: [PATCH 06/42] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=A1=9C=EC=A7=81=20=EC=83=9D=EC=84=B1(=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=9D=91=EB=8B=B5=20dto=20=EB=B0=98=ED=99=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/KakaoCallbackController.kt | 11 +++- .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 34 +++++++++++ .../kakaoLogin/service/KakaoLoginService.kt | 61 +++++++++++++++++++ src/main/resources/application.yml | 7 ++- 4 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt index 32912fc..f92edbc 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt @@ -4,11 +4,16 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto +import org.store.clothstar.kakaoLogin.service.KakaoLoginService @RestController -class KakaoCallbackController { +class KakaoCallbackController( + private val kakaoLoginService: KakaoLoginService, +) { @GetMapping("/auth/kakao/callback") - fun kakaoCallback(@RequestParam code: String): ResponseEntity { - return ResponseEntity.ok().build() + fun kakaoCallback(@RequestParam code: String): ResponseEntity { + val accessToken = kakaoLoginService.getAccessToken(code) + return ResponseEntity.ok(accessToken) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt new file mode 100644 index 0000000..fc0de89 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -0,0 +1,34 @@ +package org.store.clothstar.kakaoLogin.dto + +import com.fasterxml.jackson.annotation.JsonProperty + + + +class KakaoTokenResponseDto { + @JsonProperty("token_type") + private val tokenType: String? = null + + @JsonProperty("access_token") + val accessToken: String? = null + + /** + * 액세스 토큰과 ID 토큰의 만료 시간(초) + */ + @JsonProperty("expires_in") + private val expiresIn: Int? = null + + @JsonProperty("refresh_token") + private val refreshToken: String? = null + + /** + * 리프레시 토큰 만료 시간(초) + */ + @JsonProperty("refresh_token_expires_in") + private val refreshTokenExpiresIn: Int? = null + + @JsonProperty("id_token") + private val idToken: String? = null + + @JsonProperty("scope") + private val scope: String? = null +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt new file mode 100644 index 0000000..4bef891 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -0,0 +1,61 @@ +package org.store.clothstar.kakaoLogin.service + +import com.fasterxml.jackson.databind.ObjectMapper +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import org.springframework.util.LinkedMultiValueMap +import org.springframework.util.MultiValueMap +import org.springframework.web.reactive.function.BodyInserters +import org.springframework.web.reactive.function.client.WebClient +import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto + +@Service +class KakaoLoginService{ + + private val logger = KotlinLogging.logger {} + + @Value("\${kakao.client_id}") + private lateinit var clientId: String + + @Value("\${kakao.client_secret}") + private lateinit var clientSecret: String + + @Value("\${kakao.redirect_uri}") + private lateinit var redirectUri: String + + @Value("\${kakao.api.uri.base}") + private lateinit var baseUri: String + + @Value("\${kakao.login.uri.token}") + private lateinit var tokenUri: String + + fun getAccessToken(code:String): KakaoTokenResponseDto { + // 토큰 요청 데이터 -> MultiValueMap + val params: MultiValueMap = LinkedMultiValueMap() + params.add("code", code) + params.add("client_secret", clientSecret) + params.add("client_id", clientId) + params.add("grant_type", "authorization_code") + params.add("redirect_url", redirectUri) + + // 웹 클라이언트로 요청 보내기 + val response = WebClient.create(baseUri) + .post() + .uri(tokenUri) + .body(BodyInserters.fromFormData(params)) + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .retrieve() + .bodyToMono(String::class.java) + .block() + + // json 응답을 객체로 변환 + val objectMapper = ObjectMapper() + + val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response,KakaoTokenResponseDto::class.java) + + logger.info("Access Token : {}", kakaoToken.accessToken) + + return kakaoToken + } +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b1e9a68..baf9cd8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -47,4 +47,9 @@ kakao: client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) - admin_key: ENC(BmWFmOXxaXogq1sWkCEZu/OM2qCKiOfPfZSUCG5Xwokpm4XKKmLJMSud7+1vbP8M) \ No newline at end of file + admin_key: ENC(BmWFmOXxaXogq1sWkCEZu/OM2qCKiOfPfZSUCG5Xwokpm4XKKmLJMSud7+1vbP8M) + + api.uri.base: https://kauth.kakao.com + api.uri.user: /v2/user/me + login.redirect_url: http://localhost:8080/auth/kakao/callback + login.uri.token: /oauth/token From 7fad1249e2a4da8293809284d5c67869e6ed9d99 Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 27 Aug 2024 02:50:39 +0900 Subject: [PATCH 07/42] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C,=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EB=A1=9C=EC=A7=81=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 --- ...llbackController.kt => KakaoController.kt} | 11 ++-- .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 37 ++++-------- .../dto/KakaoUserInfoResponseDto.kt | 16 ++++++ .../kakaoLogin/service/KakaoLoginService.kt | 36 +++++++++--- .../clothstar/kakaoLogin/vo/KakaoAccount.kt | 56 +++++++++++++++++++ .../clothstar/kakaoLogin/vo/Properties.kt | 19 +++++++ src/main/resources/application.yml | 1 - 7 files changed, 137 insertions(+), 39 deletions(-) rename src/main/kotlin/org/store/clothstar/kakaoLogin/controller/{KakaoCallbackController.kt => KakaoController.kt} (64%) create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt similarity index 64% rename from src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt rename to src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index f92edbc..019f4f5 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoCallbackController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -4,16 +4,19 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto import org.store.clothstar.kakaoLogin.service.KakaoLoginService @RestController -class KakaoCallbackController( +class KakaoController( private val kakaoLoginService: KakaoLoginService, ) { @GetMapping("/auth/kakao/callback") - fun kakaoCallback(@RequestParam code: String): ResponseEntity { + fun kakaoCallback(@RequestParam code: String): ResponseEntity { + // 액세스 토큰 받아오기 val accessToken = kakaoLoginService.getAccessToken(code) - return ResponseEntity.ok(accessToken) + // 사용자 정보 받아오기 + val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) + return ResponseEntity.ok(userInfo) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt index fc0de89..f80ed75 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -2,33 +2,18 @@ package org.store.clothstar.kakaoLogin.dto import com.fasterxml.jackson.annotation.JsonProperty - - class KakaoTokenResponseDto { - @JsonProperty("token_type") - private val tokenType: String? = null - - @JsonProperty("access_token") - val accessToken: String? = null - - /** - * 액세스 토큰과 ID 토큰의 만료 시간(초) - */ - @JsonProperty("expires_in") - private val expiresIn: Int? = null - - @JsonProperty("refresh_token") - private val refreshToken: String? = null - - /** - * 리프레시 토큰 만료 시간(초) - */ - @JsonProperty("refresh_token_expires_in") - private val refreshTokenExpiresIn: Int? = null - - @JsonProperty("id_token") - private val idToken: String? = null - + // 토큰 타입, bearer로 고정 + @JsonProperty("token_type") private val tokenType: String? = null + // 사용자 액세스 토큰 값 + @JsonProperty("access_token") val accessToken: String? = null + // 액세스 토큰과 ID 토큰의 만료 시간(초) + @JsonProperty("expires_in") private val expiresIn: Int? = null + // 사용자 리프레시 토큰 값 + @JsonProperty("refresh_token") private val refreshToken: String? = null + // 리프레시 토큰 만료 시간(초) + @JsonProperty("refresh_token_expires_in") private val refreshTokenExpiresIn: Int? = null + // 인증된 사용자의 정보 조회 권한 범위 / 범위가 여러 개일 경우, 공백으로 구분 @JsonProperty("scope") private val scope: String? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt new file mode 100644 index 0000000..5d5f89d --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt @@ -0,0 +1,16 @@ +package org.store.clothstar.kakaoLogin.dto + +import com.fasterxml.jackson.annotation.JsonProperty +import org.store.clothstar.kakaoLogin.vo.KakaoAccount +import org.store.clothstar.kakaoLogin.vo.Properties + +class KakaoUserInfoResponseDto ( + // 회원번호 + @JsonProperty("id") val id: Long? = null, + // 서비스에 연결 완료된 시각, UTC* + @JsonProperty("connected_at") val connectedAt: String? = null, + // 카카오계정 정보 + @JsonProperty("kakao_account") val kakaoAccount: KakaoAccount? = null, + // 사용자 프로퍼티(Property) + @JsonProperty("properties") val properties: Properties? = null, +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index 4bef891..d68e19e 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -9,6 +9,7 @@ import org.springframework.util.MultiValueMap import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto @Service class KakaoLoginService{ @@ -24,14 +25,15 @@ class KakaoLoginService{ @Value("\${kakao.redirect_uri}") private lateinit var redirectUri: String - @Value("\${kakao.api.uri.base}") - private lateinit var baseUri: String - @Value("\${kakao.login.uri.token}") private lateinit var tokenUri: String + @Value("\${kakao.api.uri.user}") + private lateinit var userUri: String + + // 토큰 가져오기 fun getAccessToken(code:String): KakaoTokenResponseDto { - // 토큰 요청 데이터 -> MultiValueMap + // 토큰 요청 데이터 val params: MultiValueMap = LinkedMultiValueMap() params.add("code", code) params.add("client_secret", clientSecret) @@ -40,7 +42,7 @@ class KakaoLoginService{ params.add("redirect_url", redirectUri) // 웹 클라이언트로 요청 보내기 - val response = WebClient.create(baseUri) + val response = WebClient.create("https://kauth.kakao.com") .post() .uri(tokenUri) .body(BodyInserters.fromFormData(params)) @@ -51,11 +53,29 @@ class KakaoLoginService{ // json 응답을 객체로 변환 val objectMapper = ObjectMapper() - val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response,KakaoTokenResponseDto::class.java) - logger.info("Access Token : {}", kakaoToken.accessToken) - + logger.info { "Access Token : ${kakaoToken.accessToken}" } return kakaoToken } + + // 사용자 정보 가져오기 + fun getUserInfo(accessToken:String): KakaoUserInfoResponseDto { + // 웹 클라이언트로 요청 보내기 + val response = WebClient.create("https://kapi.kakao.com") + .get() + .uri(userUri) + .header("Authorization", "Bearer $accessToken") + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .retrieve() + .bodyToMono(String::class.java) + .block() + + // json 응답을 객체로 변환 + val objectMapper = ObjectMapper() + val userInfo = objectMapper.readValue(response, KakaoUserInfoResponseDto::class.java) + + logger.info { "email : ${userInfo.kakaoAccount!!.email}" } + return userInfo + } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt new file mode 100644 index 0000000..6205a42 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt @@ -0,0 +1,56 @@ +package org.store.clothstar.kakaoLogin.vo + +import com.fasterxml.jackson.annotation.JsonProperty + +class KakaoAccount ( + // 사용자 동의 시 닉네임 제공 가능 + // 필요한 동의항목: 닉네임 + @JsonProperty("profile_nickname_needs_agreement") val profileNicknameNeedsAgreement: Boolean? = null, + // 사용자 동의 시 프로필 사진 제공 가능 + // 필요한 동의항목: 프로필 사진 + @JsonProperty("profile_image_needs_agreement") val profileImageNeedsAgreement: Boolean? = null, + // 이메일 유무 여부 + // 필요한 동의항목: 카카오계정(이메일) + @JsonProperty("has_email") val hasEmail: Boolean? = null, + // 사용자 동의 시 카카오계정 대표 이메일 제공 가능 + // 필요한 동의항목: 카카오계정(이메일) + @JsonProperty("email_needs_agreement") val emailNeedsAgreement: Boolean? = null, + // 이메일 유효 여부 + // 필요한 동의항목: 카카오계정(이메일) + // true: 유효한 이메일 / false: 이메일이 다른 카카오계정에 사용돼 만료 + @JsonProperty("is_email_valid") val isEmailValid: Boolean? = null, + // 이메일 인증 여부 + // 필요한 동의항목: 카카오계정(이메일) + // true: 인증된 이메일 / false: 인증되지 않은 이메일 + @JsonProperty("is_email_verified") val isEmailVerified: Boolean? = null, + // 카카오계정 대표 이메일 + // 필요한 동의항목: 카카오계정(이메일) + @JsonProperty("email") val email: String? = null, + // 프로필 정보 + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진), 닉네임, 프로필 사진 + @JsonProperty("profile") val profile: Profile? = null, +) { + data class Profile( + // 닉네임 + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 닉네임 + @JsonProperty("nickname") val nickname: String? = null, + // 프로필 미리보기 이미지 URL + // 110px * 110px 또는 100px * 100px + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 프로필 사진 + @JsonProperty("thumbnail_image_url") val thumbnailImageUrl: String? = null, + // 프로필 사진 URL + // 640px * 640px 또는 480px * 480px + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 프로필 사진 + @JsonProperty("profile_image_url") val profileImageUrl: String? = null, + // 프로필 사진 URL이 기본 프로필 사진 URL인지 여부 + // 사용자가 등록한 프로필 사진이 없을 경우, 기본 프로필 사진 제공 + // true: 기본 프로필 사진 / false: 사용자가 등록한 프로필 사진 + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 프로필 사진 + @JsonProperty("is_default_image") val isDefaultImage: Boolean? = null, + // 닉네임이 기본 닉네임인지 여부 + // 사용자가 등록한 닉네임이 운영정책에 부합하지 않는 경우, "닉네임을 등록해주세요"가 기본 닉네임으로 적용됨 + // true: 기본 닉네임 / false: 사용자가 등록한 닉네임 + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 닉네임 + @JsonProperty("is_default_nickname") val isDefaultNickname: Boolean? = null + ) +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt new file mode 100644 index 0000000..c489859 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt @@ -0,0 +1,19 @@ +package org.store.clothstar.kakaoLogin.vo + +import com.fasterxml.jackson.annotation.JsonProperty + +class Properties ( + // 닉네임 + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 닉네임 + @JsonProperty("nickname") val nickname: String? = null, + // 프로필 사진 URL + // 640px * 640px 또는 480px * 480px + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 프로필 사진 + @JsonProperty("profile_image") val profileImage: String? = null, + // 프로필 미리보기 이미지 URL + // 110px * 110px 또는 100px * 100px + // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 프로필 사진 + @JsonProperty("thumbnail_image") val thumbnailImage: String? = null, + // 테스트용 사용자 프로퍼티 + @JsonProperty("test_property") val testProperty: String? = null, +) \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index baf9cd8..bc3fa93 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,7 +49,6 @@ kakao: client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) admin_key: ENC(BmWFmOXxaXogq1sWkCEZu/OM2qCKiOfPfZSUCG5Xwokpm4XKKmLJMSud7+1vbP8M) - api.uri.base: https://kauth.kakao.com api.uri.user: /v2/user/me login.redirect_url: http://localhost:8080/auth/kakao/callback login.uri.token: /oauth/token From ec8883c31855ff8dcba30f63c2b15e4195a56acb Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 27 Aug 2024 17:52:53 +0900 Subject: [PATCH 08/42] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B0=B1?= =?UTF-8?q?=EC=8B=A0=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=EC=8B=9C=20=EC=9E=AC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=9A=94=EC=B2=AD=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 --- .../kakaoLogin/controller/KakaoController.kt | 31 ++++++-- .../dto/KakaoRenewTokenResponseDto.kt | 12 ++++ .../dto/KakaoTokenInfoResponseDto.kt | 16 +++++ .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 3 +- .../dto/TokenUserInfoResponseDto.kt | 6 ++ .../kakaoLogin/service/KakaoLoginService.kt | 70 +++++++++++++++++-- 6 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt create mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index 019f4f5..da2783b 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -4,19 +4,42 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +import org.store.clothstar.kakaoLogin.dto.KakaoRenewTokenResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoTokenInfoResponseDto import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto +import org.store.clothstar.kakaoLogin.dto.TokenUserInfoResponseDto import org.store.clothstar.kakaoLogin.service.KakaoLoginService @RestController class KakaoController( private val kakaoLoginService: KakaoLoginService, ) { + // 인가코드 받기 - 액세스 토큰 받기 - 사용자 정보 받기 @GetMapping("/auth/kakao/callback") - fun kakaoCallback(@RequestParam code: String): ResponseEntity { - // 액세스 토큰 받아오기 + fun kakaoCallback(@RequestParam code: String): ResponseEntity { + // 액세스 토큰 받아오기 - 저장 나중에 하기 val accessToken = kakaoLoginService.getAccessToken(code) - // 사용자 정보 받아오기 + // 사용자 정보 받아오기 - 저장 나중에 하기 val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) - return ResponseEntity.ok(userInfo) + // 두 정보를 KakaoTokenUserInfoResponseDto로 합쳐서 반환 + val tokenUserInfo = TokenUserInfoResponseDto( + accessToken = accessToken, + userInfo = userInfo + ) + return ResponseEntity.ok(tokenUserInfo) + } + + // 토큰 정보 확인 + @GetMapping("/auth/kakao/accessInfo") + fun getAccessInfo(@RequestParam accessToken: String): KakaoTokenInfoResponseDto { + val tokenInfo = kakaoLoginService.getTokenInfo(accessToken) + return tokenInfo + } + + // 액세스 토큰 갱신 - 유효성 검사 후 토큰 유효기간 만료 시 재로그인 요청 + @GetMapping("/auth/kakao/check") + fun checkToken(@RequestParam accessToken: String, @RequestParam refreshToken: String): ResponseEntity { + val renewTokenInfo: KakaoRenewTokenResponseDto = kakaoLoginService.validateToken(accessToken, refreshToken) + return ResponseEntity.ok(renewTokenInfo) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt new file mode 100644 index 0000000..272fc7b --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt @@ -0,0 +1,12 @@ +package org.store.clothstar.kakaoLogin.dto + +import com.fasterxml.jackson.annotation.JsonProperty + +class KakaoRenewTokenResponseDto { + // 토큰 타입, bearer로 고정 + @JsonProperty("token_type") private val tokenType: String? = null + // 갱신된 사용자 액세스 토큰 값 + @JsonProperty("access_token") val accessToken: String? = null + // 액세스 토큰 만료 시간(초) + @JsonProperty("expires_in") val expiresIn: Int? = null +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt new file mode 100644 index 0000000..358174e --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt @@ -0,0 +1,16 @@ +package org.store.clothstar.kakaoLogin.dto + +import com.fasterxml.jackson.annotation.JsonProperty + +class KakaoTokenInfoResponseDto { + // 액세스 토큰 만료 시간(밀리초) + @JsonProperty("expiresInMillis") private val expiresInMillis: Int? = null + // 회원번호 + @JsonProperty("id") private val id: String? = null + // 액세스 토큰 만료 시간(초) + @JsonProperty("expires_in") val expiresIn: Int? = null + // 앱 아이디 + @JsonProperty("app_id") val appId: Int? = null + // 앱 아이디 + @JsonProperty("appId") val appIdd: Int? = null +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt index f80ed75..eab4c01 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -14,6 +14,5 @@ class KakaoTokenResponseDto { // 리프레시 토큰 만료 시간(초) @JsonProperty("refresh_token_expires_in") private val refreshTokenExpiresIn: Int? = null // 인증된 사용자의 정보 조회 권한 범위 / 범위가 여러 개일 경우, 공백으로 구분 - @JsonProperty("scope") - private val scope: String? = null + @JsonProperty("scope") private val scope: String? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt new file mode 100644 index 0000000..2f9912f --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt @@ -0,0 +1,6 @@ +package org.store.clothstar.kakaoLogin.dto + +class TokenUserInfoResponseDto ( + val accessToken: KakaoTokenResponseDto, + val userInfo: KakaoUserInfoResponseDto +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index d68e19e..ca52edc 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -3,16 +3,17 @@ package org.store.clothstar.kakaoLogin.service import com.fasterxml.jackson.databind.ObjectMapper import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.beans.factory.annotation.Value +import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.stereotype.Service import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient -import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto -import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto +import org.store.clothstar.kakaoLogin.dto.* +@EnableScheduling @Service -class KakaoLoginService{ +class KakaoLoginService { private val logger = KotlinLogging.logger {} @@ -32,7 +33,7 @@ class KakaoLoginService{ private lateinit var userUri: String // 토큰 가져오기 - fun getAccessToken(code:String): KakaoTokenResponseDto { + fun getAccessToken(code: String): KakaoTokenResponseDto { // 토큰 요청 데이터 val params: MultiValueMap = LinkedMultiValueMap() params.add("code", code) @@ -53,14 +54,14 @@ class KakaoLoginService{ // json 응답을 객체로 변환 val objectMapper = ObjectMapper() - val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response,KakaoTokenResponseDto::class.java) + val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response, KakaoTokenResponseDto::class.java) logger.info { "Access Token : ${kakaoToken.accessToken}" } return kakaoToken } // 사용자 정보 가져오기 - fun getUserInfo(accessToken:String): KakaoUserInfoResponseDto { + fun getUserInfo(accessToken: String): KakaoUserInfoResponseDto { // 웹 클라이언트로 요청 보내기 val response = WebClient.create("https://kapi.kakao.com") .get() @@ -78,4 +79,59 @@ class KakaoLoginService{ logger.info { "email : ${userInfo.kakaoAccount!!.email}" } return userInfo } -} \ No newline at end of file + + // 토큰 유효성 검사 + fun validateToken(accessToken: String, refreshToken: String): KakaoRenewTokenResponseDto { + return try { + getTokenInfo(accessToken) + // 액세스 토큰이 유효한 경우 토큰 갱신 + refreshAccessToken(refreshToken) + } catch (e: Exception) { + // getTokenInfo에서 예외가 발생한 경우 재로그인 필요 + throw Exception("Access Token이 만료되었습니다. 재로그인이 필요합니다.") + } + } + + // 토큰 정보 보기 + fun getTokenInfo(accessToken: String): KakaoTokenInfoResponseDto { + val response = WebClient.create("https://kapi.kakao.com") + .get() + .uri("/v1/user/access_token_info") + .header("Authorization", "Bearer $accessToken") + .retrieve() + .bodyToMono(String::class.java) + .block() + + val objectMapper = ObjectMapper() + val tokenInfo: KakaoTokenInfoResponseDto = + objectMapper.readValue(response, KakaoTokenInfoResponseDto::class.java) + + logger.info { "Access Token 만료기한 : ${tokenInfo.expiresIn}" } + return tokenInfo + } + + // 리프레시 토큰으로 액세스 토큰 갱신 + fun refreshAccessToken(refreshToken: String): KakaoRenewTokenResponseDto { + val params: MultiValueMap = LinkedMultiValueMap() + params.add("grant_type", "refresh_token") + params.add("client_id", clientId) + params.add("refresh_token", refreshToken) + params.add("client_secret", clientSecret) + + val response = WebClient.create("https://kauth.kakao.com") + .post() + .uri(tokenUri) + .body(BodyInserters.fromFormData(params)) + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .retrieve() + .bodyToMono(String::class.java) + .block() + + val objectMapper = ObjectMapper() + val kakaoToken: KakaoRenewTokenResponseDto = + objectMapper.readValue(response, KakaoRenewTokenResponseDto::class.java) + + logger.info { "갱신된 Access Token : ${kakaoToken.accessToken}" } + return kakaoToken + } + } \ No newline at end of file From 961a079bda7790f93fe38f28eb7a819340571f73 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 03:46:30 +0900 Subject: [PATCH 09/42] =?UTF-8?q?feat:=20signUpService=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/MemberServiceApplication.kt | 2 +- .../controller/AuthenticationController.kt | 30 +++++++++++++++++-- .../authentication/domain/SignUpType.kt | 6 ++++ .../service/NormalSignUpService.kt | 15 ++++++++++ .../authentication/service/SignUpService.kt | 5 ++++ .../service/SignUpServiceFactory.kt | 20 +++++++++++++ src/main/resources/order.sql | 2 ++ 7 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/member/authentication/domain/SignUpType.kt create mode 100644 src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt create mode 100644 src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpService.kt create mode 100644 src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt diff --git a/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt b/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt index 0f3a0c0..5d8d9a3 100644 --- a/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt +++ b/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt @@ -49,7 +49,7 @@ class MemberServiceApplication( fun signUp(createMemberRequest: CreateMemberRequest): Long { //인증번호 확인 - authenticationService.verifyEmailCertifyNum(createMemberRequest.email, createMemberRequest.certifyNum) +// authenticationService.verifyEmailCertifyNum(createMemberRequest.email, createMemberRequest.certifyNum) val memberId = memberService.saveMember(createMemberRequest) accountService.saveAccount(memberId, createMemberRequest) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index dd7a014..1d79eed 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -15,6 +15,10 @@ import org.store.clothstar.common.dto.ErrorResponseDTO import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO import org.store.clothstar.member.application.MemberServiceApplication +import org.store.clothstar.member.authentication.domain.SignUpType +import org.store.clothstar.member.authentication.service.NormalSignUpService +import org.store.clothstar.member.authentication.service.SignUpService +import org.store.clothstar.member.authentication.service.SignUpServiceFactory import org.store.clothstar.member.dto.request.CertifyNumRequest import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.dto.request.MemberLoginRequest @@ -25,6 +29,8 @@ import org.store.clothstar.member.dto.response.MemberResponse @RestController class AuthenticationController( private val memberServiceApplication: MemberServiceApplication, + private val normalSignUpService: NormalSignUpService, + private val signUpServiceFactory: SignUpServiceFactory, ) { private val log = KotlinLogging.logger {} @@ -62,8 +68,28 @@ class AuthenticationController( @Operation(summary = "회원가입", description = "회원가입시 회원 정보를 저장한다.") @PostMapping("/v1/members") - fun signup(@Validated @RequestBody createMemberDTO: CreateMemberRequest): ResponseEntity { - val memberId = memberServiceApplication.signUp(createMemberDTO) + fun signup(@Validated @RequestBody createMemberDTO: CreateMemberRequest?, + @RequestParam signUpType: SignUpType, + @RequestParam(required = false) code: String?, + @RequestParam(required = false) name: String?, + @RequestParam(required = false) telNo: String?, + ): ResponseEntity { +// val memberId = normalSignUpService.signUp(createMemberDTO) +// memberServiceApplication.signUp(createMemberDTO) + + val signUpService = signUpServiceFactory.getSignUpService(signUpType) + log.info{ "사인업서비스종류:"+signUpService} + + val memberId = when (signUpService) { + is NormalSignUpService -> { + if (createMemberDTO == null) { + throw IllegalArgumentException("일반 회원가입 시 회원 정보가 필요합니다.") + } + signUpService.signUp(createMemberDTO) + } + else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") + } + val saveResponseDTO = SaveResponseDTO( id = memberId, diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/domain/SignUpType.kt b/src/main/kotlin/org/store/clothstar/member/authentication/domain/SignUpType.kt new file mode 100644 index 0000000..db51178 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/authentication/domain/SignUpType.kt @@ -0,0 +1,6 @@ +package org.store.clothstar.member.authentication.domain + +enum class SignUpType { + NORMAL, + KAKAO, +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt new file mode 100644 index 0000000..3b94eaf --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -0,0 +1,15 @@ +package org.store.clothstar.member.authentication.service + +import org.springframework.stereotype.Component +import org.springframework.stereotype.Service +import org.store.clothstar.member.application.MemberServiceApplication +import org.store.clothstar.member.dto.request.CreateMemberRequest + +@Service("normalSignUpService") +class NormalSignUpService( + private val memberServiceApplication: MemberServiceApplication +): SignUpService { + override fun signUp(request: CreateMemberRequest): Long { + return memberServiceApplication.signUp(request) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpService.kt new file mode 100644 index 0000000..8351f00 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpService.kt @@ -0,0 +1,5 @@ +package org.store.clothstar.member.authentication.service + +interface SignUpService { + fun signUp(request: T): Long +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt new file mode 100644 index 0000000..bca773f --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt @@ -0,0 +1,20 @@ +package org.store.clothstar.member.authentication.service + +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.stereotype.Component +import org.store.clothstar.member.authentication.domain.SignUpType +import org.store.clothstar.member.dto.request.CreateMemberRequest + +@Component +class SignUpServiceFactory( + @Qualifier("normalSignUpService") private val normalSignUpService: SignUpService, +// @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService +) { + fun getSignUpService(signUpType: SignUpType): SignUpService<*> { + return when (signUpType) { + SignUpType.NORMAL -> normalSignUpService +// SignUpType.KAKAO -> kakaoSignUpService + else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") + } + } +} \ No newline at end of file diff --git a/src/main/resources/order.sql b/src/main/resources/order.sql index 260568b..6a8c8a0 100644 --- a/src/main/resources/order.sql +++ b/src/main/resources/order.sql @@ -77,6 +77,8 @@ from address; select * from member; select * +from account; +select * from seller; select * from category; \ No newline at end of file From b260800f41535fd7cb2b018269d7584de42d48b5 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 04:10:55 +0900 Subject: [PATCH 10/42] =?UTF-8?q?feat:=20normalSignUpService,=20kakaoSignU?= =?UTF-8?q?pService=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthenticationController.kt | 16 ++++++++++-- .../service/KakaoSignUpService.kt | 25 +++++++++++++++++++ .../service/NormalSignUpService.kt | 3 +++ .../service/SignUpServiceFactory.kt | 4 +-- 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 1d79eed..2536e31 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -16,6 +16,7 @@ import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.authentication.domain.SignUpType +import org.store.clothstar.member.authentication.service.KakaoSignUpService import org.store.clothstar.member.authentication.service.NormalSignUpService import org.store.clothstar.member.authentication.service.SignUpService import org.store.clothstar.member.authentication.service.SignUpServiceFactory @@ -87,16 +88,27 @@ class AuthenticationController( } signUpService.signUp(createMemberDTO) } + is KakaoSignUpService -> { +// if (name == null || telNo == null) { +// throw IllegalArgumentException("카카오 회원가입 시 모든 정보가 필요합니다.") +// } +// val kakaoMemberDTO = CreateMemberRequest( +// email = kakaoUserInfo.kakaoAccount!!.email, +// name = name, +// telNo = telNo, +// password = "OAuth2_Kakao", +// certifyNum = "11" +// ) + signUpService.signUp(createMemberDTO!!) + } else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } - val saveResponseDTO = SaveResponseDTO( id = memberId, statusCode = HttpStatus.CREATED.value(), message = "회원가입이 정상적으로 되었습니다.", ) - return ResponseEntity(saveResponseDTO, HttpStatus.CREATED) } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt new file mode 100644 index 0000000..3ee9cc3 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -0,0 +1,25 @@ +package org.store.clothstar.member.authentication.service + +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Service +import org.store.clothstar.member.application.MemberServiceApplication +import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.service.AccountService +import org.store.clothstar.member.service.MemberService + +@Service("kakaoSignUpService") +class KakaoSignUpService( + private val memberService: MemberService, + private val accountService: AccountService +): SignUpService { + private val log = KotlinLogging.logger {} + + override fun signUp(request: CreateMemberRequest): Long { + val memberId = memberService.saveMember(request) + accountService.saveAccount(memberId, request) + + log.info { "KAKAOSIGNUPSERVICE 입니다입니다입니다" } + + return memberId + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt index 3b94eaf..3d84798 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -1,9 +1,12 @@ package org.store.clothstar.member.authentication.service +import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Component import org.springframework.stereotype.Service import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.service.AccountService +import org.store.clothstar.member.service.MemberService @Service("normalSignUpService") class NormalSignUpService( diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt index bca773f..1cf0616 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt @@ -8,12 +8,12 @@ import org.store.clothstar.member.dto.request.CreateMemberRequest @Component class SignUpServiceFactory( @Qualifier("normalSignUpService") private val normalSignUpService: SignUpService, -// @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService + @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService ) { fun getSignUpService(signUpType: SignUpType): SignUpService<*> { return when (signUpType) { SignUpType.NORMAL -> normalSignUpService -// SignUpType.KAKAO -> kakaoSignUpService + SignUpType.KAKAO -> kakaoSignUpService else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } } From 24b4ba175982c5ce4a477a3f16b0df334978f2cc Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 04:50:55 +0900 Subject: [PATCH 11/42] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=9A=94=EC=B2=ADDTO=EB=A5=BC=20CreateMemberReques?= =?UTF-8?q?t=EC=99=80=20KakaoMemberRequest=EB=A1=9C=20=EB=82=98=EB=88=A0?= =?UTF-8?q?=EB=B0=9B=EC=9D=84=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfiguration.kt | 1 + .../controller/AuthenticationController.kt | 29 +++++++------------ .../service/KakaoSignUpService.kt | 19 ++++++++---- .../service/NormalSignUpService.kt | 8 +++-- .../service/SignUpServiceFactory.kt | 3 +- .../dto/request/CreateKakaoMemberRequest.kt | 16 ++++++++++ .../member/dto/request/KakaoMemberRequest.kt | 12 ++++++++ .../member/dto/request/SignUpRequest.kt | 6 ++++ 8 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt create mode 100644 src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt create mode 100644 src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index 406fc45..f01adca 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -70,6 +70,7 @@ class SecurityConfiguration( "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "config-service/**" + "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO" ).permitAll() .requestMatchers(HttpMethod.POST, "/v1/members").permitAll() .requestMatchers(HttpMethod.POST, "/v1/sellers/**").authenticated() diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 2536e31..cf5f08a 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -20,17 +20,13 @@ import org.store.clothstar.member.authentication.service.KakaoSignUpService import org.store.clothstar.member.authentication.service.NormalSignUpService import org.store.clothstar.member.authentication.service.SignUpService import org.store.clothstar.member.authentication.service.SignUpServiceFactory -import org.store.clothstar.member.dto.request.CertifyNumRequest -import org.store.clothstar.member.dto.request.CreateMemberRequest -import org.store.clothstar.member.dto.request.MemberLoginRequest -import org.store.clothstar.member.dto.request.ModifyPasswordRequest +import org.store.clothstar.member.dto.request.* import org.store.clothstar.member.dto.response.MemberResponse @Tag(name = "Auth", description = "회원가입과 인증에 관한 API 입니다.") @RestController class AuthenticationController( private val memberServiceApplication: MemberServiceApplication, - private val normalSignUpService: NormalSignUpService, private val signUpServiceFactory: SignUpServiceFactory, ) { private val log = KotlinLogging.logger {} @@ -69,29 +65,21 @@ class AuthenticationController( @Operation(summary = "회원가입", description = "회원가입시 회원 정보를 저장한다.") @PostMapping("/v1/members") - fun signup(@Validated @RequestBody createMemberDTO: CreateMemberRequest?, + fun signup(@Validated @RequestBody signUpRequest: SignUpRequest, @RequestParam signUpType: SignUpType, - @RequestParam(required = false) code: String?, - @RequestParam(required = false) name: String?, - @RequestParam(required = false) telNo: String?, ): ResponseEntity { -// val memberId = normalSignUpService.signUp(createMemberDTO) -// memberServiceApplication.signUp(createMemberDTO) - val signUpService = signUpServiceFactory.getSignUpService(signUpType) - log.info{ "사인업서비스종류:"+signUpService} + log.info{ "사인업서비스종류: $signUpService" } val memberId = when (signUpService) { is NormalSignUpService -> { - if (createMemberDTO == null) { + if (signUpRequest.createMemberRequest == null) { throw IllegalArgumentException("일반 회원가입 시 회원 정보가 필요합니다.") } - signUpService.signUp(createMemberDTO) + signUpService.signUp(signUpRequest.createMemberRequest) } is KakaoSignUpService -> { -// if (name == null || telNo == null) { -// throw IllegalArgumentException("카카오 회원가입 시 모든 정보가 필요합니다.") -// } + // val kakaoMemberDTO = CreateMemberRequest( // email = kakaoUserInfo.kakaoAccount!!.email, // name = name, @@ -99,7 +87,10 @@ class AuthenticationController( // password = "OAuth2_Kakao", // certifyNum = "11" // ) - signUpService.signUp(createMemberDTO!!) +// if (signUpRequest.kakaoMemberRequest == null) { +// throw IllegalArgumentException("카카오 회원가입 시 회원 정보가 필요합니다.") +// } + signUpService.signUp(signUpRequest.kakaoMemberRequest!!) } else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index 3ee9cc3..47de449 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -2,8 +2,8 @@ package org.store.clothstar.member.authentication.service import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service -import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.service.AccountService import org.store.clothstar.member.service.MemberService @@ -11,12 +11,21 @@ import org.store.clothstar.member.service.MemberService class KakaoSignUpService( private val memberService: MemberService, private val accountService: AccountService -): SignUpService { +): SignUpService { private val log = KotlinLogging.logger {} - override fun signUp(request: CreateMemberRequest): Long { - val memberId = memberService.saveMember(request) - accountService.saveAccount(memberId, request) + override fun signUp(request: KakaoMemberRequest): Long { + + val memberRequestDTO = CreateMemberRequest( + email = "asdasd@gmail.com", + password = "123123123", + name = request.name, + telNo = request.telNo, + certifyNum = "1111", + ) + + val memberId = memberService.saveMember(memberRequestDTO) + accountService.saveAccount(memberId, memberRequestDTO) log.info { "KAKAOSIGNUPSERVICE 입니다입니다입니다" } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt index 3d84798..603c23c 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -1,18 +1,20 @@ package org.store.clothstar.member.authentication.service import io.github.oshai.kotlinlogging.KotlinLogging -import org.springframework.stereotype.Component import org.springframework.stereotype.Service import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.dto.request.CreateMemberRequest -import org.store.clothstar.member.service.AccountService -import org.store.clothstar.member.service.MemberService @Service("normalSignUpService") class NormalSignUpService( private val memberServiceApplication: MemberServiceApplication ): SignUpService { + private val log = KotlinLogging.logger {} + override fun signUp(request: CreateMemberRequest): Long { + + log.info { "NORMALSIGNUPSERVICE 입니다입니다입니다" } + return memberServiceApplication.signUp(request) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt index 1cf0616..a0f2cb6 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt @@ -4,11 +4,12 @@ import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest @Component class SignUpServiceFactory( @Qualifier("normalSignUpService") private val normalSignUpService: SignUpService, - @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService + @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService ) { fun getSignUpService(signUpType: SignUpType): SignUpService<*> { return when (signUpType) { diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt new file mode 100644 index 0000000..64bb0ed --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt @@ -0,0 +1,16 @@ +package org.store.clothstar.member.dto.request + +import jakarta.validation.constraints.Email +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern + +class CreateKakaoMemberRequest ( + @field: Email(message = "유효하지 않은 이메일 형식입니다.") + val email: String, + + @field: NotBlank(message = "이름은 비어 있을 수 없습니다.") + val name: String, + + @field: Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효하지 않은 전화번호 형식입니다.") + val telNo: String, + ) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt new file mode 100644 index 0000000..d0df69d --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt @@ -0,0 +1,12 @@ +package org.store.clothstar.member.dto.request + +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern + +class KakaoMemberRequest ( + @field: NotBlank(message = "이름은 비어 있을 수 없습니다.") + val name: String, + + @field: Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효하지 않은 전화번호 형식입니다.") + val telNo: String, +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt new file mode 100644 index 0000000..ba20cd1 --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt @@ -0,0 +1,6 @@ +package org.store.clothstar.member.dto.request + +class SignUpRequest ( + val createMemberRequest: CreateMemberRequest?, + val kakaoMemberRequest: KakaoMemberRequest? +) \ No newline at end of file From 810f182bac881cc4cf7888fd74d09f75ecd22d25 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 14:38:26 +0900 Subject: [PATCH 12/42] =?UTF-8?q?feat:=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=20=EB=B6=84=EB=A6=AC=ED=9B=84,=20?= =?UTF-8?q?=EC=B9=B4=EC=B9=B4=EC=98=A4=20=EA=B3=84=EC=A0=95=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=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 --- .../kakaoLogin/controller/KakaoController.kt | 20 +++++++-------- .../kakaoLogin/service/KakaoLoginService.kt | 6 +++++ .../controller/AuthenticationController.kt | 25 +++++++++++-------- .../service/KakaoSignUpService.kt | 21 ++++++++++++++-- .../member/dto/request/KakaoMemberRequest.kt | 10 +++++++- .../member/dto/request/SignUpRequest.kt | 2 +- 6 files changed, 59 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index da2783b..0562b6f 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -16,17 +16,17 @@ class KakaoController( ) { // 인가코드 받기 - 액세스 토큰 받기 - 사용자 정보 받기 @GetMapping("/auth/kakao/callback") - fun kakaoCallback(@RequestParam code: String): ResponseEntity { + fun kakaoCallback(@RequestParam code: String): ResponseEntity { // 액세스 토큰 받아오기 - 저장 나중에 하기 - val accessToken = kakaoLoginService.getAccessToken(code) - // 사용자 정보 받아오기 - 저장 나중에 하기 - val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) - // 두 정보를 KakaoTokenUserInfoResponseDto로 합쳐서 반환 - val tokenUserInfo = TokenUserInfoResponseDto( - accessToken = accessToken, - userInfo = userInfo - ) - return ResponseEntity.ok(tokenUserInfo) +// val accessToken = kakaoLoginService.getAccessToken(code) +// // 사용자 정보 받아오기 - 저장 나중에 하기 +// val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) +// // 두 정보를 KakaoTokenUserInfoResponseDto로 합쳐서 반환 +// val tokenUserInfo = TokenUserInfoResponseDto( +// accessToken = accessToken, +// userInfo = userInfo +// ) + return ResponseEntity.ok(code) } // 토큰 정보 확인 diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index ca52edc..69d2c6b 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -42,6 +42,8 @@ class KakaoLoginService { params.add("grant_type", "authorization_code") params.add("redirect_url", redirectUri) + logger.info { "Requesting token with params: $params" } + // 웹 클라이언트로 요청 보내기 val response = WebClient.create("https://kauth.kakao.com") .post() @@ -52,6 +54,8 @@ class KakaoLoginService { .bodyToMono(String::class.java) .block() + logger.info { "Token response: $response" } + // json 응답을 객체로 변환 val objectMapper = ObjectMapper() val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response, KakaoTokenResponseDto::class.java) @@ -72,6 +76,8 @@ class KakaoLoginService { .bodyToMono(String::class.java) .block() + logger.info { "User info response: $response" } + // json 응답을 객체로 변환 val objectMapper = ObjectMapper() val userInfo = objectMapper.readValue(response, KakaoUserInfoResponseDto::class.java) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index cf5f08a..d1f6cc2 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.* import org.store.clothstar.common.dto.ErrorResponseDTO import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO +import org.store.clothstar.kakaoLogin.dto.TokenUserInfoResponseDto +import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.authentication.service.KakaoSignUpService @@ -28,6 +30,7 @@ import org.store.clothstar.member.dto.response.MemberResponse class AuthenticationController( private val memberServiceApplication: MemberServiceApplication, private val signUpServiceFactory: SignUpServiceFactory, + private val kakaoLoginService: KakaoLoginService, ) { private val log = KotlinLogging.logger {} @@ -67,6 +70,7 @@ class AuthenticationController( @PostMapping("/v1/members") fun signup(@Validated @RequestBody signUpRequest: SignUpRequest, @RequestParam signUpType: SignUpType, + @RequestParam code: String, ): ResponseEntity { val signUpService = signUpServiceFactory.getSignUpService(signUpType) log.info{ "사인업서비스종류: $signUpService" } @@ -79,18 +83,17 @@ class AuthenticationController( signUpService.signUp(signUpRequest.createMemberRequest) } is KakaoSignUpService -> { + // 액세스 토큰 받아오기 + val accessToken = kakaoLoginService.getAccessToken(code) -// val kakaoMemberDTO = CreateMemberRequest( -// email = kakaoUserInfo.kakaoAccount!!.email, -// name = name, -// telNo = telNo, -// password = "OAuth2_Kakao", -// certifyNum = "11" -// ) -// if (signUpRequest.kakaoMemberRequest == null) { -// throw IllegalArgumentException("카카오 회원가입 시 회원 정보가 필요합니다.") -// } - signUpService.signUp(signUpRequest.kakaoMemberRequest!!) + // 사용자 정보 받아오기 + val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) + + // kakaoMemberRequest의 이메일 필드 업데이트 + val updatedKakaoMemberRequest = signUpRequest.kakaoMemberRequest!!.addEmail(userInfo.kakaoAccount!!.email!!) + +// signUpService.signUp(signUpRequest.kakaoMemberRequest!!) + signUpService.signUp(updatedKakaoMemberRequest) } else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index 47de449..ed669c0 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -2,6 +2,8 @@ package org.store.clothstar.member.authentication.service import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service +import org.store.clothstar.kakaoLogin.service.KakaoLoginService +import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.service.AccountService @@ -10,14 +12,29 @@ import org.store.clothstar.member.service.MemberService @Service("kakaoSignUpService") class KakaoSignUpService( private val memberService: MemberService, - private val accountService: AccountService + private val accountService: AccountService, + private val kakaoLoginService: KakaoLoginService, ): SignUpService { private val log = KotlinLogging.logger {} override fun signUp(request: KakaoMemberRequest): Long { +// // 액세스 토큰 받아오기 - 저장 나중에 하기 +// val accessToken = kakaoLoginService.getAccessToken(request.code) +// // 사용자 정보 받아오기 - 저장 나중에 하기 +// val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) +// +// // kakaoMemberRequest의 이메일 필드 업데이트 +// val updatedKakaoMemberRequest = request.addEmail(userInfo.kakaoAccount!!.email!!) + +// val memberKakaoRequestDTO = CreateKakaoMemberRequest( +// email = request.email!!, +// name = request.name, +// telNo = request.telNo, +// ) + val memberRequestDTO = CreateMemberRequest( - email = "asdasd@gmail.com", + email = request.email!!, password = "123123123", name = request.name, telNo = request.telNo, diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt index d0df69d..47a91f6 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt @@ -9,4 +9,12 @@ class KakaoMemberRequest ( @field: Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효하지 않은 전화번호 형식입니다.") val telNo: String, -) \ No newline at end of file + +// val code: String, + + val email: String?, +) { + fun addEmail(email: String): KakaoMemberRequest { + return KakaoMemberRequest(name, telNo, email) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt index ba20cd1..03516d8 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt @@ -2,5 +2,5 @@ package org.store.clothstar.member.dto.request class SignUpRequest ( val createMemberRequest: CreateMemberRequest?, - val kakaoMemberRequest: KakaoMemberRequest? + var kakaoMemberRequest: KakaoMemberRequest? ) \ No newline at end of file From 95a38fbb051b2fd8e5a462fddbebd6ea2e8c6d49 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 15:02:00 +0900 Subject: [PATCH 13/42] =?UTF-8?q?feat:=20Member,=20Account=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=EC=97=90=EC=84=9C=20kakao=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20=EA=B4=80=EB=A0=A8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80,=20Account=EC=9D=98=20p?= =?UTF-8?q?assword=20nullable=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthenticationController.kt | 6 ++--- .../domain/CustomUserDetails.kt | 2 +- .../service/KakaoSignUpService.kt | 24 ++++--------------- .../store/clothstar/member/domain/Account.kt | 2 +- .../member/dto/request/KakaoMemberRequest.kt | 2 -- .../member/service/AccountService.kt | 2 ++ .../member/service/AccountServiceImpl.kt | 17 +++++++++++++ .../clothstar/member/service/MemberService.kt | 2 ++ .../member/service/MemberServiceImpl.kt | 21 +++++++++++++++- 9 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index d1f6cc2..5d1f2f6 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -70,7 +70,7 @@ class AuthenticationController( @PostMapping("/v1/members") fun signup(@Validated @RequestBody signUpRequest: SignUpRequest, @RequestParam signUpType: SignUpType, - @RequestParam code: String, + @RequestParam code: String?, ): ResponseEntity { val signUpService = signUpServiceFactory.getSignUpService(signUpType) log.info{ "사인업서비스종류: $signUpService" } @@ -84,7 +84,7 @@ class AuthenticationController( } is KakaoSignUpService -> { // 액세스 토큰 받아오기 - val accessToken = kakaoLoginService.getAccessToken(code) + val accessToken = kakaoLoginService.getAccessToken(code!!) // 사용자 정보 받아오기 val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) @@ -92,7 +92,7 @@ class AuthenticationController( // kakaoMemberRequest의 이메일 필드 업데이트 val updatedKakaoMemberRequest = signUpRequest.kakaoMemberRequest!!.addEmail(userInfo.kakaoAccount!!.email!!) -// signUpService.signUp(signUpRequest.kakaoMemberRequest!!) + // 카카오 회원 가입 signUpService.signUp(updatedKakaoMemberRequest) } else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/domain/CustomUserDetails.kt b/src/main/kotlin/org/store/clothstar/member/authentication/domain/CustomUserDetails.kt index 801af74..3c5bfb2 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/domain/CustomUserDetails.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/domain/CustomUserDetails.kt @@ -13,7 +13,7 @@ class CustomUserDetails( } override fun getPassword(): String { - return account.password + return account.password!! } override fun getUsername(): String { diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index ed669c0..9fec972 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -19,32 +19,16 @@ class KakaoSignUpService( override fun signUp(request: KakaoMemberRequest): Long { -// // 액세스 토큰 받아오기 - 저장 나중에 하기 -// val accessToken = kakaoLoginService.getAccessToken(request.code) -// // 사용자 정보 받아오기 - 저장 나중에 하기 -// val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) -// -// // kakaoMemberRequest의 이메일 필드 업데이트 -// val updatedKakaoMemberRequest = request.addEmail(userInfo.kakaoAccount!!.email!!) - -// val memberKakaoRequestDTO = CreateKakaoMemberRequest( -// email = request.email!!, -// name = request.name, -// telNo = request.telNo, -// ) - - val memberRequestDTO = CreateMemberRequest( + val kakaoMemberRequestDTO = CreateKakaoMemberRequest( email = request.email!!, - password = "123123123", name = request.name, telNo = request.telNo, - certifyNum = "1111", ) - val memberId = memberService.saveMember(memberRequestDTO) - accountService.saveAccount(memberId, memberRequestDTO) + val memberId = memberService.saveKakaoMember(kakaoMemberRequestDTO) + accountService.saveKakaoAccount(memberId, kakaoMemberRequestDTO) - log.info { "KAKAOSIGNUPSERVICE 입니다입니다입니다" } + log.info { "KAKAOSIGNUPSERVICE 입니다" } return memberId } diff --git a/src/main/kotlin/org/store/clothstar/member/domain/Account.kt b/src/main/kotlin/org/store/clothstar/member/domain/Account.kt index 955fe23..7f09d98 100644 --- a/src/main/kotlin/org/store/clothstar/member/domain/Account.kt +++ b/src/main/kotlin/org/store/clothstar/member/domain/Account.kt @@ -11,7 +11,7 @@ class Account( @Column(unique = true) val email: String, //getter 자동 생성 - var password: String, + var password: String? = null, @Enumerated(EnumType.STRING) var role: MemberRole, diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt index 47a91f6..1184616 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt @@ -10,8 +10,6 @@ class KakaoMemberRequest ( @field: Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효하지 않은 전화번호 형식입니다.") val telNo: String, -// val code: String, - val email: String?, ) { fun addEmail(email: String): KakaoMemberRequest { diff --git a/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt b/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt index 11d489b..e66006e 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt @@ -2,10 +2,12 @@ package org.store.clothstar.member.service import org.store.clothstar.member.domain.Account import org.store.clothstar.member.domain.MemberRole +import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest interface AccountService { fun saveAccount(memberId: Long, createMemberDTO: CreateMemberRequest): Account + fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: CreateKakaoMemberRequest): Account fun updateRole(memberId: Long, findRole: MemberRole, updateRole: MemberRole) fun updateDeletedAt(memberId: Long, findRole: MemberRole) } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt b/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt index 6f1bc2c..3123e8e 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt @@ -8,6 +8,7 @@ import org.store.clothstar.common.error.exception.DuplicatedEmailException import org.store.clothstar.common.error.exception.NotFoundAccountException import org.store.clothstar.member.domain.Account import org.store.clothstar.member.domain.MemberRole +import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.repository.AccountRepository @@ -30,6 +31,22 @@ class AccountServiceImpl( return accountRepository.save(account) } + @Transactional + override fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: CreateKakaoMemberRequest): Account { + // 이메일 중복 검사 + accountRepository.findByEmail(createKakaoMemberDTO.email)?.let { + throw DuplicatedEmailException(ErrorCode.DUPLICATED_EMAIL) + } + + val account = Account( + email = createKakaoMemberDTO.email, + role = MemberRole.USER, + userId = memberId, + ) + + return accountRepository.save(account) + } + @Transactional override fun updateRole(memberId: Long, findRole: MemberRole, updateRole: MemberRole) { val account = accountRepository.findByUserIdAndRole(memberId, findRole) diff --git a/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt b/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt index 004f039..4b4bef2 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt @@ -4,6 +4,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.store.clothstar.member.domain.Member +import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.dto.request.ModifyNameRequest import org.store.clothstar.member.dto.response.MemberResponse @@ -24,6 +25,7 @@ interface MemberService { fun modifyName(memberId: Long, modifyNameRequest: ModifyNameRequest) fun saveMember(createMemberDTO: CreateMemberRequest): Long + fun saveKakaoMember(createKakaoMemberDTO: CreateKakaoMemberRequest): Long fun getMemberByMemberId(memberId: Long): Member } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt b/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt index 800e8b0..2313717 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt @@ -14,6 +14,7 @@ import org.store.clothstar.common.error.exception.DuplicatedTelNoException import org.store.clothstar.common.error.exception.NotFoundMemberException import org.store.clothstar.member.domain.Member import org.store.clothstar.member.domain.vo.MemberShoppingActivity +import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.dto.request.ModifyNameRequest import org.store.clothstar.member.dto.response.MemberResponse @@ -108,7 +109,7 @@ class MemberServiceImpl( ?: throw NotFoundMemberException(ErrorCode.NOT_FOUND_ACCOUNT) val encodedPassword = passwordEncoder.encode(password) - val originalPassword: String = account.password + val originalPassword: String = account.password!! //valid check if (!passwordEncoder.matches(originalPassword, encodedPassword)) { @@ -134,6 +135,24 @@ class MemberServiceImpl( return member.memberId!! } + @Transactional + override fun saveKakaoMember(createKakaoMemberDTO: CreateKakaoMemberRequest): Long { + // 전화번호 중복 검사 + memberRepository.findByTelNo(createKakaoMemberDTO.telNo)?.let { + throw DuplicatedTelNoException(ErrorCode.DUPLICATED_TEL_NO) + } + + val member = Member( + telNo = createKakaoMemberDTO.telNo, + name = createKakaoMemberDTO.name, + memberShoppingActivity = MemberShoppingActivity.init(), + ) + + memberRepository.save(member) + + return member.memberId!! + } + @Transactional(readOnly = true) fun signUpValidCheck(createMemberDTO: CreateMemberRequest) { memberRepository.findByTelNo(createMemberDTO.telNo)?.let { From accbb63d1741e9f3972f4bc1b5c97c400ec4db09 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 15:04:42 +0900 Subject: [PATCH 14/42] refactor: Optimize Imports, Reformat Code --- .../common/config/SecurityConfiguration.kt | 1 + .../kakaoLogin/controller/KakaoController.kt | 7 +- .../controller/KakaoLoginPageController.kt | 5 +- .../dto/KakaoRenewTokenResponseDto.kt | 11 ++- .../dto/KakaoTokenInfoResponseDto.kt | 19 +++-- .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 23 +++-- .../dto/KakaoUserInfoResponseDto.kt | 2 +- .../dto/TokenUserInfoResponseDto.kt | 2 +- .../kakaoLogin/service/KakaoLoginService.kt | 83 ++++++++++--------- .../clothstar/kakaoLogin/vo/KakaoAccount.kt | 2 +- .../clothstar/kakaoLogin/vo/Properties.kt | 2 +- .../controller/AuthenticationController.kt | 21 +++-- .../service/KakaoSignUpService.kt | 3 +- .../service/NormalSignUpService.kt | 2 +- .../dto/request/CreateKakaoMemberRequest.kt | 4 +- .../member/dto/request/KakaoMemberRequest.kt | 2 +- .../member/dto/request/SignUpRequest.kt | 2 +- .../order/OrderUserIntegrationTest.kt | 4 +- 18 files changed, 114 insertions(+), 81 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index f01adca..f40c26a 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -67,6 +67,7 @@ class SecurityConfiguration( "/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list", "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", + "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO" "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "config-service/**" diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index 0562b6f..c308dcb 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -6,8 +6,6 @@ import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.store.clothstar.kakaoLogin.dto.KakaoRenewTokenResponseDto import org.store.clothstar.kakaoLogin.dto.KakaoTokenInfoResponseDto -import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto -import org.store.clothstar.kakaoLogin.dto.TokenUserInfoResponseDto import org.store.clothstar.kakaoLogin.service.KakaoLoginService @RestController @@ -38,7 +36,10 @@ class KakaoController( // 액세스 토큰 갱신 - 유효성 검사 후 토큰 유효기간 만료 시 재로그인 요청 @GetMapping("/auth/kakao/check") - fun checkToken(@RequestParam accessToken: String, @RequestParam refreshToken: String): ResponseEntity { + fun checkToken( + @RequestParam accessToken: String, + @RequestParam refreshToken: String + ): ResponseEntity { val renewTokenInfo: KakaoRenewTokenResponseDto = kakaoLoginService.validateToken(accessToken, refreshToken) return ResponseEntity.ok(renewTokenInfo) } diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt index 65130ee..3709200 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt @@ -4,8 +4,6 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RestController @Controller class KakaoLoginPageController { @@ -18,7 +16,8 @@ class KakaoLoginPageController { @GetMapping("/kakaoLogin/page") fun loginPage(model: Model): String { - val location = "https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri" + val location = + "https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri" model.addAttribute("location", location) return "kakaoLogin" diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt index 272fc7b..be9eae9 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt @@ -4,9 +4,14 @@ import com.fasterxml.jackson.annotation.JsonProperty class KakaoRenewTokenResponseDto { // 토큰 타입, bearer로 고정 - @JsonProperty("token_type") private val tokenType: String? = null + @JsonProperty("token_type") + private val tokenType: String? = null + // 갱신된 사용자 액세스 토큰 값 - @JsonProperty("access_token") val accessToken: String? = null + @JsonProperty("access_token") + val accessToken: String? = null + // 액세스 토큰 만료 시간(초) - @JsonProperty("expires_in") val expiresIn: Int? = null + @JsonProperty("expires_in") + val expiresIn: Int? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt index 358174e..4e5bfb6 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt @@ -4,13 +4,22 @@ import com.fasterxml.jackson.annotation.JsonProperty class KakaoTokenInfoResponseDto { // 액세스 토큰 만료 시간(밀리초) - @JsonProperty("expiresInMillis") private val expiresInMillis: Int? = null + @JsonProperty("expiresInMillis") + private val expiresInMillis: Int? = null + // 회원번호 - @JsonProperty("id") private val id: String? = null + @JsonProperty("id") + private val id: String? = null + // 액세스 토큰 만료 시간(초) - @JsonProperty("expires_in") val expiresIn: Int? = null + @JsonProperty("expires_in") + val expiresIn: Int? = null + // 앱 아이디 - @JsonProperty("app_id") val appId: Int? = null + @JsonProperty("app_id") + val appId: Int? = null + // 앱 아이디 - @JsonProperty("appId") val appIdd: Int? = null + @JsonProperty("appId") + val appIdd: Int? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt index eab4c01..d80e075 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -4,15 +4,26 @@ import com.fasterxml.jackson.annotation.JsonProperty class KakaoTokenResponseDto { // 토큰 타입, bearer로 고정 - @JsonProperty("token_type") private val tokenType: String? = null + @JsonProperty("token_type") + private val tokenType: String? = null + // 사용자 액세스 토큰 값 - @JsonProperty("access_token") val accessToken: String? = null + @JsonProperty("access_token") + val accessToken: String? = null + // 액세스 토큰과 ID 토큰의 만료 시간(초) - @JsonProperty("expires_in") private val expiresIn: Int? = null + @JsonProperty("expires_in") + private val expiresIn: Int? = null + // 사용자 리프레시 토큰 값 - @JsonProperty("refresh_token") private val refreshToken: String? = null + @JsonProperty("refresh_token") + private val refreshToken: String? = null + // 리프레시 토큰 만료 시간(초) - @JsonProperty("refresh_token_expires_in") private val refreshTokenExpiresIn: Int? = null + @JsonProperty("refresh_token_expires_in") + private val refreshTokenExpiresIn: Int? = null + // 인증된 사용자의 정보 조회 권한 범위 / 범위가 여러 개일 경우, 공백으로 구분 - @JsonProperty("scope") private val scope: String? = null + @JsonProperty("scope") + private val scope: String? = null } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt index 5d5f89d..75b91c2 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoUserInfoResponseDto.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import org.store.clothstar.kakaoLogin.vo.KakaoAccount import org.store.clothstar.kakaoLogin.vo.Properties -class KakaoUserInfoResponseDto ( +class KakaoUserInfoResponseDto( // 회원번호 @JsonProperty("id") val id: Long? = null, // 서비스에 연결 완료된 시각, UTC* diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt index 2f9912f..8c7b47d 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt @@ -1,6 +1,6 @@ package org.store.clothstar.kakaoLogin.dto -class TokenUserInfoResponseDto ( +class TokenUserInfoResponseDto( val accessToken: KakaoTokenResponseDto, val userInfo: KakaoUserInfoResponseDto ) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index 69d2c6b..e79cdef 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -9,7 +9,10 @@ import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient -import org.store.clothstar.kakaoLogin.dto.* +import org.store.clothstar.kakaoLogin.dto.KakaoRenewTokenResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoTokenInfoResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto +import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto @EnableScheduling @Service @@ -100,44 +103,44 @@ class KakaoLoginService { // 토큰 정보 보기 fun getTokenInfo(accessToken: String): KakaoTokenInfoResponseDto { - val response = WebClient.create("https://kapi.kakao.com") - .get() - .uri("/v1/user/access_token_info") - .header("Authorization", "Bearer $accessToken") - .retrieve() - .bodyToMono(String::class.java) - .block() - - val objectMapper = ObjectMapper() - val tokenInfo: KakaoTokenInfoResponseDto = - objectMapper.readValue(response, KakaoTokenInfoResponseDto::class.java) - - logger.info { "Access Token 만료기한 : ${tokenInfo.expiresIn}" } - return tokenInfo + val response = WebClient.create("https://kapi.kakao.com") + .get() + .uri("/v1/user/access_token_info") + .header("Authorization", "Bearer $accessToken") + .retrieve() + .bodyToMono(String::class.java) + .block() + + val objectMapper = ObjectMapper() + val tokenInfo: KakaoTokenInfoResponseDto = + objectMapper.readValue(response, KakaoTokenInfoResponseDto::class.java) + + logger.info { "Access Token 만료기한 : ${tokenInfo.expiresIn}" } + return tokenInfo } - // 리프레시 토큰으로 액세스 토큰 갱신 - fun refreshAccessToken(refreshToken: String): KakaoRenewTokenResponseDto { - val params: MultiValueMap = LinkedMultiValueMap() - params.add("grant_type", "refresh_token") - params.add("client_id", clientId) - params.add("refresh_token", refreshToken) - params.add("client_secret", clientSecret) - - val response = WebClient.create("https://kauth.kakao.com") - .post() - .uri(tokenUri) - .body(BodyInserters.fromFormData(params)) - .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") - .retrieve() - .bodyToMono(String::class.java) - .block() - - val objectMapper = ObjectMapper() - val kakaoToken: KakaoRenewTokenResponseDto = - objectMapper.readValue(response, KakaoRenewTokenResponseDto::class.java) - - logger.info { "갱신된 Access Token : ${kakaoToken.accessToken}" } - return kakaoToken - } - } \ No newline at end of file + // 리프레시 토큰으로 액세스 토큰 갱신 + fun refreshAccessToken(refreshToken: String): KakaoRenewTokenResponseDto { + val params: MultiValueMap = LinkedMultiValueMap() + params.add("grant_type", "refresh_token") + params.add("client_id", clientId) + params.add("refresh_token", refreshToken) + params.add("client_secret", clientSecret) + + val response = WebClient.create("https://kauth.kakao.com") + .post() + .uri(tokenUri) + .body(BodyInserters.fromFormData(params)) + .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") + .retrieve() + .bodyToMono(String::class.java) + .block() + + val objectMapper = ObjectMapper() + val kakaoToken: KakaoRenewTokenResponseDto = + objectMapper.readValue(response, KakaoRenewTokenResponseDto::class.java) + + logger.info { "갱신된 Access Token : ${kakaoToken.accessToken}" } + return kakaoToken + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt index 6205a42..77694fc 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/KakaoAccount.kt @@ -2,7 +2,7 @@ package org.store.clothstar.kakaoLogin.vo import com.fasterxml.jackson.annotation.JsonProperty -class KakaoAccount ( +class KakaoAccount( // 사용자 동의 시 닉네임 제공 가능 // 필요한 동의항목: 닉네임 @JsonProperty("profile_nickname_needs_agreement") val profileNicknameNeedsAgreement: Boolean? = null, diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt index c489859..40ee579 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/vo/Properties.kt @@ -2,7 +2,7 @@ package org.store.clothstar.kakaoLogin.vo import com.fasterxml.jackson.annotation.JsonProperty -class Properties ( +class Properties( // 닉네임 // 필요한 동의항목: 프로필 정보(닉네임/프로필 사진) 또는 닉네임 @JsonProperty("nickname") val nickname: String? = null, diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 5d1f2f6..047179d 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -14,15 +14,16 @@ import org.springframework.web.bind.annotation.* import org.store.clothstar.common.dto.ErrorResponseDTO import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO -import org.store.clothstar.kakaoLogin.dto.TokenUserInfoResponseDto import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.authentication.service.KakaoSignUpService import org.store.clothstar.member.authentication.service.NormalSignUpService -import org.store.clothstar.member.authentication.service.SignUpService import org.store.clothstar.member.authentication.service.SignUpServiceFactory -import org.store.clothstar.member.dto.request.* +import org.store.clothstar.member.dto.request.CertifyNumRequest +import org.store.clothstar.member.dto.request.MemberLoginRequest +import org.store.clothstar.member.dto.request.ModifyPasswordRequest +import org.store.clothstar.member.dto.request.SignUpRequest import org.store.clothstar.member.dto.response.MemberResponse @Tag(name = "Auth", description = "회원가입과 인증에 관한 API 입니다.") @@ -68,12 +69,13 @@ class AuthenticationController( @Operation(summary = "회원가입", description = "회원가입시 회원 정보를 저장한다.") @PostMapping("/v1/members") - fun signup(@Validated @RequestBody signUpRequest: SignUpRequest, - @RequestParam signUpType: SignUpType, - @RequestParam code: String?, + fun signup( + @Validated @RequestBody signUpRequest: SignUpRequest, + @RequestParam signUpType: SignUpType, + @RequestParam code: String?, ): ResponseEntity { val signUpService = signUpServiceFactory.getSignUpService(signUpType) - log.info{ "사인업서비스종류: $signUpService" } + log.info { "사인업서비스종류: $signUpService" } val memberId = when (signUpService) { is NormalSignUpService -> { @@ -82,6 +84,7 @@ class AuthenticationController( } signUpService.signUp(signUpRequest.createMemberRequest) } + is KakaoSignUpService -> { // 액세스 토큰 받아오기 val accessToken = kakaoLoginService.getAccessToken(code!!) @@ -90,11 +93,13 @@ class AuthenticationController( val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) // kakaoMemberRequest의 이메일 필드 업데이트 - val updatedKakaoMemberRequest = signUpRequest.kakaoMemberRequest!!.addEmail(userInfo.kakaoAccount!!.email!!) + val updatedKakaoMemberRequest = + signUpRequest.kakaoMemberRequest!!.addEmail(userInfo.kakaoAccount!!.email!!) // 카카오 회원 가입 signUpService.signUp(updatedKakaoMemberRequest) } + else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index 9fec972..a64a276 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -4,7 +4,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest -import org.store.clothstar.member.dto.request.CreateMemberRequest import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.service.AccountService import org.store.clothstar.member.service.MemberService @@ -14,7 +13,7 @@ class KakaoSignUpService( private val memberService: MemberService, private val accountService: AccountService, private val kakaoLoginService: KakaoLoginService, -): SignUpService { +) : SignUpService { private val log = KotlinLogging.logger {} override fun signUp(request: KakaoMemberRequest): Long { diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt index 603c23c..34d156a 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -8,7 +8,7 @@ import org.store.clothstar.member.dto.request.CreateMemberRequest @Service("normalSignUpService") class NormalSignUpService( private val memberServiceApplication: MemberServiceApplication -): SignUpService { +) : SignUpService { private val log = KotlinLogging.logger {} override fun signUp(request: CreateMemberRequest): Long { diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt index 64bb0ed..4d72c23 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/CreateKakaoMemberRequest.kt @@ -4,7 +4,7 @@ import jakarta.validation.constraints.Email import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Pattern -class CreateKakaoMemberRequest ( +class CreateKakaoMemberRequest( @field: Email(message = "유효하지 않은 이메일 형식입니다.") val email: String, @@ -13,4 +13,4 @@ class CreateKakaoMemberRequest ( @field: Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효하지 않은 전화번호 형식입니다.") val telNo: String, - ) \ No newline at end of file +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt index 1184616..81d76db 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt @@ -3,7 +3,7 @@ package org.store.clothstar.member.dto.request import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Pattern -class KakaoMemberRequest ( +class KakaoMemberRequest( @field: NotBlank(message = "이름은 비어 있을 수 없습니다.") val name: String, diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt index 03516d8..304a950 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt @@ -1,6 +1,6 @@ package org.store.clothstar.member.dto.request -class SignUpRequest ( +class SignUpRequest( val createMemberRequest: CreateMemberRequest?, var kakaoMemberRequest: KakaoMemberRequest? ) \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt index b90c9b1..3556625 100644 --- a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt @@ -268,7 +268,7 @@ class OrderUserIntegrationTest( //then actions.andExpect(status().isOk()) .andExpect(jsonPath("$.message").value("주문이 정상적으로 취소되었습니다.")) - assertEquals(savedOrder!!.status,Status.CANCELED) + assertEquals(savedOrder!!.status, Status.CANCELED) } @DisplayName("주문 삭제 통합테스트") @@ -287,7 +287,7 @@ class OrderUserIntegrationTest( ) val savedOrder: Order? = orderRepository.findByIdOrNull(orderId) val deletedTime = LocalDateTime.now() - val timeDifference = ChronoUnit.SECONDS.between(deletedTime,savedOrder!!.deletedAt) + val timeDifference = ChronoUnit.SECONDS.between(deletedTime, savedOrder!!.deletedAt) //then actions.andExpect(status().isOk()) From 9549f3a634c624ec7666d3a3475b5d2f503a5955 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 19:31:24 +0900 Subject: [PATCH 15/42] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=84=EC=8B=9C=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfiguration.kt | 6 +- .../controller/AuthenticationController.kt | 3 + .../service/NormalSignUpService.kt | 2 +- .../AuthenticationControllerTest.kt | 132 ++++----- .../controller/MemberSignupValidTest.kt | 276 +++++++++--------- 5 files changed, 213 insertions(+), 206 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index f40c26a..c61fea2 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -71,7 +71,11 @@ class SecurityConfiguration( "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "config-service/**" - "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO" + "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", + "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", + "/v1/members?signUpType=KAKAO", + "/v1/members/**", + "/v1/members/auth/**", ).permitAll() .requestMatchers(HttpMethod.POST, "/v1/members").permitAll() .requestMatchers(HttpMethod.POST, "/v1/sellers/**").authenticated() diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 047179d..65623fa 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -86,9 +86,12 @@ class AuthenticationController( } is KakaoSignUpService -> { + log.info { "왜 아무것도 안나와" } // 액세스 토큰 받아오기 val accessToken = kakaoLoginService.getAccessToken(code!!) + log.info { "accressToken = ${accessToken.accessToken}" } + // 사용자 정보 받아오기 val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt index 34d156a..fd80b31 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -13,7 +13,7 @@ class NormalSignUpService( override fun signUp(request: CreateMemberRequest): Long { - log.info { "NORMALSIGNUPSERVICE 입니다입니다입니다" } + log.info { "NORMALSIGNUPSERVICE 입니다" } return memberServiceApplication.signUp(request) } diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index 7db7d04..2c425f0 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -1,66 +1,66 @@ -package org.store.clothstar.member.authentication.controller - -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.http.MediaType -import org.springframework.test.context.ActiveProfiles -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional -import org.store.clothstar.common.config.redis.RedisUtil -import org.store.clothstar.member.util.CreateObject - -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -@Transactional -class AuthenticationControllerTest( - @Autowired private val mockMvc: MockMvc, - @Autowired private val objectMapper: ObjectMapper, - @Autowired private val redisUtil: RedisUtil, -) { - private val MEMBER_URL = "/v1/members" - private val LOGIN_URL = "/v1/members/login" - - @DisplayName("멤버 회원가입 통합테스트후 로그인 통합테스트") - @Test - fun signUpIntegrationTest() { - //given - //이메일과 인증번호로 redis 데이터 생성 - val email = "test@naver.com" - val certifyNum = redisUtil.createdCertifyNum() - redisUtil.createRedisData(email, certifyNum) - - //이메일과 인증번호로 create DTO 객체 생성 - val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) - val requestBody = objectMapper.writeValueAsString(createMemberRequest) - - //when - val actions = mockMvc.perform( - post(MEMBER_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - - //then - actions.andExpect(status().isCreated) - - //로그인 테스트 - //given - val memberLoginRequest = CreateObject.getMemberLoginRequest() - val loginRequestBody = objectMapper.writeValueAsString(memberLoginRequest) - - val loginActions = mockMvc.perform( - post(LOGIN_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(loginRequestBody) - ) - - loginActions.andExpect(status().isOk) - } -} \ No newline at end of file +//package org.store.clothstar.member.authentication.controller +// +//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.http.MediaType +//import org.springframework.test.context.ActiveProfiles +//import org.springframework.test.web.servlet.MockMvc +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +//import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +//import org.springframework.transaction.annotation.Transactional +//import org.store.clothstar.common.config.redis.RedisUtil +//import org.store.clothstar.member.util.CreateObject +// +//@SpringBootTest +//@AutoConfigureMockMvc +//@ActiveProfiles("test") +//@Transactional +//class AuthenticationControllerTest( +// @Autowired private val mockMvc: MockMvc, +// @Autowired private val objectMapper: ObjectMapper, +// @Autowired private val redisUtil: RedisUtil, +//) { +// private val MEMBER_URL = "/v1/members" +// private val LOGIN_URL = "/v1/members/login" +// +// @DisplayName("멤버 회원가입 통합테스트후 로그인 통합테스트") +// @Test +// fun signUpIntegrationTest() { +// //given +// //이메일과 인증번호로 redis 데이터 생성 +// val email = "test@naver.com" +// val certifyNum = redisUtil.createdCertifyNum() +// redisUtil.createRedisData(email, certifyNum) +// +// //이메일과 인증번호로 create DTO 객체 생성 +// val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) +// val requestBody = objectMapper.writeValueAsString(createMemberRequest) +// +// //when +// val actions = mockMvc.perform( +// post(MEMBER_URL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// actions.andExpect(status().isCreated) +// +// //로그인 테스트 +// //given +// val memberLoginRequest = CreateObject.getMemberLoginRequest() +// val loginRequestBody = objectMapper.writeValueAsString(memberLoginRequest) +// +// val loginActions = mockMvc.perform( +// post(LOGIN_URL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(loginRequestBody) +// ) +// +// loginActions.andExpect(status().isOk) +// } +//} \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt index ae37402..75d577a 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt @@ -1,138 +1,138 @@ -package org.store.clothstar.member.authentication.controller - -import com.fasterxml.jackson.databind.ObjectMapper -import org.assertj.core.api.Assertions -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.http.MediaType -import org.springframework.security.test.context.support.WithMockUser -import org.springframework.test.context.ActiveProfiles -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.result.MockMvcResultMatchers -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import org.springframework.transaction.annotation.Transactional -import org.store.clothstar.common.config.redis.RedisUtil -import org.store.clothstar.member.dto.request.CreateMemberRequest -import org.store.clothstar.member.dto.request.ModifyPasswordRequest - -@SpringBootTest -@AutoConfigureMockMvc -@ActiveProfiles("test") -@Transactional -class MemberSignupValidTest( - @Autowired private val mockMvc: MockMvc, - @Autowired private val objectMapper: ObjectMapper, - @Autowired private val redisUtil: RedisUtil, -) { - private val MEMBER_URL = "/v1/members" - - @DisplayName("회원가입시 비밀번호는 최소 8자리 이상이여야 한다.") - @Test - @Throws(java.lang.Exception::class) - fun signUp_passwordValidationTest() { - //given - val createMemberRequest = CreateMemberRequest( - email = "test@test.com", - password = "1234567", - name = "현수", - telNo = "010-1234-1234", - certifyNum = "gg" - ) - - val requestBody = objectMapper.writeValueAsString(createMemberRequest) - - //when - val actions = mockMvc.perform( - post(MEMBER_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - - //then - actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) - } - - @DisplayName("회원가입시 이름은 필수 값이다.") - @Test - @Throws(java.lang.Exception::class) - fun signUp_nameValidationTest() { - //given - val createMemberRequest = CreateMemberRequest( - email = "test@test.com", - password = "1234567", - name = "", - telNo = "010-1234-1234", - certifyNum = "gg" - ) - - val requestBody = objectMapper.writeValueAsString(createMemberRequest) - - //when - val actions = mockMvc.perform( - post(MEMBER_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - - //then - Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) - actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.name").value("이름은 비어 있을 수 없습니다.")) - } - - @DisplayName("회원가입시 전화번호 양식이 지켜져야 한다.") - @Test - @Throws(java.lang.Exception::class) - fun signUp_telNoValidationTest() { - //given - val createMemberRequest = CreateMemberRequest( - email = "test@test.com", - password = "1234567", - name = "", - telNo = "010", - certifyNum = "gg" - ) - - val requestBody = objectMapper.writeValueAsString(createMemberRequest) - - //when - val actions = mockMvc.perform( - post(MEMBER_URL) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - - //then - Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) - actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.telNo").value("유효하지 않은 전화번호 형식입니다.")) - } - - @DisplayName("비밀번호 변경 요청시에도 비밀번호는 8자리 이상이여야 한다.") - @WithMockUser(username = "현수", roles = ["USER"]) - @Test - @Throws(java.lang.Exception::class) - fun modifyPassword_validCheckTest() { - //given - val modifyPasswordURL = "/v1/members/pass/1" - val modifyPasswordRequest = ModifyPasswordRequest("1") - val requestBody = objectMapper.writeValueAsString(modifyPasswordRequest) - - //when - val actions = mockMvc.perform( - MockMvcRequestBuilders.patch(modifyPasswordURL) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - - //then - actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) - } -} \ No newline at end of file +//package org.store.clothstar.member.authentication.controller +// +//import com.fasterxml.jackson.databind.ObjectMapper +//import org.assertj.core.api.Assertions +//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.http.MediaType +//import org.springframework.security.test.context.support.WithMockUser +//import org.springframework.test.context.ActiveProfiles +//import org.springframework.test.web.servlet.MockMvc +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +//import org.springframework.test.web.servlet.result.MockMvcResultMatchers +//import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +//import org.springframework.transaction.annotation.Transactional +//import org.store.clothstar.common.config.redis.RedisUtil +//import org.store.clothstar.member.dto.request.CreateMemberRequest +//import org.store.clothstar.member.dto.request.ModifyPasswordRequest +// +//@SpringBootTest +//@AutoConfigureMockMvc +//@ActiveProfiles("test") +//@Transactional +//class MemberSignupValidTest( +// @Autowired private val mockMvc: MockMvc, +// @Autowired private val objectMapper: ObjectMapper, +// @Autowired private val redisUtil: RedisUtil, +//) { +// private val MEMBER_URL = "/v1/members" +// +// @DisplayName("회원가입시 비밀번호는 최소 8자리 이상이여야 한다.") +// @Test +// @Throws(java.lang.Exception::class) +// fun signUp_passwordValidationTest() { +// //given +// val createMemberRequest = CreateMemberRequest( +// email = "test@test.com", +// password = "1234567", +// name = "현수", +// telNo = "010-1234-1234", +// certifyNum = "gg" +// ) +// +// val requestBody = objectMapper.writeValueAsString(createMemberRequest) +// +// //when +// val actions = mockMvc.perform( +// post(MEMBER_URL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// actions.andExpect(status().is4xxClientError()) +// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) +// } +// +// @DisplayName("회원가입시 이름은 필수 값이다.") +// @Test +// @Throws(java.lang.Exception::class) +// fun signUp_nameValidationTest() { +// //given +// val createMemberRequest = CreateMemberRequest( +// email = "test@test.com", +// password = "1234567", +// name = "", +// telNo = "010-1234-1234", +// certifyNum = "gg" +// ) +// +// val requestBody = objectMapper.writeValueAsString(createMemberRequest) +// +// //when +// val actions = mockMvc.perform( +// post(MEMBER_URL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) +// actions.andExpect(status().is4xxClientError()) +// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.name").value("이름은 비어 있을 수 없습니다.")) +// } +// +// @DisplayName("회원가입시 전화번호 양식이 지켜져야 한다.") +// @Test +// @Throws(java.lang.Exception::class) +// fun signUp_telNoValidationTest() { +// //given +// val createMemberRequest = CreateMemberRequest( +// email = "test@test.com", +// password = "1234567", +// name = "", +// telNo = "010", +// certifyNum = "gg" +// ) +// +// val requestBody = objectMapper.writeValueAsString(createMemberRequest) +// +// //when +// val actions = mockMvc.perform( +// post(MEMBER_URL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) +// actions.andExpect(status().is4xxClientError()) +// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.telNo").value("유효하지 않은 전화번호 형식입니다.")) +// } +// +// @DisplayName("비밀번호 변경 요청시에도 비밀번호는 8자리 이상이여야 한다.") +// @WithMockUser(username = "현수", roles = ["USER"]) +// @Test +// @Throws(java.lang.Exception::class) +// fun modifyPassword_validCheckTest() { +// //given +// val modifyPasswordURL = "/v1/members/pass/1" +// val modifyPasswordRequest = ModifyPasswordRequest("1") +// val requestBody = objectMapper.writeValueAsString(modifyPasswordRequest) +// +// //when +// val actions = mockMvc.perform( +// MockMvcRequestBuilders.patch(modifyPasswordURL) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// actions.andExpect(status().is4xxClientError()) +// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) +// } +//} \ No newline at end of file From 60473d58aef25a1703f5558914d1c266ab29282f Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 20:43:45 +0900 Subject: [PATCH 16/42] =?UTF-8?q?refactor:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20-=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=BD=94=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=98=AE=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthenticationController.kt | 34 +++++-------------- .../service/KakaoSignUpService.kt | 21 +++++++----- .../member/dto/request/KakaoMemberRequest.kt | 4 ++- .../member/service/AccountService.kt | 4 +-- .../member/service/AccountServiceImpl.kt | 6 ++-- .../clothstar/member/service/MemberService.kt | 4 +-- .../member/service/MemberServiceImpl.kt | 4 +-- 7 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 65623fa..93a86d1 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.* import org.store.clothstar.common.dto.ErrorResponseDTO import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO -import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.authentication.service.KakaoSignUpService @@ -31,7 +30,6 @@ import org.store.clothstar.member.dto.response.MemberResponse class AuthenticationController( private val memberServiceApplication: MemberServiceApplication, private val signUpServiceFactory: SignUpServiceFactory, - private val kakaoLoginService: KakaoLoginService, ) { private val log = KotlinLogging.logger {} @@ -72,37 +70,23 @@ class AuthenticationController( fun signup( @Validated @RequestBody signUpRequest: SignUpRequest, @RequestParam signUpType: SignUpType, - @RequestParam code: String?, ): ResponseEntity { val signUpService = signUpServiceFactory.getSignUpService(signUpType) - log.info { "사인업서비스종류: $signUpService" } + log.info { "SignUpService 종류: $signUpService" } val memberId = when (signUpService) { + // 일반 회원가입 is NormalSignUpService -> { - if (signUpRequest.createMemberRequest == null) { - throw IllegalArgumentException("일반 회원가입 시 회원 정보가 필요합니다.") - } - signUpService.signUp(signUpRequest.createMemberRequest) + val normalMemberRequest = signUpRequest.createMemberRequest + ?: throw IllegalArgumentException("일반 회원가입 시 회원 정보가 필요합니다.") + signUpService.signUp(normalMemberRequest) } - + // 카카오 회원가입 is KakaoSignUpService -> { - log.info { "왜 아무것도 안나와" } - // 액세스 토큰 받아오기 - val accessToken = kakaoLoginService.getAccessToken(code!!) - - log.info { "accressToken = ${accessToken.accessToken}" } - - // 사용자 정보 받아오기 - val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) - - // kakaoMemberRequest의 이메일 필드 업데이트 - val updatedKakaoMemberRequest = - signUpRequest.kakaoMemberRequest!!.addEmail(userInfo.kakaoAccount!!.email!!) - - // 카카오 회원 가입 - signUpService.signUp(updatedKakaoMemberRequest) + val kakaoMemberRequest = signUpRequest.kakaoMemberRequest + ?: throw IllegalArgumentException("카카오 회원가입 시 회원 정보가 필요합니다.") + signUpService.signUp(kakaoMemberRequest) } - else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index a64a276..cfdeb93 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -3,7 +3,6 @@ package org.store.clothstar.member.authentication.service import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.stereotype.Service import org.store.clothstar.kakaoLogin.service.KakaoLoginService -import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.service.AccountService import org.store.clothstar.member.service.MemberService @@ -18,14 +17,20 @@ class KakaoSignUpService( override fun signUp(request: KakaoMemberRequest): Long { - val kakaoMemberRequestDTO = CreateKakaoMemberRequest( - email = request.email!!, - name = request.name, - telNo = request.telNo, - ) + // 액세스 토큰 받아오기 + val accessToken = kakaoLoginService.getAccessToken(request.code) - val memberId = memberService.saveKakaoMember(kakaoMemberRequestDTO) - accountService.saveKakaoAccount(memberId, kakaoMemberRequestDTO) + // 사용자 정보 받아오기 + val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) + + // kakaoMemberRequest의 이메일 필드 업데이트 + val updatedKakaoMemberRequest = + request.addEmail(userInfo.kakaoAccount!!.email!!) + + // Member DB 저장 + val memberId = memberService.saveKakaoMember(updatedKakaoMemberRequest) + // Account DB 저장 + accountService.saveKakaoAccount(memberId, updatedKakaoMemberRequest) log.info { "KAKAOSIGNUPSERVICE 입니다" } diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt index 81d76db..70985c3 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/KakaoMemberRequest.kt @@ -11,8 +11,10 @@ class KakaoMemberRequest( val telNo: String, val email: String?, + + val code: String, ) { fun addEmail(email: String): KakaoMemberRequest { - return KakaoMemberRequest(name, telNo, email) + return KakaoMemberRequest(name, telNo, email, code) } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt b/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt index e66006e..c9f6bfc 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/AccountService.kt @@ -2,12 +2,12 @@ package org.store.clothstar.member.service import org.store.clothstar.member.domain.Account import org.store.clothstar.member.domain.MemberRole -import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest interface AccountService { fun saveAccount(memberId: Long, createMemberDTO: CreateMemberRequest): Account - fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: CreateKakaoMemberRequest): Account + fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: KakaoMemberRequest): Account fun updateRole(memberId: Long, findRole: MemberRole, updateRole: MemberRole) fun updateDeletedAt(memberId: Long, findRole: MemberRole) } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt b/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt index 3123e8e..17412d8 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/AccountServiceImpl.kt @@ -8,8 +8,8 @@ import org.store.clothstar.common.error.exception.DuplicatedEmailException import org.store.clothstar.common.error.exception.NotFoundAccountException import org.store.clothstar.member.domain.Account import org.store.clothstar.member.domain.MemberRole -import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.repository.AccountRepository @Service @@ -32,9 +32,9 @@ class AccountServiceImpl( } @Transactional - override fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: CreateKakaoMemberRequest): Account { + override fun saveKakaoAccount(memberId: Long, createKakaoMemberDTO: KakaoMemberRequest): Account { // 이메일 중복 검사 - accountRepository.findByEmail(createKakaoMemberDTO.email)?.let { + accountRepository.findByEmail(createKakaoMemberDTO.email!!)?.let { throw DuplicatedEmailException(ErrorCode.DUPLICATED_EMAIL) } diff --git a/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt b/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt index 4b4bef2..3bbb22f 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/MemberService.kt @@ -4,8 +4,8 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.store.clothstar.member.domain.Member -import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.dto.request.ModifyNameRequest import org.store.clothstar.member.dto.response.MemberResponse @@ -25,7 +25,7 @@ interface MemberService { fun modifyName(memberId: Long, modifyNameRequest: ModifyNameRequest) fun saveMember(createMemberDTO: CreateMemberRequest): Long - fun saveKakaoMember(createKakaoMemberDTO: CreateKakaoMemberRequest): Long + fun saveKakaoMember(createKakaoMemberDTO: KakaoMemberRequest): Long fun getMemberByMemberId(memberId: Long): Member } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt b/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt index 2313717..1fc00d9 100644 --- a/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt +++ b/src/main/kotlin/org/store/clothstar/member/service/MemberServiceImpl.kt @@ -14,8 +14,8 @@ import org.store.clothstar.common.error.exception.DuplicatedTelNoException import org.store.clothstar.common.error.exception.NotFoundMemberException import org.store.clothstar.member.domain.Member import org.store.clothstar.member.domain.vo.MemberShoppingActivity -import org.store.clothstar.member.dto.request.CreateKakaoMemberRequest import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.dto.request.ModifyNameRequest import org.store.clothstar.member.dto.response.MemberResponse import org.store.clothstar.member.repository.AccountRepository @@ -136,7 +136,7 @@ class MemberServiceImpl( } @Transactional - override fun saveKakaoMember(createKakaoMemberDTO: CreateKakaoMemberRequest): Long { + override fun saveKakaoMember(createKakaoMemberDTO: KakaoMemberRequest): Long { // 전화번호 중복 검사 memberRepository.findByTelNo(createKakaoMemberDTO.telNo)?.let { throw DuplicatedTelNoException(ErrorCode.DUPLICATED_TEL_NO) From d5f24c3f09bfffc7f3385b4d07bb1cc363e95c48 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 31 Aug 2024 23:48:16 +0900 Subject: [PATCH 17/42] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=EC=8B=9C=20=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/exception/GlobalExceptionHandler.kt | 13 +++++++++++++ .../store/clothstar/common/dto/SaveResponseDTO.kt | 2 +- .../org/store/clothstar/common/error/ErrorCode.kt | 5 ++++- .../error/exception/InvalidSignupMemberRequest.kt | 7 +++++++ .../common/error/exception/InvalidSignupType.kt | 7 +++++++ .../controller/AuthenticationController.kt | 9 ++++++--- .../authentication/service/KakaoSignUpService.kt | 3 +-- .../authentication/service/NormalSignUpService.kt | 2 +- .../authentication/service/SignUpServiceFactory.kt | 6 ++---- 9 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt create mode 100644 src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt diff --git a/src/main/kotlin/org/store/clothstar/common/config/exception/GlobalExceptionHandler.kt b/src/main/kotlin/org/store/clothstar/common/config/exception/GlobalExceptionHandler.kt index b83d62b..c9a706c 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/exception/GlobalExceptionHandler.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/exception/GlobalExceptionHandler.kt @@ -190,6 +190,19 @@ class GlobalExceptionHandler { return ResponseEntity(errorResponseDTO, ex.errorCode.status) } + // 회원가입 관련 에러처리 + @ExceptionHandler(InvalidSignupMemberRequest::class) + fun handleInvalidSignupMemberRequest(ex: InvalidSignupMemberRequest): ResponseEntity { + val errorResponseDTO = ErrorResponseDTO(ex.errorCode.status.value(), ex.errorCode.message) + return ResponseEntity(errorResponseDTO, ex.errorCode.status) + } + + @ExceptionHandler(InvalidSignupType::class) + fun handleInvalidSignupType(ex: InvalidSignupType): ResponseEntity { + val errorResponseDTO = ErrorResponseDTO(ex.errorCode.status.value(), ex.errorCode.message) + return ResponseEntity(errorResponseDTO, ex.errorCode.status) + } + // Order 관련 에러처리 @ExceptionHandler(OrderNotFoundException::class) fun handleOrderNotFoundException(ex: OrderNotFoundException): ResponseEntity { diff --git a/src/main/kotlin/org/store/clothstar/common/dto/SaveResponseDTO.kt b/src/main/kotlin/org/store/clothstar/common/dto/SaveResponseDTO.kt index 79c46dc..10c8447 100644 --- a/src/main/kotlin/org/store/clothstar/common/dto/SaveResponseDTO.kt +++ b/src/main/kotlin/org/store/clothstar/common/dto/SaveResponseDTO.kt @@ -1,7 +1,7 @@ package org.store.clothstar.common.dto class SaveResponseDTO( - val id: Long, + val id: Long? = null, val statusCode: Int, val message: String, ) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt b/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt index 94475c9..7ec3ec0 100644 --- a/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt +++ b/src/main/kotlin/org/store/clothstar/common/error/ErrorCode.kt @@ -22,6 +22,10 @@ enum class ErrorCode( INVALID_ROLE(HttpStatus.BAD_REQUEST, "계정 ID: %d - 이 계정은 요청된 권한(%s)을 가지고 있지 않습니다."), + // 회원가입 관련 에러코드 + INVALID_SIGNUP_MEMBER_REQUEST(HttpStatus.BAD_REQUEST, "회원가입 시 회원 정보가 필요합니다."), + INVLIAD_SIGNUP_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 회원가입 유형입니다."), + // Order 관련 에러코드 NOT_FOUND_ORDER(HttpStatus.NOT_FOUND, "존재하지 않는 주문번호입니다."), INVALID_ORDER_STATUS_CONFIRMED(HttpStatus.BAD_REQUEST, "주문이 '입금확인' 상태가 아니므로 요청을 처리할 수 없습니다."), @@ -29,7 +33,6 @@ enum class ErrorCode( OUT_OF_STOCK(HttpStatus.BAD_REQUEST, "품절된 상품입니다."), INSUFFICIENT_STOCK(HttpStatus.BAD_REQUEST, "주문 개수가 상품 재고보다 더 많아 요청을 처리할 수 없습니다."); - // Product 관련 에러코드 diff --git a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt new file mode 100644 index 0000000..f28fe6a --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt @@ -0,0 +1,7 @@ +package org.store.clothstar.common.error.exception + +import org.store.clothstar.common.error.ErrorCode + +class InvalidSignupMemberRequest ( + val errorCode: ErrorCode +) : RuntimeException(errorCode.message) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt new file mode 100644 index 0000000..01de8cd --- /dev/null +++ b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt @@ -0,0 +1,7 @@ +package org.store.clothstar.common.error.exception + +import org.store.clothstar.common.error.ErrorCode + +class InvalidSignupType ( + val errorCode: ErrorCode +) : RuntimeException(errorCode.message) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 93a86d1..9be7b6d 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -14,6 +14,9 @@ import org.springframework.web.bind.annotation.* import org.store.clothstar.common.dto.ErrorResponseDTO import org.store.clothstar.common.dto.MessageDTO import org.store.clothstar.common.dto.SaveResponseDTO +import org.store.clothstar.common.error.ErrorCode +import org.store.clothstar.common.error.exception.InvalidSignupMemberRequest +import org.store.clothstar.common.error.exception.InvalidSignupType import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.authentication.service.KakaoSignUpService @@ -78,16 +81,16 @@ class AuthenticationController( // 일반 회원가입 is NormalSignUpService -> { val normalMemberRequest = signUpRequest.createMemberRequest - ?: throw IllegalArgumentException("일반 회원가입 시 회원 정보가 필요합니다.") + ?: throw InvalidSignupMemberRequest(ErrorCode.INVALID_SIGNUP_MEMBER_REQUEST) signUpService.signUp(normalMemberRequest) } // 카카오 회원가입 is KakaoSignUpService -> { val kakaoMemberRequest = signUpRequest.kakaoMemberRequest - ?: throw IllegalArgumentException("카카오 회원가입 시 회원 정보가 필요합니다.") + ?: throw InvalidSignupMemberRequest(ErrorCode.INVALID_SIGNUP_MEMBER_REQUEST) signUpService.signUp(kakaoMemberRequest) } - else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") + else -> throw InvalidSignupType(ErrorCode.INVLIAD_SIGNUP_TYPE) } val saveResponseDTO = SaveResponseDTO( diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt index cfdeb93..9d046a3 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/KakaoSignUpService.kt @@ -7,7 +7,7 @@ import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.service.AccountService import org.store.clothstar.member.service.MemberService -@Service("kakaoSignUpService") +@Service class KakaoSignUpService( private val memberService: MemberService, private val accountService: AccountService, @@ -16,7 +16,6 @@ class KakaoSignUpService( private val log = KotlinLogging.logger {} override fun signUp(request: KakaoMemberRequest): Long { - // 액세스 토큰 받아오기 val accessToken = kakaoLoginService.getAccessToken(request.code) diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt index fd80b31..b199667 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/NormalSignUpService.kt @@ -5,7 +5,7 @@ import org.springframework.stereotype.Service import org.store.clothstar.member.application.MemberServiceApplication import org.store.clothstar.member.dto.request.CreateMemberRequest -@Service("normalSignUpService") +@Service class NormalSignUpService( private val memberServiceApplication: MemberServiceApplication ) : SignUpService { diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt index a0f2cb6..254822d 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/service/SignUpServiceFactory.kt @@ -1,6 +1,5 @@ package org.store.clothstar.member.authentication.service -import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.dto.request.CreateMemberRequest @@ -8,14 +7,13 @@ import org.store.clothstar.member.dto.request.KakaoMemberRequest @Component class SignUpServiceFactory( - @Qualifier("normalSignUpService") private val normalSignUpService: SignUpService, - @Qualifier("kakaoSignUpService") private val kakaoSignUpService: SignUpService + private val normalSignUpService: SignUpService, + private val kakaoSignUpService: SignUpService ) { fun getSignUpService(signUpType: SignUpType): SignUpService<*> { return when (signUpType) { SignUpType.NORMAL -> normalSignUpService SignUpType.KAKAO -> kakaoSignUpService - else -> throw IllegalArgumentException("지원하지 않는 회원가입 유형입니다.") } } } \ No newline at end of file From cbde0898f9b23843a69001cb0c1edb8f52a59f9b Mon Sep 17 00:00:00 2001 From: subin Date: Wed, 4 Sep 2024 14:55:22 +0900 Subject: [PATCH 18/42] =?UTF-8?q?=EC=B9=B4=EC=B9=B4=EC=98=A4=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 5 +- .../controller/KakaoLoginPageController.kt | 4 +- .../kakaoLogin/service/KakaoLoginService.kt | 16 ++--- src/main/resources/application.yml | 71 ++++++++++++++++--- 4 files changed, 74 insertions(+), 22 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7a175d2..a3c00ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,6 @@ dependencies { //web implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-thymeleaf") implementation("org.springframework.boot:spring-boot-starter-webflux") //thymeleaf @@ -70,6 +69,10 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + //oauth2 + implementation ("org.springframework.boot:spring-boot-starter-oauth2-client") + implementation ("org.springframework.security:spring-security-oauth2-jose") + //etc implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") //swagger implementation("com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5") //암호화 diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt index 3709200..92bfb12 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoLoginPageController.kt @@ -8,10 +8,10 @@ import org.springframework.web.bind.annotation.GetMapping @Controller class KakaoLoginPageController { - @Value("\${kakao.client_id}") + @Value("\${spring.security.oauth2.client.registration.kakao.client_id}") private lateinit var clientId: String - @Value("\${kakao.redirect_uri}") + @Value("\${spring.security.oauth2.client.registration.kakao.redirect_uri}") private lateinit var redirectUri: String @GetMapping("/kakaoLogin/page") diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index e79cdef..979cdcd 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -20,19 +20,19 @@ class KakaoLoginService { private val logger = KotlinLogging.logger {} - @Value("\${kakao.client_id}") + @Value("\${spring.security.oauth2.client.registration.kakao.client_id}") private lateinit var clientId: String - @Value("\${kakao.client_secret}") + @Value("\${spring.security.oauth2.client.registration.kakao.client_secret}") private lateinit var clientSecret: String - @Value("\${kakao.redirect_uri}") + @Value("\${spring.security.oauth2.client.registration.kakao.redirect_uri}") private lateinit var redirectUri: String - @Value("\${kakao.login.uri.token}") + @Value("\${spring.security.oauth2.client.provider.kakao.token_uri}") private lateinit var tokenUri: String - @Value("\${kakao.api.uri.user}") + @Value("\${spring.security.oauth2.client.provider.kakao.user_info_uri}") private lateinit var userUri: String // 토큰 가져오기 @@ -48,9 +48,8 @@ class KakaoLoginService { logger.info { "Requesting token with params: $params" } // 웹 클라이언트로 요청 보내기 - val response = WebClient.create("https://kauth.kakao.com") + val response = WebClient.create(tokenUri) .post() - .uri(tokenUri) .body(BodyInserters.fromFormData(params)) .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") .retrieve() @@ -70,9 +69,8 @@ class KakaoLoginService { // 사용자 정보 가져오기 fun getUserInfo(accessToken: String): KakaoUserInfoResponseDto { // 웹 클라이언트로 요청 보내기 - val response = WebClient.create("https://kapi.kakao.com") + val response = WebClient.create(userUri) .get() - .uri(userUri) .header("Authorization", "Bearer $accessToken") .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") .retrieve() diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bc3fa93..a61b3a5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,6 +32,67 @@ spring: port: 6379 duration: 600 +#oauth2 + security: + oauth2: + client: + registration: + kakao: + client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) + client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) + redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) + scope: + - name + - email + - profile_image + authorization_grant_type: authorization_code + client_name: Kakao + client_authentication_method: post + provider: kakao + # google: + # client-id: YOUR_GOOGLE_CLIENT_ID + # client-secret: YOUR_GOOGLE_CLIENT_SECRET + # redirect-uri: "{baseUrl}/login/oauth2/code/google" + # scope: + # - profile + # - email + # authorization-grant-type: authorization_code + # client-name: Google + # client-authentication-method: post + # provider: google + # + # naver: + # client-id: YOUR_NAVER_CLIENT_ID + # client-secret: YOUR_NAVER_CLIENT_SECRET + # redirect-uri: "{baseUrl}/login/oauth2/code/naver" + # scope: + # - name + # - email + # - profile_image + # authorization-grant-type: authorization_code + # client-name: Naver + # client-authentication-method: post + # provider: naver + + + provider: + kakao: + authorization_uri: https://kauth.kakao.com/oauth/authorize + token_uri: https://kauth.kakao.com/oauth/token + user_info_uri: https://kapi.kakao.com/v2/user/me + user_name_attribute: id + # naver: + # authorization-uri: + # token-uri: + # user-info-uri: + # user-name-attribute: response + # google: + # authorization-uri: + # token-uri: + # user-info-uri: + # user-name-attribute: sub + +email.send: ENC(kGXTSlfxUWNbRoGuBwNRTJBETjMz04AChYMrwDeY3Cs=) email.send: ${email.send} springdoc: @@ -42,13 +103,3 @@ springdoc: tags-sorter: alpha # tag ?? ??? ??? ? #path: "swagger.html" # http://localhost:8080/swagger.html? ?? ?? path: "/" # http://localhost:8080? ?? ?? - -kakao: - client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) - redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) - client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) - admin_key: ENC(BmWFmOXxaXogq1sWkCEZu/OM2qCKiOfPfZSUCG5Xwokpm4XKKmLJMSud7+1vbP8M) - - api.uri.user: /v2/user/me - login.redirect_url: http://localhost:8080/auth/kakao/callback - login.uri.token: /oauth/token From 2bb92581a80d7b52b91e0e2295628a2275dbd220 Mon Sep 17 00:00:00 2001 From: subin Date: Thu, 5 Sep 2024 23:37:42 +0900 Subject: [PATCH 19/42] =?UTF-8?q?refactor:=20kakao=20api=EA=B0=92=20vault?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfiguration.kt | 4 +- src/main/resources/application.yml | 42 ++----------------- .../clothstar/common/JasyptConfigTest.kt | 2 +- 3 files changed, 6 insertions(+), 42 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index c61fea2..d7ac723 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -67,10 +67,10 @@ class SecurityConfiguration( "/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list", "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", - "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO" + "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", - "config-service/**" + "config-service/**", "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", "/v1/members?signUpType=KAKAO", diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a61b3a5..f5dc2f5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -38,9 +38,9 @@ spring: client: registration: kakao: - client_id: ENC(1c6jtMDIU7/ZloQv+k1W9YP6arHGBdguKmwtI4tU1dHAA0iYyK6pogIBiRzUp5V6) - client_secret: ENC(Z+CSlGFc9ux+mvN1t2c3mWoKv8sqoJF5gE+UoJTMHD8YZRYtOyO4WOJ9Qz7xi97E) - redirect_uri: ENC(SvHz8bAglAd/vqUrE2NAlhyfEJ1klhK0pCEq9dfQLqCPKxG8gZjjAmYd+HXakhjjd+SxflkXRt8=) + client_id: ${kakao.client.id} + client_secret: ${kakao.client.secret} + redirect_uri: http://localhost:8080/auth/kakao/callback scope: - name - email @@ -49,31 +49,6 @@ spring: client_name: Kakao client_authentication_method: post provider: kakao - # google: - # client-id: YOUR_GOOGLE_CLIENT_ID - # client-secret: YOUR_GOOGLE_CLIENT_SECRET - # redirect-uri: "{baseUrl}/login/oauth2/code/google" - # scope: - # - profile - # - email - # authorization-grant-type: authorization_code - # client-name: Google - # client-authentication-method: post - # provider: google - # - # naver: - # client-id: YOUR_NAVER_CLIENT_ID - # client-secret: YOUR_NAVER_CLIENT_SECRET - # redirect-uri: "{baseUrl}/login/oauth2/code/naver" - # scope: - # - name - # - email - # - profile_image - # authorization-grant-type: authorization_code - # client-name: Naver - # client-authentication-method: post - # provider: naver - provider: kakao: @@ -81,18 +56,7 @@ spring: token_uri: https://kauth.kakao.com/oauth/token user_info_uri: https://kapi.kakao.com/v2/user/me user_name_attribute: id - # naver: - # authorization-uri: - # token-uri: - # user-info-uri: - # user-name-attribute: response - # google: - # authorization-uri: - # token-uri: - # user-info-uri: - # user-name-attribute: sub -email.send: ENC(kGXTSlfxUWNbRoGuBwNRTJBETjMz04AChYMrwDeY3Cs=) email.send: ${email.send} springdoc: diff --git a/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt b/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt index 7b19273..0b4011b 100644 --- a/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt +++ b/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt @@ -11,7 +11,7 @@ class JasyptConfigTest { @Test fun jasypt() { - val url = "kGXTSlfxUWNbRoGuBwNRTJBETjMz04AChYMrwDeY3Cs=" + val url = "" val username = "" val password = "" From 4af26982e1f8035c47b8bdecb0b7d194b52055c3 Mon Sep 17 00:00:00 2001 From: subin Date: Fri, 6 Sep 2024 03:06:27 +0900 Subject: [PATCH 20/42] =?UTF-8?q?refactor:=20MemberSignupValidTest=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 2 +- .../application/MemberServiceApplication.kt | 2 +- .../member/dto/request/SignUpRequest.kt | 3 + .../clothstar/common/JasyptConfigTest.kt | 2 +- .../AuthenticationControllerTest.kt | 78 ++++- .../controller/MemberSignupValidTest.kt | 278 +++++++++--------- .../clothstar/member/util/CreateObject.kt | 14 +- 7 files changed, 229 insertions(+), 150 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt index d80e075..4604643 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -2,7 +2,7 @@ package org.store.clothstar.kakaoLogin.dto import com.fasterxml.jackson.annotation.JsonProperty -class KakaoTokenResponseDto { +class KakaoTokenResponseDto(s: String, accessToken: String, i: Int, s1: String, i1: Int) { // 토큰 타입, bearer로 고정 @JsonProperty("token_type") private val tokenType: String? = null diff --git a/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt b/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt index 5d8d9a3..0f3a0c0 100644 --- a/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt +++ b/src/main/kotlin/org/store/clothstar/member/application/MemberServiceApplication.kt @@ -49,7 +49,7 @@ class MemberServiceApplication( fun signUp(createMemberRequest: CreateMemberRequest): Long { //인증번호 확인 -// authenticationService.verifyEmailCertifyNum(createMemberRequest.email, createMemberRequest.certifyNum) + authenticationService.verifyEmailCertifyNum(createMemberRequest.email, createMemberRequest.certifyNum) val memberId = memberService.saveMember(createMemberRequest) accountService.saveAccount(memberId, createMemberRequest) diff --git a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt index 304a950..c44f192 100644 --- a/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt +++ b/src/main/kotlin/org/store/clothstar/member/dto/request/SignUpRequest.kt @@ -1,6 +1,9 @@ package org.store.clothstar.member.dto.request +import jakarta.validation.Valid + class SignUpRequest( + @field: Valid val createMemberRequest: CreateMemberRequest?, var kakaoMemberRequest: KakaoMemberRequest? ) \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt b/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt index 0b4011b..ba69761 100644 --- a/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt +++ b/src/test/kotlin/org/store/clothstar/common/JasyptConfigTest.kt @@ -15,7 +15,7 @@ class JasyptConfigTest { val username = "" val password = "" - println(jasyptDecoding(url)) + println(jasyptEncoding(url)) println(jasyptEncoding(username)) println(jasyptEncoding(password)) } diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index 2c425f0..6b137ae 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -3,17 +3,28 @@ //import com.fasterxml.jackson.databind.ObjectMapper //import org.junit.jupiter.api.DisplayName //import org.junit.jupiter.api.Test +//import org.mockito.ArgumentMatchers.anyString +//import org.mockito.BDDMockito.given //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.context.ActiveProfiles //import org.springframework.test.web.servlet.MockMvc //import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get //import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status //import org.springframework.transaction.annotation.Transactional //import org.store.clothstar.common.config.redis.RedisUtil +//import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto +//import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto +//import org.store.clothstar.kakaoLogin.service.KakaoLoginService +//import org.store.clothstar.kakaoLogin.vo.KakaoAccount +//import org.store.clothstar.member.authentication.domain.SignUpType +//import org.store.clothstar.member.dto.request.SignUpRequest //import org.store.clothstar.member.util.CreateObject +//import kotlin.test.assertEquals // //@SpringBootTest //@AutoConfigureMockMvc @@ -24,25 +35,30 @@ // @Autowired private val objectMapper: ObjectMapper, // @Autowired private val redisUtil: RedisUtil, //) { +// @MockBean +// private lateinit var kakaoLoginService: KakaoLoginService +// // private val MEMBER_URL = "/v1/members" // private val LOGIN_URL = "/v1/members/login" // -// @DisplayName("멤버 회원가입 통합테스트후 로그인 통합테스트") +// @DisplayName("멤버 일반 회원가입 통합테스트후 로그인 통합테스트") // @Test -// fun signUpIntegrationTest() { +// fun normalSignUpIntegrationTest() { // //given // //이메일과 인증번호로 redis 데이터 생성 // val email = "test@naver.com" // val certifyNum = redisUtil.createdCertifyNum() // redisUtil.createRedisData(email, certifyNum) // -// //이메일과 인증번호로 create DTO 객체 생성 +// // 이메일과 인증번호로 create DTO 객체 생성 // val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) -// val requestBody = objectMapper.writeValueAsString(createMemberRequest) +// val signUpRequest = SignUpRequest(createMemberRequest, null) +// val requestBody = objectMapper.writeValueAsString(signUpRequest) // -// //when +// // when // val actions = mockMvc.perform( // post(MEMBER_URL) +// .param("signUpType", SignUpType.NORMAL.toString()) // .contentType(MediaType.APPLICATION_JSON) // .content(requestBody) // ) @@ -63,4 +79,56 @@ // // loginActions.andExpect(status().isOk) // } +// +// @DisplayName("멤버 카카오 회원가입 통합테스트") +// @Test +// fun kakaoSignUpIntegrationTest() { +// //given +// val name = "수빈" +// val telNo = "010-1231-0987" +// val code = "test_auth_code" +// val accessToken = "mock_access_token" +// val refreshToken = "mock_refresh_token" +// val email = "test@kakao.com" +// +// // KakaoLoginService가 반환할 Mock 데이터 설정 +// val kakaoTokenResponseDto = KakaoTokenResponseDto("bearer", accessToken, 1000000, refreshToken, 3600) +// val kakaoUserInfoResponseDto = KakaoUserInfoResponseDto( +// id = 12345L, +// kakaoAccount = KakaoAccount( +// email = email, +// ) +// ) +// +// // KakaoLoginService의 getAccessToken, getUserInfo 메서드에 대해 mock 설정 +// given(kakaoLoginService.getAccessToken(anyString())).willReturn(kakaoTokenResponseDto) +// given(kakaoLoginService.getUserInfo(anyString())).willReturn(kakaoUserInfoResponseDto) +// +// // when - 1: 카카오 콜백 호출 +// val callbackActions = mockMvc.perform( +// get("/auth/kakao/callback") +// .param("code", code) +// ) +// +//// assertEquals(code,"test_auth_code") +// +// // then - 1: 콜백 응답이 예상대로 이루어지는지 확인 +// callbackActions.andExpect(status().isOk) +// +// // when - 2: 해당 인가 코드로 회원가입 요청 +// val kakaoMemberRequest = CreateObject.getKakaoMemberRequest(name, telNo, code) +// val signUpRequest = SignUpRequest(null, kakaoMemberRequest) +// val requestBody = objectMapper.writeValueAsString(signUpRequest) +// +// //when +// val actions = mockMvc.perform( +// post(MEMBER_URL) +// .param("signUpType", SignUpType.KAKAO.toString()) +// .contentType(MediaType.APPLICATION_JSON) +// .content(requestBody) +// ) +// +// //then +// actions.andExpect(status().isCreated) +// } //} \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt index 75d577a..983afce 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt @@ -1,138 +1,140 @@ -//package org.store.clothstar.member.authentication.controller -// -//import com.fasterxml.jackson.databind.ObjectMapper -//import org.assertj.core.api.Assertions -//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.http.MediaType -//import org.springframework.security.test.context.support.WithMockUser -//import org.springframework.test.context.ActiveProfiles -//import org.springframework.test.web.servlet.MockMvc -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -//import org.springframework.transaction.annotation.Transactional -//import org.store.clothstar.common.config.redis.RedisUtil -//import org.store.clothstar.member.dto.request.CreateMemberRequest -//import org.store.clothstar.member.dto.request.ModifyPasswordRequest -// -//@SpringBootTest -//@AutoConfigureMockMvc -//@ActiveProfiles("test") -//@Transactional -//class MemberSignupValidTest( -// @Autowired private val mockMvc: MockMvc, -// @Autowired private val objectMapper: ObjectMapper, -// @Autowired private val redisUtil: RedisUtil, -//) { -// private val MEMBER_URL = "/v1/members" -// -// @DisplayName("회원가입시 비밀번호는 최소 8자리 이상이여야 한다.") -// @Test -// @Throws(java.lang.Exception::class) -// fun signUp_passwordValidationTest() { -// //given -// val createMemberRequest = CreateMemberRequest( -// email = "test@test.com", -// password = "1234567", -// name = "현수", -// telNo = "010-1234-1234", -// certifyNum = "gg" -// ) -// -// val requestBody = objectMapper.writeValueAsString(createMemberRequest) -// -// //when -// val actions = mockMvc.perform( -// post(MEMBER_URL) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// actions.andExpect(status().is4xxClientError()) -// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) -// } -// -// @DisplayName("회원가입시 이름은 필수 값이다.") -// @Test -// @Throws(java.lang.Exception::class) -// fun signUp_nameValidationTest() { -// //given -// val createMemberRequest = CreateMemberRequest( -// email = "test@test.com", -// password = "1234567", -// name = "", -// telNo = "010-1234-1234", -// certifyNum = "gg" -// ) -// -// val requestBody = objectMapper.writeValueAsString(createMemberRequest) -// -// //when -// val actions = mockMvc.perform( -// post(MEMBER_URL) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) -// actions.andExpect(status().is4xxClientError()) -// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.name").value("이름은 비어 있을 수 없습니다.")) -// } -// -// @DisplayName("회원가입시 전화번호 양식이 지켜져야 한다.") -// @Test -// @Throws(java.lang.Exception::class) -// fun signUp_telNoValidationTest() { -// //given -// val createMemberRequest = CreateMemberRequest( -// email = "test@test.com", -// password = "1234567", -// name = "", -// telNo = "010", -// certifyNum = "gg" -// ) -// -// val requestBody = objectMapper.writeValueAsString(createMemberRequest) -// -// //when -// val actions = mockMvc.perform( -// post(MEMBER_URL) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) -// actions.andExpect(status().is4xxClientError()) -// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.telNo").value("유효하지 않은 전화번호 형식입니다.")) -// } -// -// @DisplayName("비밀번호 변경 요청시에도 비밀번호는 8자리 이상이여야 한다.") -// @WithMockUser(username = "현수", roles = ["USER"]) -// @Test -// @Throws(java.lang.Exception::class) -// fun modifyPassword_validCheckTest() { -// //given -// val modifyPasswordURL = "/v1/members/pass/1" -// val modifyPasswordRequest = ModifyPasswordRequest("1") -// val requestBody = objectMapper.writeValueAsString(modifyPasswordRequest) -// -// //when -// val actions = mockMvc.perform( -// MockMvcRequestBuilders.patch(modifyPasswordURL) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// actions.andExpect(status().is4xxClientError()) -// actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) -// } -//} \ No newline at end of file +package org.store.clothstar.member.authentication.controller + +import com.fasterxml.jackson.databind.ObjectMapper +import org.assertj.core.api.Assertions +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.http.MediaType +import org.springframework.security.test.context.support.WithMockUser +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.transaction.annotation.Transactional +import org.store.clothstar.common.config.redis.RedisUtil +import org.store.clothstar.member.authentication.domain.SignUpType +import org.store.clothstar.member.dto.request.CreateMemberRequest +import org.store.clothstar.member.dto.request.ModifyPasswordRequest +import org.store.clothstar.member.dto.request.SignUpRequest + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("test") +@Transactional +class MemberSignupValidTest( + @Autowired private val mockMvc: MockMvc, + @Autowired private val objectMapper: ObjectMapper, + @Autowired private val redisUtil: RedisUtil, +) { + private val MEMBER_URL = "/v1/members" + + @DisplayName("회원가입시 비밀번호는 최소 8자리 이상이여야 한다.") + @Test + @Throws(java.lang.Exception::class) + fun signUp_passwordValidationTest() { + //given + val createMemberRequest = CreateMemberRequest( + email = "test@test.com", + password = "1234567", + name = "현수", + telNo = "010-1234-1234", + certifyNum = "gg" + ) + val signUpRequest = SignUpRequest(createMemberRequest, null) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + val actions = mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.NORMAL.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + actions.andExpect(status().is4xxClientError()) + actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap['createMemberRequest.password']").value("비밀번호는 최소 8자 이상이어야 합니다.")) + } + + @DisplayName("회원가입시 이름은 필수 값이다.") + @Test + @Throws(java.lang.Exception::class) + fun signUp_nameValidationTest() { + //given + val createMemberRequest = CreateMemberRequest( + email = "test@test.com", + password = "1234567", + name = "", + telNo = "010-1234-1234", + certifyNum = "gg" + ) + val signUpRequest = SignUpRequest(createMemberRequest, null) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + val actions = mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.NORMAL.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) + actions.andExpect(status().is4xxClientError()) + actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.name']").value("이름은 비어 있을 수 없습니다.")) + } + + @DisplayName("회원가입시 전화번호 양식이 지켜져야 한다.") + @Test + @Throws(java.lang.Exception::class) + fun signUp_telNoValidationTest() { + //given + val createMemberRequest = CreateMemberRequest( + email = "test@test.com", + password = "1234567", + name = "", + telNo = "010", + certifyNum = "gg" + ) + val signUpRequest = SignUpRequest(createMemberRequest, null) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + val actions = mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.NORMAL.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) + actions.andExpect(status().is4xxClientError()) + actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.telNo']").value("유효하지 않은 전화번호 형식입니다.")) + } + + @DisplayName("비밀번호 변경 요청시에도 비밀번호는 8자리 이상이여야 한다.") + @WithMockUser(username = "현수", roles = ["USER"]) + @Test + @Throws(java.lang.Exception::class) + fun modifyPassword_validCheckTest() { + //given + val modifyPasswordURL = "/v1/members/pass/1" + val modifyPasswordRequest = ModifyPasswordRequest("1") + val requestBody = objectMapper.writeValueAsString(modifyPasswordRequest) + + //when + val actions = mockMvc.perform( + MockMvcRequestBuilders.patch(modifyPasswordURL) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + actions.andExpect(status().is4xxClientError()) + actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.password").value("비밀번호는 최소 8자 이상이어야 합니다.")) + } +} \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/member/util/CreateObject.kt b/src/test/kotlin/org/store/clothstar/member/util/CreateObject.kt index c0dbd89..fa7459a 100644 --- a/src/test/kotlin/org/store/clothstar/member/util/CreateObject.kt +++ b/src/test/kotlin/org/store/clothstar/member/util/CreateObject.kt @@ -3,10 +3,7 @@ package org.store.clothstar.member.util import org.store.clothstar.member.domain.* import org.store.clothstar.member.domain.vo.AddressInfo import org.store.clothstar.member.domain.vo.MemberShoppingActivity -import org.store.clothstar.member.dto.request.CreateAddressRequest -import org.store.clothstar.member.dto.request.CreateMemberRequest -import org.store.clothstar.member.dto.request.CreateSellerRequest -import org.store.clothstar.member.dto.request.MemberLoginRequest +import org.store.clothstar.member.dto.request.* class CreateObject { companion object { @@ -33,6 +30,15 @@ class CreateObject { ) } + fun getKakaoMemberRequest(name: String, telNo: String, code: String): KakaoMemberRequest { + return KakaoMemberRequest( + name = name, + telNo = telNo, + code = code, + email = null, + ) + } + fun getMemberLoginRequest(): MemberLoginRequest { return MemberLoginRequest( email = email, From b2349a3c204aae5d8798889fceada3f6c9f68397 Mon Sep 17 00:00:00 2001 From: subin Date: Fri, 6 Sep 2024 06:17:21 +0900 Subject: [PATCH 21/42] =?UTF-8?q?refactor:=20AuthenticationControllerTest?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 + .../kakaoLogin/controller/KakaoController.kt | 11 +- .../kakaoLogin/dto/KakaoTokenResponseDto.kt | 18 +- .../kakaoLogin/service/KakaoLoginService.kt | 4 +- .../AuthenticationControllerTest.kt | 282 +++++++++--------- 5 files changed, 163 insertions(+), 154 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a3c00ca..66af151 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -68,6 +68,8 @@ dependencies { testImplementation("io.kotest:kotest-runner-junit5:${kotestVersion}") testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") + testImplementation("com.squareup.okhttp3:okhttp:4.9.1") + testImplementation("com.squareup.okhttp3:mockwebserver:4.9.1") //oauth2 implementation ("org.springframework.boot:spring-boot-starter-oauth2-client") diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index c308dcb..7b6772a 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -12,18 +12,9 @@ import org.store.clothstar.kakaoLogin.service.KakaoLoginService class KakaoController( private val kakaoLoginService: KakaoLoginService, ) { - // 인가코드 받기 - 액세스 토큰 받기 - 사용자 정보 받기 + // 인가코드 받기 @GetMapping("/auth/kakao/callback") fun kakaoCallback(@RequestParam code: String): ResponseEntity { - // 액세스 토큰 받아오기 - 저장 나중에 하기 -// val accessToken = kakaoLoginService.getAccessToken(code) -// // 사용자 정보 받아오기 - 저장 나중에 하기 -// val userInfo = kakaoLoginService.getUserInfo(accessToken.accessToken!!) -// // 두 정보를 KakaoTokenUserInfoResponseDto로 합쳐서 반환 -// val tokenUserInfo = TokenUserInfoResponseDto( -// accessToken = accessToken, -// userInfo = userInfo -// ) return ResponseEntity.ok(code) } diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt index 4604643..29eb460 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenResponseDto.kt @@ -1,29 +1,31 @@ package org.store.clothstar.kakaoLogin.dto +import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty -class KakaoTokenResponseDto(s: String, accessToken: String, i: Int, s1: String, i1: Int) { +data class KakaoTokenResponseDto @JsonCreator constructor( + // 토큰 타입, bearer로 고정 @JsonProperty("token_type") - private val tokenType: String? = null + private val tokenType: String? = null, // 사용자 액세스 토큰 값 @JsonProperty("access_token") - val accessToken: String? = null + val accessToken: String? = null, // 액세스 토큰과 ID 토큰의 만료 시간(초) @JsonProperty("expires_in") - private val expiresIn: Int? = null + private val expiresIn: Int? = null, // 사용자 리프레시 토큰 값 @JsonProperty("refresh_token") - private val refreshToken: String? = null + private val refreshToken: String? = null, // 리프레시 토큰 만료 시간(초) @JsonProperty("refresh_token_expires_in") - private val refreshTokenExpiresIn: Int? = null + private val refreshTokenExpiresIn: Int? = null, // 인증된 사용자의 정보 조회 권한 범위 / 범위가 여러 개일 경우, 공백으로 구분 @JsonProperty("scope") - private val scope: String? = null -} \ No newline at end of file + private val scope: String? = null, +) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index 979cdcd..892c9b3 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -30,10 +30,10 @@ class KakaoLoginService { private lateinit var redirectUri: String @Value("\${spring.security.oauth2.client.provider.kakao.token_uri}") - private lateinit var tokenUri: String + lateinit var tokenUri: String @Value("\${spring.security.oauth2.client.provider.kakao.user_info_uri}") - private lateinit var userUri: String + lateinit var userUri: String // 토큰 가져오기 fun getAccessToken(code: String): KakaoTokenResponseDto { diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index 6b137ae..ea22142 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -1,134 +1,148 @@ -//package org.store.clothstar.member.authentication.controller -// -//import com.fasterxml.jackson.databind.ObjectMapper -//import org.junit.jupiter.api.DisplayName -//import org.junit.jupiter.api.Test -//import org.mockito.ArgumentMatchers.anyString -//import org.mockito.BDDMockito.given -//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.context.ActiveProfiles -//import org.springframework.test.web.servlet.MockMvc -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -//import org.springframework.transaction.annotation.Transactional -//import org.store.clothstar.common.config.redis.RedisUtil -//import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto -//import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto -//import org.store.clothstar.kakaoLogin.service.KakaoLoginService -//import org.store.clothstar.kakaoLogin.vo.KakaoAccount -//import org.store.clothstar.member.authentication.domain.SignUpType -//import org.store.clothstar.member.dto.request.SignUpRequest -//import org.store.clothstar.member.util.CreateObject -//import kotlin.test.assertEquals -// -//@SpringBootTest -//@AutoConfigureMockMvc -//@ActiveProfiles("test") -//@Transactional -//class AuthenticationControllerTest( -// @Autowired private val mockMvc: MockMvc, -// @Autowired private val objectMapper: ObjectMapper, -// @Autowired private val redisUtil: RedisUtil, -//) { -// @MockBean -// private lateinit var kakaoLoginService: KakaoLoginService -// -// private val MEMBER_URL = "/v1/members" -// private val LOGIN_URL = "/v1/members/login" -// -// @DisplayName("멤버 일반 회원가입 통합테스트후 로그인 통합테스트") -// @Test -// fun normalSignUpIntegrationTest() { -// //given -// //이메일과 인증번호로 redis 데이터 생성 -// val email = "test@naver.com" -// val certifyNum = redisUtil.createdCertifyNum() -// redisUtil.createRedisData(email, certifyNum) -// -// // 이메일과 인증번호로 create DTO 객체 생성 -// val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) -// val signUpRequest = SignUpRequest(createMemberRequest, null) -// val requestBody = objectMapper.writeValueAsString(signUpRequest) -// -// // when -// val actions = mockMvc.perform( -// post(MEMBER_URL) -// .param("signUpType", SignUpType.NORMAL.toString()) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// actions.andExpect(status().isCreated) -// -// //로그인 테스트 -// //given -// val memberLoginRequest = CreateObject.getMemberLoginRequest() -// val loginRequestBody = objectMapper.writeValueAsString(memberLoginRequest) -// -// val loginActions = mockMvc.perform( -// post(LOGIN_URL) -// .contentType(MediaType.APPLICATION_JSON) -// .content(loginRequestBody) -// ) -// -// loginActions.andExpect(status().isOk) -// } -// -// @DisplayName("멤버 카카오 회원가입 통합테스트") -// @Test -// fun kakaoSignUpIntegrationTest() { -// //given -// val name = "수빈" -// val telNo = "010-1231-0987" -// val code = "test_auth_code" -// val accessToken = "mock_access_token" -// val refreshToken = "mock_refresh_token" -// val email = "test@kakao.com" -// -// // KakaoLoginService가 반환할 Mock 데이터 설정 -// val kakaoTokenResponseDto = KakaoTokenResponseDto("bearer", accessToken, 1000000, refreshToken, 3600) -// val kakaoUserInfoResponseDto = KakaoUserInfoResponseDto( -// id = 12345L, -// kakaoAccount = KakaoAccount( -// email = email, -// ) -// ) -// -// // KakaoLoginService의 getAccessToken, getUserInfo 메서드에 대해 mock 설정 -// given(kakaoLoginService.getAccessToken(anyString())).willReturn(kakaoTokenResponseDto) -// given(kakaoLoginService.getUserInfo(anyString())).willReturn(kakaoUserInfoResponseDto) -// -// // when - 1: 카카오 콜백 호출 -// val callbackActions = mockMvc.perform( -// get("/auth/kakao/callback") -// .param("code", code) -// ) -// -//// assertEquals(code,"test_auth_code") -// -// // then - 1: 콜백 응답이 예상대로 이루어지는지 확인 -// callbackActions.andExpect(status().isOk) -// -// // when - 2: 해당 인가 코드로 회원가입 요청 -// val kakaoMemberRequest = CreateObject.getKakaoMemberRequest(name, telNo, code) -// val signUpRequest = SignUpRequest(null, kakaoMemberRequest) -// val requestBody = objectMapper.writeValueAsString(signUpRequest) -// -// //when -// val actions = mockMvc.perform( -// post(MEMBER_URL) -// .param("signUpType", SignUpType.KAKAO.toString()) -// .contentType(MediaType.APPLICATION_JSON) -// .content(requestBody) -// ) -// -// //then -// actions.andExpect(status().isCreated) -// } -//} \ No newline at end of file +package org.store.clothstar.member.authentication.controller + +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.DisplayName +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.transaction.annotation.Transactional +import org.store.clothstar.common.config.redis.RedisUtil +import org.store.clothstar.member.util.CreateObject +import com.fasterxml.jackson.databind.ObjectMapper +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.MediaType +import org.store.clothstar.kakaoLogin.service.KakaoLoginService +import org.store.clothstar.member.dto.request.KakaoMemberRequest +import org.store.clothstar.member.dto.request.SignUpRequest +import org.store.clothstar.member.authentication.domain.SignUpType + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("test") +@Transactional +class AuthenticationControllerTest( + @Autowired private val mockMvc: MockMvc, + @Autowired private val objectMapper: ObjectMapper, + @Autowired private val redisUtil: RedisUtil, + @Autowired private val kakaoLoginService: KakaoLoginService, +) { + private val MEMBER_URL = "/v1/members" + private val LOGIN_URL = "/v1/members/login" + + @DisplayName("멤버 일반 회원가입 통합테스트후 로그인 통합테스트") + @Test + fun normalSignUpIntegrationTest() { + //given + //이메일과 인증번호로 redis 데이터 생성 + val email = "test@naver.com" + val certifyNum = redisUtil.createdCertifyNum() + redisUtil.createRedisData(email, certifyNum) + + // 이메일과 인증번호로 create DTO 객체 생성 + val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) + val signUpRequest = SignUpRequest(createMemberRequest, null) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + // when + val actions = mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.NORMAL.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + actions.andExpect(status().isCreated) + + //로그인 테스트 + //given + val memberLoginRequest = CreateObject.getMemberLoginRequest() + val loginRequestBody = objectMapper.writeValueAsString(memberLoginRequest) + + val loginActions = mockMvc.perform( + post(LOGIN_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(loginRequestBody) + ) + + loginActions.andExpect(status().isOk) + } + + @DisplayName("멤버 카카오 회원가입 통합테스트후 로그인 통합테스트") + @Test + fun kakaoSignUpIntegrationTest() { + val mockWebServer = MockWebServer() + mockWebServer.start() + + //given + //KakaoLoginService의 tokenUri와 userUri를 MockWebServer로 설정 + kakaoLoginService.tokenUri = mockWebServer.url("/token").toString() + kakaoLoginService.userUri = mockWebServer.url("/user").toString() + + // 액세스 토큰 반환에 대한 응답 설정 + val tokenResponse = """ + { + "token_type": "bearer", + "access_token": "test_access_token", + "expires_in": 3600, + "refresh_token": "test_refresh_token", + "refresh_token_expires_in": 36000, + "scope": "account_email" + } + """ + + //사용자 정보 반환에 대한 응답 설정 + val userInfoResponse = """ + { + "id": 123456789, + "kakao_account": { + "email": "test@example.com" + } + } + """ + + //토큰 응답 추가(첫 번째 응답) + mockWebServer.enqueue( + MockResponse() + .setBody(tokenResponse) + .addHeader("Content-Type", "application/json") + ) + + //사용자 정보 응답 추가(두 번째 응답) + mockWebServer.enqueue( + MockResponse() + .setBody(userInfoResponse) + .addHeader("Content-Type", "application/json") + ) + + //요청 DTO 생성 + val kakaoMemberRequest = KakaoMemberRequest( + name = "Test User", + telNo = "010-1234-5678", + email = null, + code = "test_code" + ) + + //requestBody 설정 + val signUpRequest = SignUpRequest(null, kakaoMemberRequest) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + //when + val actions = mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.KAKAO.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + + //then + actions.andExpect(status().isCreated) + + mockWebServer.shutdown() + } +} \ No newline at end of file From e11e842fc35156d9f3e2bfa49e6989b8122da1a9 Mon Sep 17 00:00:00 2001 From: subin Date: Fri, 6 Sep 2024 06:18:25 +0900 Subject: [PATCH 22/42] refactor: Optimize Imports & Reformat Code --- .../common/config/SecurityConfiguration.kt | 4 ++-- .../error/exception/InvalidSignupMemberRequest.kt | 2 +- .../common/error/exception/InvalidSignupType.kt | 2 +- .../controller/AuthenticationController.kt | 1 + src/main/resources/application.yml | 2 +- .../controller/AuthenticationControllerTest.kt | 14 +++++++------- .../controller/MemberSignupValidTest.kt | 12 +++++++++--- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt index d7ac723..f777fd3 100644 --- a/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt +++ b/src/main/kotlin/org/store/clothstar/common/config/SecurityConfiguration.kt @@ -68,10 +68,10 @@ class SecurityConfiguration( "/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", - "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", + "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**", "config-service/**", - "/auth/**","/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", + "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", "/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO", "/v1/members?signUpType=KAKAO", "/v1/members/**", diff --git a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt index f28fe6a..039a8dd 100644 --- a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt +++ b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupMemberRequest.kt @@ -2,6 +2,6 @@ package org.store.clothstar.common.error.exception import org.store.clothstar.common.error.ErrorCode -class InvalidSignupMemberRequest ( +class InvalidSignupMemberRequest( val errorCode: ErrorCode ) : RuntimeException(errorCode.message) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt index 01de8cd..f88946b 100644 --- a/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt +++ b/src/main/kotlin/org/store/clothstar/common/error/exception/InvalidSignupType.kt @@ -2,6 +2,6 @@ package org.store.clothstar.common.error.exception import org.store.clothstar.common.error.ErrorCode -class InvalidSignupType ( +class InvalidSignupType( val errorCode: ErrorCode ) : RuntimeException(errorCode.message) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt index 9be7b6d..b69a459 100644 --- a/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt +++ b/src/main/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationController.kt @@ -90,6 +90,7 @@ class AuthenticationController( ?: throw InvalidSignupMemberRequest(ErrorCode.INVALID_SIGNUP_MEMBER_REQUEST) signUpService.signUp(kakaoMemberRequest) } + else -> throw InvalidSignupType(ErrorCode.INVLIAD_SIGNUP_TYPE) } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f5dc2f5..6a63d05 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,7 @@ spring: port: 6379 duration: 600 -#oauth2 + #oauth2 security: oauth2: client: diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index ea22142..ef18e54 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -1,25 +1,25 @@ package org.store.clothstar.member.authentication.controller +import com.fasterxml.jackson.databind.ObjectMapper import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer 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.http.MediaType import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import org.springframework.transaction.annotation.Transactional import org.store.clothstar.common.config.redis.RedisUtil -import org.store.clothstar.member.util.CreateObject -import com.fasterxml.jackson.databind.ObjectMapper -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.MediaType import org.store.clothstar.kakaoLogin.service.KakaoLoginService +import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.dto.request.KakaoMemberRequest import org.store.clothstar.member.dto.request.SignUpRequest -import org.store.clothstar.member.authentication.domain.SignUpType +import org.store.clothstar.member.util.CreateObject @SpringBootTest @AutoConfigureMockMvc diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt index 983afce..d2012cd 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/MemberSignupValidTest.kt @@ -57,7 +57,9 @@ class MemberSignupValidTest( //then actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap['createMemberRequest.password']").value("비밀번호는 최소 8자 이상이어야 합니다.")) + actions.andExpect( + MockMvcResultMatchers.jsonPath("$.errorMap['createMemberRequest.password']").value("비밀번호는 최소 8자 이상이어야 합니다.") + ) } @DisplayName("회원가입시 이름은 필수 값이다.") @@ -85,7 +87,9 @@ class MemberSignupValidTest( //then Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.name']").value("이름은 비어 있을 수 없습니다.")) + actions.andExpect( + MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.name']").value("이름은 비어 있을 수 없습니다.") + ) } @DisplayName("회원가입시 전화번호 양식이 지켜져야 한다.") @@ -113,7 +117,9 @@ class MemberSignupValidTest( //then Assertions.assertThat(createMemberRequest.password.length).isLessThan(8) actions.andExpect(status().is4xxClientError()) - actions.andExpect(MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.telNo']").value("유효하지 않은 전화번호 형식입니다.")) + actions.andExpect( + MockMvcResultMatchers.jsonPath("$.errorMap.['createMemberRequest.telNo']").value("유효하지 않은 전화번호 형식입니다.") + ) } @DisplayName("비밀번호 변경 요청시에도 비밀번호는 8자리 이상이여야 한다.") From d2b9c59494cc7c97a66c8f1eab2120d2bd4bb228 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 17 Aug 2024 15:16:52 +0900 Subject: [PATCH 23/42] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20mockK=20=ED=98=95=EC=8B=9D=EC=9C=BC?= =?UTF-8?q?=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 --- .../order/service/OrderUserServiceTest.kt | 257 ++++++++++++++++-- 1 file changed, 228 insertions(+), 29 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index c1294fc..8276319 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -1,60 +1,259 @@ package org.store.clothstar.order.service -import io.kotest.core.spec.style.BehaviorSpec -import io.kotest.matchers.shouldBe import io.mockk.every +import io.mockk.impl.annotations.InjectMockKs +import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.justRun -import io.mockk.mockk import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.springframework.data.repository.findByIdOrNull +import org.store.clothstar.common.error.ErrorCode +import org.store.clothstar.common.error.exception.order.OrderNotFoundException +import org.store.clothstar.member.domain.Address +import org.store.clothstar.member.domain.Member +import org.store.clothstar.member.domain.Seller +import org.store.clothstar.member.domain.vo.AddressInfo import org.store.clothstar.member.service.AddressService import org.store.clothstar.member.service.MemberService import org.store.clothstar.member.service.SellerService import org.store.clothstar.order.domain.Order +import org.store.clothstar.order.domain.OrderDetail +import org.store.clothstar.order.domain.vo.OrderDetailDTO +import org.store.clothstar.order.domain.vo.Price import org.store.clothstar.order.domain.vo.Status +import org.store.clothstar.order.domain.vo.TotalPrice +import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository import org.store.clothstar.order.service.OrderSave.OrderSaveFacade +import org.store.clothstar.product.domain.Item +import org.store.clothstar.product.domain.Product import org.store.clothstar.product.service.ItemService import org.store.clothstar.product.service.ProductService +import java.time.LocalDateTime +import kotlin.test.Test @ExtendWith(MockKExtension::class) -class OrderUserServiceTest : BehaviorSpec({ - - val orderRepository: OrderRepository = mockk() - val orderDetailRepository: OrderDetailRepository = mockk() - val memberService: MemberService = mockk() - val sellerService: SellerService = mockk() - val addressService: AddressService = mockk() - val itemService: ItemService = mockk() - val productService: ProductService = mockk() - val orderSaveFacade: OrderSaveFacade = mockk() - val order: Order = mockk() - - val orderService = OrderUserService( - orderSaveFacade, orderRepository, orderDetailRepository, memberService, addressService, sellerService, - productService, itemService - ) +class OrderUserServiceTest { + @InjectMockKs + lateinit var orderUserService: OrderUserService + + @MockK + lateinit var orderRepository: OrderRepository + + @MockK + lateinit var orderDetailRepository: OrderDetailRepository + + @MockK + lateinit var memberService: MemberService + + @MockK + lateinit var sellerService: SellerService + + @MockK + lateinit var addressService: AddressService + + @MockK + lateinit var itemService: ItemService + + @MockK + lateinit var productService: ProductService + + @MockK + lateinit var orderSaveFacade: OrderSaveFacade + + @MockK + lateinit var order: Order + + @MockK + lateinit var orderDetail: OrderDetail + + @MockK + lateinit var member: Member + + @MockK + lateinit var seller: Seller + + @MockK + lateinit var address: Address + + @MockK + lateinit var orderDetails: List + + @MockK + lateinit var product: Product + + @MockK + lateinit var item: Item + + @MockK + lateinit var addressInfo: AddressInfo + + @MockK + lateinit var totalPrice: TotalPrice + + @MockK + lateinit var price: Price val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" - Given("구매자 구매 확정 - 성공 테스트") { + // 단일 주문 조회 - getOrder + @Test + @DisplayName("단일 주문 조회 - 성공 테스트") + fun getOrder_success_test() { + //given + val memberId = 1L + val addressId = 2L + val productId = 3L + val itemId = 4L + + every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns order + every { order.memberId } returns memberId + every { order.addressId } returns addressId + every { order.createdAt } returns LocalDateTime.now() + every { order.orderDetails } returns mutableListOf(orderDetail) + + every { memberService.getMemberByMemberId(memberId) } returns member + every { addressService.getAddressById(addressId) } returns address + every { address.addressInfo } returns addressInfo + every { order.totalPrice } returns totalPrice + every { orderDetail.price } returns price + every { orderDetail.deletedAt } returns null + every { orderDetail.itemId } returns itemId + every { orderDetail.productId } returns productId + + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) + every { item.itemId } returns itemId + every { product.productId } returns productId + every { seller.brandName} returns "brandName" + + val orderResponse = OrderResponse.from(order,member,address) + + + //when + val result = orderUserService.getOrder(orderId) + + // then + verify(exactly = 1) { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } + } + + // 구매자 구매 확정 - completeOrder + @Test + @DisplayName("구매자 구매 확정 - 성공 테스트") + fun completeOrder_success_test() { + //given every { order.status } returns Status.DELIVERED every { orderRepository.findByIdOrNull(orderId) } returns order justRun { order.validateForStatusDELIVEREDAndDeletedAt() } every { order.updateStatus(Status.COMPLETED) } answers { every { order.status } returns Status.COMPLETED } - When("orderId가 존재하면서 정상 요청이 오면") { - orderService.completeOrder(orderId) - Then("정상 응답") { - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } - verify(exactly = 1) { order.validateForStatusDELIVEREDAndDeletedAt() } - verify(exactly = 1) { order.updateStatus(Status.COMPLETED) } - order.status shouldBe Status.COMPLETED - } + + //when + orderUserService.completeOrder(orderId) + + //then + verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { order.validateForStatusDELIVEREDAndDeletedAt() } + verify(exactly = 1) { order.updateStatus(Status.COMPLETED) } + assertEquals(Status.COMPLETED, order.status) + } + + @Test + @DisplayName("구매자 구매 확정 - 주문번호가 존재하지 않을 때 예외처리 테스트") + fun completeOrder_orderNotFound_exception_test() { + //given + every { order.status } returns Status.DELIVERED + every { orderRepository.findByIdOrNull(orderId) } returns null + + //when & then + val exception = assertThrows { + orderUserService.completeOrder(orderId) + } + assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) + } + + // 구매자 주문 취소 - cancelOrder + @Test + @DisplayName("구매자 주문 취소 - 성공 테스트") + fun cancelOrder_success_test() { + //given + every { order.status } returns Status.CONFIRMED + every { orderRepository.findByIdOrNull(orderId) } returns order + justRun { order.validateForStatusCONFIRMEDAndDeletedAt() } + every { order.updateStatus(Status.CANCELED) } answers { + every { order.status } returns Status.CANCELED + } + + //when + orderUserService.cancelOrder(orderId) + + //then + verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { order.validateForStatusCONFIRMEDAndDeletedAt() } + verify(exactly = 1) { order.updateStatus(Status.CANCELED) } + assertEquals(Status.CANCELED, order.status) + } + + @Test + @DisplayName("구매자 주문 취소 - 주문번호가 존재하지 않을 때 예외처리 테스트") + fun cancelOrder_orderNotFound_exception_test() { + //given + every { order.status } returns Status.CONFIRMED + every { orderRepository.findByIdOrNull(orderId) } returns null + + //when & then + val exception = assertThrows { + orderUserService.cancelOrder(orderId) + } + assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) + } + + // 주문 삭제 - updateDeleteAt + @Test + @DisplayName("주문 삭제 - 성공 테스트") + fun updateDeleteAt_success_test() { + //given + every { orderRepository.findByIdOrNull(orderId) } returns order + justRun { order.validateForDeletedAt() } + every { orderDetailRepository.findOrderDetailListByOrderId(orderId) } returns listOf(orderDetail) + every { orderDetail.updateDeletedAt() } answers { + every { orderDetail.deletedAt } returns LocalDateTime.now() + } + every { order.updateDeletedAt() } answers { + every { order.deletedAt } returns LocalDateTime.now() + } + + //when + orderUserService.updateDeleteAt(orderId) + + //then + verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { order.validateForDeletedAt() } + verify(exactly = 1) { orderDetailRepository.findOrderDetailListByOrderId(orderId) } + verify(exactly = 1) { orderDetail.updateDeletedAt() } + verify(exactly = 1) { order.updateDeletedAt() } + + assertNotNull(order.deletedAt) + assertNotNull(orderDetail.deletedAt) + } + + @Test + @DisplayName("주문 삭제 - 주문번호가 존재하지 않을 때 예외처리 테스트") + fun updateDeleteAt_orderNotFound_exception_test() { + //given + every { orderRepository.findByIdOrNull(orderId) } returns null + + //when & then + val exception = assertThrows { + orderUserService.updateDeleteAt(orderId) } + assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } -}) \ No newline at end of file +} \ No newline at end of file From 3a4772355c1d2a259200412a9bcc9027012ddadd Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 17 Aug 2024 16:05:05 +0900 Subject: [PATCH 24/42] =?UTF-8?q?test:=20=EB=8B=A8=EC=9D=BC=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=EC=A1=B0=ED=9A=8C=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserServiceTest.kt | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 8276319..d28b746 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -6,6 +6,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.justRun import io.mockk.verify +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.DisplayName @@ -23,10 +24,7 @@ import org.store.clothstar.member.service.MemberService import org.store.clothstar.member.service.SellerService import org.store.clothstar.order.domain.Order import org.store.clothstar.order.domain.OrderDetail -import org.store.clothstar.order.domain.vo.OrderDetailDTO -import org.store.clothstar.order.domain.vo.Price -import org.store.clothstar.order.domain.vo.Status -import org.store.clothstar.order.domain.vo.TotalPrice +import org.store.clothstar.order.domain.vo.* import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository @@ -113,34 +111,75 @@ class OrderUserServiceTest { val itemId = 4L every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns order + every { order.orderId } returns orderId every { order.memberId } returns memberId every { order.addressId } returns addressId every { order.createdAt } returns LocalDateTime.now() every { order.orderDetails } returns mutableListOf(orderDetail) + every { order.status } returns Status.CONFIRMED + every { order.paymentMethod } returns PaymentMethod.CARD every { memberService.getMemberByMemberId(memberId) } returns member + every { member.name } returns "수빈" every { addressService.getAddressById(addressId) } returns address + every { sellerService.getSellerById(memberId) } returns seller + every { address.telNo } returns "010-1111-1111" + every { address.deliveryRequest } returns "문앞" every { address.addressInfo } returns addressInfo every { order.totalPrice } returns totalPrice + every { orderDetail.orderDetailId } returns 1L + every { orderDetail.quantity } returns 1 every { orderDetail.price } returns price every { orderDetail.deletedAt } returns null every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId + every { address.receiverName } returns "수빈" + every { addressInfo.addressBasic } returns "address1" + every { addressInfo.addressDetail } returns "address2" + every { addressInfo.zipNo } returns "123-123" + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) every { item.itemId } returns itemId + every { item.finalPrice } returns 10000 + every { item.name } returns "상품옵션이름" every { product.productId } returns productId + every { product.price } returns 1000 + every { product.name } returns "상품이름" every { seller.brandName} returns "brandName" - val orderResponse = OrderResponse.from(order,member,address) + every { totalPrice.shipping } returns 3000 + every { totalPrice.products } returns 5000 + every { totalPrice.payment } returns 8000 + every { price.fixedPrice } returns 10000 + every { price.oneKindTotalPrice } returns 10000 + val expectedorderResponse = OrderResponse.from(order,member,address) + expectedorderResponse.updateOrderDetailList(listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName))) //when - val result = orderUserService.getOrder(orderId) + val orderReponse = orderUserService.getOrder(orderId) // then + assertThat(orderReponse).usingRecursiveComparison().isEqualTo(expectedorderResponse) verify(exactly = 1) { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } + verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } + verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } + } + + @Test + @DisplayName("단일 주문 조회 - 주문번호가 존재하지 않을 때 예외처리 테스트") + fun getOrder_orderNotFound_exception_test() { + //given + every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns null + + //when & then + val exception = assertThrows { + orderUserService.getOrder(orderId) + } + assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } // 구매자 구매 확정 - completeOrder From 5e6a58070168f4c5ad9d8209ca91ca3e7036c3b1 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 17 Aug 2024 16:44:54 +0900 Subject: [PATCH 25/42] =?UTF-8?q?test:=20=EB=8B=A8=EC=9D=BC=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=EC=A1=B0=ED=9A=8C=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserService.kt | 6 +- .../order/service/OrderUserServiceTest.kt | 67 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt index 51e100f..467972e 100644 --- a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt +++ b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt @@ -118,10 +118,8 @@ class OrderUserService( // Map으로부터 Id, Entity를 가져오면서 주문상세 DTO 리스트 만들기 val orderDetailDTOList: List = orderDetails.map { - val product: Product = productMap[it.productId] - ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found") - val item: Item = itemMap[it.itemId] - ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Item not found") + val product: Product = productMap[it.productId]!! + val item: Item = itemMap[it.itemId]!! val brandName: String = seller.brandName OrderDetailDTO.from(it, item, product, brandName) } diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index d28b746..294752f 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -110,53 +110,60 @@ class OrderUserServiceTest { val productId = 3L val itemId = 4L + // orderId 관련 order, member, address, seller 불러오기 every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns order - every { order.orderId } returns orderId every { order.memberId } returns memberId every { order.addressId } returns addressId - every { order.createdAt } returns LocalDateTime.now() - every { order.orderDetails } returns mutableListOf(orderDetail) - every { order.status } returns Status.CONFIRMED - every { order.paymentMethod } returns PaymentMethod.CARD - every { memberService.getMemberByMemberId(memberId) } returns member - every { member.name } returns "수빈" every { addressService.getAddressById(addressId) } returns address every { sellerService.getSellerById(memberId) } returns seller - every { address.telNo } returns "010-1111-1111" - every { address.deliveryRequest } returns "문앞" - every { address.addressInfo } returns addressInfo - every { order.totalPrice } returns totalPrice - every { orderDetail.orderDetailId } returns 1L - every { orderDetail.quantity } returns 1 - every { orderDetail.price } returns price - every { orderDetail.deletedAt } returns null - every { orderDetail.itemId } returns itemId - every { orderDetail.productId } returns productId + every { totalPrice.shipping } returns 3000 + every { totalPrice.products } returns 5000 + every { totalPrice.payment } returns 8000 + every { order.orderId } returns orderId + every { member.name } returns "수빈" + every { order.createdAt } returns LocalDateTime.now() + every { order.status } returns Status.CONFIRMED + every { order.paymentMethod } returns PaymentMethod.CARD + every { order.totalPrice } returns totalPrice + every { address.addressInfo } returns addressInfo every { address.receiverName } returns "수빈" every { addressInfo.addressBasic } returns "address1" every { addressInfo.addressDetail } returns "address2" - every { addressInfo.zipNo } returns "123-123" + every { address.telNo } returns "010-1111-1111" + every { address.deliveryRequest } returns "문앞" - every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + // 응답 DTO 생성(주문상세 리스트는 빈 상태) + val expectedorderResponse = OrderResponse.from(order,member,address) + + // productIds, itemIds로부터 Product/Item 리스트 가져오기 + every { orderDetail.itemId } returns itemId + every { orderDetail.productId } returns productId every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) - every { item.itemId } returns itemId - every { item.finalPrice } returns 10000 - every { item.name } returns "상품옵션이름" - every { product.productId } returns productId - every { product.price } returns 1000 + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + + every { orderDetail.orderDetailId } returns 1L every { product.name } returns "상품이름" + every { item.name } returns "상품옵션이름" every { seller.brandName} returns "brandName" - - every { totalPrice.shipping } returns 3000 - every { totalPrice.products } returns 5000 - every { totalPrice.payment } returns 8000 + every { product.price } returns 1000 + every { orderDetail.quantity } returns 1 + every { orderDetail.price } returns price + every { item.finalPrice } returns 10000 every { price.fixedPrice } returns 10000 every { price.oneKindTotalPrice } returns 10000 - val expectedorderResponse = OrderResponse.from(order,member,address) - expectedorderResponse.updateOrderDetailList(listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName))) + every { order.orderDetails } returns mutableListOf(orderDetail) + every { orderDetail.deletedAt } returns null + every { item.itemId } returns itemId + every { product.productId } returns productId + + // 주문상세 DTO 리스트 만들기 + val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName)) + + // 응답 DTO에 주문상세 DTO 리스트 추가 + expectedorderResponse.updateOrderDetailList(orderDetailDTOs) //when val orderReponse = orderUserService.getOrder(orderId) From 0777a313e6d98cf3d4870905567a47103ac477d1 Mon Sep 17 00:00:00 2001 From: subin Date: Sun, 18 Aug 2024 22:33:19 +0900 Subject: [PATCH 26/42] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=9D=BC=20?= =?UTF-8?q?=EC=A3=BC=EB=AC=B8=20=EC=A1=B0=ED=9A=8C=20=EB=8B=A8=EC=9C=84?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserService.kt | 7 +- .../order/service/OrderSellerServiceTest.kt | 7 + .../order/service/OrderUserServiceTest.kt | 239 ++++++++++++++++-- 3 files changed, 235 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt index 467972e..6cb8ae1 100644 --- a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt +++ b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt @@ -10,6 +10,7 @@ import org.springframework.transaction.annotation.Transactional import org.springframework.web.server.ResponseStatusException import org.store.clothstar.common.error.ErrorCode import org.store.clothstar.common.error.exception.order.InsufficientStockException +import org.store.clothstar.common.error.exception.order.InvalidOrderStatusException import org.store.clothstar.common.error.exception.order.OrderNotFoundException import org.store.clothstar.member.domain.Address import org.store.clothstar.member.domain.Member @@ -73,9 +74,9 @@ class OrderUserService( // Map으로부터 Id, Entity를 가져오면서 주문상세 DTO 리스트 만들기 val orderDetailDTOList: List = orderDetails.map { - val product: Product = productMap[it.productId] + val product: Product = productMap[it.productId]!! ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found") - val item: Item = itemMap[it.itemId] + val item: Item = itemMap[it.itemId]!! ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Item not found") val brandName: String = seller.brandName OrderDetailDTO.from(it, item, product, brandName) @@ -195,7 +196,7 @@ class OrderUserService( } if (order.status != Status.CONFIRMED) { - throw ResponseStatusException(HttpStatus.BAD_REQUEST, "이미 입금된 상태에서는 추가 주문이 불가능합니다.") + throw InvalidOrderStatusException(ErrorCode.INVALID_ORDER_STATUS_CONFIRMED) } val orderDetail = addOrderDetailRequest.toOrderDetail(order, product, item) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt index 85f70cd..7503206 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt @@ -51,6 +51,13 @@ class OrderSellerServiceTest { val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + // 판매자 주문(CONFIRMED) 리스트 조회 + @Test + @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - 성공 테스트") + fun addOrderDetail_success_test() { + + } + // 판매자 주문 승인 - approveOrder @Test @DisplayName("판매자 주문 승인 - 성공 테스트") diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 294752f..bbbd6bb 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -1,19 +1,22 @@ package org.store.clothstar.order.service -import io.mockk.every +import io.mockk.* import io.mockk.impl.annotations.InjectMockKs import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension -import io.mockk.justRun -import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.data.domain.Page +import org.springframework.data.domain.PageImpl +import org.springframework.data.domain.Pageable import org.springframework.data.repository.findByIdOrNull import org.store.clothstar.common.error.ErrorCode +import org.store.clothstar.common.error.exception.order.InsufficientStockException +import org.store.clothstar.common.error.exception.order.InvalidOrderStatusException import org.store.clothstar.common.error.exception.order.OrderNotFoundException import org.store.clothstar.member.domain.Address import org.store.clothstar.member.domain.Member @@ -25,6 +28,7 @@ import org.store.clothstar.member.service.SellerService import org.store.clothstar.order.domain.Order import org.store.clothstar.order.domain.OrderDetail import org.store.clothstar.order.domain.vo.* +import org.store.clothstar.order.dto.request.AddOrderDetailRequest import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository @@ -34,6 +38,7 @@ import org.store.clothstar.product.domain.Product import org.store.clothstar.product.service.ItemService import org.store.clothstar.product.service.ProductService import java.time.LocalDateTime +import java.util.* import kotlin.test.Test @ExtendWith(MockKExtension::class) @@ -98,18 +103,20 @@ class OrderUserServiceTest { @MockK lateinit var price: Price + @MockK + lateinit var pageable: Pageable + val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + val memberId = 1L + val addressId = 2L + val productId = 3L + val itemId = 4L // 단일 주문 조회 - getOrder @Test @DisplayName("단일 주문 조회 - 성공 테스트") fun getOrder_success_test() { //given - val memberId = 1L - val addressId = 2L - val productId = 3L - val itemId = 4L - // orderId 관련 order, member, address, seller 불러오기 every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns order every { order.memberId } returns memberId @@ -137,28 +144,28 @@ class OrderUserServiceTest { // 응답 DTO 생성(주문상세 리스트는 빈 상태) val expectedorderResponse = OrderResponse.from(order,member,address) + every { order.orderDetails } returns mutableListOf(orderDetail) + every { orderDetail.deletedAt } returns null + // productIds, itemIds로부터 Product/Item 리스트 가져오기 every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { item.itemId } returns itemId + every { product.productId } returns productId + every { orderDetail.orderDetailId } returns 1L every { product.name } returns "상품이름" every { item.name } returns "상품옵션이름" - every { seller.brandName} returns "brandName" + every { seller.brandName } returns "brandName" every { product.price } returns 1000 every { orderDetail.quantity } returns 1 every { orderDetail.price } returns price every { item.finalPrice } returns 10000 - every { price.fixedPrice } returns 10000 every { price.oneKindTotalPrice } returns 10000 - every { order.orderDetails } returns mutableListOf(orderDetail) - every { orderDetail.deletedAt } returns null - every { item.itemId } returns itemId - every { product.productId } returns productId - // 주문상세 DTO 리스트 만들기 val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName)) @@ -189,6 +196,208 @@ class OrderUserServiceTest { assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } +// // 전체 주문 페이징 조회 +// @Test +// @DisplayName("전체 주문 페이징 조회 offset 방식 - 성공 테스트") +// fun getAllOrderOffsetPaging_success_test() { +// //given +// val orders = listOf(order) +//// every { pageable.offset } returns 0L +// every { pageable.pageSize } returns 10 +// every { pageable.toOptional() } returns Optional.of(pageable) +// val ordersPage: Page = PageImpl(orders,pageable,orders.size.toLong()) +// every { orderRepository.findAll(pageable) } returns ordersPage +// +// // order 관련 member, address, seller 불러오기 +// every { order.memberId } returns memberId +// every { order.addressId } returns addressId +// every { memberService.getMemberByMemberId(memberId) } returns member +// every { addressService.getAddressById(addressId) } returns address +// every { sellerService.getSellerById(memberId) } returns seller +// +// every { totalPrice.shipping } returns 3000 +// every { totalPrice.products } returns 5000 +// every { totalPrice.payment } returns 8000 +// every { order.orderId } returns orderId +// every { member.name } returns "수빈" +// every { order.createdAt } returns LocalDateTime.now() +// every { order.status } returns Status.CONFIRMED +// every { order.paymentMethod } returns PaymentMethod.CARD +// every { order.totalPrice } returns totalPrice +// every { address.addressInfo } returns addressInfo +// every { address.receiverName } returns "수빈" +// every { addressInfo.addressBasic } returns "address1" +// every { addressInfo.addressDetail } returns "address2" +// every { address.telNo } returns "010-1111-1111" +// every { address.deliveryRequest } returns "문앞" +// +// // 응답 DTO 생성(주문상세 리스트는 빈 상태) +// val expectedorderResponse = OrderResponse.from(order,member,address) +// every { order.orderDetails } returns mutableListOf(orderDetail) +// every { orderDetails.size } returns 3 +// +//// every { orderDetails.iterator() } returns +// +// // productIds, itemIds로부터 Product/Item 리스트 가져오기 +// every { orderDetail.itemId } returns itemId +// every { orderDetail.productId } returns productId +// every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) +// every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) +// +// every { orderDetail.orderDetailId } returns 1L +// every { product.name } returns "상품이름" +// every { item.name } returns "상품옵션이름" +// every { seller.brandName} returns "brandName" +// every { product.price } returns 1000 +// every { orderDetail.quantity } returns 1 +// every { orderDetail.price } returns price +// every { item.finalPrice } returns 10000 +// every { price.fixedPrice } returns 10000 +// every { price.oneKindTotalPrice } returns 10000 +// +// every { order.orderDetails } returns mutableListOf(orderDetail) +// every { orderDetail.deletedAt } returns null +// every { item.itemId } returns itemId +// every { product.productId } returns productId +// +// // 주문상세 DTO 리스트 만들기 +// val orderDetils: List = listOf(orderDetail) +// val orderDetailDTOs: List = orderDetails.map{ +// it -> OrderDetailDTO.from(it,item,product,seller.brandName) +// } +// +// // 응답 DTO에 주문상세 DTO 리스트 추가 +// expectedorderResponse.updateOrderDetailList(orderDetailDTOs) +// +// //when +// val orderReponses: Page = orderUserService.getAllOrderOffsetPaging(pageable) +// +// // then +// assertThat(orderReponses.content.get(0)).usingRecursiveComparison().isEqualTo(expectedorderResponse) +// verify(exactly = 1) { orderRepository.findAll(pageable) } +// verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } +// verify(exactly = 1) { addressService.getAddressById(addressId) } +// verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } +// verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } +// } +// +// @Test +// @DisplayName("전체 주문 페이징 조회 slice 방식 - 성공 테스트") +// fun getAllOrderSlicePaging_success_test() { +// +// } + +// @Test +// @DisplayName("주문 상세 추가 - 성공 테스트") +// fun addOrderDetail_success_test() { +// val addOrderDetailRequest = AddOrderDetailRequest( +// orderId = orderId, +// productId = productId, +// itemId = itemId, +// quantity = 1, +// ) +// every { orderRepository.findByIdOrNull(orderId) } returns order +// every { productService.getProductById(productId) } returns product +// every { itemService.getItemById(itemId) } returns item +// every { item.stock } returns 10 +// every { order.status } returns Status.CONFIRMED +// +// every { product.price } returns 1000 +// every { product.productId } returns productId +// every { item.itemId } returns itemId +// every { orderDetail.order } returns order +// every { orderDetail.quantity } returns addOrderDetailRequest.quantity +// every { orderDetail.price } returns price +// every { orderDetail.orderDetailId } returns 123L +// every { orderDetail.price.oneKindTotalPrice } returns 10000 +// +// every { addOrderDetailRequest.toOrderDetail(order, product, item) } returns orderDetail +// justRun { orderDetailRepository.save(orderDetail) } +// +// every { order.totalPrice } returns totalPrice +// every { totalPrice.products } returns 10000 +// every { totalPrice.shipping } returns 3000 +// every { totalPrice.payment } returns 130000 +// +// val newTotalProductsPrice = order.totalPrice.products + orderDetail.price.oneKindTotalPrice +// val newTotalPaymentPrice = +// order.totalPrice.products + order.totalPrice.shipping + orderDetail.price.oneKindTotalPrice +// +// justRun { order.totalPrice.updatePrices(newTotalProductsPrice,newTotalPaymentPrice) } +// justRun { orderUserService.updateProductStock(item, orderDetail.quantity) } +// +// //when +// val orderDetailId = orderUserService.addOrderDetail(addOrderDetailRequest) +// +// //then +// assertEquals(orderDetailId, 123L) +// verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } +// } + + @Test + @DisplayName("주문 상세 추가 - 주문번호가 존재하지 않을 때 예외처리 테스트") + fun addOrderDetail_orderNotFound_exception_test() { + //given + val addOrderDetailRequest = AddOrderDetailRequest( + orderId = orderId, + productId = productId, + itemId = itemId, + quantity = 1, + ) + every { orderRepository.findByIdOrNull(orderId) } returns null + + //when & then + val exception = assertThrows { + orderUserService.addOrderDetail(addOrderDetailRequest) + } + assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) + } + + @Test + @DisplayName("주문 상세 추가 - 주문수량이 재고보다 더 많을 때 예외처리 테스트") + fun addOrderDetail_insufficientStock_exception_test() { + //given + val addOrderDetailRequest = AddOrderDetailRequest( + orderId = orderId, + productId = productId, + itemId = itemId, + quantity = 100, + ) + every { orderRepository.findByIdOrNull(orderId) } returns order + every { productService.getProductById(productId) } returns product + every { itemService.getItemById(itemId) } returns item + every { item.stock } returns 10 + + //when & then + val exception = assertThrows { + orderUserService.addOrderDetail(addOrderDetailRequest) + } + assertEquals(ErrorCode.INSUFFICIENT_STOCK, exception.errorCode) + } + + @Test + @DisplayName("주문 상세 추가 - 주문상태가 CONFIRMED가 아닐 때 예외처리 테스트") + fun addOrderDetail_invalidOrderStatusException_exception_test() { + //given + val addOrderDetailRequest = AddOrderDetailRequest( + orderId = orderId, + productId = productId, + itemId = itemId, + quantity = 1, + ) + every { orderRepository.findByIdOrNull(orderId) } returns order + every { productService.getProductById(productId) } returns product + every { itemService.getItemById(itemId) } returns item + every { item.stock } returns 10 + every { order.status } returns Status.DELIVERED + + //when & then + val exception = assertThrows { + orderUserService.addOrderDetail(addOrderDetailRequest) + } + assertEquals(ErrorCode.INVALID_ORDER_STATUS_CONFIRMED, exception.errorCode) + } + // 구매자 구매 확정 - completeOrder @Test @DisplayName("구매자 구매 확정 - 성공 테스트") From f3d33e2fbe48bb881ca8e8e96007d0e4cb47d674 Mon Sep 17 00:00:00 2001 From: subin Date: Mon, 19 Aug 2024 00:57:51 +0900 Subject: [PATCH 27/42] =?UTF-8?q?test:=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=B6=94=EA=B0=80=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserServiceTest.kt | 93 ++++++++++--------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index bbbd6bb..d5d7b1e 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -287,52 +287,53 @@ class OrderUserServiceTest { // // } -// @Test -// @DisplayName("주문 상세 추가 - 성공 테스트") -// fun addOrderDetail_success_test() { -// val addOrderDetailRequest = AddOrderDetailRequest( -// orderId = orderId, -// productId = productId, -// itemId = itemId, -// quantity = 1, -// ) -// every { orderRepository.findByIdOrNull(orderId) } returns order -// every { productService.getProductById(productId) } returns product -// every { itemService.getItemById(itemId) } returns item -// every { item.stock } returns 10 -// every { order.status } returns Status.CONFIRMED -// -// every { product.price } returns 1000 -// every { product.productId } returns productId -// every { item.itemId } returns itemId -// every { orderDetail.order } returns order -// every { orderDetail.quantity } returns addOrderDetailRequest.quantity -// every { orderDetail.price } returns price -// every { orderDetail.orderDetailId } returns 123L -// every { orderDetail.price.oneKindTotalPrice } returns 10000 -// -// every { addOrderDetailRequest.toOrderDetail(order, product, item) } returns orderDetail -// justRun { orderDetailRepository.save(orderDetail) } -// -// every { order.totalPrice } returns totalPrice -// every { totalPrice.products } returns 10000 -// every { totalPrice.shipping } returns 3000 -// every { totalPrice.payment } returns 130000 -// -// val newTotalProductsPrice = order.totalPrice.products + orderDetail.price.oneKindTotalPrice -// val newTotalPaymentPrice = -// order.totalPrice.products + order.totalPrice.shipping + orderDetail.price.oneKindTotalPrice -// -// justRun { order.totalPrice.updatePrices(newTotalProductsPrice,newTotalPaymentPrice) } -// justRun { orderUserService.updateProductStock(item, orderDetail.quantity) } -// -// //when -// val orderDetailId = orderUserService.addOrderDetail(addOrderDetailRequest) -// -// //then -// assertEquals(orderDetailId, 123L) -// verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } -// } + @Test + @DisplayName("주문 상세 추가 - 성공 테스트") + fun addOrderDetail_success_test() { + // 요청 DTO 모킹 + val addOrderDetailRequest = mockk() + every { addOrderDetailRequest.orderId } returns orderId + every { addOrderDetailRequest.productId } returns productId + every { addOrderDetailRequest.itemId } returns itemId + every { addOrderDetailRequest.quantity } returns 1 + + // 요청 DTO와 관련된 order, product, item 불러오기 + every { orderRepository.findByIdOrNull(orderId) } returns order + every { productService.getProductById(productId) } returns product + every { itemService.getItemById(itemId) } returns item + + every { item.stock } returns 10 + every { order.status } returns Status.CONFIRMED + + every { product.price } returns 1000 + every { product.productId } returns productId + every { item.itemId } returns itemId + + every { addOrderDetailRequest.toOrderDetail(order, product, item) } returns orderDetail + every { orderDetailRepository.save(orderDetail) } returns orderDetail + + every { order.totalPrice } returns totalPrice + every { orderDetail.price } returns price + every { order.totalPrice.products } returns 10000 + every { orderDetail.price.oneKindTotalPrice } returns 10000 + every { order.totalPrice.shipping } returns 3000 + + every { orderDetail.quantity } returns addOrderDetailRequest.quantity + + justRun { order.totalPrice.updatePrices(any(),any()) } + justRun { item.updateStock(any()) } + + justRun { orderUserService.updateProductStock(item, orderDetail.quantity) } + every { orderDetail.orderDetailId } returns 123L + + //when + val orderDetailId: Long = orderUserService.addOrderDetail(addOrderDetailRequest) + + //then + assertEquals(orderDetailId, orderDetail.orderDetailId) + verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderDetailRepository.save(orderDetail) } + } @Test @DisplayName("주문 상세 추가 - 주문번호가 존재하지 않을 때 예외처리 테스트") From 0a61895b56701d7acbae02ff57883a7ea78d9f81 Mon Sep 17 00:00:00 2001 From: subin Date: Mon, 19 Aug 2024 01:56:34 +0900 Subject: [PATCH 28/42] =?UTF-8?q?test:=20=ED=8C=90=EB=A7=A4=EC=9E=90=20?= =?UTF-8?q?=EC=A3=BC=EB=AC=B8=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=8B=A8=EC=9C=84=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 --- .../order/service/OrderSellerServiceTest.kt | 106 +++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt index 7503206..2b8f18a 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt @@ -6,6 +6,7 @@ import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.justRun import io.mockk.verify +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows @@ -13,14 +14,23 @@ import org.junit.jupiter.api.extension.ExtendWith import org.springframework.data.repository.findByIdOrNull import org.store.clothstar.common.error.ErrorCode import org.store.clothstar.common.error.exception.order.OrderNotFoundException +import org.store.clothstar.member.domain.Address +import org.store.clothstar.member.domain.Member +import org.store.clothstar.member.domain.Seller +import org.store.clothstar.member.domain.vo.AddressInfo import org.store.clothstar.member.service.AddressService import org.store.clothstar.member.service.MemberService import org.store.clothstar.member.service.SellerService import org.store.clothstar.order.domain.Order -import org.store.clothstar.order.domain.vo.Status +import org.store.clothstar.order.domain.OrderDetail +import org.store.clothstar.order.domain.vo.* +import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderRepository +import org.store.clothstar.product.domain.Item +import org.store.clothstar.product.domain.Product import org.store.clothstar.product.service.ItemService import org.store.clothstar.product.service.ProductService +import java.time.LocalDateTime import kotlin.test.Test @ExtendWith(MockKExtension::class) @@ -49,13 +59,107 @@ class OrderSellerServiceTest { @MockK lateinit var order: Order + @MockK + lateinit var member: Member + + @MockK + lateinit var seller: Seller + + @MockK + lateinit var address: Address + + @MockK + lateinit var product: Product + + @MockK + lateinit var item: Item + + @MockK + lateinit var addressInfo: AddressInfo + + @MockK + lateinit var totalPrice: TotalPrice + + @MockK + lateinit var price: Price + + @MockK + lateinit var orderDetail: OrderDetail + val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + val memberId = 1L + val addressId = 2L + val productId = 3L + val itemId = 4L // 판매자 주문(CONFIRMED) 리스트 조회 @Test @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - 성공 테스트") fun addOrderDetail_success_test() { + //given + every { orderRepository.findConfirmedAndNotDeletedOrders() } returns listOf(order) + every { order.memberId } returns memberId + every { order.addressId } returns addressId + every { memberService.getMemberByMemberId(memberId) } returns member + every { addressService.getAddressById(addressId) } returns address + every { sellerService.getSellerById(memberId) } returns seller + + every { totalPrice.shipping } returns 3000 + every { totalPrice.products } returns 5000 + every { totalPrice.payment } returns 8000 + every { order.orderId } returns orderId + every { member.name } returns "수빈" + every { order.createdAt } returns LocalDateTime.now() + every { order.status } returns Status.CONFIRMED + every { order.paymentMethod } returns PaymentMethod.CARD + every { order.totalPrice } returns totalPrice + every { address.addressInfo } returns addressInfo + every { address.receiverName } returns "수빈" + every { addressInfo.addressBasic } returns "address1" + every { addressInfo.addressDetail } returns "address2" + every { address.telNo } returns "010-1111-1111" + every { address.deliveryRequest } returns "문앞" + + val expectedorderResponse = OrderResponse.from(order,member,address) + every { order.orderDetails } returns mutableListOf(orderDetail) + every { orderDetail.deletedAt } returns null + + // productIds, itemIds로부터 Product/Item 리스트 가져오기 + every { orderDetail.itemId } returns itemId + every { orderDetail.productId } returns productId + every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + + every { item.itemId } returns itemId + every { product.productId } returns productId + + every { orderDetail.orderDetailId } returns 1L + every { product.name } returns "상품이름" + every { item.name } returns "상품옵션이름" + every { seller.brandName } returns "brandName" + every { product.price } returns 1000 + every { orderDetail.quantity } returns 1 + every { orderDetail.price } returns price + every { item.finalPrice } returns 10000 + every { price.oneKindTotalPrice } returns 10000 + + // 주문상세 DTO 리스트 만들기 + val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName)) + + // 응답 DTO에 주문상세 DTO 리스트 추가 + expectedorderResponse.updateOrderDetailList(orderDetailDTOs) + //when + val orderReponse = orderSellerService.getConfirmedOrders() + + //then + assertThat(orderReponse).usingRecursiveComparison().isEqualTo(listOf(expectedorderResponse)) + verify(exactly = 1) { orderRepository.findConfirmedAndNotDeletedOrders() } + verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } + verify(exactly = 1) { addressService.getAddressById(addressId) } + verify(exactly = 1) { sellerService.getSellerById(memberId) } + verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } + verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } } // 판매자 주문 승인 - approveOrder From ab792da28b013c3255654a542a566a74766082d9 Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 20 Aug 2024 00:07:50 +0900 Subject: [PATCH 29/42] =?UTF-8?q?test:=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EB=8B=A8=EC=9C=84=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserService.kt | 2 - .../order/service/OrderUserServiceTest.kt | 247 +++++++++++------- 2 files changed, 154 insertions(+), 95 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt index 6cb8ae1..1dfcd8a 100644 --- a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt +++ b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt @@ -75,9 +75,7 @@ class OrderUserService( // Map으로부터 Id, Entity를 가져오면서 주문상세 DTO 리스트 만들기 val orderDetailDTOList: List = orderDetails.map { val product: Product = productMap[it.productId]!! - ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Product not found") val item: Item = itemMap[it.itemId]!! - ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Item not found") val brandName: String = seller.brandName OrderDetailDTO.from(it, item, product, brandName) } diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index d5d7b1e..21f07a7 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -10,9 +10,7 @@ import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith -import org.springframework.data.domain.Page -import org.springframework.data.domain.PageImpl -import org.springframework.data.domain.Pageable +import org.springframework.data.domain.* import org.springframework.data.repository.findByIdOrNull import org.store.clothstar.common.error.ErrorCode import org.store.clothstar.common.error.exception.order.InsufficientStockException @@ -196,96 +194,159 @@ class OrderUserServiceTest { assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } -// // 전체 주문 페이징 조회 -// @Test -// @DisplayName("전체 주문 페이징 조회 offset 방식 - 성공 테스트") -// fun getAllOrderOffsetPaging_success_test() { -// //given -// val orders = listOf(order) -//// every { pageable.offset } returns 0L -// every { pageable.pageSize } returns 10 -// every { pageable.toOptional() } returns Optional.of(pageable) -// val ordersPage: Page = PageImpl(orders,pageable,orders.size.toLong()) -// every { orderRepository.findAll(pageable) } returns ordersPage -// -// // order 관련 member, address, seller 불러오기 -// every { order.memberId } returns memberId -// every { order.addressId } returns addressId -// every { memberService.getMemberByMemberId(memberId) } returns member -// every { addressService.getAddressById(addressId) } returns address -// every { sellerService.getSellerById(memberId) } returns seller -// -// every { totalPrice.shipping } returns 3000 -// every { totalPrice.products } returns 5000 -// every { totalPrice.payment } returns 8000 -// every { order.orderId } returns orderId -// every { member.name } returns "수빈" -// every { order.createdAt } returns LocalDateTime.now() -// every { order.status } returns Status.CONFIRMED -// every { order.paymentMethod } returns PaymentMethod.CARD -// every { order.totalPrice } returns totalPrice -// every { address.addressInfo } returns addressInfo -// every { address.receiverName } returns "수빈" -// every { addressInfo.addressBasic } returns "address1" -// every { addressInfo.addressDetail } returns "address2" -// every { address.telNo } returns "010-1111-1111" -// every { address.deliveryRequest } returns "문앞" -// -// // 응답 DTO 생성(주문상세 리스트는 빈 상태) -// val expectedorderResponse = OrderResponse.from(order,member,address) -// every { order.orderDetails } returns mutableListOf(orderDetail) -// every { orderDetails.size } returns 3 -// -//// every { orderDetails.iterator() } returns -// -// // productIds, itemIds로부터 Product/Item 리스트 가져오기 -// every { orderDetail.itemId } returns itemId -// every { orderDetail.productId } returns productId -// every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) -// every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) -// -// every { orderDetail.orderDetailId } returns 1L -// every { product.name } returns "상품이름" -// every { item.name } returns "상품옵션이름" -// every { seller.brandName} returns "brandName" -// every { product.price } returns 1000 -// every { orderDetail.quantity } returns 1 -// every { orderDetail.price } returns price -// every { item.finalPrice } returns 10000 -// every { price.fixedPrice } returns 10000 -// every { price.oneKindTotalPrice } returns 10000 -// -// every { order.orderDetails } returns mutableListOf(orderDetail) -// every { orderDetail.deletedAt } returns null -// every { item.itemId } returns itemId -// every { product.productId } returns productId -// -// // 주문상세 DTO 리스트 만들기 -// val orderDetils: List = listOf(orderDetail) -// val orderDetailDTOs: List = orderDetails.map{ -// it -> OrderDetailDTO.from(it,item,product,seller.brandName) -// } -// -// // 응답 DTO에 주문상세 DTO 리스트 추가 -// expectedorderResponse.updateOrderDetailList(orderDetailDTOs) -// -// //when -// val orderReponses: Page = orderUserService.getAllOrderOffsetPaging(pageable) -// -// // then -// assertThat(orderReponses.content.get(0)).usingRecursiveComparison().isEqualTo(expectedorderResponse) -// verify(exactly = 1) { orderRepository.findAll(pageable) } -// verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } -// verify(exactly = 1) { addressService.getAddressById(addressId) } -// verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } -// verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } -// } -// -// @Test -// @DisplayName("전체 주문 페이징 조회 slice 방식 - 성공 테스트") -// fun getAllOrderSlicePaging_success_test() { -// -// } + @Test + @DisplayName("전체 주문 페이징 조회 offset 방식 - 성공 테스트") + fun getAllOrderOffsetPaging_success_test() { + // given + val pageable: Pageable = PageRequest.of(0, 10) + val orders: Page = PageImpl(listOf(order)) + every { orderRepository.findAll(pageable) } returns orders + + // order 관련 member, address, seller 불러오기 + every { order.memberId } returns memberId + every { order.addressId } returns addressId + every { memberService.getMemberByMemberId(memberId) } returns member + every { addressService.getAddressById(addressId) } returns address + every { sellerService.getSellerById(memberId) } returns seller + + every { totalPrice.shipping } returns 3000 + every { totalPrice.products } returns 5000 + every { totalPrice.payment } returns 8000 + every { order.orderId } returns orderId + every { member.name } returns "수빈" + every { order.createdAt } returns LocalDateTime.now() + every { order.status } returns Status.CONFIRMED + every { order.paymentMethod } returns PaymentMethod.CARD + every { order.totalPrice } returns totalPrice + every { address.addressInfo } returns addressInfo + every { address.receiverName } returns "수빈" + every { addressInfo.addressBasic } returns "address1" + every { addressInfo.addressDetail } returns "address2" + every { address.telNo } returns "010-1111-1111" + every { address.deliveryRequest } returns "문앞" + + // 응답 DTO 생성(주문상세 리스트는 빈 상태) + val expectedOrderResponse = OrderResponse.from(order, member, address) + + every { order.orderDetails } returns mutableListOf(orderDetail) + every { orderDetail.deletedAt } returns null + + every { product.productId } returns productId + + // productIds, itemIds로부터 Product/Item 리스트 가져오기 + every { orderDetail.itemId } returns itemId + every { orderDetail.productId } returns productId + every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + + every { item.itemId } returns itemId + every { product.productId } returns productId + + every { orderDetail.orderDetailId } returns 1L + every { product.name } returns "상품이름" + every { item.name } returns "상품옵션이름" + every { seller.brandName } returns "brandName" + every { product.price } returns 1000 + every { orderDetail.quantity } returns 1 + every { orderDetail.price } returns price + every { item.finalPrice } returns 10000 + every { price.oneKindTotalPrice } returns 10000 + + // 주문상세 DTO 리스트 만들기 + val orderDetailDTOs: List = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) + + // 응답 DTO 생성 및 주문상세 DTO 리스트 추가 + expectedOrderResponse.updateOrderDetailList(orderDetailDTOs) + + // when + val orderResponses: Page = orderUserService.getAllOrderOffsetPaging(pageable) + + // then + assertThat(orderResponses.content).usingRecursiveComparison().isEqualTo(listOf(expectedOrderResponse)) + verify(exactly = 1) { orderRepository.findAll(pageable) } + verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } + verify(exactly = 1) { addressService.getAddressById(addressId) } + verify(exactly = 1) { sellerService.getSellerById(memberId) } + verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } + verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + } + + @Test + @DisplayName("전체 주문 페이징 조회 slice 방식 - 성공 테스트") + fun getAllOrderSlicePaging_success_test() { + // given + val pageable: Pageable = PageRequest.of(0, 10) + val orders: Page = PageImpl(listOf(order)) + every { orderRepository.findAll(pageable) } returns orders + + // order 관련 member, address, seller 불러오기 + every { order.memberId } returns memberId + every { order.addressId } returns addressId + every { memberService.getMemberByMemberId(memberId) } returns member + every { addressService.getAddressById(addressId) } returns address + every { sellerService.getSellerById(memberId) } returns seller + + every { totalPrice.shipping } returns 3000 + every { totalPrice.products } returns 5000 + every { totalPrice.payment } returns 8000 + every { order.orderId } returns orderId + every { member.name } returns "수빈" + every { order.createdAt } returns LocalDateTime.now() + every { order.status } returns Status.CONFIRMED + every { order.paymentMethod } returns PaymentMethod.CARD + every { order.totalPrice } returns totalPrice + every { address.addressInfo } returns addressInfo + every { address.receiverName } returns "수빈" + every { addressInfo.addressBasic } returns "address1" + every { addressInfo.addressDetail } returns "address2" + every { address.telNo } returns "010-1111-1111" + every { address.deliveryRequest } returns "문앞" + + // 응답 DTO 생성(주문상세 리스트는 빈 상태) + val expectedOrderResponse = OrderResponse.from(order, member, address) + + every { order.orderDetails } returns mutableListOf(orderDetail) + every { orderDetail.deletedAt } returns null + + every { product.productId } returns productId + + // productIds, itemIds로부터 Product/Item 리스트 가져오기 + every { orderDetail.itemId } returns itemId + every { orderDetail.productId } returns productId + every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) + every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + + every { item.itemId } returns itemId + every { product.productId } returns productId + + every { orderDetail.orderDetailId } returns 1L + every { product.name } returns "상품이름" + every { item.name } returns "상품옵션이름" + every { seller.brandName } returns "brandName" + every { product.price } returns 1000 + every { orderDetail.quantity } returns 1 + every { orderDetail.price } returns price + every { item.finalPrice } returns 10000 + every { price.oneKindTotalPrice } returns 10000 + + // 주문상세 DTO 리스트 만들기 + val orderDetailDTOs: List = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) + + // 응답 DTO 생성 및 주문상세 DTO 리스트 추가 + expectedOrderResponse.updateOrderDetailList(orderDetailDTOs) + + // when + val orderResponses: Slice = orderUserService.getAllOrderSlicePaging(pageable) + + // then + assertThat(orderResponses.content).usingRecursiveComparison().isEqualTo(listOf(expectedOrderResponse)) + verify(exactly = 1) { orderRepository.findAll(pageable) } + verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } + verify(exactly = 1) { addressService.getAddressById(addressId) } + verify(exactly = 1) { sellerService.getSellerById(memberId) } + verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } + verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + } @Test @DisplayName("주문 상세 추가 - 성공 테스트") From 7e801a3c19a901b9b5d0d436600ac2514e370734 Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 20 Aug 2024 00:10:43 +0900 Subject: [PATCH 30/42] refactor: Optimize Imports & Reformat Code --- .../order/service/OrderUserService.kt | 2 - .../order/service/OrderSellerServiceTest.kt | 8 ++-- .../order/service/OrderUserServiceTest.kt | 40 ++++++++----------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt index 1dfcd8a..6873ef2 100644 --- a/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt +++ b/src/main/kotlin/org/store/clothstar/order/service/OrderUserService.kt @@ -4,10 +4,8 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.springframework.data.repository.findByIdOrNull -import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional -import org.springframework.web.server.ResponseStatusException import org.store.clothstar.common.error.ErrorCode import org.store.clothstar.common.error.exception.order.InsufficientStockException import org.store.clothstar.common.error.exception.order.InvalidOrderStatusException diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt index 2b8f18a..45860f4 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt @@ -120,15 +120,15 @@ class OrderSellerServiceTest { every { address.telNo } returns "010-1111-1111" every { address.deliveryRequest } returns "문앞" - val expectedorderResponse = OrderResponse.from(order,member,address) + val expectedorderResponse = OrderResponse.from(order, member, address) every { order.orderDetails } returns mutableListOf(orderDetail) every { orderDetail.deletedAt } returns null // productIds, itemIds로부터 Product/Item 리스트 가져오기 every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) - every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) + every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) every { item.itemId } returns itemId every { product.productId } returns productId @@ -144,7 +144,7 @@ class OrderSellerServiceTest { every { price.oneKindTotalPrice } returns 10000 // 주문상세 DTO 리스트 만들기 - val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName)) + val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) // 응답 DTO에 주문상세 DTO 리스트 추가 expectedorderResponse.updateOrderDetailList(orderDetailDTOs) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 21f07a7..e22e8ec 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -1,9 +1,12 @@ package org.store.clothstar.order.service -import io.mockk.* +import io.mockk.every import io.mockk.impl.annotations.InjectMockKs import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension +import io.mockk.justRun +import io.mockk.mockk +import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull @@ -30,13 +33,11 @@ import org.store.clothstar.order.dto.request.AddOrderDetailRequest import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository -import org.store.clothstar.order.service.OrderSave.OrderSaveFacade import org.store.clothstar.product.domain.Item import org.store.clothstar.product.domain.Product import org.store.clothstar.product.service.ItemService import org.store.clothstar.product.service.ProductService import java.time.LocalDateTime -import java.util.* import kotlin.test.Test @ExtendWith(MockKExtension::class) @@ -65,9 +66,6 @@ class OrderUserServiceTest { @MockK lateinit var productService: ProductService - @MockK - lateinit var orderSaveFacade: OrderSaveFacade - @MockK lateinit var order: Order @@ -83,9 +81,6 @@ class OrderUserServiceTest { @MockK lateinit var address: Address - @MockK - lateinit var orderDetails: List - @MockK lateinit var product: Product @@ -101,9 +96,6 @@ class OrderUserServiceTest { @MockK lateinit var price: Price - @MockK - lateinit var pageable: Pageable - val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" val memberId = 1L val addressId = 2L @@ -140,7 +132,7 @@ class OrderUserServiceTest { every { address.deliveryRequest } returns "문앞" // 응답 DTO 생성(주문상세 리스트는 빈 상태) - val expectedorderResponse = OrderResponse.from(order,member,address) + val expectedorderResponse = OrderResponse.from(order, member, address) every { order.orderDetails } returns mutableListOf(orderDetail) every { orderDetail.deletedAt } returns null @@ -148,8 +140,8 @@ class OrderUserServiceTest { // productIds, itemIds로부터 Product/Item 리스트 가져오기 every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) - every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) + every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) every { item.itemId } returns itemId every { product.productId } returns productId @@ -165,7 +157,7 @@ class OrderUserServiceTest { every { price.oneKindTotalPrice } returns 10000 // 주문상세 DTO 리스트 만들기 - val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail,item,product,seller.brandName)) + val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) // 응답 DTO에 주문상세 DTO 리스트 추가 expectedorderResponse.updateOrderDetailList(orderDetailDTOs) @@ -236,8 +228,8 @@ class OrderUserServiceTest { // productIds, itemIds로부터 Product/Item 리스트 가져오기 every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) - every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) + every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) every { item.itemId } returns itemId every { product.productId } returns productId @@ -253,7 +245,8 @@ class OrderUserServiceTest { every { price.oneKindTotalPrice } returns 10000 // 주문상세 DTO 리스트 만들기 - val orderDetailDTOs: List = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) + val orderDetailDTOs: List = + listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) // 응답 DTO 생성 및 주문상세 DTO 리스트 추가 expectedOrderResponse.updateOrderDetailList(orderDetailDTOs) @@ -313,8 +306,8 @@ class OrderUserServiceTest { // productIds, itemIds로부터 Product/Item 리스트 가져오기 every { orderDetail.itemId } returns itemId every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId))} returns listOf(product) - every { itemService.findByIdIn(listOf(itemId))} returns listOf(item) + every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) + every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) every { item.itemId } returns itemId every { product.productId } returns productId @@ -330,7 +323,8 @@ class OrderUserServiceTest { every { price.oneKindTotalPrice } returns 10000 // 주문상세 DTO 리스트 만들기 - val orderDetailDTOs: List = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) + val orderDetailDTOs: List = + listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) // 응답 DTO 생성 및 주문상세 DTO 리스트 추가 expectedOrderResponse.updateOrderDetailList(orderDetailDTOs) @@ -381,7 +375,7 @@ class OrderUserServiceTest { every { orderDetail.quantity } returns addOrderDetailRequest.quantity - justRun { order.totalPrice.updatePrices(any(),any()) } + justRun { order.totalPrice.updatePrices(any(), any()) } justRun { item.updateStock(any()) } justRun { orderUserService.updateProductStock(item, orderDetail.quantity) } From 9968e30650c36a01145b3a54e0927cca10c59f12 Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 20 Aug 2024 00:16:44 +0900 Subject: [PATCH 31/42] =?UTF-8?q?refactor:=20OrderUserServiceTest=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/store/clothstar/order/service/OrderUserServiceTest.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index e22e8ec..887ad5d 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -33,6 +33,7 @@ import org.store.clothstar.order.dto.request.AddOrderDetailRequest import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository +import org.store.clothstar.order.service.OrderSave.OrderSaveFacade import org.store.clothstar.product.domain.Item import org.store.clothstar.product.domain.Product import org.store.clothstar.product.service.ItemService @@ -51,6 +52,9 @@ class OrderUserServiceTest { @MockK lateinit var orderDetailRepository: OrderDetailRepository + @MockK + lateinit var orderSaveFacade: OrderSaveFacade + @MockK lateinit var memberService: MemberService From a5891def4f53471bd6c93ca729fff41936d12533 Mon Sep 17 00:00:00 2001 From: subin Date: Tue, 20 Aug 2024 00:54:10 +0900 Subject: [PATCH 32/42] =?UTF-8?q?test:=20=EC=A3=BC=EB=AC=B8=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=8B=A8=EC=9C=84=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 --- .../order/service/OrderUserServiceTest.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 887ad5d..70535b0 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -1,5 +1,6 @@ package org.store.clothstar.order.service +import io.kotest.matchers.ints.exactly import io.mockk.every import io.mockk.impl.annotations.InjectMockKs import io.mockk.impl.annotations.MockK @@ -30,6 +31,9 @@ import org.store.clothstar.order.domain.Order import org.store.clothstar.order.domain.OrderDetail import org.store.clothstar.order.domain.vo.* import org.store.clothstar.order.dto.request.AddOrderDetailRequest +import org.store.clothstar.order.dto.request.CreateOrderDetailRequest +import org.store.clothstar.order.dto.request.CreateOrderRequest +import org.store.clothstar.order.dto.request.OrderRequestWrapper import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository @@ -346,6 +350,20 @@ class OrderUserServiceTest { verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } } + @Test + @DisplayName("주문 저장") + fun saveOrder() { + //given + val orderRequestWrapper = mockk() + every { orderUserService.saveOrder(orderRequestWrapper) } returns orderId + + //when + orderUserService.saveOrder(orderRequestWrapper) + + //then + verify(exactly = 1) { orderSaveFacade.saveOrder(orderRequestWrapper) } + } + @Test @DisplayName("주문 상세 추가 - 성공 테스트") fun addOrderDetail_success_test() { From 1dad90cd657424d1ec4f69597b0e98dabe52672a Mon Sep 17 00:00:00 2001 From: subin Date: Wed, 4 Sep 2024 03:45:55 +0900 Subject: [PATCH 33/42] =?UTF-8?q?refactor:=20=ED=8E=98=EC=9D=B4=EC=A7=95?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C,=20=EC=A3=BC=EB=AC=B8=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=B6=94=EA=B0=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/clothstar/order/service/OrderUserServiceTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 70535b0..90db813 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -278,7 +278,7 @@ class OrderUserServiceTest { // given val pageable: Pageable = PageRequest.of(0, 10) val orders: Page = PageImpl(listOf(order)) - every { orderRepository.findAll(pageable) } returns orders + every { orderRepository.findAllByOrderByOrderIdDesc(pageable) } returns orders // order 관련 member, address, seller 불러오기 every { order.memberId } returns memberId @@ -342,7 +342,7 @@ class OrderUserServiceTest { // then assertThat(orderResponses.content).usingRecursiveComparison().isEqualTo(listOf(expectedOrderResponse)) - verify(exactly = 1) { orderRepository.findAll(pageable) } + verify(exactly = 1) { orderRepository.findAllByOrderByOrderIdDesc(pageable) } verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } verify(exactly = 1) { addressService.getAddressById(addressId) } verify(exactly = 1) { sellerService.getSellerById(memberId) } @@ -395,6 +395,8 @@ class OrderUserServiceTest { every { orderDetail.price.oneKindTotalPrice } returns 10000 every { order.totalPrice.shipping } returns 3000 + justRun { order.addOrderDetail(any()) } + every { orderDetail.quantity } returns addOrderDetailRequest.quantity justRun { order.totalPrice.updatePrices(any(), any()) } From c635fa16100fca0f893d2f0f9142d706a4838ad9 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 7 Sep 2024 03:53:55 +0900 Subject: [PATCH 34/42] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=9C=84=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/OrderUserServiceTest.kt | 595 ++++++++++-------- 1 file changed, 335 insertions(+), 260 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt index 90db813..8c52392 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderUserServiceTest.kt @@ -1,6 +1,5 @@ package org.store.clothstar.order.service -import io.kotest.matchers.ints.exactly import io.mockk.every import io.mockk.impl.annotations.InjectMockKs import io.mockk.impl.annotations.MockK @@ -31,8 +30,6 @@ import org.store.clothstar.order.domain.Order import org.store.clothstar.order.domain.OrderDetail import org.store.clothstar.order.domain.vo.* import org.store.clothstar.order.dto.request.AddOrderDetailRequest -import org.store.clothstar.order.dto.request.CreateOrderDetailRequest -import org.store.clothstar.order.dto.request.CreateOrderRequest import org.store.clothstar.order.dto.request.OrderRequestWrapper import org.store.clothstar.order.dto.response.OrderResponse import org.store.clothstar.order.repository.OrderDetailRepository @@ -74,122 +71,109 @@ class OrderUserServiceTest { @MockK lateinit var productService: ProductService - @MockK - lateinit var order: Order - - @MockK - lateinit var orderDetail: OrderDetail - - @MockK - lateinit var member: Member - - @MockK - lateinit var seller: Seller - - @MockK - lateinit var address: Address - - @MockK - lateinit var product: Product + @Test + @DisplayName("단일 주문 조회 - 성공 테스트") + fun getOrder_success_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } - @MockK - lateinit var item: Item + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } - @MockK - lateinit var addressInfo: AddressInfo + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } - @MockK - lateinit var totalPrice: TotalPrice + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } - @MockK - lateinit var price: Price + val member = mockk { + every { name } returns "수빈" + } - val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" - val memberId = 1L - val addressId = 2L - val productId = 3L - val itemId = 4L + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } - // 단일 주문 조회 - getOrder - @Test - @DisplayName("단일 주문 조회 - 성공 테스트") - fun getOrder_success_test() { - //given - // orderId 관련 order, member, address, seller 불러오기 - every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns order - every { order.memberId } returns memberId - every { order.addressId } returns addressId - every { memberService.getMemberByMemberId(memberId) } returns member - every { addressService.getAddressById(addressId) } returns address - every { sellerService.getSellerById(memberId) } returns seller - - every { totalPrice.shipping } returns 3000 - every { totalPrice.products } returns 5000 - every { totalPrice.payment } returns 8000 - every { order.orderId } returns orderId - every { member.name } returns "수빈" - every { order.createdAt } returns LocalDateTime.now() - every { order.status } returns Status.CONFIRMED - every { order.paymentMethod } returns PaymentMethod.CARD - every { order.totalPrice } returns totalPrice - every { address.addressInfo } returns addressInfo - every { address.receiverName } returns "수빈" - every { addressInfo.addressBasic } returns "address1" - every { addressInfo.addressDetail } returns "address2" - every { address.telNo } returns "010-1111-1111" - every { address.deliveryRequest } returns "문앞" + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } - // 응답 DTO 생성(주문상세 리스트는 빈 상태) - val expectedorderResponse = OrderResponse.from(order, member, address) + val seller = mockk { + every { brandName } returns "brandName" + } - every { order.orderDetails } returns mutableListOf(orderDetail) - every { orderDetail.deletedAt } returns null + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } - // productIds, itemIds로부터 Product/Item 리스트 가져오기 - every { orderDetail.itemId } returns itemId - every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) - every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) - - every { item.itemId } returns itemId - every { product.productId } returns productId - - every { orderDetail.orderDetailId } returns 1L - every { product.name } returns "상품이름" - every { item.name } returns "상품옵션이름" - every { seller.brandName } returns "brandName" - every { product.price } returns 1000 - every { orderDetail.quantity } returns 1 - every { orderDetail.price } returns price - every { item.finalPrice } returns 10000 - every { price.oneKindTotalPrice } returns 10000 + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } - // 주문상세 DTO 리스트 만들기 + // OrderDetailDTO 생성 val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) - // 응답 DTO에 주문상세 DTO 리스트 추가 - expectedorderResponse.updateOrderDetailList(orderDetailDTOs) + // 기대하는 OrderResponse 생성 + val expectedOrderResponse = OrderResponse.from(order, member, address).apply { + updateOrderDetailList(orderDetailDTOs) + } - //when - val orderReponse = orderUserService.getOrder(orderId) + // Mock 동작 설정 + every { orderRepository.findByOrderIdAndDeletedAtIsNull(any()) } returns order + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById((any())) } returns address + every { sellerService.getSellerById((any())) } returns seller + every { productService.findByProductIdIn(any()) } returns listOf(product) + every { itemService.findByIdIn(any()) } returns listOf(item) + + // when + val orderResponse = orderUserService.getOrder(order.orderId) // then - assertThat(orderReponse).usingRecursiveComparison().isEqualTo(expectedorderResponse) - verify(exactly = 1) { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } - verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } - verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } - verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } + assertThat(orderResponse).usingRecursiveComparison().isEqualTo(expectedOrderResponse) + verify(exactly = 1) { orderRepository.findByOrderIdAndDeletedAtIsNull(any()) } + verify(exactly = 1) { memberService.getMemberByMemberId(any()) } + verify(exactly = 1) { itemService.findByIdIn(any()) } + verify(exactly = 1) { productService.findByProductIdIn(any()) } } @Test @DisplayName("단일 주문 조회 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun getOrder_orderNotFound_exception_test() { //given - every { orderRepository.findByOrderIdAndDeletedAtIsNull(orderId) } returns null + every { orderRepository.findByOrderIdAndDeletedAtIsNull(any()) } returns null //when & then val exception = assertThrows { - orderUserService.getOrder(orderId) + orderUserService.getOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } @@ -197,60 +181,85 @@ class OrderUserServiceTest { @Test @DisplayName("전체 주문 페이징 조회 offset 방식 - 성공 테스트") fun getAllOrderOffsetPaging_success_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } + + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } + + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } + + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } + + val member = mockk { + every { name } returns "수빈" + } + + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } + + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } + + val seller = mockk { + every { brandName } returns "brandName" + } + + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } + + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } + // given val pageable: Pageable = PageRequest.of(0, 10) val orders: Page = PageImpl(listOf(order)) every { orderRepository.findAll(pageable) } returns orders // order 관련 member, address, seller 불러오기 - every { order.memberId } returns memberId - every { order.addressId } returns addressId - every { memberService.getMemberByMemberId(memberId) } returns member - every { addressService.getAddressById(addressId) } returns address - every { sellerService.getSellerById(memberId) } returns seller - - every { totalPrice.shipping } returns 3000 - every { totalPrice.products } returns 5000 - every { totalPrice.payment } returns 8000 - every { order.orderId } returns orderId - every { member.name } returns "수빈" - every { order.createdAt } returns LocalDateTime.now() - every { order.status } returns Status.CONFIRMED - every { order.paymentMethod } returns PaymentMethod.CARD - every { order.totalPrice } returns totalPrice - every { address.addressInfo } returns addressInfo - every { address.receiverName } returns "수빈" - every { addressInfo.addressBasic } returns "address1" - every { addressInfo.addressDetail } returns "address2" - every { address.telNo } returns "010-1111-1111" - every { address.deliveryRequest } returns "문앞" + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById(any()) } returns address + every { sellerService.getSellerById(any()) } returns seller // 응답 DTO 생성(주문상세 리스트는 빈 상태) val expectedOrderResponse = OrderResponse.from(order, member, address) - every { order.orderDetails } returns mutableListOf(orderDetail) - every { orderDetail.deletedAt } returns null - - every { product.productId } returns productId - - // productIds, itemIds로부터 Product/Item 리스트 가져오기 - every { orderDetail.itemId } returns itemId - every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) - every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) - - every { item.itemId } returns itemId - every { product.productId } returns productId - - every { orderDetail.orderDetailId } returns 1L - every { product.name } returns "상품이름" - every { item.name } returns "상품옵션이름" - every { seller.brandName } returns "brandName" - every { product.price } returns 1000 - every { orderDetail.quantity } returns 1 - every { orderDetail.price } returns price - every { item.finalPrice } returns 10000 - every { price.oneKindTotalPrice } returns 10000 + every { productService.findByProductIdIn(any()) } returns listOf(product) + every { itemService.findByIdIn(any()) } returns listOf(item) // 주문상세 DTO 리스트 만들기 val orderDetailDTOs: List = @@ -265,70 +274,96 @@ class OrderUserServiceTest { // then assertThat(orderResponses.content).usingRecursiveComparison().isEqualTo(listOf(expectedOrderResponse)) verify(exactly = 1) { orderRepository.findAll(pageable) } - verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } - verify(exactly = 1) { addressService.getAddressById(addressId) } - verify(exactly = 1) { sellerService.getSellerById(memberId) } - verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } - verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + verify(exactly = 1) { memberService.getMemberByMemberId(any()) } + verify(exactly = 1) { addressService.getAddressById(any()) } + verify(exactly = 1) { sellerService.getSellerById(any()) } + verify(exactly = 1) { productService.findByProductIdIn(any()) } + verify(exactly = 1) { itemService.findByIdIn(any()) } } @Test @DisplayName("전체 주문 페이징 조회 slice 방식 - 성공 테스트") fun getAllOrderSlicePaging_success_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } + + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } + + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } + + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } + + val member = mockk { + every { name } returns "수빈" + } + + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } + + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } + + val seller = mockk { + every { brandName } returns "brandName" + } + + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } + + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } + // given val pageable: Pageable = PageRequest.of(0, 10) val orders: Page = PageImpl(listOf(order)) every { orderRepository.findAllByOrderByOrderIdDesc(pageable) } returns orders // order 관련 member, address, seller 불러오기 - every { order.memberId } returns memberId - every { order.addressId } returns addressId - every { memberService.getMemberByMemberId(memberId) } returns member - every { addressService.getAddressById(addressId) } returns address - every { sellerService.getSellerById(memberId) } returns seller - - every { totalPrice.shipping } returns 3000 - every { totalPrice.products } returns 5000 - every { totalPrice.payment } returns 8000 - every { order.orderId } returns orderId - every { member.name } returns "수빈" - every { order.createdAt } returns LocalDateTime.now() - every { order.status } returns Status.CONFIRMED - every { order.paymentMethod } returns PaymentMethod.CARD - every { order.totalPrice } returns totalPrice - every { address.addressInfo } returns addressInfo - every { address.receiverName } returns "수빈" - every { addressInfo.addressBasic } returns "address1" - every { addressInfo.addressDetail } returns "address2" - every { address.telNo } returns "010-1111-1111" - every { address.deliveryRequest } returns "문앞" + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById(any()) } returns address + every { sellerService.getSellerById(any()) } returns seller // 응답 DTO 생성(주문상세 리스트는 빈 상태) val expectedOrderResponse = OrderResponse.from(order, member, address) - every { order.orderDetails } returns mutableListOf(orderDetail) - every { orderDetail.deletedAt } returns null - - every { product.productId } returns productId - // productIds, itemIds로부터 Product/Item 리스트 가져오기 - every { orderDetail.itemId } returns itemId - every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) - every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) - - every { item.itemId } returns itemId - every { product.productId } returns productId - - every { orderDetail.orderDetailId } returns 1L - every { product.name } returns "상품이름" - every { item.name } returns "상품옵션이름" - every { seller.brandName } returns "brandName" - every { product.price } returns 1000 - every { orderDetail.quantity } returns 1 - every { orderDetail.price } returns price - every { item.finalPrice } returns 10000 - every { price.oneKindTotalPrice } returns 10000 + every { productService.findByProductIdIn(any()) } returns listOf(product) + every { itemService.findByIdIn(any()) } returns listOf(item) // 주문상세 DTO 리스트 만들기 val orderDetailDTOs: List = @@ -343,11 +378,11 @@ class OrderUserServiceTest { // then assertThat(orderResponses.content).usingRecursiveComparison().isEqualTo(listOf(expectedOrderResponse)) verify(exactly = 1) { orderRepository.findAllByOrderByOrderIdDesc(pageable) } - verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } - verify(exactly = 1) { addressService.getAddressById(addressId) } - verify(exactly = 1) { sellerService.getSellerById(memberId) } - verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } - verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + verify(exactly = 1) { memberService.getMemberByMemberId(any()) } + verify(exactly = 1) { addressService.getAddressById(any()) } + verify(exactly = 1) { sellerService.getSellerById(any()) } + verify(exactly = 1) { productService.findByProductIdIn(any()) } + verify(exactly = 1) { itemService.findByIdIn(any()) } } @Test @@ -355,7 +390,7 @@ class OrderUserServiceTest { fun saveOrder() { //given val orderRequestWrapper = mockk() - every { orderUserService.saveOrder(orderRequestWrapper) } returns orderId + every { orderUserService.saveOrder(orderRequestWrapper) } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" //when orderUserService.saveOrder(orderRequestWrapper) @@ -367,50 +402,66 @@ class OrderUserServiceTest { @Test @DisplayName("주문 상세 추가 - 성공 테스트") fun addOrderDetail_success_test() { - // 요청 DTO 모킹 - val addOrderDetailRequest = mockk() - every { addOrderDetailRequest.orderId } returns orderId - every { addOrderDetailRequest.productId } returns productId - every { addOrderDetailRequest.itemId } returns itemId - every { addOrderDetailRequest.quantity } returns 1 + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { oneKindTotalPrice } returns 10000 + } - // 요청 DTO와 관련된 order, product, item 불러오기 - every { orderRepository.findByIdOrNull(orderId) } returns order - every { productService.getProductById(productId) } returns product - every { itemService.getItemById(itemId) } returns item + val item = mockk { + every { itemId } returns 1L + every { stock } returns 10 + } - every { item.stock } returns 10 - every { order.status } returns Status.CONFIRMED + val product = mockk { + every { productId } returns 1L + every { price } returns 1000 + } - every { product.price } returns 1000 - every { product.productId } returns productId - every { item.itemId } returns itemId + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + } - every { addOrderDetailRequest.toOrderDetail(order, product, item) } returns orderDetail - every { orderDetailRepository.save(orderDetail) } returns orderDetail + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { status } returns Status.CONFIRMED + every { totalPrice } returns orderTotalPrice + every { totalPrice.products } returns 5000 + every { totalPrice.shipping } returns 3000 + } - every { order.totalPrice } returns totalPrice - every { orderDetail.price } returns price - every { order.totalPrice.products } returns 10000 - every { orderDetail.price.oneKindTotalPrice } returns 10000 - every { order.totalPrice.shipping } returns 3000 + val addOrderDetailRequest = mockk { + every { orderId } returns order.orderId + every { productId } returns 1L + every { itemId } returns 1L + every { quantity } returns 1 + } - justRun { order.addOrderDetail(any()) } + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns addOrderDetailRequest.quantity + every { orderDetailId } returns 1L + } - every { orderDetail.quantity } returns addOrderDetailRequest.quantity + // 요청 DTO와 관련된 order, product, item 불러오기 + every { orderRepository.findByIdOrNull(any()) } returns order + every { productService.getProductById(any()) } returns product + every { itemService.getItemById(any()) } returns item + + every { addOrderDetailRequest.toOrderDetail(order, product, item) } returns orderDetail + every { orderDetailRepository.save(orderDetail) } returns orderDetail + justRun { order.addOrderDetail(any()) } justRun { order.totalPrice.updatePrices(any(), any()) } justRun { item.updateStock(any()) } - justRun { orderUserService.updateProductStock(item, orderDetail.quantity) } - every { orderDetail.orderDetailId } returns 123L //when val orderDetailId: Long = orderUserService.addOrderDetail(addOrderDetailRequest) //then assertEquals(orderDetailId, orderDetail.orderDetailId) - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { orderDetailRepository.save(orderDetail) } } @@ -419,12 +470,12 @@ class OrderUserServiceTest { fun addOrderDetail_orderNotFound_exception_test() { //given val addOrderDetailRequest = AddOrderDetailRequest( - orderId = orderId, - productId = productId, - itemId = itemId, + orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05", + productId = 1L, + itemId = 1L, quantity = 1, ) - every { orderRepository.findByIdOrNull(orderId) } returns null + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { @@ -438,15 +489,20 @@ class OrderUserServiceTest { fun addOrderDetail_insufficientStock_exception_test() { //given val addOrderDetailRequest = AddOrderDetailRequest( - orderId = orderId, - productId = productId, - itemId = itemId, + orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05", + productId = 1L, + itemId = 1L, quantity = 100, ) - every { orderRepository.findByIdOrNull(orderId) } returns order - every { productService.getProductById(productId) } returns product - every { itemService.getItemById(itemId) } returns item - every { item.stock } returns 10 + val item = mockk { + every { stock } returns 10 + } + val product = mockk {} + val order = mockk {} + + every { orderRepository.findByIdOrNull(any()) } returns order + every { productService.getProductById(any()) } returns product + every { itemService.getItemById(any()) } returns item //when & then val exception = assertThrows { @@ -460,16 +516,22 @@ class OrderUserServiceTest { fun addOrderDetail_invalidOrderStatusException_exception_test() { //given val addOrderDetailRequest = AddOrderDetailRequest( - orderId = orderId, - productId = productId, - itemId = itemId, + orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05", + productId = 1L, + itemId = 1L, quantity = 1, ) - every { orderRepository.findByIdOrNull(orderId) } returns order - every { productService.getProductById(productId) } returns product - every { itemService.getItemById(itemId) } returns item - every { item.stock } returns 10 - every { order.status } returns Status.DELIVERED + val item = mockk { + every { stock } returns 10 + } + val product = mockk {} + val order = mockk { + every { status } returns Status.DELIVERED + } + + every { orderRepository.findByIdOrNull(any()) } returns order + every { productService.getProductById(any()) } returns product + every { itemService.getItemById(any()) } returns item //when & then val exception = assertThrows { @@ -483,18 +545,20 @@ class OrderUserServiceTest { @DisplayName("구매자 구매 확정 - 성공 테스트") fun completeOrder_success_test() { //given - every { order.status } returns Status.DELIVERED - every { orderRepository.findByIdOrNull(orderId) } returns order + val order = mockk { + every { status } returns Status.DELIVERED + } + every { orderRepository.findByIdOrNull(any()) } returns order justRun { order.validateForStatusDELIVEREDAndDeletedAt() } every { order.updateStatus(Status.COMPLETED) } answers { every { order.status } returns Status.COMPLETED } //when - orderUserService.completeOrder(orderId) + orderUserService.completeOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") //then - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { order.validateForStatusDELIVEREDAndDeletedAt() } verify(exactly = 1) { order.updateStatus(Status.COMPLETED) } assertEquals(Status.COMPLETED, order.status) @@ -504,12 +568,14 @@ class OrderUserServiceTest { @DisplayName("구매자 구매 확정 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun completeOrder_orderNotFound_exception_test() { //given - every { order.status } returns Status.DELIVERED - every { orderRepository.findByIdOrNull(orderId) } returns null + val order = mockk { + every { status } returns Status.DELIVERED + } + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { - orderUserService.completeOrder(orderId) + orderUserService.completeOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } @@ -519,18 +585,20 @@ class OrderUserServiceTest { @DisplayName("구매자 주문 취소 - 성공 테스트") fun cancelOrder_success_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns order + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns order justRun { order.validateForStatusCONFIRMEDAndDeletedAt() } every { order.updateStatus(Status.CANCELED) } answers { every { order.status } returns Status.CANCELED } //when - orderUserService.cancelOrder(orderId) + orderUserService.cancelOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") //then - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { order.validateForStatusCONFIRMEDAndDeletedAt() } verify(exactly = 1) { order.updateStatus(Status.CANCELED) } assertEquals(Status.CANCELED, order.status) @@ -540,12 +608,14 @@ class OrderUserServiceTest { @DisplayName("구매자 주문 취소 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun cancelOrder_orderNotFound_exception_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns null + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { - orderUserService.cancelOrder(orderId) + orderUserService.cancelOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } @@ -555,9 +625,14 @@ class OrderUserServiceTest { @DisplayName("주문 삭제 - 성공 테스트") fun updateDeleteAt_success_test() { //given - every { orderRepository.findByIdOrNull(orderId) } returns order + val order = mockk { + every { status } returns Status.CONFIRMED + } + val orderDetail = mockk {} + + every { orderRepository.findByIdOrNull(any()) } returns order justRun { order.validateForDeletedAt() } - every { orderDetailRepository.findOrderDetailListByOrderId(orderId) } returns listOf(orderDetail) + every { orderDetailRepository.findOrderDetailListByOrderId(any()) } returns listOf(orderDetail) every { orderDetail.updateDeletedAt() } answers { every { orderDetail.deletedAt } returns LocalDateTime.now() } @@ -566,12 +641,12 @@ class OrderUserServiceTest { } //when - orderUserService.updateDeleteAt(orderId) + orderUserService.updateDeleteAt("4b1a17b5-45f0-455a-a5e3-2c863de18b05") //then - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { order.validateForDeletedAt() } - verify(exactly = 1) { orderDetailRepository.findOrderDetailListByOrderId(orderId) } + verify(exactly = 1) { orderDetailRepository.findOrderDetailListByOrderId(any()) } verify(exactly = 1) { orderDetail.updateDeletedAt() } verify(exactly = 1) { order.updateDeletedAt() } @@ -583,11 +658,11 @@ class OrderUserServiceTest { @DisplayName("주문 삭제 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun updateDeleteAt_orderNotFound_exception_test() { //given - every { orderRepository.findByIdOrNull(orderId) } returns null + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { - orderUserService.updateDeleteAt(orderId) + orderUserService.updateDeleteAt("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } From 827de7332bdecc58c260d6445df87e4aeb61de27 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 7 Sep 2024 08:17:57 +0900 Subject: [PATCH 35/42] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9A=94=EC=B2=AD=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kakaoLogin/controller/KakaoController.kt | 19 ------- .../dto/KakaoRenewTokenResponseDto.kt | 17 ------ .../dto/KakaoTokenInfoResponseDto.kt | 25 -------- .../dto/TokenUserInfoResponseDto.kt | 6 -- .../kakaoLogin/service/KakaoLoginService.kt | 57 ------------------- 5 files changed, 124 deletions(-) delete mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt delete mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt delete mode 100644 src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt index 7b6772a..d576b28 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/controller/KakaoController.kt @@ -4,8 +4,6 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import org.store.clothstar.kakaoLogin.dto.KakaoRenewTokenResponseDto -import org.store.clothstar.kakaoLogin.dto.KakaoTokenInfoResponseDto import org.store.clothstar.kakaoLogin.service.KakaoLoginService @RestController @@ -17,21 +15,4 @@ class KakaoController( fun kakaoCallback(@RequestParam code: String): ResponseEntity { return ResponseEntity.ok(code) } - - // 토큰 정보 확인 - @GetMapping("/auth/kakao/accessInfo") - fun getAccessInfo(@RequestParam accessToken: String): KakaoTokenInfoResponseDto { - val tokenInfo = kakaoLoginService.getTokenInfo(accessToken) - return tokenInfo - } - - // 액세스 토큰 갱신 - 유효성 검사 후 토큰 유효기간 만료 시 재로그인 요청 - @GetMapping("/auth/kakao/check") - fun checkToken( - @RequestParam accessToken: String, - @RequestParam refreshToken: String - ): ResponseEntity { - val renewTokenInfo: KakaoRenewTokenResponseDto = kakaoLoginService.validateToken(accessToken, refreshToken) - return ResponseEntity.ok(renewTokenInfo) - } } \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt deleted file mode 100644 index be9eae9..0000000 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoRenewTokenResponseDto.kt +++ /dev/null @@ -1,17 +0,0 @@ -package org.store.clothstar.kakaoLogin.dto - -import com.fasterxml.jackson.annotation.JsonProperty - -class KakaoRenewTokenResponseDto { - // 토큰 타입, bearer로 고정 - @JsonProperty("token_type") - private val tokenType: String? = null - - // 갱신된 사용자 액세스 토큰 값 - @JsonProperty("access_token") - val accessToken: String? = null - - // 액세스 토큰 만료 시간(초) - @JsonProperty("expires_in") - val expiresIn: Int? = null -} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt deleted file mode 100644 index 4e5bfb6..0000000 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/KakaoTokenInfoResponseDto.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.store.clothstar.kakaoLogin.dto - -import com.fasterxml.jackson.annotation.JsonProperty - -class KakaoTokenInfoResponseDto { - // 액세스 토큰 만료 시간(밀리초) - @JsonProperty("expiresInMillis") - private val expiresInMillis: Int? = null - - // 회원번호 - @JsonProperty("id") - private val id: String? = null - - // 액세스 토큰 만료 시간(초) - @JsonProperty("expires_in") - val expiresIn: Int? = null - - // 앱 아이디 - @JsonProperty("app_id") - val appId: Int? = null - - // 앱 아이디 - @JsonProperty("appId") - val appIdd: Int? = null -} \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt deleted file mode 100644 index 8c7b47d..0000000 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/dto/TokenUserInfoResponseDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.store.clothstar.kakaoLogin.dto - -class TokenUserInfoResponseDto( - val accessToken: KakaoTokenResponseDto, - val userInfo: KakaoUserInfoResponseDto -) \ No newline at end of file diff --git a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt index 892c9b3..512257b 100644 --- a/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt +++ b/src/main/kotlin/org/store/clothstar/kakaoLogin/service/KakaoLoginService.kt @@ -9,8 +9,6 @@ import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap import org.springframework.web.reactive.function.BodyInserters import org.springframework.web.reactive.function.client.WebClient -import org.store.clothstar.kakaoLogin.dto.KakaoRenewTokenResponseDto -import org.store.clothstar.kakaoLogin.dto.KakaoTokenInfoResponseDto import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto @@ -86,59 +84,4 @@ class KakaoLoginService { logger.info { "email : ${userInfo.kakaoAccount!!.email}" } return userInfo } - - // 토큰 유효성 검사 - fun validateToken(accessToken: String, refreshToken: String): KakaoRenewTokenResponseDto { - return try { - getTokenInfo(accessToken) - // 액세스 토큰이 유효한 경우 토큰 갱신 - refreshAccessToken(refreshToken) - } catch (e: Exception) { - // getTokenInfo에서 예외가 발생한 경우 재로그인 필요 - throw Exception("Access Token이 만료되었습니다. 재로그인이 필요합니다.") - } - } - - // 토큰 정보 보기 - fun getTokenInfo(accessToken: String): KakaoTokenInfoResponseDto { - val response = WebClient.create("https://kapi.kakao.com") - .get() - .uri("/v1/user/access_token_info") - .header("Authorization", "Bearer $accessToken") - .retrieve() - .bodyToMono(String::class.java) - .block() - - val objectMapper = ObjectMapper() - val tokenInfo: KakaoTokenInfoResponseDto = - objectMapper.readValue(response, KakaoTokenInfoResponseDto::class.java) - - logger.info { "Access Token 만료기한 : ${tokenInfo.expiresIn}" } - return tokenInfo - } - - // 리프레시 토큰으로 액세스 토큰 갱신 - fun refreshAccessToken(refreshToken: String): KakaoRenewTokenResponseDto { - val params: MultiValueMap = LinkedMultiValueMap() - params.add("grant_type", "refresh_token") - params.add("client_id", clientId) - params.add("refresh_token", refreshToken) - params.add("client_secret", clientSecret) - - val response = WebClient.create("https://kauth.kakao.com") - .post() - .uri(tokenUri) - .body(BodyInserters.fromFormData(params)) - .header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8") - .retrieve() - .bodyToMono(String::class.java) - .block() - - val objectMapper = ObjectMapper() - val kakaoToken: KakaoRenewTokenResponseDto = - objectMapper.readValue(response, KakaoRenewTokenResponseDto::class.java) - - logger.info { "갱신된 Access Token : ${kakaoToken.accessToken}" } - return kakaoToken - } } \ No newline at end of file From ef3f9306b7fa5309567aa7aa4b047323b2a04aad Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 7 Sep 2024 08:51:38 +0900 Subject: [PATCH 36/42] =?UTF-8?q?test:=20=EC=B9=B4=EC=B9=B4=EC=98=A4,=20?= =?UTF-8?q?=EC=9D=BC=EB=B0=98=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=B2=98=EB=A6=AC=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthenticationControllerTest.kt | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index ef18e54..4b7b8b4 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -3,8 +3,10 @@ package org.store.clothstar.member.authentication.controller import com.fasterxml.jackson.databind.ObjectMapper import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest @@ -12,9 +14,12 @@ import org.springframework.http.MediaType import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import org.springframework.transaction.annotation.Transactional import org.store.clothstar.common.config.redis.RedisUtil +import org.store.clothstar.common.error.ErrorCode +import org.store.clothstar.common.error.exception.InvalidSignupMemberRequest +import org.store.clothstar.common.error.exception.order.OrderNotFoundException import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.dto.request.KakaoMemberRequest @@ -145,4 +150,58 @@ class AuthenticationControllerTest( mockWebServer.shutdown() } + + @DisplayName("멤버 카카오 회원가입에서 요청DTO가 NULL일 경우 에러처리 테스트") + @Test + fun normalSignUpExceptionTest() { + //kakaoMemberRequest 요청 DTO 생성 + val kakaoMemberRequest = KakaoMemberRequest( + name = "Test User", + telNo = "010-1234-5678", + email = null, + code = "test_code" + ) + + // kakaoMemberRequest를 null로 맞춤 + val signUpRequest = SignUpRequest(null, kakaoMemberRequest) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + //when & then + mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.NORMAL.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode").value(400)) + .andExpect(jsonPath("$.message").value("회원가입 시 회원 정보가 필요합니다.")) + } + + @DisplayName("멤버 카카오 회원가입에서 요청DTO가 NULL일 경우 에러처리 테스트") + @Test + fun kakaoSignUpExceptionTest() { + // createMemberRequest 생성을 위한 이메일과 인증번호로 redis 데이터 생성 + val email = "test@naver.com" + val certifyNum = redisUtil.createdCertifyNum() + redisUtil.createRedisData(email, certifyNum) + + // kakaoMemberRequest를 null로 맞춤 + val createMemberRequest = CreateObject.getCreateMemberRequest(email, certifyNum) + val signUpRequest = SignUpRequest(createMemberRequest, null) + val requestBody = objectMapper.writeValueAsString(signUpRequest) + + //when & then + mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.KAKAO.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode").value(400)) + .andExpect(jsonPath("$.message").value("회원가입 시 회원 정보가 필요합니다.")) + } } \ No newline at end of file From 9aacf5ecbc4bdde88ce931260270dbc9aed45f1b Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 7 Sep 2024 09:46:37 +0900 Subject: [PATCH 37/42] =?UTF-8?q?refactor:=20OrderSellerServiceTest=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20&=20=EC=83=81=ED=92=88?= =?UTF-8?q?=EC=9E=AC=EA=B3=A0=EA=B0=80=200=EC=9D=BC=20=EB=95=8C,=20?= =?UTF-8?q?=EC=9E=AC=EA=B3=A0=EA=B0=80=20=EB=B6=80=EC=A1=B1=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/OrderUserIntegrationTest.kt | 84 ++++- .../order/service/OrderSellerServiceTest.kt | 356 +++++++++++++----- 2 files changed, 349 insertions(+), 91 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt index 3556625..67ef3a2 100644 --- a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt @@ -1,6 +1,8 @@ package org.store.clothstar.order import com.fasterxml.jackson.databind.ObjectMapper +import io.mockk.every +import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -13,8 +15,7 @@ import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.ResultActions import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import org.springframework.transaction.annotation.Transactional import org.store.clothstar.category.repository.CategoryJpaRepository import org.store.clothstar.member.domain.Member @@ -33,6 +34,9 @@ import org.store.clothstar.order.dto.request.OrderRequestWrapper import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository import org.store.clothstar.order.util.CreateOrderObject +import org.store.clothstar.product.domain.Item +import org.store.clothstar.product.domain.type.DisplayStatus +import org.store.clothstar.product.domain.type.SaleStatus import org.store.clothstar.product.repository.ItemRepository import org.store.clothstar.product.repository.ProductRepository import java.time.LocalDateTime @@ -198,6 +202,82 @@ class OrderUserIntegrationTest( assertEquals(1, savedOrder.orderDetails.size) } + @Test + @DisplayName("주문 생성시 상품재고가 0일경우 에러처리 테스트") + fun testCreateOrder_itemStockZeroException() { + //given + val member: Member = memberRepository.save(CreateOrderObject.getMember()) + val address = addressRepository.save(CreateOrderObject.getAddress(member)) + val category = categoryRepository.save(CreateOrderObject.getCategory()) + sellerRepository.save(CreateOrderObject.getSeller(member)) + val product = productRepository.save(CreateOrderObject.getProduct(member, category)) + val item = itemRepository.save(CreateOrderObject.getItem(product)).apply { + stock = 0 + } + + val createOrderRequest = CreateOrderRequest( + paymentMethod = PaymentMethod.CARD, + memberId = member.memberId!!, + addressId = address.addressId!! + ) + val createOrderDetailRequest = CreateOrderDetailRequest( + productId = product.productId!!, + itemId = item.itemId!!, + quantity = 1 + ) + val orderRequestWrapper = OrderRequestWrapper(createOrderRequest, createOrderDetailRequest) + val requestBody = objectMapper.writeValueAsString(orderRequestWrapper) + + //when & then + val actions: ResultActions = mockMvc.perform( + MockMvcRequestBuilders.post(ORDER_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode").value(400)) + .andExpect(jsonPath("$.message").value("품절된 상품입니다.")) + } + + @Test + @DisplayName("주문 생성시, 주문개수가 상품재고보다 많을 때 재고 부족 예외처리 테스트") + fun testCreateOrder_InsufficientStock_Exception() { + //given + val member: Member = memberRepository.save(CreateOrderObject.getMember()) + val address = addressRepository.save(CreateOrderObject.getAddress(member)) + val category = categoryRepository.save(CreateOrderObject.getCategory()) + sellerRepository.save(CreateOrderObject.getSeller(member)) + val product = productRepository.save(CreateOrderObject.getProduct(member, category)) + val item = itemRepository.save(CreateOrderObject.getItem(product)).apply { + stock = 1 + } + + val createOrderRequest = CreateOrderRequest( + paymentMethod = PaymentMethod.CARD, + memberId = member.memberId!!, + addressId = address.addressId!! + ) + val createOrderDetailRequest = CreateOrderDetailRequest( + productId = product.productId!!, + itemId = item.itemId!!, + quantity = 5 + ) + val orderRequestWrapper = OrderRequestWrapper(createOrderRequest, createOrderDetailRequest) + val requestBody = objectMapper.writeValueAsString(orderRequestWrapper) + + //when & then + val actions: ResultActions = mockMvc.perform( + MockMvcRequestBuilders.post(ORDER_URL) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode").value(400)) + .andExpect(jsonPath("$.message").value("주문 개수가 상품 재고보다 더 많아 요청을 처리할 수 없습니다.")) + } + @Test @DisplayName("주문상세 추가") fun testAddOrderDetail() { diff --git a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt index 45860f4..dc43454 100644 --- a/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/service/OrderSellerServiceTest.kt @@ -5,6 +5,7 @@ import io.mockk.impl.annotations.InjectMockKs import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension import io.mockk.justRun +import io.mockk.mockk import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertEquals @@ -12,6 +13,8 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.springframework.data.repository.findByIdOrNull +import org.springframework.http.HttpStatus +import org.springframework.web.server.ResponseStatusException import org.store.clothstar.common.error.ErrorCode import org.store.clothstar.common.error.exception.order.OrderNotFoundException import org.store.clothstar.member.domain.Address @@ -56,92 +59,85 @@ class OrderSellerServiceTest { @MockK lateinit var productService: ProductService - @MockK - lateinit var order: Order - - @MockK - lateinit var member: Member + // 판매자 주문(CONFIRMED) 리스트 조회 + @Test + @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - 성공 테스트") + fun addOrderDetail_success_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } - @MockK - lateinit var seller: Seller + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } - @MockK - lateinit var address: Address + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } - @MockK - lateinit var product: Product + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } - @MockK - lateinit var item: Item + val member = mockk { + every { name } returns "수빈" + } - @MockK - lateinit var addressInfo: AddressInfo + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } - @MockK - lateinit var totalPrice: TotalPrice + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } - @MockK - lateinit var price: Price + val seller = mockk { + every { brandName } returns "brandName" + } - @MockK - lateinit var orderDetail: OrderDetail + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } - val orderId = "4b1a17b5-45f0-455a-a5e3-2c863de18b05" - val memberId = 1L - val addressId = 2L - val productId = 3L - val itemId = 4L + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } - // 판매자 주문(CONFIRMED) 리스트 조회 - @Test - @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - 성공 테스트") - fun addOrderDetail_success_test() { //given every { orderRepository.findConfirmedAndNotDeletedOrders() } returns listOf(order) - every { order.memberId } returns memberId - every { order.addressId } returns addressId - every { memberService.getMemberByMemberId(memberId) } returns member - every { addressService.getAddressById(addressId) } returns address - every { sellerService.getSellerById(memberId) } returns seller - - every { totalPrice.shipping } returns 3000 - every { totalPrice.products } returns 5000 - every { totalPrice.payment } returns 8000 - every { order.orderId } returns orderId - every { member.name } returns "수빈" - every { order.createdAt } returns LocalDateTime.now() - every { order.status } returns Status.CONFIRMED - every { order.paymentMethod } returns PaymentMethod.CARD - every { order.totalPrice } returns totalPrice - every { address.addressInfo } returns addressInfo - every { address.receiverName } returns "수빈" - every { addressInfo.addressBasic } returns "address1" - every { addressInfo.addressDetail } returns "address2" - every { address.telNo } returns "010-1111-1111" - every { address.deliveryRequest } returns "문앞" + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById(any()) } returns address + every { sellerService.getSellerById(any()) } returns seller val expectedorderResponse = OrderResponse.from(order, member, address) - every { order.orderDetails } returns mutableListOf(orderDetail) - every { orderDetail.deletedAt } returns null // productIds, itemIds로부터 Product/Item 리스트 가져오기 - every { orderDetail.itemId } returns itemId - every { orderDetail.productId } returns productId - every { productService.findByProductIdIn(listOf(productId)) } returns listOf(product) - every { itemService.findByIdIn(listOf(itemId)) } returns listOf(item) - - every { item.itemId } returns itemId - every { product.productId } returns productId - - every { orderDetail.orderDetailId } returns 1L - every { product.name } returns "상품이름" - every { item.name } returns "상품옵션이름" - every { seller.brandName } returns "brandName" - every { product.price } returns 1000 - every { orderDetail.quantity } returns 1 - every { orderDetail.price } returns price - every { item.finalPrice } returns 10000 - every { price.oneKindTotalPrice } returns 10000 + every { productService.findByProductIdIn(any()) } returns listOf(product) + every { itemService.findByIdIn(any()) } returns listOf(item) // 주문상세 DTO 리스트 만들기 val orderDetailDTOs = listOf(OrderDetailDTO.from(orderDetail, item, product, seller.brandName)) @@ -155,11 +151,185 @@ class OrderSellerServiceTest { //then assertThat(orderReponse).usingRecursiveComparison().isEqualTo(listOf(expectedorderResponse)) verify(exactly = 1) { orderRepository.findConfirmedAndNotDeletedOrders() } - verify(exactly = 1) { memberService.getMemberByMemberId(memberId) } - verify(exactly = 1) { addressService.getAddressById(addressId) } - verify(exactly = 1) { sellerService.getSellerById(memberId) } - verify(exactly = 1) { productService.findByProductIdIn(listOf(productId)) } - verify(exactly = 1) { itemService.findByIdIn(listOf(itemId)) } + verify(exactly = 1) { memberService.getMemberByMemberId(any()) } + verify(exactly = 1) { addressService.getAddressById(any()) } + verify(exactly = 1) { sellerService.getSellerById(any()) } + verify(exactly = 1) { productService.findByProductIdIn(any()) } + verify(exactly = 1) { itemService.findByIdIn(any()) } + } + + @Test + @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - productId가 NULL일 경우 예외처리 테스트") + fun addOrderDetail_productIdNullException_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } + + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } + + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } + + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } + + val member = mockk { + every { name } returns "수빈" + } + + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } + + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } + + val seller = mockk { + every { brandName } returns "brandName" + } + + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } + + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } + + //given + every { orderRepository.findConfirmedAndNotDeletedOrders() } returns listOf(order) + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById(any()) } returns address + every { sellerService.getSellerById(any()) } returns seller + + //`productService.findByProductIdIn()` 호출 시 빈 리스트 반환 설정 + every { productService.findByProductIdIn(any()) } returns listOf() + every { itemService.findByIdIn(any()) } returns listOf() + + //when + val exception = assertThrows { + orderSellerService.getConfirmedOrders() + } + + //then + assertThat(exception.statusCode).isEqualTo(HttpStatus.NOT_FOUND) + assertThat(exception.reason).isEqualTo("Product not found") + } + + @Test + @DisplayName("판매자 주문(CONFIRMED) 리스트 조회 - itemId가 NULL일 경우 예외처리 테스트") + fun addOrderDetail_itemIdNullException_test() { + // Mock 객체 생성 및 설정 + val orderPrice = mockk { + every { fixedPrice } returns 10000 + every { oneKindTotalPrice } returns 10000 + } + + val orderDetail = mockk { + every { price } returns orderPrice + every { quantity } returns 1 + every { orderDetailId } returns 1L + every { deletedAt } returns null + every { itemId } returns 1L + every { productId } returns 1L + } + + val item = mockk { + every { itemId } returns 1L + every { name } returns "상품옵션이름" + every { finalPrice } returns 10000 + } + + val product = mockk { + every { productId } returns 1L + every { name } returns "상품이름" + every { price } returns 1000 + } + + val member = mockk { + every { name } returns "수빈" + } + + val orderAddressInfo = mockk { + every { addressBasic } returns "address1" + every { addressDetail } returns "address2" + } + + val address = mockk
{ + every { addressInfo } returns orderAddressInfo + every { receiverName } returns "수빈" + every { telNo } returns "010-1111-1111" + every { deliveryRequest } returns "문앞" + } + + val seller = mockk { + every { brandName } returns "brandName" + } + + val orderTotalPrice = mockk { + every { shipping } returns 3000 + every { products } returns 5000 + every { payment } returns 8000 + } + + val order = mockk { + every { orderId } returns "4b1a17b5-45f0-455a-a5e3-2c863de18b05" + every { memberId } returns 1L + every { addressId } returns 1L + every { createdAt } returns LocalDateTime.now() + every { status } returns Status.CONFIRMED + every { paymentMethod } returns PaymentMethod.CARD + every { totalPrice } returns orderTotalPrice + every { orderDetails } returns mutableListOf(orderDetail) + } + + //given + every { orderRepository.findConfirmedAndNotDeletedOrders() } returns listOf(order) + every { memberService.getMemberByMemberId(any()) } returns member + every { addressService.getAddressById(any()) } returns address + every { sellerService.getSellerById(any()) } returns seller + + //`itemService.findByIdIn()` 호출 시 빈 리스트 반환 설정 + every { productService.findByProductIdIn(any()) } returns listOf(product) + every { itemService.findByIdIn(any()) } returns listOf() + + //when + val exception = assertThrows { + orderSellerService.getConfirmedOrders() + } + + //then + assertThat(exception.statusCode).isEqualTo(HttpStatus.NOT_FOUND) + assertThat(exception.reason).isEqualTo("Item not found") } // 판매자 주문 승인 - approveOrder @@ -167,18 +337,20 @@ class OrderSellerServiceTest { @DisplayName("판매자 주문 승인 - 성공 테스트") fun approveOrder_success_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns order + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns order justRun { order.validateForStatusCONFIRMEDAndDeletedAt() } every { order.updateStatus(Status.PROCESSING) } answers { every { order.status } returns Status.PROCESSING } //when - orderSellerService.approveOrder(orderId) + orderSellerService.approveOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") //then - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { order.validateForStatusCONFIRMEDAndDeletedAt() } verify(exactly = 1) { order.updateStatus(Status.PROCESSING) } assertEquals(Status.PROCESSING, order.status) @@ -188,12 +360,14 @@ class OrderSellerServiceTest { @DisplayName("판매자 주문 승인 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun approveOrder_orderNotFound_exception_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns null + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { - orderSellerService.approveOrder(orderId) + orderSellerService.approveOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } @@ -203,18 +377,20 @@ class OrderSellerServiceTest { @DisplayName("판매자 주문 취소 - 성공 테스트") fun cancelOrder_success_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns order + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns order justRun { order.validateForStatusCONFIRMEDAndDeletedAt() } every { order.updateStatus(Status.CANCELED) } answers { every { order.status } returns Status.CANCELED } //when - orderSellerService.cancelOrder(orderId) + orderSellerService.cancelOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") //then - verify(exactly = 1) { orderRepository.findByIdOrNull(orderId) } + verify(exactly = 1) { orderRepository.findByIdOrNull(any()) } verify(exactly = 1) { order.validateForStatusCONFIRMEDAndDeletedAt() } verify(exactly = 1) { order.updateStatus(Status.CANCELED) } assertEquals(Status.CANCELED, order.status) @@ -224,12 +400,14 @@ class OrderSellerServiceTest { @DisplayName("판매자 주문 취소 - 주문번호가 존재하지 않을 때 예외처리 테스트") fun cancelOrder_orderNotFound_exception_test() { //given - every { order.status } returns Status.CONFIRMED - every { orderRepository.findByIdOrNull(orderId) } returns null + val order = mockk { + every { status } returns Status.CONFIRMED + } + every { orderRepository.findByIdOrNull(any()) } returns null //when & then val exception = assertThrows { - orderSellerService.cancelOrder(orderId) + orderSellerService.cancelOrder("4b1a17b5-45f0-455a-a5e3-2c863de18b05") } assertEquals(ErrorCode.NOT_FOUND_ORDER, exception.errorCode) } From 88271c3bf56a1f82a76239d1595fbbb2ac9324b3 Mon Sep 17 00:00:00 2001 From: subin Date: Sat, 7 Sep 2024 09:56:45 +0900 Subject: [PATCH 38/42] refactor: Optimize Imports & Reformat Code refactor: Optimize Imports & Reformat Code --- .../AuthenticationControllerTest.kt | 27 ++++++++----------- .../order/OrderUserIntegrationTest.kt | 9 ++----- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt index 4b7b8b4..73c7da5 100644 --- a/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt +++ b/src/test/kotlin/org/store/clothstar/member/authentication/controller/AuthenticationControllerTest.kt @@ -3,10 +3,8 @@ package org.store.clothstar.member.authentication.controller import com.fasterxml.jackson.databind.ObjectMapper import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest @@ -17,9 +15,6 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import org.springframework.transaction.annotation.Transactional import org.store.clothstar.common.config.redis.RedisUtil -import org.store.clothstar.common.error.ErrorCode -import org.store.clothstar.common.error.exception.InvalidSignupMemberRequest -import org.store.clothstar.common.error.exception.order.OrderNotFoundException import org.store.clothstar.kakaoLogin.service.KakaoLoginService import org.store.clothstar.member.authentication.domain.SignUpType import org.store.clothstar.member.dto.request.KakaoMemberRequest @@ -193,15 +188,15 @@ class AuthenticationControllerTest( val requestBody = objectMapper.writeValueAsString(signUpRequest) //when & then - mockMvc.perform( - post(MEMBER_URL) - .param("signUpType", SignUpType.KAKAO.toString()) - .contentType(MediaType.APPLICATION_JSON) - .content(requestBody) - ) - .andExpect(status().isBadRequest) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.errorCode").value(400)) - .andExpect(jsonPath("$.message").value("회원가입 시 회원 정보가 필요합니다.")) - } + mockMvc.perform( + post(MEMBER_URL) + .param("signUpType", SignUpType.KAKAO.toString()) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ) + .andExpect(status().isBadRequest) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode").value(400)) + .andExpect(jsonPath("$.message").value("회원가입 시 회원 정보가 필요합니다.")) + } } \ No newline at end of file diff --git a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt index 67ef3a2..42af404 100644 --- a/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt +++ b/src/test/kotlin/org/store/clothstar/order/OrderUserIntegrationTest.kt @@ -1,8 +1,6 @@ package org.store.clothstar.order import com.fasterxml.jackson.databind.ObjectMapper -import io.mockk.every -import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -34,9 +32,6 @@ import org.store.clothstar.order.dto.request.OrderRequestWrapper import org.store.clothstar.order.repository.OrderDetailRepository import org.store.clothstar.order.repository.OrderRepository import org.store.clothstar.order.util.CreateOrderObject -import org.store.clothstar.product.domain.Item -import org.store.clothstar.product.domain.type.DisplayStatus -import org.store.clothstar.product.domain.type.SaleStatus import org.store.clothstar.product.repository.ItemRepository import org.store.clothstar.product.repository.ProductRepository import java.time.LocalDateTime @@ -229,7 +224,7 @@ class OrderUserIntegrationTest( val requestBody = objectMapper.writeValueAsString(orderRequestWrapper) //when & then - val actions: ResultActions = mockMvc.perform( + mockMvc.perform( MockMvcRequestBuilders.post(ORDER_URL) .contentType(MediaType.APPLICATION_JSON) .content(requestBody) @@ -267,7 +262,7 @@ class OrderUserIntegrationTest( val requestBody = objectMapper.writeValueAsString(orderRequestWrapper) //when & then - val actions: ResultActions = mockMvc.perform( + mockMvc.perform( MockMvcRequestBuilders.post(ORDER_URL) .contentType(MediaType.APPLICATION_JSON) .content(requestBody) From 2bf141f500b90a3634c36d2bf7536a903d5a25df Mon Sep 17 00:00:00 2001 From: KIM MINA Date: Mon, 9 Sep 2024 12:46:13 +0900 Subject: [PATCH 39/42] docs: update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 97ac895..ea17843 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,6 @@ ## 프로젝트 기간 이 프로젝트의 기간은 2024년 7월 23일부터 2024년 8월 30일까지입니다. + +## CI/CD Architecture +![image](https://github.com/user-attachments/assets/8dc985e5-68b8-4982-897b-c8e60d8b1b6f) From 9c5331755e66741e17a6006fb08ed881ad1ab74c Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Mon, 9 Sep 2024 13:04:04 +0900 Subject: [PATCH 40/42] =?UTF-8?q?chore:=20docker-compose=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20EC2=20=EC=A0=84=EC=86=A1=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dev-aws-CI-CD.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/dev-aws-CI-CD.yml b/.github/workflows/dev-aws-CI-CD.yml index 11784b8..1837dba 100644 --- a/.github/workflows/dev-aws-CI-CD.yml +++ b/.github/workflows/dev-aws-CI-CD.yml @@ -73,6 +73,15 @@ jobs: - name: Show docker-compose.yml Content run: cat docker-compose.yml # docker-compose.yml 파일 내용 확인 + - name: Send docker-compose.yml + uses: appleboy/scp-action@master + with: + username: ubuntu + host: ${{ secrets.AWS_DEV_HOSTNAME }} + key: ${{ secrets.AWS_DEV_PRIVATE_KEY }} + source: "./docker-compose.yml" + target: "/home/ubuntu/" + # Send nginx.conf to a temporary location - name: Send nginx.conf to Home Directory uses: appleboy/scp-action@master From 601acc09b062fd015a56ba565558f390a56087d3 Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Mon, 9 Sep 2024 13:10:31 +0900 Subject: [PATCH 41/42] =?UTF-8?q?chore:=20Send=20docker-compose.yml,=20ngi?= =?UTF-8?q?nx.conf=20=EB=A1=9C=EC=A7=81=20=ED=95=A9=EC=B9=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dev-aws-CI-CD.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/dev-aws-CI-CD.yml b/.github/workflows/dev-aws-CI-CD.yml index 1837dba..9b8a176 100644 --- a/.github/workflows/dev-aws-CI-CD.yml +++ b/.github/workflows/dev-aws-CI-CD.yml @@ -73,25 +73,15 @@ jobs: - name: Show docker-compose.yml Content run: cat docker-compose.yml # docker-compose.yml 파일 내용 확인 - - name: Send docker-compose.yml + - name: Send docker-compose.yml and nginx.conf to Home Directory uses: appleboy/scp-action@master with: username: ubuntu host: ${{ secrets.AWS_DEV_HOSTNAME }} key: ${{ secrets.AWS_DEV_PRIVATE_KEY }} - source: "./docker-compose.yml" + source: "./docker-compose.yml,./nginx/conf.d/nginx.conf" target: "/home/ubuntu/" - - # Send nginx.conf to a temporary location - - name: Send nginx.conf to Home Directory - uses: appleboy/scp-action@master - with: - username: ubuntu - host: ${{ secrets.AWS_DEV_HOSTNAME }} - key: ${{ secrets.AWS_DEV_PRIVATE_KEY }} - source: "./nginx/conf.d/nginx.conf" - target: "/home/ubuntu" # 정확한 파일 경로 지정 - strip_components: 3 # 경로 구성 요소를 제거하여 파일만 전송 + strip_components: 2 # nginx.conf 경로 구성 요소를 제거하여 파일만 전송 # Move nginx.conf from Home Directory to /etc/nginx/conf.d - name: Move nginx.conf to /etc/nginx/conf.d From 86b3a175381911d953ea40e05d4cedbad45e284d Mon Sep 17 00:00:00 2001 From: Ogu1208 Date: Mon, 9 Sep 2024 13:18:23 +0900 Subject: [PATCH 42/42] =?UTF-8?q?chore:=20depth=203=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dev-aws-CI-CD.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-aws-CI-CD.yml b/.github/workflows/dev-aws-CI-CD.yml index 9b8a176..60bf6d7 100644 --- a/.github/workflows/dev-aws-CI-CD.yml +++ b/.github/workflows/dev-aws-CI-CD.yml @@ -81,7 +81,7 @@ jobs: key: ${{ secrets.AWS_DEV_PRIVATE_KEY }} source: "./docker-compose.yml,./nginx/conf.d/nginx.conf" target: "/home/ubuntu/" - strip_components: 2 # nginx.conf 경로 구성 요소를 제거하여 파일만 전송 + strip_components: 3 # nginx.conf 경로 구성 요소를 제거하여 파일만 전송 # Move nginx.conf from Home Directory to /etc/nginx/conf.d - name: Move nginx.conf to /etc/nginx/conf.d