-
Notifications
You must be signed in to change notification settings - Fork 2
Season 2 Day 8 (2021. 03. 07)
모임날짜: 2020-03-07(일)
참여자:강인한, 김근욱, 최승연, 한승엽
주제: 1.JPA / 2.RESTFUL API / 3.JWT / 4.Controller Advice
기타: Spring boot -> nest.js 로 (잠시) 전환!
내용 살펴보기: https://www.notion.so/seungyeondev/10-ab67fabc814b4313a3ffee5c7e5d684c
Representational State Transfe라는 용어의 약자이다. 자원을 URI로 표시하고 해당 자원의 상태를 주고 받는 것을 의미한다.
- 자원(Resource): URI
- 행위(Verb): HTTP METHOD
- 표현(Representations)
- Server-Client구조
- REST Server: API를 제공하고 비즈니스 로직 처리 및 저장을 책임진다.
- Client: 사용자 인증이나 context(세션, 로그인 정보) 등을 직접 관리하고 책임진다.
- 서로간 의존성이 줄어든다.
- Stateless(무상태)
- REST는 Server에서 Client Context를 유지X
- 각각의 요청을 별개의 것으로 인식
- Cacheable
- HTTP의 기존 웹 표준을 그대로 사용하기 때문에 HTTP가 가진 캐싱 기능 적용이 가능하다.
- Layered System
- Client는 REST API 서버만 호출
- REST API서버는 필요한 계층을 유연하게 추가할 수 있다. ex) 암호화, 로드밸런싱..
- Interface 일관성
- HTTP표준를 지킨다면 언어나 플랫폼의 제약을 받지 않는다.
Rest 기반의 규칙들을 지켜서 설계된 API를 Rest API 혹은 Restful API이라고 한다.
- URI는 정보의 자원을 표현해야 한다.
- 주소에 행위보다는 명사가 들어가야 한다.
- 대문자 보다는 소문자
- URI에 HTTP METHOD가 들어가서는 안된다.
- ex) member/delete/id?=1 (X)
- 가독성을 높일때는 '_' 보다는 '-' 사용
- 파일 확장자는 URI에 표시하지 않는다.
- URI의 마지막엔 '/'를 사용하지 않는다.
try/catch 블록은 원래 추하다. 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
-
Error와 Exception
- Error : 시스템에 비정상적인 상황이 발생했을 경우
- Exception : 에러와 달리 개발자들이 만든 코드의 작업 중에 예외상황이 발생했을 경우
따라서 애플리케이션에서는 Error에 대한 처리는 신경 쓰지 않아도 된다.
-
Exception 클래스에도 크게 체크예외와 언체크예외로 구분된다.
- 체크 예외 : RuntimeException 클래스를 상속하지 않은 예외 서브클래스들
- 언체크 예외 : RuntimeException을 상속한 서브클래스들
- ErrorResponse
-
Class
@AllArgsConstructor @Getter @Setter @ToString public class ErrorResponse { int status; String message; String code; List<FieldError> errors; public ErrorResponse(ErrorCode errorCode){ this.status = errorCode.status; this.code = errorCode.code; this.message = errorCode.message; } public ErrorResponse(ErrorCode errorCode, List<FieldError> errors){ this(errorCode); this.errors = errors; } public ErrorResponse(ErrorCode errorCode, BindingResult bindingResult){ this(errorCode, FieldError.of(bindingResult)); } }
-
JSON
{ "message": " Invalid Input Value", "status": 400, // "errors":[], 비어있을 경우 null 이 아닌 빈 배열을 응답한다. "errors": [ { "field": "name.last", "value": "", "reason": "must not be empty" }, { "field": "name.first", "value": "", "reason": "must not be empty" } ], "code": "C001" }
- message : 에러에 대한 message를 작성합니다.
- status : http status code를 작성합니다. header 정보에도 포함된 정보이니 굳이 추가하지 않아도 됩니다.
- errors : 요청 값에 대한
field
,value
,reason
작성합니다. 대부분@Valid
어노테이션이랑 같이 사용- 만약 errors에 바인딩된 결과가 없을 경우 null이 아니라 빈 배열
[]
을 응답해줍니다. null 객체는 절대 리턴하지 않습니다. null이 의미하는 것이 애매합니다.
- 만약 errors에 바인딩된 결과가 없을 경우 null이 아니라 빈 배열
- code : 에러에 할당되는 유니크한 코드값입니다.
-
ErrorCode enum 정의
@AllArgsConstructor public enum ErrorCode { INVALID_INPUT_VALUE(400,"C001","Bad Request: Invalid Input Value"), ENTITY_NOT_FOUND(400, "C002", "Bad Request: Entity Not Found"), INVALID_TYPE_VALUE(400, "C003", "Bad Request: Invalid Type Value"), HANDLE_ACCESS_DENIED(403, "C004", "Access is Denied"), NOT_FOUND(404,"C005","Not Found"), METHOD_NOT_ALLOWED(405, "C006", " Invalid Input Value"), DUPLICATED_INPUT_VALUE(409, "C007", "Conflict: Duplicated Input Value"), INTERNAL_SERVER_ERROR(500, "C008","Internal Server Error"), // Member LOGIN_INPUT_INVALID(400, "M002", "Login input is invalid"), EXPIRED_TOKEN(401, "T001", "Invalid Token: Token expired"), INVALID_TOKEN(401, "T002", "Invalid Token"); int status; String code; String message; }
-
@ControllerAdvice 는 모든 @Controller에서 발생할 수 있는 예외를 전역적으로 처리해주는 annotation
cf) Error 객체를 body로 전달하고 싶은 경우 @RestControllerAdvice를 사용
@ExceptionHandler
는 @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다. ({ }) 안에 처리할 예외 클래스를 넣은 후 매서드로 처리 내용을 구현하면 된다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
ErrorResponse response = new ErrorResponse(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult());
return ResponseEntity.badRequest().body(response);
}
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBaseException(BusinessException e) {
ErrorResponse response = new ErrorResponse(e.errorCode,e.errors);
return ResponseEntity.status(e.errorCode.status).body(response);
}
}
- Base Exception
@AllArgsConstructor
public class BusinessException extends RuntimeException {
ErrorCode errorCode;
List<FieldError> errors;
String message;
}
- Custom Exception
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(String field, String value) {
super(ErrorCode.LOGIN_INPUT_INVALID,
Collections.singletonList(new FieldError(field, value, "Not Found")),
"Login Input Invalid");
}
public UserNotFoundException() {
super(ErrorCode.LOGIN_INPUT_INVALID, Collections.emptyList(), "Login Input Invalid");
}
}
- 사용 예시
@Override
public Member signin(SigninDTO signinDTO){
Member member = userRepository.findByName(signinDTO.getName()).orElseThrow(UserNotFoundException::new);
if(!passwordEncoder.matches(signinDTO.getPassword(), member.getPassword()))
throw new WrongPasswordException();
return member;
}
- Valid annotation을 이용한 예외 입력 처리
implementation("org.springframework.boot:spring-boot-starter-validation")
- 조건을 주고 싶은 필드마다 조건 명시
@Getter
public class SignupDTO {
@NotBlank
@Size(min=3,max=20,message="3~20자 사이로 작성해야 합니다.")
String name;
@NotBlank
String phone;
@Pattern(
regexp = "^(?=.*?[A-Za-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,20}$",
message = "비밀번호는 영어, 숫자, 특수문자를 모두 포함해 8~20자 사이에서 작성해 주세요."
)
String password;
public Member toEntity(PasswordEncoder encoder){
return Member.builder()
.password(encoder.encode(password))
.phone(phone)
.name(name)
.build();
}
}
// requestbody로 받는 인자에 @Valid 어노테이션 추가
@PostMapping(value = "/signup/customer")
public ResponseEntity<String> signupCustomer(@RequestBody @Valid SignupDTO signupDTO){
authService.checkDuplicated(signupDTO);
authService.createUser(signupDTO, "ROLE_USER");
return ResponseEntity.ok("");
}
![](https://user-images.githubusercontent.com/46068444/107880924-25961d00-6f25-11eb-856e-aafa7be9725b.png)