API 서버 개발을 본격적으로 진행해 보기 위해
현재 API 인터페이스 및 결과 데이터의 구조를 살펴보고
확장 가능한 형태로 설계할 것이다.
API는 제공 대상이 클라이언트 APP이나 WEB 개발자이다. (프론트앤드)
한 번 배포되고 공유한 API는 구조를 쉽게 바꿀 수 없기 때문에 처음부터 효율적이로 확장 가능한 형태로 모델을 설계하고 시작하는게 좋다.
그래서 다음과 같이 HttpMethod를 사용하고
Restful 한 API를 만들기 위해 몇 가지 규칙을 적용 할 것이다.
post:쓰기
get:읽기
put:수정
delete:삭제
Post /auth/login
Post /auth/signup
...
// 기존 USER 정보
{
"msrl": 1,
"uid": "test@test.com",
"name": "test"
}
// 표준화한 USER 정보 결과 데이터 + API 요청 결과 데이터
{
"data": {
"msrl": 1,
"uid": "test@test.com",
"name": "test"
},
"success": true
"code": 0,
"message": "성공하였습니다."
}
API의 처리 상태 및 메시지를 내려주는 데이터로 구성된다.
success는 API의 성공, 실패 여부를 나타내고
code, msg는 해당 상황에서의 응답 코드와 응답 메시지를 나타낸다.
package com.checkmate.backend.common;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @package : com.checkmate.backend.common
* @name: CommonResult.java
* @date : 2022/05/19 7:18 오후
* @author : jifrozen
* @version : 1.0.0
* @description : REST API 결과 모델 생성
* @modified :
**/
@Getter
@Setter
public class CommonResult {
@Schema(description = "응답 성공 여부 = true/false")
private boolean success;
@Schema(description = "응답 코드 번호")
private int code;
@Schema(description = "응답 메시지")
private String msg;
}
Generic Interface에 를 지정하여
어떤 형태의 값도 넣을 수 있도록 구현한다.
또한, CommonResult를 상속받아 API 요청 결과도 같이 출력된다.
package com.checkmate.backend.common;
import lombok.Getter;
import lombok.Setter;
/**
* @package : com.checkmate.backend.common
* @name: SingleResult.java
* @date : 2022/05/19 7:20 오후
* @author : jifrozen
* @version : 1.0.0
* @description : 단일건 api 결과 처리
* @modified :
**/
@Getter
@Setter
public class SingleResult<T> extends CommonResult {
private T data;
}
API 결과가 다중 건인 경우에 대한 데이터 모델이다.
결과 필드를 List 형태로 선언하고
Generic Interface에 를 지정하여
어떤 형태의 List 값도 넣을 수 있도록 구현한다.
또한, CommonResult를 상속받아 API 요청 결과도 같이 출력한다.
package com.checkmate.backend.common;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
/**
* @package : com.checkmate.backend.common
* @name: ListResult.java
* @date : 2022/05/19 7:20 오후
* @author : jifrozen
* @version : 1.0.0
* @description : 여러건 api 결과 처리
* @modified :
**/
@Getter
@Setter
public class ListResult<T> extends CommonResult {
private List<T> list;
}
결과 모델에 데이터를 넣어주는 역할을 하는 Service를 정의한다. Controller에서 항상 ResponseService를 호출하여 결과 모델로 반환해준다.
package com.checkmate.backend.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.checkmate.backend.common.CommonResult;
import com.checkmate.backend.common.ListResult;
import com.checkmate.backend.common.SingleResult;
/**
* @package : com.checkmate.backend.service
* @name: ResponseService.java
* @date : 2022/05/19 7:50 오후
* @author : jifrozen
* @version : 1.0.0
* @description : 결과 모델을 처리할 서비스
* @modified :
**/
@Service
public class ResponseService {
// 단일건 결과 처리
public <T> SingleResult<T> getSingleResult(T data) {
SingleResult<T> result = new SingleResult<>();
result.setData(data);
setSuccessResult(result);
return result;
}
// 다중건 결과 처리
public <T> ListResult<T> getListResult(List<T> list) {
ListResult<T> result = new ListResult<>();
result.setList(list);
setSuccessResult(result);
return result;
}
// 성공 결과만 처리
public CommonResult getSuccessResult() {
CommonResult result = new CommonResult();
setSuccessResult(result);
return result;
}
// 성공하면 api 성공 데이터 세팅
private void setSuccessResult(CommonResult result) {
result.setSuccess(true);
result.setCode(CommonResponse.SUCCESS.getCode());
result.setMsg(CommonResponse.SUCCESS.getMsg());
}
// 실패 결과만 처리
public CommonResult getFailResult(int code, String msg) {
CommonResult result = new CommonResult();
result.setSuccess(false);
result.setCode(code);
result.setMsg(msg);
return result;
}
public enum CommonResponse {
SUCCESS(0, "성공"),
FAIL(-1, "실패");
int code;
String msg;
CommonResponse(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
}
@ApiOperation(value = "업체 메뉴 조회", notes = "업체 메뉴를 조회한다.")
@GetMapping(value = "/{storeId}/menus")
public ListResult<Menu> menus(
@ApiParam(value = "업체 Id", required = true) @PathVariable long storeId
) {
return responseService.getListResult(menuService.getMenus(storeId));
}
### 단일건 처리는 SingleResult()
@ApiImplicitParams({
@ApiImplicitParam(name = "X-AUTH-TOKEN", value = "로그인 성공 후 access_token", required = true, dataType = "String", paramType = "header")
})
@ApiOperation(value = "회원 정보 조회", notes = "회원 마이페이지 조회")
@GetMapping(value = "/customer")
public SingleResult<Customer> getCustomer() throws Throwable {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String email = authentication.getName();
return responseService.getSingleResult(userService.getCustomer(email));
}
@ApiOperation(value = "비밀번호 확인")
@ApiImplicitParams({
@ApiImplicitParam(name = "X-AUTH-TOKEN", value = "로그인 성공 후 access_token", required = true, dataType = "String", paramType = "header")
})
@PostMapping(value = "/password/verification")
public CommonResult verifyPassword(
@ApiParam(value = "비밀번호 확인", required = true) @RequestParam String password) throws
Throwable {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String email = authentication.getName();
if (!userService.isValidPassword(email, password)) {
throw new PasswordNotMatchedException();
}
return responseService.getSuccessResult();
}
출처 - https://velog.io/@jifrozen/Dining-together-REST-API-구조-설계하기
https://pepega.tistory.com/26