Table of Contents
Feign Client 예제
Feign Client 예제를 작성합니다.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
데이타 전송용 클래스
@Getter
@RequiredArgsConstructor
public enum ResponseCode implements Serializable {
OK("000", "OK"),
DATA_NOT_FOUND("404", "데이타가 존재하지 않습니다."),
private final String key;
private final String title;
}
@Getter
@Setter
@NoArgsConstructor
public class ApiResponseMessage implements Serializable {
private String code;
private String message;
public ApiResponseMessage(ResponseCode responseCode) {
this.code = responseCode.getKey();
this.message = responseCode.getTitle();
}
public ApiResponseMessage(ResponseCode responseCode, String message) {
this.code = responseCode.getKey();
this.message = message;
}
}
@Getter
@Setter
@NoArgsConstructor
public class ApiResponseWithData extends ApiResponseMessage implements Serializable {
private Object data;
public ApiResponseWithData(ResponseCode responseCode, Object data) {
super(responseCode);
this.data = data;
}
}
API Side
오류 상황도 ResponseEntity.ok
로 리턴해야 합니다.
@Service
@RequiredArgsConstructor
public class CompanyService {
public ResponseEntity<?> get(Long companyId) {
Optional<Company> company = repository.findById(companyId);
if (company.isPresent()) {
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, converter.toDto(company.get())));
} else {
return ResponseEntity.ok(new ApiResponseMessage(ResponseCode.DATA_NOT_FOUND));
}
}
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/account")
public class CompanyController {
@GetMapping("/{companyId}")
public ResponseEntity<?> get(@PathVariable Long companyId) {
return service.get(companyId);
}
}
Client 설정
feign:
retry:
period: 3
max-period: 5
max-attempt: 5
account-api:
url: http://localhost:8081/v1/account
dependencies {
implementation 'org.springframework.cloud:spring-cloud-openfeign-core:3.1.5'
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.5'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0'
}
@EnableFeignClients
를 설정합니다.
@EnableFeignClients
@SpringBootApplication
public class WarehouseApplication {
public static void main(String[] args) {
SpringApplication.run(WarehouseApplication.class, args);
}
}
public class FeignClientConfig implements Jackson2ObjectMapperBuilderCustomizer {
public FeignClientConfig() {
}
@Bean
public FeignFormatterRegistrar localDateFeignFormatterRegister() {
return (registry) -> {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
};
}
@Bean
public FeignErrorDecoder decoder() {
return new FeignErrorDecoder();
}
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder
.featuresToEnable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.timeZone(TimeZone.getDefault())
// .modulesToInstall(new Module[]{new JavaTimeModule()})
.locale(Locale.getDefault()).simpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
}
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()){
case 400:
break;
case 404:
if(methodKey.contains("getOrders")){
return new ResponseStatusException(HttpStatus.valueOf(response.status()),
"User's orders is empty.");
}
break;
default:
return new Exception(response.reason());
}
return null;
}
}
public class FeignRetryConfig {
@Value("${feign.retry.period}")
private long period;
@Value("${feign.retry.max-period}")
private long maxPeriod;
@Value("${feign.retry.max-attempt}")
private int maxAttempt;
public FeignRetryConfig() {
}
@Bean
public Retryer retryer() {
return new Retryer.Default(this.period, this.maxPeriod, this.maxAttempt);
}
}
Client
@FeignClient(name = "account-api", url = "${feign.account-api.url}", configuration = {FeignClientConfig.class, FeignRetryConfig.class})
public interface AccountService {
@GetMapping("/{companyId}")
ApiResponseWithData getByCompanyId(@PathVariable("companyId") Long companyId);
}
@Service
@RequiredArgsConstructor
public class ItemService {
private final ItemRepository repository;
private final AccountService accountService;
private final ItemConverter converter = Mappers.getMapper(ItemConverter.class);
@Transactional
public ResponseEntity<?> create(ItemDto dto) {
ApiResponseWithData response = accountService.getByCompanyId(dto.getCompanyId());
if (!ResponseCode.OK.getKey().equals(response.getCode())) {
return ResponseEntity.badRequest().body(new ApiResponseMessage(ResponseCode.COMPANYID_NOT_FOUND));
}
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
CompanyDto companyDto = mapper.convertValue(response.getData(), CompanyDto.class);
System.out.println(companyDto.getCompanyName());
return ResponseEntity.ok(new ApiResponseWithData(ResponseCode.OK, converter.toDto(repository.save(converter.toEntity(dto)))));
}
}