From 404ecf4d625781404b01713c3ff0b3349a5e46e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=98=81=EB=AF=BC?= Date: Fri, 21 Jun 2024 00:18:14 +0900 Subject: [PATCH 1/4] feat: add parameter resolver for memberId --- .../config/MemberIdParameterResolver.java | 21 +++++++++++++++++++ .../kr/mafoo/user/config/RequestMemberId.java | 11 ++++++++++ .../kr/mafoo/user/config/WebFluxConfig.java | 15 +++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java create mode 100644 user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java create mode 100644 user-service/src/main/java/kr/mafoo/user/config/WebFluxConfig.java diff --git a/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java b/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java new file mode 100644 index 0000000..089343b --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java @@ -0,0 +1,21 @@ +package kr.mafoo.user.config; + + +import org.springframework.core.MethodParameter; +import org.springframework.web.reactive.BindingContext; +import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +public class MemberIdParameterResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterAnnotation(RequestMemberId.class) != null; + } + + @Override + public Mono resolveArgument(MethodParameter parameter, BindingContext bindingContext, ServerWebExchange exchange) { + String memberId = exchange.getRequest().getHeaders().getFirst("X-MEMBER-ID"); + return Mono.justOrEmpty(memberId); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java b/user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java new file mode 100644 index 0000000..8e3be44 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java @@ -0,0 +1,11 @@ +package kr.mafoo.user.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER, ElementType.TYPE_PARAMETER}) +public @interface RequestMemberId { +} diff --git a/user-service/src/main/java/kr/mafoo/user/config/WebFluxConfig.java b/user-service/src/main/java/kr/mafoo/user/config/WebFluxConfig.java new file mode 100644 index 0000000..449f4ca --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/config/WebFluxConfig.java @@ -0,0 +1,15 @@ +package kr.mafoo.user.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.config.EnableWebFlux; +import org.springframework.web.reactive.config.WebFluxConfigurer; +import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer; + +@EnableWebFlux +@Configuration +public class WebFluxConfig implements WebFluxConfigurer { + @Override + public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) { + configurer.addCustomResolver(new MemberIdParameterResolver()); + } +} From 7d04635350a7f2648471d77b276bc9012411aeb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=98=81=EB=AF=BC?= Date: Fri, 21 Jun 2024 00:29:10 +0900 Subject: [PATCH 2/4] feat: implement member query, quit --- user-service/build.gradle.kts | 2 + .../main/java/kr/mafoo/user/api/MeApi.java | 10 ++++- .../user/config/WebExceptionHandler.java | 17 ++++++++ .../mafoo/user/controller/MeController.java | 16 +++++-- .../dto/response/ErrorResponse.java | 20 +++++++++ .../dto/response/MemberResponse.java | 7 +++ .../kr/mafoo/user/domain/MemberEntity.java | 43 +++++++++++++++++++ .../mafoo/user/exception/DomainException.java | 13 ++++++ .../kr/mafoo/user/exception/ErrorCode.java | 12 ++++++ .../exception/MemberNotFoundException.java | 7 +++ .../user/repository/MemberRepository.java | 9 ++++ .../kr/mafoo/user/service/MemberService.java | 25 +++++++++++ .../src/main/resources/application.yaml | 3 ++ 13 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 user-service/src/main/java/kr/mafoo/user/config/WebExceptionHandler.java create mode 100644 user-service/src/main/java/kr/mafoo/user/controller/dto/response/ErrorResponse.java create mode 100644 user-service/src/main/java/kr/mafoo/user/domain/MemberEntity.java create mode 100644 user-service/src/main/java/kr/mafoo/user/exception/DomainException.java create mode 100644 user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java create mode 100644 user-service/src/main/java/kr/mafoo/user/exception/MemberNotFoundException.java create mode 100644 user-service/src/main/java/kr/mafoo/user/repository/MemberRepository.java create mode 100644 user-service/src/main/java/kr/mafoo/user/service/MemberService.java diff --git a/user-service/build.gradle.kts b/user-service/build.gradle.kts index cecff71..60e0093 100644 --- a/user-service/build.gradle.kts +++ b/user-service/build.gradle.kts @@ -31,6 +31,8 @@ dependencies { testImplementation("io.projectreactor:reactor-test") testImplementation("org.springframework.security:spring-security-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") + implementation("org.projectlombok:lombok:1.18.32") + annotationProcessor("org.projectlombok:lombok:1.18.32") } tasks.withType { diff --git a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java index 64f451a..d6da19d 100644 --- a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java +++ b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java @@ -1,7 +1,9 @@ package kr.mafoo.user.api; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import kr.mafoo.user.config.RequestMemberId; import kr.mafoo.user.controller.dto.response.MemberResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -13,9 +15,13 @@ public interface MeApi { @Operation(summary = "내 정보 조회", description = "현재 토큰 주인의 정보를 조회합니다.") @GetMapping - Mono getMemberWhoRequested(); + Mono getMemberWhoRequested( + @RequestMemberId @Parameter(hidden = true) String memberId + ); @Operation(summary = "탈퇴", description = "현재 토큰 주인이 탈퇴합니다.") @PostMapping("/quit") - Mono deleteMemberWhoRequested(); + Mono deleteMemberWhoRequested( + @RequestMemberId @Parameter(hidden = true) String memberId + ); } diff --git a/user-service/src/main/java/kr/mafoo/user/config/WebExceptionHandler.java b/user-service/src/main/java/kr/mafoo/user/config/WebExceptionHandler.java new file mode 100644 index 0000000..b5fddc1 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/config/WebExceptionHandler.java @@ -0,0 +1,17 @@ +package kr.mafoo.user.config; + +import kr.mafoo.user.controller.dto.response.ErrorResponse; +import kr.mafoo.user.exception.DomainException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class WebExceptionHandler { + @ExceptionHandler(DomainException.class) + public ResponseEntity handleDomainException(DomainException exception) { + return ResponseEntity + .badRequest() + .body(ErrorResponse.fromErrorCode(exception.getErrorCode())); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/controller/MeController.java b/user-service/src/main/java/kr/mafoo/user/controller/MeController.java index fbd375d..2cfb6dd 100644 --- a/user-service/src/main/java/kr/mafoo/user/controller/MeController.java +++ b/user-service/src/main/java/kr/mafoo/user/controller/MeController.java @@ -2,18 +2,26 @@ import kr.mafoo.user.api.MeApi; import kr.mafoo.user.controller.dto.response.MemberResponse; +import kr.mafoo.user.service.MemberService; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; +@RequiredArgsConstructor @RestController public class MeController implements MeApi { + private final MemberService memberService; + @Override - public Mono getMemberWhoRequested() { - return Mono.just(new MemberResponse("test", "송영민")); + public Mono getMemberWhoRequested(String memberId) { + return memberService + .getMemberByMemberId(memberId) + .map(MemberResponse::fromEntity); } @Override - public Mono deleteMemberWhoRequested() { - return Mono.empty(); + public Mono deleteMemberWhoRequested(String memberId) { + return memberService + .quitMemberByMemberId(memberId); } } diff --git a/user-service/src/main/java/kr/mafoo/user/controller/dto/response/ErrorResponse.java b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/ErrorResponse.java new file mode 100644 index 0000000..d8b6d4c --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/ErrorResponse.java @@ -0,0 +1,20 @@ +package kr.mafoo.user.controller.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.mafoo.user.exception.ErrorCode; + +@Schema(description = "에러 응답") +public record ErrorResponse( + @Schema(description = "에러 코드", example = "ME0001") + String code, + + @Schema(description = "에러 메시지", example = "사용자를 찾을 수 없습니다") + String message +) { + public static ErrorResponse fromErrorCode(ErrorCode errorCode) { + return new ErrorResponse( + errorCode.getCode(), + errorCode.getMessage() + ); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberResponse.java b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberResponse.java index f75d5c8..a7a239d 100644 --- a/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberResponse.java +++ b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberResponse.java @@ -1,6 +1,7 @@ package kr.mafoo.user.controller.dto.response; import io.swagger.v3.oas.annotations.media.Schema; +import kr.mafoo.user.domain.MemberEntity; @Schema(description = "사용자 정보 응답") public record MemberResponse( @@ -10,4 +11,10 @@ public record MemberResponse( @Schema(description = "사용자 이름", example = "송영민") String name ) { + public static MemberResponse fromEntity(MemberEntity memberEntity) { + return new MemberResponse( + memberEntity.getId(), + memberEntity.getName() + ); + } } diff --git a/user-service/src/main/java/kr/mafoo/user/domain/MemberEntity.java b/user-service/src/main/java/kr/mafoo/user/domain/MemberEntity.java new file mode 100644 index 0000000..048b10e --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/domain/MemberEntity.java @@ -0,0 +1,43 @@ +package kr.mafoo.user.domain; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@Table("member") +public class MemberEntity { + @Id + @Column("member_id") + private String id; + + @Column("name") + private String name; + + @CreatedDate + @Column("created_at") + private LocalDateTime createdAt; + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + MemberEntity that = (MemberEntity) obj; + + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/exception/DomainException.java b/user-service/src/main/java/kr/mafoo/user/exception/DomainException.java new file mode 100644 index 0000000..604bde7 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/exception/DomainException.java @@ -0,0 +1,13 @@ +package kr.mafoo.user.exception; + +import lombok.Getter; + +@Getter +public class DomainException extends RuntimeException { + private final ErrorCode errorCode; + + public DomainException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java b/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java new file mode 100644 index 0000000..59f0902 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java @@ -0,0 +1,12 @@ +package kr.mafoo.user.exception; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum ErrorCode { + MEMBER_NOT_FOUND("ME0001", "사용자를 찾을 수 없습니다"); + private final String code; + private final String message; +} diff --git a/user-service/src/main/java/kr/mafoo/user/exception/MemberNotFoundException.java b/user-service/src/main/java/kr/mafoo/user/exception/MemberNotFoundException.java new file mode 100644 index 0000000..09dbd35 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/exception/MemberNotFoundException.java @@ -0,0 +1,7 @@ +package kr.mafoo.user.exception; + +public class MemberNotFoundException extends DomainException { + public MemberNotFoundException() { + super(ErrorCode.MEMBER_NOT_FOUND); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/repository/MemberRepository.java b/user-service/src/main/java/kr/mafoo/user/repository/MemberRepository.java new file mode 100644 index 0000000..e8da018 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/repository/MemberRepository.java @@ -0,0 +1,9 @@ +package kr.mafoo.user.repository; + +import kr.mafoo.user.domain.MemberEntity; +import org.springframework.data.r2dbc.repository.R2dbcRepository; +import reactor.core.publisher.Mono; + +public interface MemberRepository extends R2dbcRepository { + Mono deleteMemberById(String memberId); +} diff --git a/user-service/src/main/java/kr/mafoo/user/service/MemberService.java b/user-service/src/main/java/kr/mafoo/user/service/MemberService.java new file mode 100644 index 0000000..94deb7b --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/service/MemberService.java @@ -0,0 +1,25 @@ +package kr.mafoo.user.service; + +import kr.mafoo.user.domain.MemberEntity; +import kr.mafoo.user.exception.MemberNotFoundException; +import kr.mafoo.user.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Mono; + +@RequiredArgsConstructor +@Service +public class MemberService { + private final MemberRepository memberRepository; + + public Mono quitMemberByMemberId(String memberId) { + return memberRepository.deleteMemberById(memberId); + } + + public Mono getMemberByMemberId(String memberId) { + return memberRepository + .findById(memberId) + .switchIfEmpty(Mono.error(new MemberNotFoundException())); + } + +} diff --git a/user-service/src/main/resources/application.yaml b/user-service/src/main/resources/application.yaml index 1a7f479..273e526 100644 --- a/user-service/src/main/resources/application.yaml +++ b/user-service/src/main/resources/application.yaml @@ -5,3 +5,6 @@ spring: url: ${MYSQL_URL} username: ${MYSQL_USERNAME} password: ${MYSQL_PASSWORD} +logging: + level: + org.springframework.data.r2dbc: DEBUG From 6f146ccefc60a106e6785823badee25754438832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=98=81=EB=AF=BC?= Date: Fri, 21 Jun 2024 00:31:26 +0900 Subject: [PATCH 3/4] fix: change post quit to delete --- user-service/src/main/java/kr/mafoo/user/api/MeApi.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java index d6da19d..8179658 100644 --- a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java +++ b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java @@ -5,8 +5,8 @@ import io.swagger.v3.oas.annotations.tags.Tag; import kr.mafoo.user.config.RequestMemberId; import kr.mafoo.user.controller.dto.response.MemberResponse; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import reactor.core.publisher.Mono; @@ -20,7 +20,7 @@ Mono getMemberWhoRequested( ); @Operation(summary = "탈퇴", description = "현재 토큰 주인이 탈퇴합니다.") - @PostMapping("/quit") + @DeleteMapping Mono deleteMemberWhoRequested( @RequestMemberId @Parameter(hidden = true) String memberId ); From 4ae2404b610a2163c3077bd216affec8e8ac08d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=86=A1=EC=98=81=EB=AF=BC?= Date: Sat, 22 Jun 2024 18:59:48 +0900 Subject: [PATCH 4/4] refactor: move annotation folder --- .../kr/mafoo/user/{config => annotation}/RequestMemberId.java | 2 +- user-service/src/main/java/kr/mafoo/user/api/MeApi.java | 2 +- .../java/kr/mafoo/user/config/MemberIdParameterResolver.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename user-service/src/main/java/kr/mafoo/user/{config => annotation}/RequestMemberId.java (89%) diff --git a/user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java b/user-service/src/main/java/kr/mafoo/user/annotation/RequestMemberId.java similarity index 89% rename from user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java rename to user-service/src/main/java/kr/mafoo/user/annotation/RequestMemberId.java index 8e3be44..a7c9843 100644 --- a/user-service/src/main/java/kr/mafoo/user/config/RequestMemberId.java +++ b/user-service/src/main/java/kr/mafoo/user/annotation/RequestMemberId.java @@ -1,4 +1,4 @@ -package kr.mafoo.user.config; +package kr.mafoo.user.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java index 8179658..92ade94 100644 --- a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java +++ b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java @@ -3,7 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import kr.mafoo.user.config.RequestMemberId; +import kr.mafoo.user.annotation.RequestMemberId; import kr.mafoo.user.controller.dto.response.MemberResponse; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; diff --git a/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java b/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java index 089343b..856e51d 100644 --- a/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java +++ b/user-service/src/main/java/kr/mafoo/user/config/MemberIdParameterResolver.java @@ -1,6 +1,7 @@ package kr.mafoo.user.config; +import kr.mafoo.user.annotation.RequestMemberId; import org.springframework.core.MethodParameter; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;