From 37aebaf3684439c6cffd750c10167b39124eb955 Mon Sep 17 00:00:00 2001 From: SEONG JUN SHIN Date: Tue, 16 Jan 2024 17:12:12 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Google=20OAuth=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pointer/controller/ControllerAdvice.java | 53 +++++++++++++++++++ .../pointer/controller/LoginController.java | 9 +++- .../pointer/controller/UserController.java | 17 +++--- src/main/java/gdsc/pointer/dao/UserDao.java | 23 ++++++-- src/main/java/gdsc/pointer/domain/User.java | 2 +- .../pointer/dto/request/login/UserDto.java | 4 ++ .../dto/response/error/ErrorResponse.java | 14 +++++ .../exception/CustomExceptionContext.java | 4 +- .../exception/notfound/NotFoundException.java | 11 ++++ .../notfound/user/UserNotFoundException.java | 11 ++++ .../gdsc/pointer/service/LoginService.java | 23 +++++--- .../gdsc/pointer/service/UserService.java | 2 + .../gdsc/pointer/service/UserServiceImpl.java | 20 +++++++ 13 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 src/main/java/gdsc/pointer/controller/ControllerAdvice.java create mode 100644 src/main/java/gdsc/pointer/dto/response/error/ErrorResponse.java create mode 100644 src/main/java/gdsc/pointer/exception/notfound/NotFoundException.java create mode 100644 src/main/java/gdsc/pointer/exception/notfound/user/UserNotFoundException.java diff --git a/src/main/java/gdsc/pointer/controller/ControllerAdvice.java b/src/main/java/gdsc/pointer/controller/ControllerAdvice.java new file mode 100644 index 0000000..6a566cd --- /dev/null +++ b/src/main/java/gdsc/pointer/controller/ControllerAdvice.java @@ -0,0 +1,53 @@ +package gdsc.pointer.controller; + +import gdsc.pointer.dto.response.error.ErrorResponse; +import gdsc.pointer.exception.PointerException; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.sql.SQLException; +import java.util.Objects; + +@Slf4j +@RestControllerAdvice +public class ControllerAdvice { + private static final int FIELD_ERROR_CODE_INDEX = 0; + private static final int FIELD_ERROR_MESSAGE_INDEX = 1; + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleInputFieldException(MethodArgumentNotValidException e) { + FieldError mainError = e.getFieldErrors().get(0); + String[] errorInfo = Objects.requireNonNull(mainError.getDefaultMessage()).split(":"); + + int code = Integer.parseInt(errorInfo[FIELD_ERROR_CODE_INDEX]); + String message = errorInfo[FIELD_ERROR_MESSAGE_INDEX]; + + return ResponseEntity.badRequest().body(new gdsc.pointer.dto.response.error.ErrorResponse(code, message)); + } + + @ExceptionHandler(SQLException.class) + public ResponseEntity sqlExceptionHandle(PointerException e) { + return ResponseEntity.status(e.getHttpStatus()).body(new ErrorResponse(e.getCode(), e.getMessage())); + } + + @ExceptionHandler(PointerException.class) + public ResponseEntity handleSubwayException(PointerException e) { + return ResponseEntity.status(e.getHttpStatus()).body(new ErrorResponse(e.getCode(), e.getMessage())); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity unhandledException(Exception e, HttpServletRequest request) { + log.error("UnhandledException: {} {} errMessage={}\n", + request.getMethod(), + request.getRequestURI(), + e.getMessage() + ); + return ResponseEntity.internalServerError() + .body(new ErrorResponse(9999, "일시적으로 접속이 원활하지 않습니다. 지하철 서비스 팀에 문의 부탁드립니다.")); + } +} diff --git a/src/main/java/gdsc/pointer/controller/LoginController.java b/src/main/java/gdsc/pointer/controller/LoginController.java index 56c967a..9b8b36d 100644 --- a/src/main/java/gdsc/pointer/controller/LoginController.java +++ b/src/main/java/gdsc/pointer/controller/LoginController.java @@ -1,7 +1,11 @@ package gdsc.pointer.controller; +import gdsc.pointer.dto.request.login.UserDto; +import gdsc.pointer.dto.response.ResultDto; import gdsc.pointer.service.LoginService; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @@ -12,8 +16,9 @@ public class LoginController { private final LoginService loginService; @GetMapping("/code/{registrationId}") - public void googleLogin(@RequestParam String code, @PathVariable String registrationId) { - loginService.socialLogin(code, registrationId); + public ResponseEntity googleLogin(@RequestParam String code, @PathVariable String registrationId) throws Exception { + UserDto userDto = loginService.socialLogin(code, registrationId); + return ResponseEntity.ok(ResultDto.res(HttpStatus.OK, "회원 가입 완료", userDto)); } } diff --git a/src/main/java/gdsc/pointer/controller/UserController.java b/src/main/java/gdsc/pointer/controller/UserController.java index 95a1d83..2a4dd95 100644 --- a/src/main/java/gdsc/pointer/controller/UserController.java +++ b/src/main/java/gdsc/pointer/controller/UserController.java @@ -21,7 +21,15 @@ public class UserController { @GetMapping("getUsers") public ResponseEntity getUsers() throws Exception { List users = userService.getUsers(); - return ResponseEntity.ok(ResultDto.res(HttpStatus.OK, "회원 조회 완료", users)); + return ResponseEntity.ok(ResultDto.res(HttpStatus.OK, "모든 회원 조회 완료", users)); + } + @GetMapping("/getUserDetail/{id}") + public ResponseEntity getUserDetail(@PathVariable("id") String id) throws Exception{ + User user = userService.getUserDetail(id); + if (user == null) { + return ResponseEntity.ok(ResultDto.res(HttpStatus.NOT_FOUND, "존재하지 않는 회원입니다.", null)); + } + return ResponseEntity.ok(ResultDto.res(HttpStatus.OK, "회원 정보 조회 완료", user)); } @PostMapping() @@ -29,11 +37,8 @@ public ResponseEntity addUser(@RequestBody UserDto userDto) throws Exception{ userService.addUser(userDto); return ResponseEntity.ok(ResultDto.res(HttpStatus.CREATED, "회원 가입 완료")); } -// -// @GetMapping("/getUserDetail") -// public ResponseEntity getUserDetail(@RequestParam("id") String id) throws Exception{ -// return userService.getUserDetail(id); -// } + + // // @PutMapping("/updateUser/{id}") // public ResponseEntity updateUser(@PathVariable("id") String id, @RequestBody User user) throws Exception{ diff --git a/src/main/java/gdsc/pointer/dao/UserDao.java b/src/main/java/gdsc/pointer/dao/UserDao.java index 0634637..c4b1778 100644 --- a/src/main/java/gdsc/pointer/dao/UserDao.java +++ b/src/main/java/gdsc/pointer/dao/UserDao.java @@ -1,13 +1,13 @@ package gdsc.pointer.dao; import com.google.api.core.ApiFuture; -import com.google.cloud.firestore.Firestore; -import com.google.cloud.firestore.QueryDocumentSnapshot; -import com.google.cloud.firestore.QuerySnapshot; +import com.google.cloud.firestore.*; import com.google.firebase.cloud.FirestoreClient; import gdsc.pointer.domain.User; import gdsc.pointer.dto.request.login.UserDto; +import gdsc.pointer.exception.notfound.user.UserNotFoundException; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Repository; import java.util.ArrayList; @@ -31,11 +31,24 @@ public List getUsers() throws Exception { } - + public User getUserDetail(String id) throws Exception { + Firestore firestore = FirestoreClient.getFirestore(); + DocumentReference documentReference = + firestore.collection(COLLECTION_NAME).document(id); + ApiFuture apiFuture = documentReference.get(); + DocumentSnapshot documentSnapshot = apiFuture.get(); + User user = null; + if(documentSnapshot.exists()){ + user = documentSnapshot.toObject(User.class); + } + return user; + } public void addUser(UserDto userDto) throws Exception { - + Firestore firestore = FirestoreClient.getFirestore(); + ApiFuture apiFuture = + firestore.collection(COLLECTION_NAME).document(userDto.getId()).set(userDto); } diff --git a/src/main/java/gdsc/pointer/domain/User.java b/src/main/java/gdsc/pointer/domain/User.java index a40d820..1dd730b 100644 --- a/src/main/java/gdsc/pointer/domain/User.java +++ b/src/main/java/gdsc/pointer/domain/User.java @@ -12,7 +12,7 @@ public class User { private String id; - private String name; private String email; + private String nickname; } diff --git a/src/main/java/gdsc/pointer/dto/request/login/UserDto.java b/src/main/java/gdsc/pointer/dto/request/login/UserDto.java index 62e1e89..707663e 100644 --- a/src/main/java/gdsc/pointer/dto/request/login/UserDto.java +++ b/src/main/java/gdsc/pointer/dto/request/login/UserDto.java @@ -1,8 +1,12 @@ package gdsc.pointer.dto.request.login; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; @Getter +@NoArgsConstructor +@AllArgsConstructor public class UserDto { private String id; private String email; diff --git a/src/main/java/gdsc/pointer/dto/response/error/ErrorResponse.java b/src/main/java/gdsc/pointer/dto/response/error/ErrorResponse.java new file mode 100644 index 0000000..bf92335 --- /dev/null +++ b/src/main/java/gdsc/pointer/dto/response/error/ErrorResponse.java @@ -0,0 +1,14 @@ +package gdsc.pointer.dto.response.error; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class ErrorResponse { + private int code; + private String message; +} diff --git a/src/main/java/gdsc/pointer/exception/CustomExceptionContext.java b/src/main/java/gdsc/pointer/exception/CustomExceptionContext.java index 64d5b7d..97d18db 100644 --- a/src/main/java/gdsc/pointer/exception/CustomExceptionContext.java +++ b/src/main/java/gdsc/pointer/exception/CustomExceptionContext.java @@ -8,10 +8,10 @@ public enum CustomExceptionContext implements ExceptionContext { // BAD_REQUEST - DUPLICATE_USER_ID_ERROR("이미 구글 로그인을 통해 회원가입을 진행한 이력이 있습니다.", 1000); - + DUPLICATE_USER_ID_ERROR("이미 구글 로그인을 통해 회원가입을 진행한 이력이 있습니다.", 1000), // NOT_FOUND + USER_NOT_FOUND_ERROR("존재하지 않는 회원입니다.", 2000); // UNAUTHORIZED private final String message; diff --git a/src/main/java/gdsc/pointer/exception/notfound/NotFoundException.java b/src/main/java/gdsc/pointer/exception/notfound/NotFoundException.java new file mode 100644 index 0000000..a03cbb7 --- /dev/null +++ b/src/main/java/gdsc/pointer/exception/notfound/NotFoundException.java @@ -0,0 +1,11 @@ +package gdsc.pointer.exception.notfound; + +import gdsc.pointer.exception.ExceptionContext; +import gdsc.pointer.exception.PointerException; +import org.springframework.http.HttpStatus; + +public class NotFoundException extends PointerException { + public NotFoundException(ExceptionContext context) { + super(HttpStatus.NOT_FOUND, context.getMessage(), context.getCode()); + } +} diff --git a/src/main/java/gdsc/pointer/exception/notfound/user/UserNotFoundException.java b/src/main/java/gdsc/pointer/exception/notfound/user/UserNotFoundException.java new file mode 100644 index 0000000..f6583e0 --- /dev/null +++ b/src/main/java/gdsc/pointer/exception/notfound/user/UserNotFoundException.java @@ -0,0 +1,11 @@ +package gdsc.pointer.exception.notfound.user; + +import gdsc.pointer.exception.notfound.NotFoundException; + +import static gdsc.pointer.exception.CustomExceptionContext.USER_NOT_FOUND_ERROR; + +public class UserNotFoundException extends NotFoundException { + public UserNotFoundException() { + super(USER_NOT_FOUND_ERROR); + } +} diff --git a/src/main/java/gdsc/pointer/service/LoginService.java b/src/main/java/gdsc/pointer/service/LoginService.java index 6276c46..2c9d9be 100644 --- a/src/main/java/gdsc/pointer/service/LoginService.java +++ b/src/main/java/gdsc/pointer/service/LoginService.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import gdsc.pointer.dao.UserDao; +import gdsc.pointer.dto.request.login.UserDto; +import gdsc.pointer.exception.badrequest.user.DuplicateUserIdException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -35,7 +37,7 @@ public class LoginService { private final RestTemplate restTemplate = new RestTemplate(); - public void socialLogin(String code, String registrationId) { + public UserDto socialLogin(String code, String registrationId) throws Exception { String accessToken = getAccessToken(code, registrationId); JsonNode userResourceNode = getUserResource(accessToken, registrationId); @@ -50,15 +52,22 @@ public void socialLogin(String code, String registrationId) { // 처음 로그인 하는 유저라면 Firebase DB에 정보 저장 O // 이미 존재하는 유저의 id라면 DB에 정보 저장 X + if (validateDuplicateUser(id)) { + throw new DuplicateUserIdException(); + } - validateDuplicateUser(id); - + UserDto userDto = new UserDto(id, email, nickname); + userDao.addUser(userDto); + return userDto; } - private void validateDuplicateUser(String id) { - - - + private boolean validateDuplicateUser(String id) throws Exception { + if (userDao.getUserDetail(id) != null) { + // 이미 존재하는 유저인 경우 + return true; + } else { + return false; + } } private String getAccessToken(String authorizationCode, String registrationId) { diff --git a/src/main/java/gdsc/pointer/service/UserService.java b/src/main/java/gdsc/pointer/service/UserService.java index fdc2f3f..1eb997a 100644 --- a/src/main/java/gdsc/pointer/service/UserService.java +++ b/src/main/java/gdsc/pointer/service/UserService.java @@ -7,5 +7,7 @@ public interface UserService { List getUsers() throws Exception; + + User getUserDetail(String id) throws Exception; void addUser(UserDto userDto) throws Exception; } diff --git a/src/main/java/gdsc/pointer/service/UserServiceImpl.java b/src/main/java/gdsc/pointer/service/UserServiceImpl.java index da8a628..924ca46 100644 --- a/src/main/java/gdsc/pointer/service/UserServiceImpl.java +++ b/src/main/java/gdsc/pointer/service/UserServiceImpl.java @@ -3,11 +3,14 @@ import gdsc.pointer.dao.UserDao; import gdsc.pointer.domain.User; import gdsc.pointer.dto.request.login.UserDto; +import gdsc.pointer.exception.badrequest.user.DuplicateUserIdException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; +@Slf4j @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { @@ -19,11 +22,28 @@ public List getUsers() throws Exception { return userDao.getUsers(); } + @Override + public User getUserDetail(String id) throws Exception { + return userDao.getUserDetail(id); + } + @Override public void addUser(UserDto userDto) throws Exception { + if (validateDuplicateUser(userDto.getId())){ + throw new DuplicateUserIdException(); + } userDao.addUser(userDto); } + + private boolean validateDuplicateUser(String id) throws Exception { + if (userDao.getUserDetail(id) != null) { + // 이미 존재하는 유저인 경우 + return true; + } else { + return false; + } + } // // @Override // public ResponseEntity getUserDetail(String id) throws Exception {