Skip to content

Commit

Permalink
Merge branch 'main' into feat/#10
Browse files Browse the repository at this point in the history
  • Loading branch information
kingjinyong committed Dec 6, 2024
2 parents 45bd47d + 49692a1 commit 0194da7
Show file tree
Hide file tree
Showing 16 changed files with 433 additions and 32 deletions.
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand All @@ -33,6 +34,12 @@ dependencies {

// swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'

//유효성 검사
implementation 'org.springframework.boot:spring-boot-starter-validation'

// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.bookmile.backend.domain.user.controller;

import com.bookmile.backend.domain.user.dto.req.SignInReqDto;
import com.bookmile.backend.domain.user.dto.req.SignUpReqDto;
import com.bookmile.backend.domain.user.dto.res.UserResDto;
import com.bookmile.backend.domain.user.service.UserService;
import com.bookmile.backend.global.common.CommonResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.bookmile.backend.global.common.StatusCode.*;

@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;

@PostMapping("/sign-up")
public ResponseEntity<CommonResponse<UserResDto>> signUp(@RequestBody @Valid SignUpReqDto signUpReqDto) {
return ResponseEntity.status(SIGN_UP.getStatus())
.body(CommonResponse.from(SIGN_UP.getMessage(),userService.signUp(signUpReqDto)));
}

@PostMapping("/sign-in")
public ResponseEntity<CommonResponse<UserResDto>> signIn(@RequestBody @Valid SignInReqDto signInReqDto) {
return ResponseEntity.status(SIGN_IN.getStatus())
.body(CommonResponse.from(SIGN_IN.getMessage(),userService.signIn(signInReqDto)));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.bookmile.backend.domain.user.dto.req;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class SignInReqDto {

@NotBlank(message = "이메일은 필수 입력 사항입니다.")
@Schema(description = "회원의 이메일 주소", example = "[email protected]")
@Pattern(regexp="^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])+[.][a-zA-Z]{2,3}$", message="이메일 형식이 올바르지 않습니다.")
private String email;

@NotBlank(message = "비빌번호는 필수 입력 사항입니다.")
@Schema(description = "회원의 비밀번호", example = "userPassword!")
@Pattern(regexp = "^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])+[.][a-zA-Z]{2,3}$", message = "비밀번호는 8~16자의 영문 대소문자, 숫자, 특수문자로 이루어져야 합니다.")
private String password;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.bookmile.backend.domain.user.dto.req;

import com.bookmile.backend.domain.user.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SignUpReqDto {


@NotBlank(message = "이메일은 필수 입력 사항입니다.")
@Schema(description = "회원의 이메일 주소", example = "[email protected]")
@Pattern(regexp="^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])+[.][a-zA-Z]{2,3}$", message="이메일 형식이 올바르지 않습니다.")
private String email;

@NotBlank(message = "비빌번호는 필수 입력 사항입니다.")
@Schema(description = "회원의 비밀번호", example = "userPassword!")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,16}$", message = "비밀번호는 8~16자의 영문 대소문자, 숫자, 특수문자로 이루어져야 합니다.")
private String password;

@NotBlank(message = "비빌번호 확인는 필수 입력 사항입니다.")
@Schema(description = "회원의 비밀번호 확인", example = "userPassword!")
// 확인용이므로 Pattern 이 아니라, password 와 매치하기만 하면 됨.
private String checkPassword;


public User toEntity(String email, String password) {
return User.builder()
.email(email)
.password(password)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.bookmile.backend.domain.user.dto.res;

import com.bookmile.backend.domain.user.entity.User;
import lombok.Builder;
import lombok.Getter;

@Getter
public class UserResDto {
private final Long id;
private final String email;

@Builder
private UserResDto(Long id, String email) {
this.id = id;
this.email = email;
}

public static UserResDto toDto(User user) {
return UserResDto.builder()
.id(user.getId())
.email(user.getEmail())
.build();
}
}
42 changes: 10 additions & 32 deletions src/main/java/com/bookmile/backend/domain/user/entity/User.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,27 @@
package com.bookmile.backend.domain.user.entity;

import com.bookmile.backend.domain.review.entity.Review;
import com.bookmile.backend.domain.userGroup.entity.UserGroup;
import com.bookmile.backend.global.config.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import jakarta.persistence.*;

import lombok.*;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class User extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long id;

@OneToMany(mappedBy = "user")
private List<UserGroup> userGroup = new ArrayList<>();

@OneToMany(mappedBy = "user")
private List<Review> review = new ArrayList<>();

@Column(nullable = false)
private String name;
@Column(nullable = true, unique = true)
private String nickname;

@Column(nullable = false)
@Column(nullable = false, unique = true)
private String email;

@Column(nullable = false)
@Column(nullable = true)
private String password;

@Column
Expand All @@ -48,13 +30,9 @@ public class User extends BaseEntity {
@Column(nullable = false)
private Boolean isDeleted = false;

public void addUserGroup(UserGroup userGroup) {
this.userGroup.add(userGroup);
userGroup.addUser(this);
}

public User(String name, String email, String password, String image) {
this.name = name;
@Builder
public User(String nickname, String email, String password, String image) {
this.nickname = nickname;
this.email = email;
this.password = password;
this.image = image;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.bookmile.backend.domain.user.repository;

import com.bookmile.backend.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
Boolean existsByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bookmile.backend.domain.user.service;

import com.bookmile.backend.domain.user.dto.req.SignInReqDto;
import com.bookmile.backend.domain.user.dto.req.SignUpReqDto;
import com.bookmile.backend.domain.user.dto.res.UserResDto;

public interface UserService {
UserResDto signUp(SignUpReqDto signUpReqDto);
UserResDto signIn(SignInReqDto signInReqDto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bookmile.backend.domain.user.service.impl;

import com.bookmile.backend.domain.user.dto.req.SignInReqDto;
import com.bookmile.backend.domain.user.dto.req.SignUpReqDto;
import com.bookmile.backend.domain.user.dto.res.UserResDto;
import com.bookmile.backend.domain.user.entity.User;
import com.bookmile.backend.domain.user.repository.UserRepository;
import com.bookmile.backend.domain.user.service.UserService;
import com.bookmile.backend.global.exception.CustomException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import static com.bookmile.backend.global.common.StatusCode.*;

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

@Override
public UserResDto signUp(SignUpReqDto signUpReqDto) {
existsByEmail(signUpReqDto.getEmail());

// 비밀번호 일치 여부 확인
if (!(signUpReqDto.getPassword().equals(signUpReqDto.getCheckPassword())))
throw new CustomException(PASSWORD_NOT_MATCH);

String enCodePassword = passwordEncoder.encode(signUpReqDto.getPassword());

User user = userRepository.save(signUpReqDto.toEntity(signUpReqDto.getEmail(), enCodePassword));
return UserResDto.toDto(user);
}

@Override
public UserResDto signIn(SignInReqDto signInReqDto) {
User user = findByEmail(signInReqDto.getEmail());

if(!passwordEncoder.matches(signInReqDto.getPassword(), user.getPassword()))
throw new CustomException(AUTHENTICATION_FAILED); // 유저는 아이디, 비밀번호 중 한개만 틀려도 '일치하는 정보가 없음' 메세지 표시

return UserResDto.toDto(user);
}


private void existsByEmail(String email) {
if(userRepository.existsByEmail(email)){
throw new CustomException(USER_ALREADY_EXISTS);
};
}

private User findByEmail(String email) {
return userRepository.findByEmail(email).orElseThrow(() -> new CustomException(AUTHENTICATION_FAILED));
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.bookmile.backend.global.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Getter;

@Getter
@JsonInclude(JsonInclude.Include.NON_NULL)// Null 값인 필드 json으로 보낼 시 제외
//@RequiredArgsConstructor(staticName = "of") 이 어노테이션으로 아래 static 메소드를 만들 수 있음.
public class CommonResponse<T> {
private final String message;
private final T response;

@Builder
private CommonResponse(String message, T response) {
this.message = message;
this.response = response;
}

// <제네릭 타입> 반환타입 메소드이름()
// 제네릭 타입을 정의해야 메소드를 호출할 때마다 다른 타입을 사용할 수 있다
public static <T> CommonResponse<T> from(String message, T response) {
return new CommonResponse<T>(message, response);

}

// response 없이 메시지만 보낼 때
public static CommonResponse<Object> from(String message){
return CommonResponse.builder()
.message(message)
.build();
}
}

35 changes: 35 additions & 0 deletions src/main/java/com/bookmile/backend/global/common/StatusCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.bookmile.backend.global.common;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

import static org.springframework.http.HttpStatus.*;
import static org.springframework.http.HttpStatus.CONFLICT;

@Getter
@RequiredArgsConstructor
public enum StatusCode {

/* User */
SIGN_UP(CREATED, "회원가입이 완료되었습니다."),
SIGN_IN(OK, "로그인에 성공하였습니다."),

/* 400 BAD_REQUEST : 잘못된 요청 */
PASSWORD_NOT_MATCH(BAD_REQUEST, "비밀번호가 일치하지 않습니다."),

/* 401 UNAUTHORIZED : 비인증 사용자 */
AUTHENTICATION_FAILED(UNAUTHORIZED, "회원의 정보가 일치하지 않습니다."),

/* 403 FORBIDDEN : 권한 없음 */

/* 404 NOT_FOUND : 존재하지 않는 리소스 */
INPUT_VALUE_INVALID(NOT_FOUND, "유효하지 않은 입력입니다."),
USER_NOT_FOUND(NOT_FOUND, "존재하는 회원이 없습니다."),

/* 409 CONFLICT : 리소스 충돌 */
USER_ALREADY_EXISTS(CONFLICT, "이미 존재하는 회원입니다.");

private final HttpStatus status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.bookmile.backend.global.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class JpaAuditingConfig {
}
Loading

0 comments on commit 0194da7

Please sign in to comment.