Skip to content

Commit

Permalink
Merge pull request #12 from comento-backend-camp/feature/9
Browse files Browse the repository at this point in the history
[feat:#9] 이메일 등록, 검증
  • Loading branch information
hyejungg authored Sep 23, 2021
2 parents d3fea81 + fc688fb commit 4b51789
Show file tree
Hide file tree
Showing 17 changed files with 500 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ticket/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
// validation
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
25 changes: 25 additions & 0 deletions ticket/src/main/java/comento/backend/ticket/config/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package comento.backend.ticket.config;

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

@Getter
@AllArgsConstructor
public enum ErrorCode {
/**
* @see : 예외케이스 정의
*/

INVALID_VALUE(400, HttpStatus.BAD_REQUEST, "BAD REQUEST", "잘못된 요청입니다. 다시 요청해주세요."),
NO_USER(401, HttpStatus.UNAUTHORIZED,"UNAUTHORIZED", "등록되지 않은 사용자입니다"),
NO_DATA(404, HttpStatus.NOT_FOUND, "NOT FOUND ERROR", "요청할 수 없는 리소스입니다."),
INVALID_USER(409, HttpStatus.CONFLICT, "CONFLICT", "이미 존재하는 사용자입니다."),
INVALID_SEAT(409, HttpStatus.CONFLICT, "CONFLICT", "이미 예약된 좌석입니다."),
SERVER_ERROR(500, HttpStatus.INTERNAL_SERVER_ERROR, "INTERVAL_SERVER ERROR", "서버 에러입니다.");

private final Integer status;
private final HttpStatus httpStatus;
private final String message;
private final String reason;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package comento.backend.ticket.config;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Data
@AllArgsConstructor
@Builder
public class ErrorResponse<T>{
private Integer status;
private String message;
private String reason;

//공통된 Error Message를 전송할 때 사용
public static ErrorResponse res(final Integer status, final String message, final String reason){
return ErrorResponse.builder()
.status(status)
.message(message)
.reason(reason)
.build();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package comento.backend.ticket.config;

import com.fasterxml.jackson.core.JsonParseException;
import comento.backend.ticket.config.customException.DuplicatedException;
import comento.backend.ticket.config.customException.NoAuthException;
import comento.backend.ticket.config.customException.NotFoundDataException;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolationException;
import java.util.NoSuchElementException;

@RestControllerAdvice //@Conroller 전역에서 발생 가능한 예외를 잡아 처리
@Slf4j
public class GlobalExceptionHandler {
private static ErrorCode errorCode;

/**
* 특정 Exception을 지정하여 별도로 처리
*/

//ConstraintViolationException.class 는 유효성 검사 실패시 (@Validated)
@ExceptionHandler({JsonParseException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class, MethodArgumentTypeMismatchException.class})
public static ResponseEntity missMatchExceptionHandler(Throwable t){
errorCode = ErrorCode.INVALID_VALUE;
log.error(errorCode.getStatus() + errorCode.getMessage(), t);
return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()),
errorCode.getHttpStatus());
}

@ExceptionHandler(NoAuthException.class)
public static ResponseEntity noAuthExceptionHandler(NoAuthException e){
errorCode = ErrorCode.NO_USER;
log.error(errorCode.getStatus() + errorCode.getMessage(), e);
return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()),
errorCode.getHttpStatus());
}

@ExceptionHandler({NoSuchElementException.class, NotFoundException.class, NotFoundDataException.class})
public static ResponseEntity notFoundExceptionHandler(Throwable t){
errorCode = ErrorCode.NO_DATA;
log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t);
return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()),
errorCode.getHttpStatus());
}

@ExceptionHandler(DuplicatedException.class)
public static ResponseEntity duplicateExceptionHandler(DuplicatedException e){
if (e.getMessage() == "UserService") { //호출된 곳이 UserService
errorCode = ErrorCode.INVALID_USER;
log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e);
}else{ //좌석 예약 시
errorCode = ErrorCode.INVALID_SEAT;
log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), e);
}
return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()),
errorCode.getHttpStatus());
}

@ExceptionHandler({IllegalStateException.class, RuntimeException.class})
public static ResponseEntity IllExceptionHandler(Throwable t){
errorCode = ErrorCode.SERVER_ERROR;
log.error(errorCode.getStatus()+ " " + errorCode.getMessage(), t);
return new ResponseEntity<>(ErrorResponse.res(errorCode.getStatus(), errorCode.getMessage(), errorCode.getReason()),
errorCode.getHttpStatus());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package comento.backend.ticket.config;

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

@Getter
@AllArgsConstructor
public enum SuccessCode {

OK(200, HttpStatus.OK, "OK"),
CREATED(201, HttpStatus.CREATED, "CREATED");

private final Integer status;
private final HttpStatus httpStatus;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package comento.backend.ticket.config;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Data
@AllArgsConstructor
@Builder
public class SuccessResponse<T> {
private Integer status;
private String message;
private Object data;

//공통된 Success Message를 전송할 때 사용
public static SuccessResponse res(final Integer status, final String message, final Object data){
return SuccessResponse.builder()
.status(status)
.message(message)
.data(data)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package comento.backend.ticket.config.customException;

public class DuplicatedException extends RuntimeException{
public DuplicatedException() {
}

public DuplicatedException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package comento.backend.ticket.config.customException;

public class NoAuthException extends RuntimeException{
public NoAuthException() {
}

public NoAuthException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package comento.backend.ticket.config.customException;

public class NotFoundDataException extends RuntimeException{
public NotFoundDataException() {
}

public NotFoundDataException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package comento.backend.ticket.controller;

import comento.backend.ticket.config.SuccessCode;
import comento.backend.ticket.config.SuccessResponse;
import comento.backend.ticket.config.customException.NotFoundDataException;
import comento.backend.ticket.domain.User;
import comento.backend.ticket.dto.UserDto;
import comento.backend.ticket.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class UserController {
private SuccessCode successCode = SuccessCode.CREATED;
private final UserService userService;
private static final String CREATED_MSG = "등록 성공";

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/signup")
public ResponseEntity addEmail(@Validated @RequestBody UserDto userDto){
userService.saveUser(userDto);
return new ResponseEntity<>(SuccessResponse.res(successCode.getStatus(), successCode.getMessage(), CREATED_MSG),
successCode.getHttpStatus());
}
}
37 changes: 37 additions & 0 deletions ticket/src/main/java/comento/backend/ticket/domain/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package comento.backend.ticket.domain;

import lombok.Builder;
import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;
import org.springframework.data.annotation.CreatedDate;

import javax.persistence.*;
import java.util.Date;

@Entity
@Data
@Table(name="User")
public class User{

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

@Column(nullable = true)
private String email;

@Column(name="create_at")
@CreationTimestamp //Entity가 생성되어 저장될 때 시간이 자동 저장
@Temporal(TemporalType.TIMESTAMP)
private Date createAt;

public User(){}

@Builder
public User(long id, String email, Date create_at) {
this.id = id;
this.email = email;
this.createAt = createAt;
}
}
26 changes: 26 additions & 0 deletions ticket/src/main/java/comento/backend/ticket/dto/UserDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package comento.backend.ticket.dto;

import comento.backend.ticket.domain.User;
import lombok.Data;
import lombok.Getter;

import javax.validation.constraints.Email;

@Getter
@Data
public class UserDto {
@Email
private String email;

public UserDto(){}

public UserDto(String email) {
this.email = email;
}

public User toEntity(){
return User.builder()
.email(email)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package comento.backend.ticket.repository;

import comento.backend.ticket.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package comento.backend.ticket.service;

import comento.backend.ticket.config.customException.DuplicatedException;
import comento.backend.ticket.domain.User;
import comento.backend.ticket.dto.UserDto;
import comento.backend.ticket.repository.UserRepository;
import org.springframework.stereotype.Service;

import java.util.Objects;


@Service
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}

public User saveUser(UserDto userDto){
User user = userDto.toEntity();

if(!checkEmailDuplicate(user.getEmail())) {
throw new DuplicatedException("UserService");
}

return userRepository.save(user);
}

private boolean checkEmailDuplicate(String email){
User user = userRepository.findByEmail(email).orElseGet(()->null);
return Objects.isNull(user); //null이면 true
}
}
29 changes: 29 additions & 0 deletions ticket/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATTERN" value = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />

<!-- appender : log의 형태 설정, log 메세지가 출력될 대상(console/file)결정 -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/ticket-reservation-server.log</file>
<rollingPolicy class = "ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 일자별 로그파일 생성 -->
<fileNamePattern>logs/ticket-reservation-server-%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 최대 보관주기. 30일 이상된 파일은 자동으로 제거 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>

<!-- INFO 이상 level 출력 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>

<logger name="org.springframework" level="warn">
<appender-ref ref = "ROLLING_FILE"/>
</logger>
</configuration>
Loading

0 comments on commit 4b51789

Please sign in to comment.