Skip to content

Commit

Permalink
Merge pull request #220 from urinaner/feature/134-2
Browse files Browse the repository at this point in the history
[BE] 예외처리 정리
  • Loading branch information
2Jin1031 authored Dec 24, 2024
2 parents 93c78ac + c37e60c commit 61f04e4
Show file tree
Hide file tree
Showing 34 changed files with 134 additions and 260 deletions.
1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
implementation 'ch.qos.logback:logback-classic'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' //Swagger
implementation 'org.springframework.boot:spring-boot-starter-validation'

// security
implementation 'org.springframework.boot:spring-boot-starter-security'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ public class BoardController {

@Operation(summary = "게시판 생성 API 입니다.", description = "게시판 생성입니다.")
@PostMapping(consumes = "multipart/form-data")
public ResponseEntity<Long> createBoard(@RequestPart(value = "boardReqDto") BoardReqDto boardReqDto,
public ResponseEntity<Long> createBoard(@RequestPart(value = "boardReqDto") @Valid BoardReqDto boardReqDto,
@RequestPart(value = "boardFiles", required = false) List<MultipartFile> multipartFileList) {
Long boardId = boardService.saveBoard(boardReqDto, multipartFileList);
return new ResponseEntity<>(boardId, HttpStatus.OK);
}

@Operation(summary = "모든 게시판 조회 API", description = "모든 게시판의 리스트 반환")
@GetMapping
public ResponseDto<List<BoardResDto>> getAllBoards(@Valid @ModelAttribute PageRequestDto pageRequest) {
public ResponseDto<List<BoardResDto>> getAllBoards(@ModelAttribute @Valid PageRequestDto pageRequest) {

Page<BoardResDto> boardList = boardService.getAllBoards(pageRequest.toPageable());
return ResponseDto.ok(boardList.getNumber(), boardList.getTotalPages(), boardList.getContent());
}
@Operation(summary = "카테고리별 게시판 조회 API", description = "카테고리별 게시판 리스트 반환")
@GetMapping("/category/{category}")
public ResponseDto<List<BoardResDto>> getBoardsByCategory(@PathVariable("category") Category category,
@Valid @ModelAttribute PageRequestDto pageRequest) {
@ModelAttribute @Valid PageRequestDto pageRequest) {

Page<BoardResDto> boardList = boardService.getBoardsByCategory(category, pageRequest.toPageable());
return ResponseDto.ok(boardList.getNumber(), boardList.getTotalPages(), boardList.getContent());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.example.backend.board.domain.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
Expand All @@ -10,11 +11,20 @@
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BoardReqDto {

@NotBlank(message = "제목은 필수 입력값입니다.")
private String title;

@NotBlank(message = "내용은 필수 입력값입니다.")
private String content;

@NotBlank(message = "작성자는 필수 입력값입니다.")
private String writer;

@Schema(hidden = true)
private List<String> fileList;

@NotBlank(message = "카테고리는 필수 입력값입니다.")
private String category;

@Builder
Expand All @@ -28,4 +38,4 @@ private BoardReqDto(String title, String content, String writer, String category
public void setFileList(List<String> fileList) {
this.fileList = fileList;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
@RequiredArgsConstructor
public enum BoardExceptionType implements BaseExceptionType {
NOT_FOUND_BOARD(NOT_FOUND, "게시글을 찾을 수 없습니다"),
REQUIRED_TITLE(BAD_REQUEST, "제목은 필수 입력값입니다."),
REQUIRED_CONTENT(BAD_REQUEST, "내용은 필수 입력값입니다."),
REQUIRED_CATEGORY(BAD_REQUEST, "카티고리는 필수 입력값입니다"),
REQUIRED_DEPARTMENT_ID(BAD_REQUEST, "부서 ID는 필수 입력값입니다."),
REQUIRED_FILE(BAD_REQUEST, "파일이 비어 있습니다.")
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,12 @@ public class BoardService {

@Transactional
public Long saveBoard(BoardReqDto boardReqDto, List<MultipartFile> multipartFileList) {
validateUserRequiredFields(boardReqDto);

fileUpload(boardReqDto, multipartFileList);

Board board = Board.of(boardReqDto);
Board savedBoard = boardRepository.save(board);
return savedBoard.getId();
}

private void validateUserRequiredFields(BoardReqDto dto) {
if (dto.getTitle() == null || dto.getTitle().isEmpty()) {
throw new BoardException(BoardExceptionType.REQUIRED_TITLE);
}
if (dto.getContent() == null || dto.getContent().isEmpty()) {
throw new BoardException(BoardExceptionType.REQUIRED_CONTENT);
}
if (dto.getCategory() == null || dto.getCategory().isEmpty() || !Category.contains(dto.getCategory())) {
throw new BoardException(BoardExceptionType.REQUIRED_CATEGORY);
}
}

public BoardResDto getBoard(Long boardId) {
Board board = findBoardById(boardId);
return BoardResDto.of(board);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
Expand All @@ -11,17 +12,22 @@
@Getter
@Setter
public class PageRequestDto {
@Min(0)

@Min(value = 0, message = "페이지 번호는 0 이상이어야 합니다.")
private int page = 0;

@Min(1) @Max(100)
@Min(value = 1, message = "페이지 크기는 최소 1이어야 합니다.")
@Max(value = 100, message = "페이지 크기는 최대 100이어야 합니다.")
private int size = 10;

@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "정렬 기준은 알파벳, 숫자, 또는 언더스코어(_)만 포함할 수 있습니다.")
private String sort = "id";

@Pattern(regexp = "^(ASC|DESC)$", message = "정렬 방향은 'ASC' 또는 'DESC'만 가능합니다.")
private String sortDirection = "ASC";

public Pageable toPageable() {
Sort.Direction direction = Sort.Direction.valueOf(sortDirection.toUpperCase());
return PageRequest.of(page, size, direction, sort);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.backend.common.exception.paging.InvalidPaginationParameterException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

Expand All @@ -27,14 +24,6 @@ ResponseEntity<ExceptionResponse> handleException(HttpServletRequest request, Ba
.body(new ExceptionResponse(type.errorMessage()));
}

@ExceptionHandler(MissingServletRequestParameterException.class)
ResponseEntity<ExceptionResponse> handleMissingParams(MissingServletRequestParameterException e) {
String errorMessage = e.getParameterName() + " 값이 누락 되었습니다.";
log.info("잘못된 요청이 들어왔습니다. 내용: {}", errorMessage);
return ResponseEntity.status(BAD_REQUEST)
.body(new ExceptionResponse(errorMessage));
}

@ExceptionHandler(Exception.class)
ResponseEntity<ExceptionResponse> handleException(HttpServletRequest request, Exception e) {
log.error("예상하지 못한 예외가 발생했습니다. URI: {}, ", request.getRequestURI(), e);
Expand All @@ -49,26 +38,9 @@ ResponseEntity<ExceptionResponse> handleHttpMessageNotReadableException(HttpServ
.body(new ExceptionResponse("요청 본문이 비어 있습니다."));
}

@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<ExceptionResponse> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error("DataIntegrityViolationException occurred: ", e);

//참조 제약 조건 위반 확인
if (e.getMessage().contains("FOREIGN KEY") || e.getMessage().contains("foreign key")) {
return ResponseEntity.badRequest()
.body(new ExceptionResponse("해당 데이터를 삭제하기 전에 연관된 데이터를 먼저 삭제해주세요."));
}

return ResponseEntity.badRequest()
.body(new ExceptionResponse("데이터 처리 중 오류가 발생했습니다."));
}


@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseEntity<ExceptionResponse> validException(
MethodArgumentNotValidException e) {

@ExceptionHandler(BindException.class)
public ResponseEntity<ExceptionResponse> bindException(BindException e) {
return ResponseEntity.status(BAD_REQUEST)
.body(new ExceptionResponse(e.getMessage()));
.body(new ExceptionResponse(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()));
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.example.backend.department.domain.dto.Department;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -8,13 +10,28 @@
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class DepartmentReqDto {

@NotBlank(message = "한글 이름은 필수 입력값입니다.")
@Size(max = 255, message = "한글 이름은 최대 255자까지 입력 가능합니다.")
private String koreanName;

@Size(max = 255, message = "영문 이름은 최대 255자까지 입력 가능합니다.")
private String englishName;

private String intro;

@Size(max = 15, message = "전화번호는 최대 15자까지 입력 가능합니다.")
private String phoneN;

@Size(max = 255, message = "위치는 최대 255자까지 입력 가능합니다.")
private String location;

private String educationalObjective;

@Size(max = 255, message = "근무 시간은 최대 255자까지 입력 가능합니다.")
private String workHour;

@Size(max = 2083, message = "지도 링크는 최대 2083자까지 입력 가능합니다.")
private String map;

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
@RequiredArgsConstructor
public enum DepartmentExceptionType implements BaseExceptionType {
NOT_FOUND_DEPARTMENT(NOT_FOUND, "부서을 찾을 수 없습니다"),
REQUIRED_KOREAN_NAME(NOT_FOUND, "한글 이름은 필수 입력값입니다."),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.springframework.transaction.annotation.Transactional;

import static org.example.backend.department.exception.DepartmentExceptionType.NOT_FOUND_DEPARTMENT;
import static org.example.backend.department.exception.DepartmentExceptionType.REQUIRED_KOREAN_NAME;

@Service
@RequiredArgsConstructor
Expand All @@ -20,18 +19,11 @@ public class DepartmentService {

@Transactional
public Long saveDepartment(DepartmentReqDto departmentReqDto) {
validateDepartmentRequiredFields(departmentReqDto);
Department department = Department.of(departmentReqDto);
departmentRepository.save(department);
return department.getId();
}

private void validateDepartmentRequiredFields(DepartmentReqDto dto) {
if (dto.getKoreanName() == null || dto.getKoreanName().isEmpty()) {
throw new DepartmentException(REQUIRED_KOREAN_NAME);
}
}

public DepartmentResDto getDepartment(Long departmentId) {
Department department = findDepartmentById(departmentId);
return DepartmentResDto.of(department);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.example.backend.global.aop.AuthUserResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;
Expand All @@ -20,4 +21,11 @@ public WebConfig(AuthUserResolver authUserResolver) {
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(authUserResolver);
}

@Override
public void addCorsMappings(CorsRegistry corsRegistry) {

corsRegistry.addMapping("/**")
.allowedOrigins("http://localhost:3000");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -36,7 +35,7 @@ public class ProfessorController {
@Operation(summary = "교수 생성 API", description = "교수 생성")
@PostMapping(consumes = "multipart/form-data")
public ResponseEntity<Long> createProfessor(
@RequestPart(value = "professorReqDto") ProfessorReqDto professorReqDto,
@RequestPart(value = "professorReqDto") @Valid ProfessorReqDto professorReqDto,
@RequestPart(value = "profileImage", required = false) MultipartFile multipartFile
) {
Long professorId = professorService.saveProfessor(professorReqDto, multipartFile);
Expand Down
Loading

0 comments on commit 61f04e4

Please sign in to comment.