[Spring] Rest API
2024. 7. 6. 23:35ㆍBE/Spring
1. Rest란?
- Representational State Transfer
- 하나의 URI는 하나의 고유한 리소스를 대표하도록 설계된다는 개념.
URI + HTTP Method (GET/POST/PUT/DELETE)
HTTP URI를 통해 제어할 자원을 명시하고, 어떤 제어를 명령할지는 HTTP Method를 통해 설정하는 방식.
Method | 설명 |
POST | Create |
GET | Read |
PUT, PATCH | Update |
DELETE | Delete |
서비스 유형 | 설명 |
기존의 서비스 | 요청 처리 후, 가공된 데이터를 이용해 특정 플랫폼에 적합한 형태의 View로 반환 |
Rest 서비스 | 데이터 처리만 하거나, 처리 후 반환할 데이터가 있다면 JSON이나 XML 형식으로 반환 |
2. Rest의 특징
Rest의 특징 | 설명 |
Stateless!!! | 항상 결과가 무조건 동일해야 한다. |
Uniform Interface | 데이터가 표준 형식으로 전송될 수 있도록 구성 요소 간 통합 인터페이스를 사용한다. |
Cacheable | HTTP를 비롯한 네트워크 프로토콜에서 제공하는 캐싱 기능을 적용할 수 있어야 한다. |
Self-descriptiveness | API를 통해 전송되는 내용은 별도 문서 없이 쉽게 이해할 수 있도록 자체 표현 구조를 지녀야 한다. |
Client-Server | 클라이언트와 서버로 분리되어야하며 서로 의존성이 없어야 한다. |
Layered System | 요청된 정보를 검색하는데 있어 계층 구조로 분리되어야 한다. |
3. Rest 관련 Annotation
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@GetMapping(value = "/user")
public ResponseEntity<?> userList() {
logger.debug("userList call");
try {
List<MemberDto> list = memberService.listMember(null);
if(list != null && !list.isEmpty()) {
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} else {
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return exceptionHandling(e);
}
}
@RequestBody
를 사용하려면 Http Request Header의 Content-Type이 json이어야 한다.
4. ResponseEntity
ResponseEntity
는 HttpEntity
클래스를 상속받아 구현한 클래스다.
HttpEntity
는 HttpHeaders
와 HttpBody
를 가지고 있는데 여기에 HttpStatusCode
status code를 추가하기 위해서 ResponseEntity
를 사용한다.
@GetMapping(value = "/user")
public ResponseEntity<?> userList() {
logger.debug("userList call");
try {
List<MemberDto> list = memberService.listMember(null);
if(list != null && !list.isEmpty()) {
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} else {
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return exceptionHandling(e);
}
}
?
: 와일드카드. 무엇을 보낼지 미정.
public class CustomResult<T> {
private int statusCode;
private String message;
private T body;
...
}
ResponseEntity
를 사용해도 되지만 응답을 위한 별도의 class를 정의하기도 한다.
이때 Http Status는 무조건 200
번으로 보내고 커스텀 응답코드를 사용해서 응답 결과를 표시하는 경우가 많다.
5. Jackson library
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@GetMapping(value = "/user")
public ResponseEntity<?> userList() {
logger.debug("userList call");
try {
List<MemberDto> list = memberService.listMember(null);
if(list != null && !list.isEmpty()) {
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} else {
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return exceptionHandling(e);
}
}
Jackson-data-binding
이 알아서 JSON으로 변환해서 전송한다.
자바 객체를 반환해도 JSON으로 변환해서 반환한다.
(Jackson-data-binding
이 없으면 toString()
결과를 전송한다.)
<!-- pom.xml -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind-version}</version>
</dependency>
사용하기 위해서는 pom.xml
에 추가해야 한다.
XML로 전송하기 하는 jackson-dataformat-xml
도 있다.
6. 예시
가. GET
1) ajax
fetch(`${root}/admin/user`)
.then((response_ => response.json())
.then((data) => makeList(data));
2) RestController
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@GetMapping(value = "/user")
public ResponseEntity<?> userList() {
try {
List<MemberDto> list = memberService.listMember(null);
if(list != null && !list.isEmpty()) {
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} else {
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
} catch (Exception e) {
return exceptionHandling(e);
}
}
@GetMapping
나. POST
1) ajax
let registerinfo = {
userName: document.querySelector("#username").value,
userId: document.querySelector("#userid").value,
userPwd: document.querySelector("#userpwd").value,
emailId: document.querySelector("#emailid").value,
emailDomain: document.querySelector("#emaildomain").value,
};
let config = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(registerinfo),
};
fetch(`${root}/admin/user`, config)
.then((response) => response.json())
.then((data) => makeList(data));
2) RestController
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@PostMapping(value = "/user")
public ResponseEntity<?> userRegister(@RequestBody MemberDto memberDto) {
try {
memberService.joinMember(memberDto)
List<MemberDto> list = memberService.listMember(null);
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} catch (Exception e) {
return exceptionHandling(e);
}
}
@PostMapping
@RequestBody
:registerinfo
가 JSON로 주어졌기 때문에@RequestBody
가 필요하다.
다. PUT
1) ajax
let modifyinfo = {
...
};
let config = {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(modifyinfo),
};
fetch(`${root}/admin/user`, config)
.then((response) => response.json())
.then((data) => makeList(data));
2) RestController
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@PutMapping(value = "/user")
public ResponseEntity<?> userModify(@RequestBody MemberDto memberDto) {
logger.debug("userModify memberDto : {}", memberDto);
try {
memberService.updateMember(memberDto);
List<MemberDto> list = memberService.listMember(null);
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} catch (Exception e) {
return exceptionHandling(e);
}
@PutMapping
라. DELETE
1) ajax
let id = ... ;
let config = {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
};
fetch(`${root}/admin/user/${id}`, config)
.then((response) => response.json())
.then((data) => makeList(data));
2) RestController
@RestController
@RequestMapping("/admin")
@CrossOrigin("*")
public class AdminUserController {
...
@DeleteMapping(value = "/user/{userid}")
public ResponseEntity<?> userDelete(@PathVariable("userid") String userId) {
try {
memberService.deleteMember(userId);
List<MemberDto> list = memberService.listMember(null);
return new ResponseEntity<List<MemberDto>>(list, HttpStatus.OK);
} catch (Exception e) {
return exceptionHandling(e);
}
}
6. 암묵적인 규칙
명확하게 정해진 표준은 없지만 암묵적인 규칙은 있다.
- URI는 명사를 사용한다.
- 하이픈(
-
)은 사용하지만 언더바(_
)는 사용하지 않는다. - 특별한 경우를 제외하고 대문자를 사용하지 않는다.
- URI 마지막에 슬래시(
/
)를 사용하지 않는다. - 확장자를 포함된 파일 이름을 직접 포함하지 않는다.
- 반드시 실행 결과를 반환한다. 예외가 발생해도 관련된 정보를 반환해야 한다.
'BE > Spring' 카테고리의 다른 글
[Spring] SOP & CORS (0) | 2024.07.06 |
---|---|
[Spring] SpringBoot (0) | 2024.07.06 |
[Spring] 비동기 통신 (0) | 2024.07.06 |
[Spring] MyBatis-Spring module (0) | 2024.07.06 |
[MyBatis] 동적 SQL (0) | 2024.07.06 |