티스토리 뷰
스프링/스프링부트 RestAPI 프로젝트
spring boot REST API Web 프로젝트 (4) - Http 메소드를 기준으로 API 설계 (Controller, Service)
구름뭉치 2021. 8. 7. 10:00스프링 부트 REST API WEB 프로젝트
깃헙 링크
https://github.com/choiwoonsik/springboot_RestApi_App_Project/tree/main/restApiSpringBootApp
수행 목록
- 환경구성 및 helloworld 출력
- H2 DB 연동
- Swagger API 문서 연동
- REST API 설계
- RestControllerAdvice를 이용한 통합 예외 처리
- Entity - DTO 분리
- MessageSource를 이용해 예외 메시지 다국화
- JPA Aduting을 이용해 객체 생성시간/수정시간 적용
- 스프링 시큐리티 + Jwt를 이용해서 인증 및 권한 체크
- 스프링 시큐리티 AuthenticationEntryPoint, AccessDenied로 인증 및 인가 예외처리
- Jwt AccessToken + RefreshToken으로 보안성과 사용자 편의성 고도화하기
- JUnit Test (단위 테스트)
- JUnit Test (통합 테스트)
- OAuth 2.0 정리
- OAuth 2.0 카카오 로그인 part.1 Authorization code + Token 발급
- OAuth 2.0 카카오 로그인 part.2 토큰으로 회원 가입 / 로그인
- OAuth 2.0 카카오 로그인 테스트 검증
- 환경별 설정을 위해서profile 분리하기
API를 구현할 때 확장가능하도록 Rest API로 구현하기 위해서 아래와 같이 단계적으로 구현을 해보자.
API 설계시 꼭 지켜야할 점은
- Entity를 그대로 반환해서는 안된다. (DTO를 사용)
- 리소스의 사용에 맞게 HTTP Method를 사용해야한다.
- 반환 시 배열로 바로 반환하지 않고 Json 형식에 맞게 data로 넣어줘야 한다. (확장성을 위해)
1. 리소스의 사용 목적에 맞게 HTTP Method 정의
User 엔티티에 대해 먼저 http 메소드를 정의하자.
조회
- GET /v1/users : 모든 회원 목록을 조회
- GET /v1/user/{param} : param을 기준으로 회원을 조회
- id, email, name 등에 따라서 단일 객체, 복수 객체가 반환된다.
등록
- POST /v1/user : 회원 등록
수정
- PUT /v1/user : 회원 정보 수정
삭제
- DELETE /v1/user{userId} : 회원 삭제
2. 결과 데이터의 구조를 표준화해서 정의
기존 User 정보를 바탕으로 응답 데이터 구조를 짜면된다
기존 User 데이터
{
"userId": 1,
"email": "운식@이메일.com",
"name": "최똥꼬"
}
User 정보를 바탕으로 응답 데이터 구성 (데이터 + api 요청 결과 데이터)
{
data: {
"userId": 1,
"email": "운식@이메일.com",
"name": "최똥꼬"
},
"success": ture,
"code": 0,
"message": "성공하였습니다."
}
응답 모델 3가지 구현
com.restApi.restApiSpringBootApp 하위에 model.response 폴더를 만들고 응답을 담을 3가지 모델을 만들자.
공통 응답 모델
전달될 데이터와 별개로 API의 처리여부, 상태, 메시지가 담긴 데이터이다. 이 응답은 다른 모든 응답이 상속받아서 갖도록 한다.
@Getter
@Setter
public class CommonResult {
@ApiModelProperty(value = "응답 성공 여부: T/F")
private boolean success;
@ApiModelProperty(value = "응답 코드: >= 0 정상, < 0 비정상")
private int code;
@ApiModelProperty(value = "응답 메시지")
private String msg;
}
단일 응답 모델
API 반환값이 단일 객체일 경우 해당 모델로 처리한다. 공통 응답 모델을 상속받았으므로 API 응답 관련 정보도 포함되어있다. 또한 타입을 Generic타입인 <T>로 선언해서 User Entity가 아닌 다른 엔티티에도 적용이 가능하도록 설계했다.
@Getter
@Setter
public class SingleResult<T> extends CommonResult {
private T data;
}
다중 응답 모델
@Getter
@Setter
public class ListResult<T> extends CommonResult {
private List<T> data;
}
3. API가 반환한 모델을 처리할 Service 구현
결과 모델에 데이터를 넣어주는 service를 구현한다. ResponseService 클래스를 service 패키지를 생성해서 안에 생성한다.
1. 공통 응답 모델을 Success / Fail로 처리하기위한 Enum 클래스를 생성한다.
@Getter
@AllArgsConstructor
public enum CommonResponse {
SUCCESS(0, "성공하였습니다."),
FAIL(-1, "실패하였습니다.");
private int code;
private String msg;
}
2. 유형별로 ResponseService를 구현해준다.
@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;
}
// 실패 결과만 처리
public CommonResult getFailResult() {
CommonResult result = new CommonResult();
setFailResult(result);
return result;
}
// API 요청 성공 시 응답 모델을 성공 데이터로 세팅
private void setSuccessResult(CommonResult result) {
result.setSuccess(true);
result.setCode(CommonResponse.SUCCESS.getCode());
result.setMsg(CommonResponse.SUCCESS.getMsg());
}
// API 요청 실패 시 응답 모델을 실패 데이터로 세팅
private void setFailResult(CommonResult result) {
result.setSuccess(false);
result.setCode(CommonResponse.FAIL.getCode());
result.setMsg(CommonResponse.FAIL.getMsg());
}
}
- 단일 응답, 복수 응답 별로 나눠서 API 응답을 받고, 공통 응답 부분을 성공 여부에 따라 T/F 로 처리해준다.
- 성공여부, 실패 여부만을 반환하는 메소드도 선언해준다.
- 회원 삭제 시 삭제가 성공하면 성공응답을 반환하는 용도로 사용할 수 있다.
4. Http Method와 정형화된 주소체계로 Controller 구현
위에서 정의한 GET, POST, PUT, DELETE에 맞춰서 Mapping 테이블을 완성한다.
@Api(tags = {"1. User"})
@RequiredArgsConstructor
@RestController
@RequestMapping("/v1")
public class UserController {
private final UserJpaRepo userJpaRepo;
private final ResponseService responseService;
@ApiOperation(value = "회원 단건 검색", notes = "userId로 회원을 조회합니다.")
@GetMapping("/user/{userId}")
public SingleResult<User> findUserByKey(@ApiParam(value = "회원 ID", required = true) @PathVariable Long userId) {
return responseService
.getSingleResult(userJpaRepo.findById(userId).orElse(null));
}
@ApiOperation(value = "회원 목록 조회", notes = "모든 회원을 조회합니다.")
@GetMapping("/users")
public ListResult<User> findAllUser() {
return responseService
.getListResult(userJpaRepo.findAll());
}
@ApiOperation(value = "회원 등록", notes = "회원을 등록합니다.")
@PostMapping("/user")
public SingleResult<User> save(@ApiParam(value = "회원 이메일", required = true) @RequestParam String email,
@ApiParam(value = "회원 이름", required = true) @RequestParam String name) {
User user = User.builder()
.email(email)
.name(name)
.build();
return responseService.getSingleResult(userJpaRepo.save(user));
}
@ApiOperation(value = "회원 수정", notes = "회원 정보를 수정합니다.")
@PutMapping("/user")
public SingleResult<User> modify(@ApiParam(value = "회원 아이디", required = true) @RequestParam Long userId,
@ApiParam(value = "회원 이메일", required = true) @RequestParam String email,
@ApiParam(value = "회원 이름", required = true) @RequestParam String name) {
User user = User.builder()
.userId(userId)
.email(email)
.name(name)
.build();
return responseService.getSingleResult(userJpaRepo.save(user));
}
@ApiOperation(value = "회원 삭제", notes = "회원을 삭제합니다.")
@DeleteMapping("/user/{userId}")
public CommonResult delete(@ApiParam(value = "회원 아이디", required = true) @PathVariable Long userId) {
userJpaRepo.deleteById(userId);
return responseService.getSuccessResult();
}
}
- 현재 DTO가 아닌 User Entity를 그대로 반환하고 있는 구조이므로 필히 변경이 필요하다.
결과
반응형
'스프링 > 스프링부트 RestAPI 프로젝트' 카테고리의 다른 글
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday