- 회원가입을 진행하면, 변수의 값을 검증하는 상황이 많이 발생한다.
- 이렇게 의도한 변수와 다른 형태의 변수가 들어오지 않도록 검증을 해야한다.
- 하지만 각각의 변수마다 검증메서드를 통해서 검증하는것은 굉장히 번거로운 작업이다.
- 어노테이션 형태로 제약 조건을 달아줘서 쉽게 검증할 수 있도록 돕는 API이다.
Bean Validation
은 인터페이스로 된 명세일 뿐이고 실제 동작할 수 있도록 구현한 것이 Hibernate Validator이다.implementation 'org.springframework.boot:spring-boot-starter-validation'
package com.sparta.springauth.dto; import jakarta.validation.constraints.*; import lombok.Getter; @Getter public class ProductRequestDto { @NotBlank private String name; @Email private String email; @Positive(message = "양수만 가능합니다.") private int price; @Negative(message = "음수만 가능합니다.") private int discount; @Size(min=2, max=10) private String link; @Max(10) private int max; @Min(2) private int min; }
- 이렇게 어노테이션 형태 편리하게 제약조건을 달아주면 된다.
@PostMapping("/validation") @ResponseBody public ProductRequestDto testValid(@RequestBody @Valid ProductRequestDto requestDto) { return requestDto; }
- 그리고
Bean Validation
을 적용한 해당 Object에@Valid
를 통해 validation을 실행한다.
- Validation 과정에서 예외가 발생하면
BindingResult
객체에 오류에 대한 정보가 담긴다.bindingResult.getFieldErrors()
를 통해
발생한 오류들에 대한 정보가 담긴List<FieldError>
리스트를 가져올 수 있다.
- 지금까지는 Client와 Server간의 요청을 처리하는 개발을 진행해왔다.
- 하지만 다른 Open API를 이용해야한다면, 그때는 우리의 서버가 Client의 입장이 되어 요청을 해야한다.
- Spring에서는 서버에서 다른 서버로 간편하게 요청할 수 있도록
RestTemplate
기능을 제공한다.
- PC 한대로 프로젝트 2개를 만들어서 Client(8080포트), Server(7070포트)으로 구현을 하였다.
Client
- RestTemplate을 주입 받는다.
private final RestTemplate restTemplate; // RestTemplateBuilder의 build()를 사용하여 RestTemplate을 생성합니다. public RestTemplateService(RestTemplateBuilder builder) { this.restTemplate = builder.build(); }
- 요청 받은 검색어를 Query String 방식으로
Server
로 RestTemplate를 사용하여 요청한다.public ItemDto getCallObject(String query) { // 요청 URL 만들기 URI uri = UriComponentsBuilder .fromUriString("http://localhost:7070") .path("/api/server/get-call-obj") .queryParam("query", query) .encode() .build() .toUri(); log.info("uri = " + uri); ResponseEntity<ItemDto> responseEntity = restTemplate.getForEntity(uri, ItemDto.class); log.info("statusCode = " + responseEntity.getStatusCode()); return responseEntity.getBody(); }
- Spring의
UriComponentsBuilder
를 사용해서 URI를 손쉽게 만들 수 있다.- RestTemplate의
getForEntity
는 Get 방식으로 해당 URI의 서버에 요청을 진행한다.
- 첫 번째 파라미터에는
URI
, 두 번째 파라미터에는전달 받은 데이터와 매핑하여 인스턴스화할 클래스의 타입
을 주면됨- 요청의 결과값에 대해서 직접 JSON TO Object를 구현할 필요없이 RestTemplate을 사용하면 자동으로 처리 해준다.
- 따라서
response.getBody()
를 사용하여 두 번째 파라미터로 전달한 클래스 타입으로 자동 변환된 객체를 가져올 수 있다.
Server
- Server 입장의 서버에서 itemList를 조회하여 요청받은 검색어에 맞는 Item을 반환한다.
public Item getCallObject(String query) { for (Item item : itemList) { if(item.getTitle().equals(query)) { return item; } } return null; }
만약 결과값이 다중 JSON으로 넘어온다면?
public List<ItemDto> fromJSONtoItems(String responseEntity) { JSONObject jsonObject = new JSONObject(responseEntity); JSONArray items = jsonObject.getJSONArray("items"); List<ItemDto> itemDtoList = new ArrayList<>(); for (Object item : items) { ItemDto itemDto = new ItemDto((JSONObject) item); itemDtoList.add(itemDto); } return itemDtoList; }
JSON To Object
를 사용하지 않고 일단String
값 그대로를 가져와서
- 문자열 정보를
JSONObject
로 바꾸기
JSONObject
에서 items 배열 꺼내기
JSONArray
로 for문 돌면서 상품 하나씩JSONObject
에서ItemDto
로 변환하기
Client
- 요청 받은 검색어를
Query String
방식으로 Server로 RestTemplate를 사용하여 요청한다.public ItemDto postCall(String query) { // 요청 URL 만들기 URI uri = UriComponentsBuilder .fromUriString("http://localhost:7070") .path("/api/server/post-call/{query}") .encode() .build() .expand(query) .toUri(); log.info("uri = " + uri); User user = new User("Robbie", "1234"); ResponseEntity<ItemDto> responseEntity = restTemplate.postForEntity(uri, user, ItemDto.class); log.info("statusCode = " + responseEntity.getStatusCode()); return responseEntity.getBody(); }
UriComponentsBuilder
의expand
를 사용하여{query}
안의 값을 동적으로 처리할 수 있다.- RestTemplate의
postForEntity
는Post
방식으로 해당 URI의 서버에 요청을 진행한다.
- 첫 번째 파라미터에는
URI
, 두 번째 파라미터에는HTTP Body에 넣어줄 데이터
를 주면됨- Java 객체를 두 번째 파라미터에 넣으면 자동으로
JSON
형태로 변환됨- 세 번째 파라미터에는
전달 받은 데이터와 매핑하여 인스턴스화할 클래스의 타입
을 주면됨
Server
- Server에서 itemList를 조회하여 요청받은 검색어에 맞는 Item을 반환한다.
public Item postCall(String query, UserRequestDto userRequestDto) { System.out.println("userRequestDto.getUsername() = " + userRequestDto.getUsername()); System.out.println("userRequestDto.getPassword() = " + userRequestDto.getPassword()); return getCallObject(query); }
- 전달 받은 HTTP Body의 User 데이터를 확인한다.
Client
- RestTemplate의 exchange를 사용한다.
public List<ItemDto> exchangeCall(String token) { // 요청 URL 만들기 URI uri = UriComponentsBuilder .fromUriString("http://localhost:7070") .path("/api/server/exchange-call") .encode() .build() .toUri(); log.info("uri = " + uri); User user = new User("Robbie", "1234"); RequestEntity<User> requestEntity = RequestEntity .post(uri) .header("X-Authorization", token) .body(user); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); return fromJSONtoItems(responseEntity.getBody()); }
- exchange 메서드의 첫 번째 파라미터에
RequestEntity
객체를 만들어 전달해주면uri
,header
,body
의 정보를 한번에 전달할 수 있다.
Server
- 전달된 header와 body의 정보를 확인할 수 있다.
public ItemResponseDto exchangeCall(String token, UserRequestDto requestDto) { System.out.println("token = " + token); System.out.println("requestDto.getUsername() = " + requestDto.getUsername()); System.out.println("requestDto.getPassword() = " + requestDto.getPassword()); return getCallList(); }
이제까지는 local서버에서 꼼지락거리는 느낌의 개발이었지만, 오늘은 Naver의 Open API를 활용해보았는데 상당히 재밌었따.
메이플스토리나 로스트아크, 롤 처럼 전적검색하는 사이트를 보면서 참 신기하다고 생각했는데 이제는 작동원리를 알게되어 참 신기하다.
강의도 얼마 안남았으니 빨리 털고 코딩하고싶다 :)