새소식

TIL

[TIL] 230617 <Spring> @Transactional, @ResponseStatus, HTTP 상태 코드

  • -

 

[update와 delete 메서드에서의 @Transactional 사용 유무]

updateSchedule 메서드와 deleteSchedule 메서드는 데이터베이스 작업의 성격과 관련하여 다른 요구사항을 갖고 있습니다.


  1. updateSchedule 메서드

  • 데이터 무결성 유지 :
    updateSchedule 메서드는 일정을 업데이트하는 작업을 수행함. 이 작업은 데이터베이스의 무결성을 유지해야 함.
    ex) 두 번 이상의 사용자가 동시에 동일한 일정을 수정하려고 시도할 경우, 각각의 변경 내용이 충돌 없이 적절히 반영되어야 함!!!
          ㄴ 트랜잭션의 원자성과 일관성이 보장되어야 함
          ㄴ 일정을 업데이트하는 동안 다른 사용자가 해당 일정을 조회하는 경우, 일관된 데이터를 반환해야 함
  • 복수 작업의 원자성 :
    updateSchedule 메서드는 데이터베이스에 대해 여러 개의 작업을 수행가능
    ex) 비밀번호 확인, 일정 업데이트
          ㄴ 이러한 작업들은 하나의 트랜잭션 내에서 원자적으로 수행되어야 하며, 모든 작업이 성공하거나 실패해야 함
  • 동시성 제어 :
    여러 사용자가 동시에 updateSchedule 메서드를 호출가능
     이러한 경우, 데이터베이스의 일관성을 유지하기 위해 트랜잭션의 격리성이 보장되어야 합니다.

    → updateSchedule 메서드에는 "@Transactional 어노테이션"을 사용하여 위의 요구사항을 충족시킴!!!



    2. deleteSchedule 메서드

    • 단일 작업의 원자성 :
      deleteSchedule 메서드는 단일한 작업을 수행함. 즉, 데이터베이스에서 하나의 일정을 삭제하는 작업만 수행됨.
      ㄴ 복수의 단계로 나눠지는 것이 아니며, 단일 작업으로 완료될 수 있음.
    • 영향 범위 :
      deleteSchedule 메서드는 다른 사용자나 작업에 큰 영향을 미치지 않음.
      한 번에 하나의 일정만 삭제되며, 다른 사용자의 일정 조회나 수정 작업에 영향을 주지 않습니다.
    • 트랜잭션 부하 최소화 :
      deleteSchedule 메서드는 단일 작업이므로 트랜잭션의 범위를 최소화하여 데이터베이스 리소스를 효율적으로 사용가능.
      복수의 작업을 하나의 트랜잭션으로 묶는 것보다 단일 작업을 수행하는 트랜잭션을 사용하는 것이 더 효율적!

      → deleteSchedule 메서드에는 @Transactional 어노테이션을 사용하지 않아도 됨!!!

 

 


[@ResponseStatus 어노테이션]

@ResponseStatus 어노테이션은 Spring MVC에서 예외가 발생할 때 HTTP 응답 상태 코드를 설정하는 데 사용
@ResponseStatus를 예외 클래스에 적용하여 예외 핸들러 메서드를 작성하지 않아도 
    특정 예외가 발생했을 때 클라이언트에게 반환할 HTTP 상태 코드를 지정가능

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
    public NotFoundException(String message) {
        super(message);
    }
}

 

사용 방법

  • @ResponseStatus는 클래스 레벨에서 사용됨
  • value 속성 또는 code 속성으로 HTTP 상태 코드를 지정
  • reason 속성으로 응답 본문에 포함될 메시지를 설정 가능
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
public class NotFoundException extends RuntimeException {
    public NotFoundException(String message) {
        super(message);
    }
}

 

 


글로벌 예외 처리

  • 전역적으로 예외를 처리하고 특정 상태 코드를 반환하도록 설정가능
  • 전역 예외 처리 핸들러를 사용하면 다양한 예외에 대해 일관된 응답을 제공 가능
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleIllegalArgumentException(IllegalArgumentException ex) {
        return new ErrorResponse("Invalid argument", ex.getMessage());
    }

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex) {
        return new ErrorResponse("Not found", ex.getMessage());
    }
}

class ErrorResponse {
    private String error;
    private String message;

    public ErrorResponse(String error, String message) {
        this.error = error;
        this.message = message;
    }

    // getters and setters
}

 

 


[전역 예외 처리 핸들러]

  • 전역 예외 처리 핸들러를 사용하면 애플리케이션 전체에서 발생하는 다양한 예외에 대해 일관된 응답을 제공 가능
  • 애플리케이션의 예외 처리 로직을 중앙 집중화하고, 예외에 대한 일관된 응답을 클라이언트에게 제공할 수 있어 코드의 유지보수성과 가독성을 높일 수 있음
  • Spring 프레임워크에서는 전역 예외 처리를 위해 @ControllerAdvice와 @ExceptionHandler 어노테이션을 사용
    1. @ControllerAdvice 클래스 작성: 예외 처리를 담당하는 전역 핸들러 클래스 생성
    2. @ExceptionHandler 메소드 작성: 각 예외 유형에 대한 처리 메소드를 정의

 

1) 사용자 정의 예외 클래스 정의

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
    public BadRequestException(String message) {
        super(message);
    }
}


2) 전역 예외 처리 핸들러 작성

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity<ErrorResponse> handleBadRequestException(BadRequestException ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, WebRequest request) {
        ErrorResponse errorResponse = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred");
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
  • handleResourceNotFoundException: ResourceNotFoundException이 발생했을 때 404 상태 코드와 함께 사용자 정의 에러 메시지를 반환
  • handleBadRequestException: BadRequestException이 발생했을 때 400 상태 코드와 함께 사용자 정의 에러 메시지를 반환
  • handleGlobalException: 모든 예외를 포괄적으로 처리하는 메소드.예상치 못한 예외가 발생했을 때 500 상태 코드와 함께 일반적인 에러 메시지를 반환

 

3) 에러 응답 객체 정의

public class ErrorResponse {
    private int statusCode;
    private String message;

    public ErrorResponse(int statusCode, String message) {
        this.statusCode = statusCode;
        this.message = message;
    }

    // Getters and Setters
}

 

 


[HTTP 상태 코드의 종류]

 

  • '@ResponseStatus' 어노테이션에서 사용하는 `HttpStatus` 클래스에는 다양한 HTTP 상태 코드가 정의되어 있음
  • 이 클래스는 Spring 프레임워크에서 제공하며, 표준 HTTP 상태 코드를 쉽게 사용할 수 있도록 Enum 형태로 제공
  • 이 상태 코드는 `@ResponseStatus` 어노테이션과 함께 사용하여 특정 예외가 발생할 때 클라이언트에게 적절한 HTTP 상태 코드를 반환할 수 있도록 함!

 

Informational (1xx)
- CONTINUE (100) : 클라이언트가 요청을 계속 진행할 수 있습니다.
- SWITCHING_PROTOCOLS (101): 프로토콜 전환이 이루어졌습니다.
- PROCESSING (102): 요청이 처리 중임을 나타냅니다 (WebDAV).


Success (2xx)
- OK (200): 요청이 성공적으로 처리되었습니다.
- CREATED (201): 요청이 성공적으로 처리되었고 새로운 리소스가 생성되었습니다.
- ACCEPTED (202): 요청이 수락되었지만 아직 처리되지 않았습니다.
- NON_AUTHORITATIVE_INFORMATION (203): 성공적으로 처리되었지만 다른 소스에서 가져온 정보가 포함되어 있습니다.
- NO_CONTENT (204): 요청이 성공적으로 처리되었지만 반환할 콘텐츠가 없습니다.
- RESET_CONTENT (205): 요청이 성공적으로 처리되었으며 클라이언트는 문서를 새로 고쳐야 합니다.
- PARTIAL_CONTENT (206): 부분적인 응답을 반환합니다.
- MULTI_STATUS (207): 여러 상태를 포함하는 다중 상태 응답 (WebDAV).
- ALREADY_REPORTED (208): 이전에 보고된 리소스에 대한 응답 (WebDAV).
- IM_USED (226): 서버가 GET 요청에 대해 인스턴트 메서드를 사용했다는 응답.


Redirection (3xx)
- MULTIPLE_CHOICES (300): 여러 가지 가능한 응답이 있습니다.
- MOVED_PERMANENTLY (301): 요청된 리소스가 영구적으로 이동되었습니다.
- FOUND (302): 요청된 리소스가 일시적으로 다른 위치에 있습니다.
- SEE_OTHER (303): 클라이언트가 다른 URI를 사용하여 요청해야 합니다.
- NOT_MODIFIED (304): 클라이언트는 캐시된 버전을 사용해야 합니다.
- TEMPORARY_REDIRECT (307): 요청된 리소스가 일시적으로 다른 위치에 있으며 동일한 요청 메소드를 사용해야 합니다.
- PERMANENT_REDIRECT (308): 요청된 리소스가 영구적으로 다른 위치에 있으며 동일한 요청 메소드를 사용해야 합니다.


Client Error (4xx)
- BAD_REQUEST (400): 잘못된 요청입니다.
- UNAUTHORIZED (401): 인증이 필요합니다.
- PAYMENT_REQUIRED (402): 결제가 필요합니다.
- FORBIDDEN (403): 접근이 금지되었습니다.
- NOT_FOUND (404): 요청한 리소스를 찾을 수 없습니다.
- METHOD_NOT_ALLOWED (405): 요청한 메소드가 허용되지 않습니다.
- NOT_ACCEPTABLE (406): 요청한 리소스를 제공할 수 없습니다.
- PROXY_AUTHENTICATION_REQUIRED (407): 프록시 인증이 필요합니다.
- REQUEST_TIMEOUT (408): 요청 시간이 초과되었습니다.
- CONFLICT (409): 요청이 리소스의 현재 상태와 충돌합니다.
- GONE (410): 요청한 리소스가 더 이상 사용할 수 없습니다.
- LENGTH_REQUIRED (411): 요청에 `Content-Length` 헤더가 필요합니다.
- PRECONDITION_FAILED (412): 전제 조건이 만족되지 않았습니다.
- PAYLOAD_TOO_LARGE (413): 요청된 엔티티가 너무 큽니다.
- URI_TOO_LONG (414): 요청된 URI가 너무 깁니다.
- UNSUPPORTED_MEDIA_TYPE (415): 지원되지 않는 미디어 타입입니다.
- REQUESTED_RANGE_NOT_SATISFIABLE (416): 요청한 범위를 만족할 수 없습니다.
- EXPECTATION_FAILED (417): `Expect` 헤더의 요구사항을 만족할 수 없습니다.
- I_AM_A_TEAPOT (418): (RFC 2324에 정의된 장난 코드) 나는 주전자입니다.
- UNPROCESSABLE_ENTITY (422): 요청은 이해되었으나 처리할 수 없습니다 (WebDAV).
- LOCKED (423): 리소스가 잠겨 있습니다 (WebDAV).
- FAILED_DEPENDENCY (424): 이전 요청이 실패했기 때문에 현재 요청도 실패했습니다 (WebDAV).
- TOO_EARLY (425): 서버가 요청을 처리하기에 너무 이릅니다.
- UPGRADE_REQUIRED (426): 다른 프로토콜로 업그레이드해야 합니다.
- PRECONDITION_REQUIRED (428): 요청에 전제 조건이 필요합니다.
- TOO_MANY_REQUESTS (429): 너무 많은 요청을 보냈습니다.
- REQUEST_HEADER_FIELDS_TOO_LARGE (431): 요청 헤더 필드가 너무 큽니다.
- UNAVAILABLE_FOR_LEGAL_REASONS (451): 법적인 이유로 사용할 수 없습니다.


Server Error (5xx)
- INTERNAL_SERVER_ERROR (500): 서버 내부 오류입니다.
- NOT_IMPLEMENTED (501): 서버가 요청된 기능을 지원하지 않습니다.
- BAD_GATEWAY (502): 게이트웨이 또는 프록시 서버에서 잘못된 응답을 수신했습니다.
- SERVICE_UNAVAILABLE (503): 서버가 현재 요청을 처리할 수 없습니다.
- GATEWAY_TIMEOUT (504): 게이트웨이 또는 프록시 서버가 상위 서버로부터 응답을 기다리는 동안 시간이 초과되었습니다.
- HTTP_VERSION_NOT_SUPPORTED (505): 서버가 요청에 사용된 HTTP 버전을 지원하지 않습니다.
- VARIANT_ALSO_NEGOTIATES (506): 서버 내부 구성 오류로 변형 리소스가 협상되었습니다.
- INSUFFICIENT_STORAGE (507): 서버가 요청을 저장할 수 없습니다 (WebDAV).
- LOOP_DETECTED (508): 서버가 무한 루프를 감지했습니다 (WebDAV).
- NOT_EXTENDED (510): 요청된 정책이 필요합니다.
- NETWORK_AUTHENTICATION_REQUIRED (511): 네트워크 인증이 필요합니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.