개발자 끄적끄적

RestAPI와 예외상황 처리 본문

웹프레임워크

RestAPI와 예외상황 처리

햏치 2024. 5. 8. 14:44

 

 

 

<@RestController = @Controller + @RespnoseBody>
- Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있다

  - @Controller : 전통적은 Spring MVC 컨트롤러
    - 'View'를 반환하기 위해 사용

  - @RestController d: HTTP ResponseBody가 생성
    - Spring MVC의 컨트롤러를 사용하면서 'Data를 반환해야 하는 경우'가 생기는데 컨트롤러에서는 데이터를 반환하기위해 @ResponseBody 어노테이션을 활용한다
  이를 통해서 Controller로 JSON 형태로 데이터를 변환 할 수 있다
  즉, JSON의 형태로 객체 데이터를 반환하는 것이다





<@RequestMapping>
- 클라이언트의 요청(url)에 맞는 클래스나 메서드를 연결시켜주는 어노테이션이다. 이 어노테이션은 그 위치에 따라 의미가 다르다
  - 클래스 레벨 : 공통 주소
  - 메서드 레벨 : 공통 주소 외 나머지 하위 주소
  - 단, 클래스 레벨에 @RequestMapping이 없다면,메서드 레벨은 단독 요청 처리 주소이다

- @RequestMapping 작성 방법
  - @RequestMapping("url") : 요청방식(GET/POST)에 관계없이 url이 일치하는 요청을 처리한다
  - @RequestMapping(value="url", method=RequestMethod.Get | POST) : 지정된 요청 방식에 따라 처리한다
  - 요청 방식에 관계없이 요청을 처리할 경우 메서드 레벨에서 GET/POST 방식을 분후여 Mapping해야 한다 -> @GetMapping('url') / @PostMapping('url')




OfferRestController.java 코드-------------------------------------------------------------------------------------------------------------------------
package kr.ac.hansung.cse.controller;

import kr.ac.hansung.cse.exceptioin.OfferNotFoundException;
import kr.ac.hansung.cse.model.Offer;
import kr.ac.hansung.cse.service.OfferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.List;

@RestController //@Controller + @ResponseBody
@RequestMapping("/api/offers")
public class OfferRestController {

    @Autowired
    private OfferService offerService;

    //CRUD

    @GetMapping("/{id}") //return 값은 Offer, 하나의 Offer를 줘야한다
    public ResponseEntity<Offer> getOffer(@PathVariable int id){
        //조회
        Offer offer = offerService.getOfferById(id);

        if(offer==null) { //만약 offer에 id가 없다면
            throw new OfferNotFoundException(id); //예외상황 -> Custom Exception
        }
        return new ResponseEntity<>(offer, HttpStatus.OK); //offer는 body에들어가고 HttpStatus.OK는 status에 들어간다
    }


    @GetMapping //모든 Offer를 return, 함수 이름은 'getOffers'
    public ResponseEntity<List<Offer>> getOffers(){
        List<Offer> offers = offerService.getAllOffers();

        if(offers.isEmpty()) { //만약 offer에 id가 없다면
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        return new ResponseEntity<>(offers, HttpStatus.OK); //offer는 body에들어가고 HttpStatus.OK는 status에 들어간다
    }


    //insert를 생성하기 위해서는 @POSTMapping
    @PostMapping //id는 필요없다(생성하는 것이므로)
    public ResponseEntity<Void> createOffer(@RequestBody Offer offer){

        offerService.insertOffer(offer);

        HttpHeaders headers = new HttpHeaders();
        //url 생성
        UriComponents uriComponents = UriComponentsBuilder
                .fromPath("api/offers/{id}")
                .buildAndExpand(offer.getId())
                .encode();

        URI loactionUri = uriComponents.toUri();
        headers.setLocation(loactionUri);

        return new ResponseEntity<>(headers, HttpStatus.CREATED);

    }

    //update는 @PutMapping, update할 때도 id가 필요
    @PutMapping("/{id}")
    public ResponseEntity<Offer> updateOffer(@PathVariable int id, @RequestBody Offer offer) {

        //조회를 한 다음에 수정을 한 다음 update
        Offer currentOffer = offerService.getOfferById(id);

        if (currentOffer == null)
            throw new OfferNotFoundException(id);

            currentOffer.setName(offer.getName());
            currentOffer.setEmail(offer.getEmail());
            currentOffer.setText(offer.getText());

            //데이터를 업데이트
            offerService.updateOffer(currentOffer);

            //ResonpseEntity에 넣어준다
            return new ResponseEntity<>(currentOffer, HttpStatus.OK);

    }

        //delete는 @DeleteMapping, delete 할 때는 id가 필요, id값을 받기 위해서 @PathVariable
        @DeleteMapping("/{id}")
        public ResponseEntity<Void> deleteOffer(@PathVariable int id){

            Offer currentOffer = offerService.getOfferById(id);
            if (currentOffer == null)
                throw new OfferNotFoundException(id);

            offerService.deleteOfferById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        }
        
    }
-----------------------------------------------------------------------------------------------------------------------------------------




<예외처리(Excpetion) 종류>


1. Throwable class
- Java 언어의 모든 오류 및 예외의 슈퍼 클래스이다
- 이 클래스의 인스턴스(또는 그 서브 클래스 중 하나)인 객체만 Java Virtual Machine에 의해 발생되거나 Java throw문에 의해서 발생
- 예외의 comple-time checking을 위해, RunTimeException 또는 Error의 서브 클래스가 아닌 Throwable 및 Throwalbe의 서브 클래스는 Checked 예외로 간주


2. Error class
- Error는 Throwable의 하위 클래스로서, 응용 프로그램의 심각한 문제를 나타내는 클래스이다
- 시스템에 비정상적인 상황이 생겼을 때 발생한다(시스템 레벨에서 발생)
- Error의 서브 클래스를 throws 선언할 필요는 없다
- Error 및 해당 서브 클래스는 예외 컴파일 시간 검사를 위해 unchcked Exception으로 간주된다


3. Exception class
- Exception 클래스와 그 subclass는 응용프로그램이 catch할 수 있는 조건을 나타내는 Throwable 형식이다
- Exception 클래스와 RuntimeException의 subclass가 아닌 서브 클래스는 checked exception이다


4. RunTimeException
- Java Virtual Machine의 정상적인 작동 중에 발생할 수 있는 예외의 슈퍼 클래스이다
- RuntimeException 및 해당 서브 클래스는 unchecked exception이다
  unchecked exception은 메서드 또는 생성자의 실행에 의해 발생한다




<Checked Exception vs. Unchecked Exception>

1. checked exception
- 반드시 예외 처리를 해야한다(try~catch문)
- 컴파일 단계에서 확인 가능
- 예외 발생 시 트랜잭션 roll back 하지 않는다
- RuntimeException을 제외한 Exception의 하위 클래스

2. unchecked exception
- 명시적 에외 처리를 강제하지 않는다
- 실행 단계에서 확인 가능하다
- 예외 발생 시 트랜잭션 roll back한다
- RuntimeException의 하위 클래스






*Getter vs. Setter
- pirvate에서 관리하는 메스드
- private할 때 다른 클래스에서 접근하려면 해당 클래스 안의 메서드를 통해서만 가능하다. 그 관리하는 메서드를 Getter, Setter라고 한다

1. Getter 
- Private를 외부로 꺼내는 메서드
- 내부의 멤버변수에 저장된 값을 외부로 리ㄷ턴
- 메서드 명은 주로 getXXX() 메서드 형식으로 지정
- XXX는 해당 멤버변수의 변수명을 사용

2. Setter 
- Private에 값을 넣는 메서드
- 외부러부터 데이터를 전달받아 멤버변수에 저장
- 매개변수만 있고, 리턴값은 없는 메서드로 정의





GlobalExceptionController코드-----------------------------------------------------------------------------------------------------------------------

@RestControllerAdvice //전역(모든) Controller에서 예외 처리 가능
public class GlobalExceptionController {
    
    @ExceptionHandler(OfferNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleOfferNotFoundException(HttpServletRequest req, OfferNotFoundException ex) {
        String requestUri = req.getRequestURI();

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setRequestUri(requestUri);
        errorResponse.setErrorCode("offer.notfound.exception");
        errorResponse.setErrorMsg("offer with id" + ex.getOfferId() + "not found");

        return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.NOT_FOUND); //body(ResponseEntity)에 넘겨지게된다

    }



ErrorResponse.java 코드 ----------------------------------------------------------------------------------------------------------------------

package kr.ac.hansung.cse.model;

import lombok.Data;

@Data //getter, setter, 인자가 없는(있는) 생성자 등
public class ErrorResponse {
    private String errorCode;
    private String errorMsg;
    private String requestUri;
}
-----------------------------------------------------------------------------------------------------------------------------------------



참조 : https://mangkyu.tistory.com/49

'웹프레임워크' 카테고리의 다른 글

Spring Boot  (1) 2024.05.16
RestAPI  (0) 2024.05.01
JPA  (0) 2024.04.23