본문 바로가기
Programming/SpringBoot

[Spring Boot] RESTful Service 강의 정리(2) / User Service API 구현

by prinha 2020. 8. 24.
반응형

 

[User Service API 구현]

 

1) Domain Class 생성

package com.example.restfulwebservice.user;

import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Date;

@Data
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Date joinDate;
}

 

 

2) DAO + Service class 생성 (db연동을 하지 않을 것이기때문에 DAO 클래스가 딱히 필요없음)

package com.example.restfulwebservice.user;

import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

// 통합 클래스 이름 Dao + Service
@Service
public class UserDaoService {

    private static List<User> users = new ArrayList<>();

    private static int userCount = 3;

    // static block
    static{
        users.add(new User(1, "Kenneth",new Date()));
        users.add(new User(2, "Alice",new Date()));
        users.add(new User(3, "Elena",new Date()));
    }

    // 모든 유저 리스트 반환
    public List<User> findAll(){
        return users;
    }

    // 유저 회원 가입 + 유저 count up
    public User save(User user){
        if(user.getId()== null){
            user.setId(++userCount);
        }
        users.add(user);
        return user;
    }

    // 한명의 유저 반환
    public User findOne(int id){
        for(User user : users){
            if(user.getId()== id){
                return user;
            }
        }
        return null;
    }
}

 

 

3) Controller class 생성 -> service를 파라미터로 받아 의존성 주입 

package com.example.restfulwebservice.user;

import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    private UserDaoService service;

    // 생성자를 통한 의존성 주입
    public UserController(UserDaoService service){
        this.service=service;
    }

 

 

4) 사용자 목록 조회를 위한 API 구현 -> Controller에 GET HTTP Method 구현

package com.example.restfulwebservice.user;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
public class UserController {

    private UserDaoService service;

    // 생성자를 통한 의존성 주입
    public UserController(UserDaoService service){
        this.service=service;
    }

    // 사용자 리스트 리턴
    @GetMapping("/users")
    public List<User> retrieveAllUsers(){
        return service.findAll();
    }

    // 사용자 한명 리턴
    // GET /users/1 or /user/2  
    // --> controller로 전달되면 String 형태로 전달됨
    // --> 자동으로 int 형 매핑
    @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable  int id){
        return service.findOne(id);
    } 
}

 

사용자 리스트 리턴

 

id별 사용자(한명) 조회

 

 

 

 

5) 사용자 등록을 위한 API 구현 -> Controller에 POST HTTP Method 구현

package com.example.restfulwebservice.user;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
public class UserController {

    private UserDaoService service;

    // 생성자를 통한 의존성 주입
    public UserController(UserDaoService service){
        this.service=service;
    }

    // 사용자 리스트 리턴
    @GetMapping("/users")
    public List<User> retrieveAllUsers(){
        return service.findAll();
    }

    // 사용자 한명 리턴
    // GET /users/1 or /user/2  
    // --> controller로 전달되면 String 형태로 전달됨
    // --> 자동으로 int 형 매핑
    @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable  int id){
        return service.findOne(id);
    }

    // 새로운 사용자를 등록하는 method 구현
    @PostMapping("/users")
    public void createUser(@RequestBody User user){
        User savedUser = service.save(user);
    }
}

 

view단을 구현하지 않았기때문에 postman으로 확인해보기 - HTTP POST Method 등록 요청

 

void method라 반환값은 없지만 200번 성공 코드 확인

 

앞서 등록했던 user값 확인

 

http://localhost:8088/users의 경우 GetMapping과 PostMapping으로 나뉘어짐
-> http method를 달리하면 전혀 다른 요청이 된다.

GET -> 유저 리스트 요청
POST -> 새로운 유저 등록

 

 

 

6) HTTP Status Code 제어

// 새로운 사용자를 등록하는 method 구현 
  @PostMapping("/users")
  public ResponseEntity<User> createUser(@RequestBody User user){
     User savedUser = service.save(user);

     // 사용자에게 요청 값을 변환해주기
     // fromCurrentRequest() :현재 요청되어진 request값을 사용한다는 뜻
     // path : 반환 시켜줄 값
     // savedUser.getId() : {id} 가변변수에 새롭게 만들어진 id값 저장
     // toUri() : URI형태로 변환
     URI location = ServletUriComponentsBuilder.fromCurrentRequest()
             .path("/{id}")
             .buildAndExpand(savedUser.getId())
             .toUri();

     return ResponseEntity.created(location).build();
}

 

 

 

새로운 유저 등록해주기

 

201 Created 상태 코드 (새로운 유저 등록 완료)

 

등록 완료된 유저 id값으로 확인해주기

 

ServletUriComponentsBuilder

한 가지의 http method를 사용하는게 아니라 적절한 status code의 반환 필요
-> 좋은 REST API를 설계하는 방법
-> 네트워크 트래픽 감소
-> 좀 더 효율적인 어플리케이션 개발 가능

클라이언트가 지금 생성된 사용자의 id(서버가 자동으로 생성)를 알기 위해서는 서버에 또 한번 요청을 해야한다.
그것을 피하기 위해서 ResponseEntity<>를 사용하여 요청한 정보에 대한 HTTP 상태코드를 리턴받는다.

 

 

 

7) HTTP Status Code 제어를 위한 Exception handling

존재하지않는 사용자인데 200 코드 리턴-> 서버측의 오류가 아니기때문에 프로그램 실행에는 아무런 이상이 없다는 뜻

 

// UserDAOService

public User findOne(int id){
    for(User user : users){
        if(user.getId()== id){
            return user;
        }
    }
    return null;
}
// UserController

@GetMapping("/users/{id}")
public User retrieveUser(@PathVariable  int id){
    User user = service.findOne(id);

    if(user==null){
        throw new UserNotFoundException(String.format("ID[%s] not found", id));
    }
    return user;
}
// 예외 클래스 생성

package com.example.restfulwebservice.user;
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

500 Internal Server Error 예외발생

 

 

 

8) HTTP Status Code 제어를 위한 Exception handling - 특정한 예외 지정

@ResponseStatus(HttpStatus.NOT_FOUND)

// UserNotFoundException

package com.example.restfulwebservice.user;

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

// HTTP Status Code
// 2XX -> OK
// 4XX -> Client 오류
// 5XX -> Server 오류
// 데이터가 존재하지않는 오류기때문에
// '5XX 오류메시지'가 아니라 'NotFound' 오류메시지지 전달해주기
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

404 Not Found Status Code 전달 받음

 

 

 

9) Spring AOP를 이용한 Exception Handling

exception 패키지- 클래스 생성

// ExceptionResponse

package com.example.restfulwebservice.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;

// 예외처리를 하기위해 사용되는 자바 객체
// 모든 Controller에서 사용할 수 있는 일반화된 예외 클래스
// AOP 기능 사용
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ExceptionResponse {
    private Date timestamp; // 예외가 발생한 시간 정보
    private String message; // 예외가 발생한 메시지
    private String details; // 예외 상세 정보
}
package com.example.restfulwebservice.exception;

import com.example.restfulwebservice.user.UserNotFoundException;
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.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Date;

// ResponseEntityExceptionHandler를 상속받는 클래스
// 사용하는 시스템에서 에러가 발생하게되면,
// 에러를 핸들링하기 위해 스프링 부트에서 제공되는 클래스
@RestController
@ControllerAdvice
// 모든 컨트롤러가 실행이 될때 @ControllerAdvice 어노테이션을 가지고 있는 빈이 실행되게 되어있는데
// 만약 에러가 발생하면 예외 핸들러 클래스가 실행됨
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    // 모든 예외처리를 처리해주는 메소드
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request){

        // handleAllException(Exception ex, WebRequest request)
        // Exception ex : 에러객체
        // WebRequest : 어디서 발생했는지에 대한 정보

        ExceptionResponse exceptionResponse = new ExceptionResponse( new Date(), ex.getMessage(), request.getDescription(false));

        // 서버에서 가장 일반화되어있는 오류 : 500번 / HttpStatus.INTERNAL_SERVER_ERROR
        return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

모든 예외처리 500 code 리턴

 

 

package com.example.restfulwebservice.exception;

import com.example.restfulwebservice.user.UserNotFoundException;
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.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.Date;

// ResponseEntityExceptionHandler를 상속받는 클래스
// 사용하는 시스템에서 에러가 발생하게되면,
// 에러를 핸들링하기 위해 스프링 부트에서 제공되는 클래스
@RestController
@ControllerAdvice
// 모든 컨트롤러가 실행이 될때 @ControllerAdvice 어노테이션을 가지고 있는 빈이 실행되게 되어있는데
// 만약 에러가 발생하면 예외 핸들러 클래스가 실행됨
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    // 사용자가 존재하지않았을 때 사용하는 UserNotFound Exception
    @ExceptionHandler(UserNotFoundException.class)
    public final ResponseEntity<Object> handleUserNotFoundException(Exception ex, WebRequest request){

        // handleAllException(Exception ex, WebRequest request)
        // Exception ex : 에러객체
        // WebRequest : 어디서 발생했는지에 대한 정보

        ExceptionResponse exceptionResponse = new ExceptionResponse( new Date(), ex.getMessage(), request.getDescription(false));

        // NOT_FOUND
        return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);
    }
}

404 Not Found 코드 리턴

 

 

 

10) 사용자 삭제를 위한 API 구현 - DELETE HTTP Method

String.format() 메소드는 C언어의 printf 함수와 같은 용도로 사용되며,

출력 포맷을 하나의 문자열로 만들수 있기 때문에 불필요한 문자열 결합도 필요없고, 가독성도 높아져서 자주 사용합니다. 

 // UserDAOService
 
 // 사용자 삭제
 public User deleteById(int id){
    Iterator<User> iterator = users.iterator();

    while (iterator.hasNext()){
        User user = iterator.next();

        if(user.getId()==id){
            iterator.remove();
            return user;
        }
    }
    return null;
 }
 // UserController
 
 @DeleteMapping("/users/{id}")
 public void deleteUser(@PathVariable int id){
     User user = service.deleteById(id);
     
     if(user==null){
         throw new UserNotFoundException(String.format("ID[%s] not found", id));
     }
 }

 

DELETE 작업

 

USER 삭제 완료

 

 

반응형