[신세계I&C KDT][Spring Boot] #45 REST API #2 (0626)

박현아·2024년 7월 1일
0

신세계아이앤씨 KDT

목록 보기
49/56

10. POST 응답처리 개선하기

1) status 명시적으로 설정

  • ResponseEntity의 메서드를 사용하여 status 값을 반환할 수 있음
  • status 값 종류
    200 : 성공
    201 : POST 나 PUT 으로 게시물 작성이나 회원 가입 등의 새로운 데이터를 서버에 생성하는(쓰는, 넣는) 작업이 성공했을 때
    400 : bad request (return ResponseEntity.badRequest().build();)
    404 : Not Found
    405 : Method Not allowed
    500 : Internal Server Error
	// 데이터 저장
		@PostMapping("persons2")
		public ResponseEntity<PersonDTO> save2(@RequestBody PersonDTO dto) {

			log.info("logger:findById:{}", dto);
			int n = personService.save(dto);
			
			//return ResponseEntity.badRequest().build(); //400
			//return ResponseEntity.notFound().build(); //404
			//return ResponseEntity.internalServerError().build(); //500
			//return ResponseEntity.noContent().build(); //204
			//return ResponseEntity.created(null).build(); //201
			//return ResponseEntity.status(201).build(); //201
			//return ResponseEntity.ok().build(); //201
			return ResponseEntity.ok(dto); //200
		}

2) status + location 알려주기

// 데이터 저장
	@PostMapping("persons2")
	public ResponseEntity<PersonDTO> save2(@RequestBody PersonDTO dto) {

		log.info("logger:findById:{}", dto);
		int n = personService.save(dto);
		
		URI location = ServletUriComponentsBuilder.fromCurrentRequest()
													.path("/{id}")
													.buildAndExpand(dto.getId())
													.toUri();
		
		return ResponseEntity.created(location).build(); //201
	}

11. HATEOAS (헤이티오스 : Hypermidia As The Engine Of Application State), REST API 성숙도 모델 4 단계

1) 개요

2) 구현

(1) 의존성

(2) EntityModel과 WebMvcLinkBuilder 이용

WebMvcLinkBuilder: 추가적인 링크 생성
EntityModel : 링크 저장소 역할

@GetMapping("/persons/{id}")
	public EntityModel<PersonDTO> findById(@PathVariable int id) {

		log.info("logger:findById:::::{}", id);
		
		PersonDTO dto = personService.findById(id);
		////////////////////
		// 저장소 만들기
		EntityModel<PersonDTO> entityModel = EntityModel.of(dto);
		
		// 링크1 - 현재 요청 링크
		WebMvcLinkBuilder link1 = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).findById(id));
		
		// 링크2 - 다음 요청 링크
		WebMvcLinkBuilder link2 = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).findById(id+1));
		
		// 링크3 - 다음 요청 링크
		WebMvcLinkBuilder link3 = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(this.getClass()).findAll());
		
		// 링크를 모델에 저장
		entityModel.add(link1.withRel("current_link"));
		entityModel.add(link2.withRel("next_link"));
		entityModel.add(link3.withRel("all_link"));
		
		return entityModel;
	}

12. 요청한 데이터가 없을 때 처리 방법 개선

1) 개요

현재는 요청한 데이터가 없어도 status 값은 200이고 Body는 no Content로 반환됨.
이 상황을 좀 더 직관적인 status로 알려주자

2) 구현

(1) 사용자 예외 클래스 생성

public class UserException extends RuntimeException {
	
    public UserException(String mesg) {
    	super(mesg);
    }
}

(2) Controller 코드

@GetMapping("/persons/{id}")
	public PersonDTO findById(@PathVariable int id) {

		log.info("logger:findById:::::{}", id);
		PersonDTO dto = personService.findById(id);
		
		if(dto == null) {
			throw new UserException("요청한 " + id +"에 해당하는 데이터가 없습니다 !!!!");
		}
		
		return dto;
	}

13. 폼 유효성 체크

1) 의존성 체크

2) DTO에 조건 지정

public class PersonDTO {

	int id;
	
	@NotEmpty(message="이름은 필수입네다")
	String username;
	
	@Past(message="과거 날짜만 가능합니다")
	LocalDate birthdate;
}

3) Controller 파라미터 @Valid 지정 (REST 에서는 BindingResult가 필요 없음)

@PostMapping("/persons")
	public PersonDTO save(@Valid @RequestBody   PersonDTO dto) {
		log.info("logger: PersonDTO-{}", dto);			
		int n = personService.save(dto);
		return dto;
	}

==> 여기까지 설정한 후 실습하면 조건에 위배되었을 경우에 400 에러가 발생이 된다

4) 발생된 예외에 대한 커스터마이징 처리

  • @ControllerAdvice + extends ResponseEntityExceptionHandler
@ControllerAdvice  // 전역 예외처리
public class CustomResponseEntityExceptionHandler 
extends ResponseEntityExceptionHandler {

	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(
			MethodArgumentNotValidException ex,
			HttpHeaders headers, 
			HttpStatus status, 
			WebRequest request) {
	
		//에러메시지 저장
		ErrorDetails details = ErrorDetails.builder()
										   .timestamp(LocalDateTime.now())
				                           .message(ex.getMessage())
				                           .path(request.getDescription(false))
				                           .build();
		
	
		return new ResponseEntity(details, HttpStatus.BAD_REQUEST);
	}
}

		@Data
		@AllArgsConstructor
		@NoArgsConstructor
		@Builder
		@Getter
		class ErrorDetails{
			
			LocalDateTime timestamp;
			String message;
			String path;
		}

14. I18N

1) 리소스 번들 파일 작성

src/main/resource
			bundle
            	message.properties (기본)
                message_en.properties (영어)
                message_ko.properties (한국어)

2) application.properties에 번들 파일 등록

#리소스 번들 파일 등록
spring.messages.basename=bundle/message
spring.messages.encoding=utf-8

spring.messages.fallback-to-system-locale=false

3) Controller에서 번들 참조

  • MessageSource API 이용

4) 요청 header 값으로 언어 선택

  • Talend Test API에서 header 설정

15. 필터링 (Filtering)

1) 기능 2 가지

  • json의 키값 변경 : @JsonProperty("변경할key")
  • 값 제외 (예민 정보나 쓰레기 정보) : @JsonIgnore
    ==> 두 개 같이 사용 불가능

2) 구현

public class PersonDTO {

	int id;
	
	@JsonProperty("user_name")
	String username;
	
	@JsonIgnore
	LocalDate birthdate;
}

16. REST 기반의 Spring Security 적용

1) 의존성 설정

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-security</artifactId>
</dependency>

17. Token

1) 개요

  • REST 기반의 어플리케이션에서 사용되는 인증 방식
  • 사용자의 id + pw + 추가 정보를 암호화한 것

2) Token 구성 요소

  • header, payload, signature 3 가지로 구성

(1) header

type : JWT
알고리즘

(2) payload

  • 사용자가 원하는 데이터 설정
  • 표준 속성이 제공
    ( sub : 주제,
    exp : 만기일,
    iat : token 생성 시간
    ..)

(3) signature

  • 시크릿 정보 (토큰이 유효한지 체크 가능)
    ==> 암호화된 토큰의 decode는 jwt.io에서 확인이 가능함

3) Token flow

request:
     {
       "userid":"inky4832",
	   "passwd":"1234",
	    추가정보:값
      }

response:
      {
        "token":토큰값
	   }

===> react에서 local storage에 저장해두고 필요시 사용됨

4) token header 설정

{
  Authorization: Bearer 토큰값
}

0개의 댓글