[Spring Boot] RESTful API ResponseEntity Example

[Spring Boot] RESTful API ResponseEntity Example

반응형

[Spring Boot] RESTful API ResponseEntity Example

제대로된 API Response 형태를 전달한 경험이 주로 없다보니..

문득 REST API Response Body 전달 형식이 궁금해졌다. :0

간단한 Response 구조로 테스트해보자.

Class

package com.example.responseApi.api.*

DTO

Response 결과로 넘겨줄 객체

@Data public class Member { private Long id; private String name; private String dept; public Member(String name, String dept) { this.name = name; this.dept = dept; } }

Repository

Database 연동 대체

@Slf4j @Repository public class MemberRepository { private static Map store = new HashMap<>(); //static 사용 private static long sequence = 0L; //static 사용 public Member save(Member member) { member.setId(++sequence); log.info("save: member={}", member); store.put(member.getId(), member); return member; } public Optional findById(Long id) { return Optional.ofNullable(store.get(id)); } public Optional findByLoginId(String loginId) { return findAll().stream() .filter(m -> m.getId().equals(loginId)) .findFirst(); } public List findAll() { return new ArrayList<>(store.values()); } public void clearStore() { store.clear(); } }

Response

Response 시 사용할 class

@Data @Builder @AllArgsConstructor @NoArgsConstructor public class BasicResponse { private Integer code; private HttpStatus httpStatus; private String message; private Integer count; private List result; }

Controller

API Controller

@RequestMapping("/api") @RestController @RequiredArgsConstructor public class ApiController { private final MemberRepository memberRepository; @GetMapping("/{id}") public ResponseEntity find(@PathVariable Long id) { BasicResponse basicResponse = new BasicResponse(); Optional member = memberRepository.findById(id); if (member.isPresent()) { basicResponse = BasicResponse.builder() .code(HttpStatus.OK.value()) .httpStatus(HttpStatus.OK) .message("사용자 조회 성공") .result(Arrays.asList(member.get())) .count(1).build(); } else { basicResponse = BasicResponse.builder() .code(HttpStatus.NOT_FOUND.value()) .httpStatus(HttpStatus.NOT_FOUND) .message("사용자를 찾을 수 없습니다.") .result(Collections.emptyList()) .count(0).build(); } return new ResponseEntity<>(basicResponse, basicResponse.getHttpStatus()); } @GetMapping("/all") public ResponseEntity list() { List memberList = memberRepository.findAll(); BasicResponse basicResponse = BasicResponse.builder() .code(HttpStatus.OK.value()) .httpStatus(HttpStatus.OK) .message("전체 사용자 조회 성공") .result(new ArrayList<>(memberList)) .count(memberList.size()).build(); return new ResponseEntity<>(basicResponse, HttpStatus.OK); } }

Test Data Init

package com.example.responseApi;

API 테스트를 위한 테스트 데이터 생성

@Component @RequiredArgsConstructor public class TestDataInit { private final MemberRepository memberRepository; @PostConstruct public void init() { memberRepository.save(new Member("Cristoval", "back-end")); memberRepository.save(new Member("Aaron", "front-end")); } }

Request Test

사용자를 찾을 수 없을 경우

http://localhost:8080/api/0

{ "code": 404, "httpStatus": "NOT_FOUND", "message": "사용자를 찾을 수 없습니다.", "count": 0, "result": [] }

사용자 조회 성공

http://localhost:8080/api/1

{ "code": 200, "httpStatus": "OK", "message": "사용자 조회 성공", "count": 1, "result": [ { "id": 1, "name": "Cristoval", "dept": "back-end" } ] }

전체 사용자 조회

http://localhost:8080/api/all

{ "code": 200, "httpStatus": "OK", "message": "전체 사용자 조회 성공", "count": 2, "result": [ { "id": 1, "name": "Cristoval", "dept": "back-end" }, { "id": 2, "name": "Aaron", "dept": "front-end" } ] }

HTTP 상태코드

2xx

Successful: 요청 정상 처리

Code

200 OK

201 Created (POST)

202 Accepted (batch)

204 No Content

3xx

Redirection: 요청을 완료를 위해 추가 행동 필요

Redirect

웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동

영구 리다이렉션 : 특정 리소스의 URI가 영구적으로 이동 (301, 308)

일시 리다이렉션 : 일시적인 변경 (302, 303, 307) PRG(Post/Redirect/Get)에 사용 / 새로고침 중복 주문 방지

특수 리다이렉션 : 결과 대신 캐시 사용

Code

300 Multiple Choices (X)

301 Moved Permanently 리다이렉트 시 Get 으로 변하고, 본문 손실

302 Found 리다이렉트 시 GET 으로 변하고, 본문 제거

303 See Other 리다이렉트 시 GET 으로 변경

304 Not Modified 클라이언트에게 리소스가 수정되지 않았음을 알려줌 (캐시 재사용)

307 Temporary Redirect 리다이렉트 시 메서드와 본문 유지

308 Permanent Redirect 리다이렌트 시 POST , 본문 유지

4xx

Client Error

오류의 원인은 클라이언트

Code

400 Bad Request 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음

401 Unauthorized 클라이언트가 해당 리소스에 대한 인증이 필요 인증(Authentication): 로그인 인가(Authorization): 권한

403 Forbidden 서버가 요청을 이해했지만 승인을 거부 (접근 권한 제한)

404 Not Found 요청 리소스를 찾을 수 없음

5xx

Server Error

서버 문제로 오류 발생

Code

500 Internal Server Error 서버 내부 문제로 오류 발생 (애매하면 500)

503 Service Unavailable 서비스 이용 불가

Project

PROJECT

Reference

from http://data-make.tistory.com/710 by ccl(A) rewrite - 2021-12-14 23:01:33