Spring Cloud Gateway 로 연결 후에 RestTemplate 를 사용하여 받아와도 되지만 하나하나 파싱해줘야 하는 번거로움을 개선 시켜줄 Feign 을 사용해 보기로했다. 원래 Spring cloud 환경을 위해 만들어진 라이브러리인만큼 한번쯤은 사용해 봐야겠다고 생각했는데 막상 써보니 엄청 편했다.
build.gradle
implementation "org.springframework.cloud:spring-cloud-starter-openfeign"
Feign Client
@FeignClient(name = "Rule", url = "http://localhost:9000") //${custom.server}
public interface StudyRuleFeign {
@RequestMapping(method = RequestMethod.GET, value = "/api/rule/v1/search") //studyrule
RsData<List<StudyRuleConsumeDto>> getStudyRule();
}
인터페이스로 Feign 을 정의하고 호출할 Controller 를 정의해보자
@GetMapping("/api/test")
public RsData<List<StudyRuleConsumeDto>> test() {
return feign.getStudyRule();
}
호출되는 rule코드
/**
* 전체 리스트 조회
* return:
* List<name, about, xp, count, provider, difficulty>
*/
@GetMapping("/v1/search")
@Operation(summary = "모든 규칙을 검색합니다.", tags = "조회")
public RsData<List<RuleDto>> searchRule() {
List<Rule> rules = ruleService.getRuleList();
List<RuleDto> collect = rules.stream()
.map(RuleDto::new)
.toList();
return new RsData<>("S-1","Rule 목록" ,collect);
}
간단한 연결 테스트를 하기위한거라 따로 데이터를 넣어주진 않았다.
Postman 을 활용하여 호출
쿼리도 잘 나가는데
om.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.baekersolved.domain.dto.response.RsData` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1909) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:408) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1354) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1420) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185) ~[jackson-databind-2.14.3.jar:2.14.3]
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) ~[jackson-databind-2.14.3.jar:2.14.3]
.
.
.
다음과 같은 에러 발생 생성자가 없다는 뜻이었고 Feign을 호출 하기 위해선 생성자가 필요하다 는 뜻 같아서 RsData 에 @NoArgsConstructor 를 추가 해줬다.
호출이 성공된 것을 확인 해봤습니다. 파싱을 여러번 해야하는 API 에 대해 RestTemplate 와 비교해 보겠습니다.
넘어오는 API 값 입니다. swagger 에선 1개의 값만 나오지만 여러개의 값이 있는 데이터 입니다.
{
"resultCode": "string",
"msg": "string",
"data": [
{
"id": 0,
"baekJoonName": "string",
"bronze": 0,
"sliver": 0,
"gold": 0,
"diamond": 0,
"ruby": 0,
"platinum": 0,
"solvedCount": 0
}
],
"success": true,
"fail": true
}
public RsData<List<MemberDto>> getMemberDtoList() throws ParseException {
RestTemplate restTemplate = new RestTemplate();
String jsonStr = restTemplate.getForObject(MEMBER_URL, String.class); // Memeber api
JSONParser jsonParser = new JSONParser();
Object object = jsonParser.parse(jsonStr);
JSONObject jsonObject = (JSONObject) object;
JSONArray jsonArray = (JSONArray) jsonObject.get("data");
for (Object o : jsonArray) {
JSONObject parseJson = (JSONObject) o;
MemberDto memberDto = new MemberDto(
(Long) parseJson.get("id"), (String) parseJson.get("baekJoonName"),
(int) parseJson.get("bronze"), (int) parseJson.get("silver"),
(int) parseJson.get("gold"), (int) parseJson.get("platinum"),
(int) parseJson.get("diamond"), (int) parseJson.get("ruby"));
memberDtoList.add(memberDto);
}
return RsData.of("S-1", "회원데이터", memberDtoList);
}
Controller
@RequestMapping(method = RequestMethod.GET, value = "/api/member/get/v1/all")
RsData<List<MemberDto>> getMember();
Service
public RsData<List<MemberDto>> getMemberDtoList() {
return feign.getMember();
}
값을 많이 받아와야하고 복잡한 파싱이 들어갈 수록 매우 유용하게 사용 할 수있다.
간단하게 호출 할 수있지만 Spring webflux 환경은 비동기 방식으로 처리된다. 따라서 이러한 호출하나하나의 동시성을 해결해줘야하고 최종적으론 DB commit 수준을 고려하든 DB Lock 을 고려하든 이러한 이슈를 하나하나 씩 해결해 나가고자 합니다.