Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/main/java/com/moiming/core/BaseControllerAdvice.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.moiming.core;

import com.moiming.core.exception.CustomNoResultException;
import com.moiming.core.exception.DuplicationException;
import java.util.List;
import java.util.stream.Collectors;
import jakarta.validation.ConstraintViolationException;
import java.util.ArrayList;
import org.springframework.http.HttpStatus;
Expand All @@ -10,9 +13,6 @@
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.List;
import java.util.stream.Collectors;

@ControllerAdvice
public class BaseControllerAdvice {

Expand Down Expand Up @@ -56,4 +56,11 @@ public ResponseEntity<String> noResultExceptionExceptionHandler(CustomNoResultEx
.status(HttpStatus.NOT_FOUND)
.body(e.getMessage());
}

@ExceptionHandler(DuplicationException.class)
public ResponseEntity<String> duplicationExceptionHandler(DuplicationException e) {
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(e.getMessage());
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/moiming/core/exception/DuplicationException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.moiming.core.exception;

public class DuplicationException extends RuntimeException {
public DuplicationException() {
super("중복된 데이터가 존재합니다.");
}

public DuplicationException(String message) {
super(message);
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/moiming/user/application/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.moiming.user.application;

import static com.moiming.user.domain.UserErrorMessage.EXIST_EMAIL;
import static com.moiming.user.domain.UserErrorMessage.EXIST_ID;

import com.moiming.core.exception.DuplicationException;
import com.moiming.user.domain.Email;
import com.moiming.user.domain.User;
import com.moiming.user.domain.UserRepository;
import com.moiming.user.dto.SignUpRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;


@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {

private final UserRepository userRepository;

/**
* 회원가입을 진행합니다.
*/
public Long signUp(SignUpRequest request) {
log.info("signUp request: {}", request);

User singUpUser = User.builder().userId(request.userId())
.password(request.password())
.email(Email.of(request.email()))
.name(request.name())
.birthDate(request.birthDate())
.build();


userRepository.findByUserId(singUpUser.getUserId()).ifPresent(user -> {
throw new DuplicationException(EXIST_ID.getMessage());
});

userRepository.findByEmail(singUpUser.getEmail()).ifPresent(user -> {
throw new DuplicationException(EXIST_EMAIL.getMessage());
});

return userRepository.save(singUpUser)
.getSeq();
}

}
51 changes: 51 additions & 0 deletions src/main/java/com/moiming/user/domain/Email.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.moiming.user.domain;

import jakarta.persistence.Embeddable;
import jakarta.persistence.Transient;
import java.util.regex.Pattern;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EqualsAndHashCode(of = "value")
public class Email {
private static final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$";
private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
private static final String EMAIL_AT = "@";

@Transient
private String id;

@Transient
private String domain;

private String value;

private Email(String email) {
validate(email);

String[] split = email.split(EMAIL_AT);
this.id = split[0];
this.domain = split[1];
this.value = email;
}

public static Email of(String email) {
return new Email(email);
}

private void validate(String email) {
if (email == null || email.isEmpty()) {
throw new IllegalArgumentException("이메일 주소가 비어있습니다..");
}

if (!EMAIL_PATTERN.matcher(email).find()) {
throw new IllegalArgumentException("잘못된 이메일 형식입니다.");
}
}
}
104 changes: 104 additions & 0 deletions src/main/java/com/moiming/user/domain/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.moiming.user.domain;

import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

@Entity
@Table(name = "users")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "USER_SEQ")
@Comment("유저 KEY")
private Long seq;

@NotNull
@Size(min = 4, max = 20)
@Column(name = "USER_ID", unique = true)
@Comment("유저 아이디")
private String userId;

@Embedded
@NotNull
@AttributeOverrides(
@AttributeOverride(name = "value", column = @Column(name = "EMAIL", unique = true, nullable = false, columnDefinition = "VARCHAR(50)"))
)
@Comment("유저 이메일")
private Email email;

@NotNull
@Size(max = 20)
@Column(name = "NAME")
@Comment("유저 이름")
private String name;

@NotNull
@Size(max = 40)
@Column(name = "PASSWORD")
@Comment("패스워드")
private String password;

@NotNull
@Column(name = "BIRTH_DATE")
@Comment("생일")
private LocalDate birthDate;

@NotNull
@Size(max = 1)
@Column(name = "USE_YN")
@Comment("사용여부")
private String useYn;

@NotNull
@Size(max = 1)
@Column(name = "AUTH_YN")
@Comment("이메일 인증 여부")
private String authYn;

@Builder
private User(Long seq, String userId, Email email, String name, String password, LocalDate birthDate) {
validateNonEmpty(userId, "유저아이디");
validateNonEmpty(password, "패스워드");
validateNonEmpty(name, "이름");
validateNonNull(birthDate, "생년월일");

this.seq = seq;
this.userId = userId;
this.email = email;
this.name = name;
this.password = password;
this.birthDate = birthDate;
this.useYn = "Y";
this.authYn = "N";
}

private void validateNonEmpty(String value, String fieldName) {
if (value == null || value.isEmpty()) {
throw new IllegalArgumentException(fieldName + "이(가) 비어있습니다.");
}
}

private <T> void validateNonNull(T value, String fieldName) {
if (value == null) {
throw new IllegalArgumentException(fieldName + "이(가) 비어있습니다.");
}
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/moiming/user/domain/UserErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.moiming.user.domain;

import lombok.Getter;

public enum UserErrorMessage {
EXIST_ID("이미 존재하는 아이디입니다."),
EXIST_EMAIL("이미 존재하는 이메일입니다.");

@Getter
private final String message;

UserErrorMessage(String message) {
this.message = message;
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/moiming/user/domain/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.moiming.user.domain;

import java.util.Optional;

public interface UserRepository {
User save(User user);
User saveAndFlush(User user);

Optional<User> findByUserId(String id);

Optional<User> findByEmail(Email email);
}
38 changes: 38 additions & 0 deletions src/main/java/com/moiming/user/dto/SignUpRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.moiming.user.dto;


import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDate;
import lombok.Builder;

@Builder
@Schema(description = "회원 가입 요청")
public record SignUpRequest(
@NotNull(message = "아이디는 필수입니다.")
@Size(min = 4, max = 20, message = "아이디는 4자 이상 20자 이하로 입력해주세요.")
@Schema(description = "아이디", example = "moiming")
String userId,

@NotNull
@Size(min = 4, max = 20, message = "비밀번호는 4자 이상 20자 이하로 입력해주세요.")
@Schema(description = "비밀번호", example = "asddad")
String password,

@NotNull
@Email(message = "이메일 형식이 올바르지 않습니다.")
@Schema(description = "이메일", example = "sadas@asd.com")
String email,

@NotNull
@Size(min = 1, max = 20, message = "이름은 1자 이상 20자 이하로 입력해주세요.")
@Schema(description = "이름", example = "이름")
String name,

@NotNull(message = "생년월일은 필수입니다.")
@Schema(description = "생년월일", example = "2021-08-23")
LocalDate birthDate
) {
}
9 changes: 9 additions & 0 deletions src/main/java/com/moiming/user/infra/JpaUserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moiming.user.infra;

import com.moiming.user.domain.User;
import com.moiming.user.domain.UserRepository;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaUserRepository extends JpaRepository<User, Long>, UserRepository {

}
24 changes: 24 additions & 0 deletions src/main/java/com/moiming/user/web/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moiming.user.web;

import com.moiming.user.application.UserService;
import com.moiming.user.dto.SignUpRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@Tag(name = "유저", description = "유저 관련 API")
public class UserController {

private final UserService userService;
@PostMapping("/sign-up")
@Operation(summary = "회원가입", description = "회원가입을 진행합니다.")
public void signUp(@Valid @RequestBody SignUpRequest request) {
userService.signUp(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.moiming.config;

import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;


public class AcceptanceTestExecutionListener extends AbstractTestExecutionListener {

@Override
public void afterTestMethod(final TestContext testContext) {
final JdbcTemplate jdbcTemplate = getJdbcTemplate(testContext);
final List<String> truncateQueries = getTruncateQueries(jdbcTemplate);
truncateTables(jdbcTemplate, truncateQueries);
}

private List<String> getTruncateQueries(final JdbcTemplate jdbcTemplate) {
return jdbcTemplate.queryForList(
"SELECT Concat('TRUNCATE TABLE ', TABLE_NAME, ';') AS q FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'",
String.class);
}

private JdbcTemplate getJdbcTemplate(final TestContext testContext) {
return testContext.getApplicationContext().getBean(JdbcTemplate.class);
}

private void truncateTables(final JdbcTemplate jdbcTemplate, final List<String> truncateQueries) {
execute(jdbcTemplate, "SET REFERENTIAL_INTEGRITY FALSE");
truncateQueries.forEach(v -> execute(jdbcTemplate, v));
execute(jdbcTemplate, "SET REFERENTIAL_INTEGRITY TRUE");
}

private void execute(final JdbcTemplate jdbcTemplate, final String query) {
jdbcTemplate.execute(query);
}

}
Loading