Feign Client 예제

By | 2023년 1월 8일
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)))));
    }
}

답글 남기기