diff --git a/Assignments/README.md b/Assignments/README.md deleted file mode 100644 index c38edd6..0000000 --- a/Assignments/README.md +++ /dev/null @@ -1 +0,0 @@ -# KimDongHwi \ No newline at end of file diff --git a/ThirdSeminar/build.gradle b/ThirdSeminar/build.gradle index ba82953..d670000 100644 --- a/ThirdSeminar/build.gradle +++ b/ThirdSeminar/build.gradle @@ -20,6 +20,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'mysql:mysql-connector-java:8.0.33' + implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/advice/ControllerExceptionAdvice.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/advice/ControllerExceptionAdvice.java new file mode 100644 index 0000000..c4e50b8 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/advice/ControllerExceptionAdvice.java @@ -0,0 +1,22 @@ +package sopt.org.ThirdSeminar.common.advice; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import sopt.org.ThirdSeminar.common.dto.ApiResponseDto; +import sopt.org.ThirdSeminar.exception.ErrorStatus; + +@RestControllerAdvice +public class ControllerExceptionAdvice { + + /* + * 400 BAD_REQUEST + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(MethodArgumentNotValidException.class) + protected ApiResponseDto handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { + return ApiResponseDto.error(ErrorStatus.VALIDATION_REQUEST_MISSING_EXCEPTION); + } +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/dto/ApiResponseDto.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/dto/ApiResponseDto.java new file mode 100644 index 0000000..196ad73 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/common/dto/ApiResponseDto.java @@ -0,0 +1,29 @@ +package sopt.org.ThirdSeminar.common.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import sopt.org.ThirdSeminar.exception.ErrorStatus; +import sopt.org.ThirdSeminar.exception.SuccessStatus; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ApiResponseDto { + private final int code; + private final String message; + private T data; + + public static ApiResponseDto success(SuccessStatus successStatus) { + return new ApiResponseDto<>(successStatus.getHttpStatus().value(), successStatus.getMessage()); + } + + public static ApiResponseDto success(SuccessStatus successStatus, T data) { + return new ApiResponseDto(successStatus.getHttpStatus().value(), successStatus.getMessage(), data); + } + + public static ApiResponseDto error(ErrorStatus errorStatus) { + return new ApiResponseDto<>(errorStatus.getHttpStatus().value(), errorStatus.getMessage()); + } +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/UserController.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/UserController.java new file mode 100644 index 0000000..cac7283 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/UserController.java @@ -0,0 +1,28 @@ +package sopt.org.ThirdSeminar.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import sopt.org.ThirdSeminar.common.dto.ApiResponseDto; +import sopt.org.ThirdSeminar.controller.dto.request.UserRequestDto; +import sopt.org.ThirdSeminar.controller.dto.response.UserResponseDto; +import sopt.org.ThirdSeminar.exception.SuccessStatus; +import sopt.org.ThirdSeminar.service.UserService; + +import javax.validation.Valid; + +@RestController +@RequiredArgsConstructor +public class UserController { + + private final UserService userService; + + @PostMapping("/user/signup") + @ResponseStatus(HttpStatus.CREATED) + public ApiResponseDto create(@RequestBody @Valid final UserRequestDto request) { + return ApiResponseDto.success(SuccessStatus.SIGNUP_SUCCESS, userService.create(request)); + } +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/request/UserRequestDto.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/request/UserRequestDto.java new file mode 100644 index 0000000..e1fdf5f --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/request/UserRequestDto.java @@ -0,0 +1,29 @@ +package sopt.org.ThirdSeminar.controller.dto.request; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserRequestDto { + @Email(message = "이메일 형식에 맞지 않습니다") + @NotNull + private String email; + + @NotBlank + @Pattern(regexp = "^[가-힣a-zA-Z]{2,10}$", message = "닉네임 형식에 맞지 않습니다.") //정규 표현식, 데이터 검증 + private String nickname; + + @NotBlank + @Pattern( + regexp="(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,20}", + message = "비밀번호는 영문 대,소문자와 숫자, 특수기호가 적어도 1개 이상씩 포함된 8자 ~ 20자의 비밀번호여야 합니다." + ) + private String password; +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/response/UserResponseDto.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/response/UserResponseDto.java new file mode 100644 index 0000000..e9f00cd --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/controller/dto/response/UserResponseDto.java @@ -0,0 +1,17 @@ +package sopt.org.ThirdSeminar.controller.dto.response; + + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserResponseDto { + private Long userId; + private String nickname; + + public static UserResponseDto of(Long userId, String nickname) { + return new UserResponseDto(userId, nickname); + } +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/domain/User.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/domain/User.java new file mode 100644 index 0000000..2683137 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/domain/User.java @@ -0,0 +1,31 @@ +package sopt.org.ThirdSeminar.domain; + + +import lombok.*; + +import javax.persistence.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String nickname; + + @Column(nullable = false) + private String email; + + @Column(nullable = false) + private String password; + + @Builder + public User(String nickname, String email, String password) { + this.nickname = nickname; + this.email = email; + this.password = password; + } +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/ErrorStatus.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/ErrorStatus.java new file mode 100644 index 0000000..c9ecc88 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/ErrorStatus.java @@ -0,0 +1,34 @@ +package sopt.org.ThirdSeminar.exception; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum ErrorStatus { + /* + BAD_REQUEST + */ + VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + VALIDATION_REQUEST_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "요청값이 입력되지 않았습니다."), + + + /* + CONFLICT + */ + CONFLICT_EMAIL_EXCEPTION(HttpStatus.CONFLICT, "이미 등록된 이메일입니다."), + CONFLICT_NICKNAME_EXCEPTION(HttpStatus.CONFLICT, "이미 등록된 닉네임입니다."), + + /* + SERVER_ERROR + */ + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "예상치 못한 서버 에러가 발생했습니다."), + BAD_GATEWAY_EXCEPTION(HttpStatus.BAD_GATEWAY, "일시적인 에러가 발생하였습니다.\n잠시 후 다시 시도해주세요!"), + SERVICE_UNAVAILABLE_EXCEPTION(HttpStatus.SERVICE_UNAVAILABLE, "현재 점검 중입니다.\n잠시 후 다시 시도해주세요!"), + ; + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/SuccessStatus.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/SuccessStatus.java new file mode 100644 index 0000000..061cc0b --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/exception/SuccessStatus.java @@ -0,0 +1,18 @@ +package sopt.org.ThirdSeminar.exception; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum SuccessStatus { + /* + user + */ + SIGNUP_SUCCESS(HttpStatus.CREATED, "회원가입이 완료되었습니다."),; + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/infrastructure/UserRepository.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/infrastructure/UserRepository.java new file mode 100644 index 0000000..db2ed60 --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/infrastructure/UserRepository.java @@ -0,0 +1,8 @@ +package sopt.org.ThirdSeminar.infrastructure; + +import org.springframework.data.repository.Repository; +import sopt.org.ThirdSeminar.domain.User; + +public interface UserRepository extends Repository { + void save(User user); +} diff --git a/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/service/UserService.java b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/service/UserService.java new file mode 100644 index 0000000..ebb210f --- /dev/null +++ b/ThirdSeminar/src/main/java/sopt/org/ThirdSeminar/service/UserService.java @@ -0,0 +1,28 @@ +package sopt.org.ThirdSeminar.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import sopt.org.ThirdSeminar.controller.dto.request.UserRequestDto; +import sopt.org.ThirdSeminar.controller.dto.response.UserResponseDto; +import sopt.org.ThirdSeminar.domain.User; +import sopt.org.ThirdSeminar.infrastructure.UserRepository; + +@Service +@RequiredArgsConstructor +public class UserService { + private final UserRepository userRepository; + + @Transactional + public UserResponseDto create(UserRequestDto request) { + User user = User.builder() + .email(request.getEmail()) + .nickname(request.getNickname()) + .password(request.getPassword()) + .build(); + + userRepository.save(user); + + return UserResponseDto.of(user.getId(), user.getNickname()); + } +} diff --git a/ThirdSeminar/src/main/resources/application.properties b/ThirdSeminar/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/ThirdSeminar/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ThirdSeminar/src/main/resources/application.yml b/ThirdSeminar/src/main/resources/application.yml new file mode 100644 index 0000000..d13bdb2 --- /dev/null +++ b/ThirdSeminar/src/main/resources/application.yml @@ -0,0 +1,22 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/sopt_32?useSSL=true&useUnicode=true&serverTimezone=Asia/Seoul + username: sopt_server + password: 1234 + + jpa: + show-sql: true + hibernate: + ddl-auto: create + properties: + hibernate: + format_sql: true + +logging: + level: + org: + hibernate: + type: + descriptor: + sql: trace \ No newline at end of file