[Java] RESTful API 설계

이희수·2024년 5월 15일

REST / RESTful API

API란?

API(Application Programming Interface)는 한 프로그램에서 다른 프로그램으로 데이터를 주고받기 위한 방법을 말한다.

REST란?

  • 웹 애플리케이션을 개발하기 위한 아키텍쳐 스타일 중 하나로 클라이언트와 서버 간의 '통신 방식'을 규정한 것
  • 해당 통신 방식은 'HTTP 프로토콜'을 기반으로 하며 자원, 행위, 표현 세 가지 요소로 구성 됨

REST API (Representational State Transfer)란?

REST 아키텍쳐 스타일에 따라 구성한 API를 의미

RESTful API란?

  • HTTP를 위한 아키텍쳐 스타일 중 하나로 REST의 원칙을 따르는 웹 서비스를 '구현하는 방식'이며 웹 서비스를 개발하는 방식
  • 클라이언트와 서버 간 자원을 주고받을 때 '일괄적인 방식' 을 제공하며 이를 통해 서버와 클라이언트의 역할을 '명확하게 분리'하고 서로 '독립적'으로 개발할 수 있게 된다.

REST API 와 RESTful API의 차이점

  • REST API는 REST 아키텍쳐 스타일을 따르는 API이며 RESTful API는 REST API를 제공하는 웹 서비스를 의미
  • 결론적으로는 RESTful API는 REST API의 원칙을 따르기 때문에 REST API보다 조금 더 RESTful 하다고 볼 수 있다.

RESTful 하다 의미는?

REST API의 원칙을 따라 구성한 웹 서비스를 의미한다. 자원을 URL로 표현하고 HTTP Method를 이용해 해당 자원에 대해 CRUD 작업을 수행하며 리소스와 행위를 명시적으로 분리하여 사용함으로써 URL만 보고도 어떤 동작을 수행하는지 알 수 있도록 구성한 것을 의미

REST의 요소

1. 자원(Resource)이란?

  • 웹 상에 존재하는 데이터나 정보와 같은 모든 것을 의미 ex) 사용자,댓글,이미지 등
  • 자원의 이름 구성은 동사가 아닌 '명사' 를 사용하는것이 권장됨

2. 행위(Verb)란?

- HTTP Method를 이용한 자원에 대한 행위(조회,생성,수정,삭제)를 의미

HTTP Method행위예시
GET자원을 조회사용자 정보 조회: GET/users/{user_id}
POST자원을 생성사용자 정보 생성: POST/users
PUT자원을 수정사용자 정보 수정: PUT/users/{user_id}
PATCH자원 일부를 수정사용자 정보 일부 수정 : PATCH/users/{user_id}
DELETE자원을 삭제사용자 정보 삭제: DELETE/users/{user_id}

3. 표현(Representation)이란?

  • RESTful에서 클라이언트와 서버 간의 데이터 통신을 주고받는 '데이터 형식(JSON,XML,HTML)' 을 의미
분류JSONXMLHTML
문법가볍고 간결하며 쉽게 읽고 쓸 수 있음JSON과 비교해 더 많은 데이터를 필요로하며 상세함데이터 교환을 위한 것이 아니라 웹 페이지 렌더링을 위해 설계됨
가독성읽고 구문 분석하기 쉽고 복잡한 데이터 구조와 배열을 지원함읽고 구문 분석하기 쉽고 복잡한 데이터 구조와 스키마 유효성 검사를 지원함읽고 구문 분석하기 쉽고 기본 데이터 구조와 링크를 지원함
유연성쉽게 확장하고 사용자 정의할 수 있으며 대부분의 프로그래밍 언어를 지원함이름 공간과 사용자 정의 태그를 지원하지만 확장하는 데 더 많은 노력이 필요함제한된 유연성으로 복잡한 데이터 교환에는 적합하지 않음
인기도웹 개발, 모바일 앱 및 API에서 널리 사용됨기업 응용 프로그램 및 레거시 시스템에서 널리 사용됨웹 개발에서 널리 사용됨, 그러나 API에는 권장하지 않음

4. RESTful API 상태코드(Status Code)

  • RESTful API 에서는 HTTP 상태 코드를 사용하여 클라이언트에게 요청의 성공 또는 실패를 알려줌
  • 상태코드는 3자리 숫자로 표현되며 각각의 숫자는 특정한 의미를 가지고 있음

전체적인 상태코드 형태

상태코드상태 정보의미
1xxInformational요청이 수신되었으며 처리가 계속됨을 의미
2xxSuccess요청이 성공적으로 처리되었음을 의미
3xxRedirection요청이 완료되기 위해 추가 작업이 필요함을 의미
4xxClient Error클라이언트 오류로 인해 요청이 실패함을 의미
5xxServer Error서버 오류로 인해 요청이 실패함을 의미

일반적으로 사용되는 상태코드 종류

상태코드상태 정보의미
200OK요청이 성공적으로 처리되었음을 의미, 클라이언트에게 요청한 데이터를 반환함
201Created새로운 리소스가 성공적으로 생성되었음을 의미
204No Content요청이 성공적으로 처리되었지만, 반환할 데이터가 없음을 의미
400Bad Request클라이언트 요청이 잘못되었음을 의미
401Unauthorized클라이언트가 인증되지 않았음을 의미
403Forbidden클라이언트가 요청한 리소스에 접근할 권한이 없음을 의미
404Not Found요청한 리소스를 찾을 수 없음을 의미
500Internal Server Error서버에서 오류가 발생하여 요청을 처리할 수 없음을 의미

RESTful API 구현

@Controller 와 @RestController의 차이

  • @Controller는 일반적으로 HTTP 요청을 처리하고 'View'를 반환하는데 사용됨
  • @RestController는 HTTP 요청에 대한 RESTful 웹 서비스의 JSON,XML 등의 '데이터'를 반환하는데 사용됨
Annotation설명비고
@ControllerHTTP 요청을 처리하는 컨트롤러 클래스를 정의하는 어노테이션Spring MVC View를 반환하는데 사용됨
@RestControllerRESTful 웹 서비스를 위한 컨트롤러 클래스를 정의하는 어노테이션RESTful 웹 서비스의 JSON,XML 등의 응답을 반환하는데 사용됨

RESTful API의 행위(Verb) Annotation

AnnotationHTTPMethod 역할비고(예시)
@GetMappingGET클라이언트가 리소스를 조회할 때 사용@GetMapping(value="/users")
@PostMappingPOST클라이언트가 리소스를 생성할 때 사용@PostMapping(value="/users")
@PutMappingPUT클라이언트가 리소스를 갱신할 때 사용@PutMapping(value="/users")
@PatchMappingPATCH클라이언트가 리소스 일부를 갱신할 때 사용@PatchMapping(value="/users")
@DeleteMappingDELETE클라이언트가 리소스를 삭제할 때 사용@DeleteMapping(value="/users")
@RequestMappingALL요청 메서드(GET, POST, PUT, DELETE 등)와 URL 매핑을 함께 지정할 수 있다. @RequestMapping(value="/users", method=RequestMethod.GET)

💡 @RequestMapping 과 @XXMapping 중 무엇을 사용해야 할까?

  • 간단한 HTTP 요청에 대해서는 @XXMapping을 사용하는 것이 코드 가독성과 유지보수성을 높일 수 있다. 하지만 여러 개의 HTTP 요청 메소드에 대해 하나의 메소드로 처리해야 하는 경우는 @RequestMapping을 사용해야 한다.

RESTful API의 표현(Representation) Annotation

1. JSON 데이터 형식의 전송 방식

구분GET 방식POST 방식
전송 데이터URL 끝에 파라미터를 붙여서 서버에 요청을 보내는 방식HTTP Body에 담아서 요청을 보내는 방식
전송 데이터 크기 제한데이터의 양에 제한 있음데이터의 양에 제한 없음
캐싱 가능 여부가능불가능
보안데이터 노출 가능성 있음데이터 노출 가능성 낮음
사용 예시검색어 전송, 페이지 요청로그인,회원가입,게시글 작성

💡 캐싱이란?

  • 이전에 요청한 데이터를 저장해 두었다가 다음 요청 시에 바로 제공하는 것, 네트워크 대역폭을 절약하고, 서버의 부하를 줄일 수 있다.

2. 데이터 형식 주요 어노테이션

@PathVariable : 'URL 경로의 일부'를 매개변수로 전달받는 어노테이션

@GetMapping("/user/{name}")
    public String findByName(@PathVariable("name") String name){
        return "Name: "+name;
    }

http://localhost:8080/user/user1 로 접속시

@RequestParam : 'HTTP 요청 파라미터'를 매개변수로 전달받는 어노테이션

@GetMapping("/user")
    public User saveUser(@RequestParam("name") String name, @RequestParam("studentId") Long studentId){
        User user = new User();
        user.setName(name);
        user.setStudentId(studentId);
        return user;
    }

@RequestBody : HTTP 요청의 '본문(body)'을 매개변수로 전달받는 어노테이션

@PostMapping("/user")
    public User saveUser(@RequestBody User user){
        // user 객체를 받아서 이름을 변경
        user.setName("user2");
        return user;
    }

ResponseBody : HTTP 응답의 '본문(body)'을 생성하는 메소드에 적용하는 어노테이션

@ResponseBody
    @GetMapping("/data")
    public User getUser(){
        User user = new User();
        user.setName("user1");
        user.setStudentId(1234L);
        return user;
    }

💡 RequestBody와 ResponseBody 차이?

RequestBody: HTTP요청의 body 내용을 java 객체로 변환
ResponseBody: java 객체의 내용을 HTTP 응답의 body로 변환

위 예시에는 RequestBody만 써도 응답 body에 객체가 들어오던데요??

@RestController를 사용하는 경우에는 자동으로 return 값에 ResponseBody를 붙여 body에 전달되기 때문에 @ResponseBody 생략이 가능!

ResponseStatus : HTTP 응답의 상태 코드를 지정하는 어노테이션

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

위와 같이 응답을 설정해주고 싶을때 사용한다.

RESTfulAPI 구성하기

💡 RESTfulAPI 설계 과정

  1. 자원(Resource)을 정의한다.
  2. 행위(HTTP Method)를 정의한다.
  3. 표현(Representation)을 정의한다.
  4. 상태 코드(Status Code)를 정의한다.
  5. API 문서화를 정의한다.
자원 : Endpoint행위 : HTTP Method표현 : 데이터 전송 방식상태 코드설명구성 예시
userGETGET 전송방식, parameter or URL 경로성공 시 HttpStatus.OK 반환사용자를 조회하는 API입니다.GET /user/{userId}
userPOSTPOST 전송방식, JSON 데이터 전송성공 시 HttpStatus.OK 반환사용자를 등록하는 API입니다.POST /user
userPUTPOST 전송방식, JSON 데이터 전송성공 시 HttpStatus.OK 반환사용자를 수정하는 API입니다PUT /user
userDELETEPOST 전송방식, JSON 데이터 전송성공 시 HttpStatus.OK 반환사용자를 삭제하는 API입니다DELETE /user

ResponseEntity

Spring Framework에서 제공하는 클래스 중 HttpEntity라는 클래스가 존재한다. 이것은 HTTP 요청(Request) 또는 응답(Response)에 해당하는 HttpHeaderHttpBody를 포함하는 클래스이다.

이러한 HttpEntity 클래스를 상속받아 구현한 클래스가 RequestEntity, ResponseEntity 클래스이다. ResponseEntity는 사용자의 HttpRequest에 대한 응답 데이터를 포함하는 클래스이다. 따라서 HttpStatus,HttpHeaders,HttpBody를 포함한다.

 @PostMapping("/user")
    public ResponseEntity saveUser(@RequestBody User user){
        // user 객체를 받아서 이름을 변경
        user.setName("user2");
        return new ResponseEntity(HttpStatus.OK);
    }

위와 같이 코드를 짜고 POSTMAN으로 요청을 보내면 상태코드가 200으로 오는것을 확인할 수 있다.

또한 상태코드(Status),헤더(Headers),응답데이터(ResponseData)를 담는 생성자도 존재한다.

public class ResponseEntity<T> extends HttpEntity<T> {
	
	public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
		super(body, headers);
		Assert.notNull(status, "HttpStatus must not be null");
		this.status = status;
	}
}

위의 ResponseEntity를 이용해서 클라이언트에게 응답을 보내는 예제를 정리해보자.

import lombok.Data;

@Data
public class Message {

    private StatusEnum status;
    private String message;
    private Object data;

    public Message() {
        this.status = StatusEnum.BAD_REQUEST;
        this.data = null;
        this.message = null;
    }
}

Message라는 클래스를 만들어 상태코드, 메시지, 데이터를 담을 필드를 추가한다.

public enum StatusEnum {

    OK(200, "OK"),
    BAD_REQUEST(400, "BAD_REQUEST"),
    NOT_FOUND(404, "NOT_FOUND"),
    INTERNAL_SERER_ERROR(500, "INTERNAL_SERVER_ERROR");

    int statusCode;
    String code;

    StatusEnum(int statusCode, String code) {
        this.statusCode = statusCode;
        this.code = code;
    }
}

상태코드로 보낼 몇가지의 예시 enum

@PostMapping("/user")
    public ResponseEntity<Message> saveUser(@RequestBody User user){
        // user 객체를 받아서 이름을 변경
        user.setName("user2");

        Message message = new Message();
        HttpHeaders headers= new HttpHeaders();
        headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));
        message.setStatus(StatusEnum.OK);
        message.setMessage("성공 코드");
        message.setData(user);

        return new ResponseEntity<>(message, headers, HttpStatus.OK);
    }

이전의 코드에서 Message 클래스를 통해 StatusCode, ResponseMessage, ResponseData를 담아서 클라이언트에게 응답을 보내는 코드이다.

참고 자료
참고 자료

profile
백엔드 개발자로 살아남기

0개의 댓글