-
Client 모듈 구성하기프로젝트/선착순 쿠폰 발급 시스템 2023. 2. 27. 00:01728x90
Client 모듈이란?
우리가 서버를 구성하다 보면 또 다른 서버로부터 데이터를 요청받고 처리해야 할 수 있습니다.
이때 Rest 방식으로 API를 호출하기 위해서 RestTemplate, WebClient, FeignClient 등이 있습니다.
이렇게 외부 서비스를 호출하기 위해서 Client 모듈을 두고 해당 모듈에 다른 서버로부터 데이터를 요청받고 처리하는 코드를 작성하고자 합니다.
이렇게 되면 외부 호출건에 대한 수정은 Client 모듈에서 이루어지게 됩니다.
Client 모듈 구성하기
1. settings.gradle.kts 수정
//추가 include("client")
2. 프로젝트 root에 client 디렉터리 생성 및 gradle refresh
3. buidl.gradle.kts에 의존성 추가
import org.springframework.boot.gradle.tasks.bundling.BootJar val jar: Jar by tasks val bootJar: BootJar by tasks bootJar.enabled = false jar.enabled = true dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.cloud:spring-cloud-starter-openfeign") implementation("io.github.openfeign:feign-okhttp") } dependencyManagement { imports{ mavenBom("org.springframework.cloud:spring-cloud-dependencies:2021.0.1") } }
client 모듈에서는 실행을 하지 않기 때문에 boorJar.enabled = false로 설정하고 jar.enabled = true로 설정합니다.
4. application-client.yml 설정
coupon: clients: test: endpoint: http://localhost:9090 feign: okhttp: enable: true client: config: default: connectTimeout: 5000 readTimeout: 10000 loggerLevel: full httpclient: max-connections: 1000
endpoint에는 http://localhost:9090 (테스트로 띄울 서버)의 엔드포인트가 들어갑니다.
5. 환경 구성하기
com.demo.client.common 패키지를 구성합니다.
여기에 하위로 annotation, config 패키지를 구성합니다.
최종적으로는 common 패키지는 다음과 같이 연출됩니다.
6. config 패키지 하위에서 yml 설정 불러오기
@ConstructorBinding @ConfigurationProperties(prefix = "coupon.clients") class FeignClientProperties( val test: Test, ) { companion object{ data class Test( val endpoint: String, ) } }
위의 yml설정에서 endpoint를 설정했었습니다.
@ConsturctorBinding은 객체가 생성되었을 때 설정되어야 하는 값을 setting 시킬 수 있습니다.
@ConfigurationProperties은 *.properties, *.yml 파일에 있는 property를 클래스에 값을 가져와서 바인딩할 수 있도록 해주는 어노테이션입니다.
coupon.clients가 prefix로 붙어있는 test.endpoint의 값을 가져오도록 설정되었습니다.
이후 @SpringBootApplication 코드쪽에 @ConfigurationPropertiesScan을 추가해줍니다.
@ConfigurationPropertiesScan @SpringBootApplication class CouponApplication fun main(args: Array<String>) { runApplication<CouponApplication>(*args) }
7. feignClient 설정하기
@Configuration @EnableConfigurationProperties(FeignClientProperties::class) @EnableFeignClients(basePackages = ["com.demo.client"]) class FeignClientConfig: ApplicationListener<ApplicationReadyEvent> { override fun onApplicationEvent(event: ApplicationReadyEvent) { Security.setProperty("networkaddress.cache.ttl","1") Security.setProperty("networkaddress.cache.negative.ttl","3") } }
@Configuration으로 스프링빈으로 등록해줍니다.
@EnableConfigurationProperties로 FeignClientProperties에서 클래스에서 등록된 Properties값을 활용할 수 있도록 합니다.
@EnableFeingClients는 하위 클래스를 탐색하면서 @FeignClient를 찾아 구현체를 생성하는 역할을 담당합니다.
ApplicationLisnter는 스프링부트가 시작되었을 때 이벤트를 받아 다음과 같은 설정을 수행하도록 합니다.
- "networkaddress.cache.ttl" ,"1" (성공 시 1초가 지났을 때 DNS Server를 통해 다시 lookup)
- "networkaddress.cache.negative.ttl","3"(실패시 3초가 지났을 때 DNS Server를 통해 다시 lookup)
TTL 전략을 쓰지 않으면 한번 lookup한 도메인 이름을 VM이 내려갈 때까지 계속 캐싱하고 있습니다.
외부에 있는 서버의 IP가 변경되는 경우 예전 IP로 접속하는 문제를 막을 수 있습니다.
8. 해당 설정을 활용하기위한 Annotation 만들기
@Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) @Import(FeignClientConfig::class) annotation class EnableClient()
@Target은 사용자가 만든 어노테이션이 CLASS에 부착될 수 있도록 타입을 지정합니다.
@Retention은 해당 어노테이션이 RUNTIME시점까지 살아 남아 있을지를 지정합니다.
@Import는 @Configuration 어노테이션이 선언된 스프링 설정 클래스를 가져옵니다.
이로 인해 SpringBootApplication부분에 FeignClientConfig의 설정 부분들이 전부 들어가게 됩니다.
@EnableClient @ConfigurationPropertiesScan @SpringBootApplication class CouponApplication fun main(args: Array<String>) { runApplication<CouponApplication>(*args) }
9. FeignClient 개발
test 패키지에 client, config 패키지를 하위로 만들어냅니다.
최종적으로 다음과 같이 구성됩니다.
@FeignClient( name = "test", url = "\${coupon.clients.test.endpoint:sample.com}", configuration = [TestConfiguration::class] ) interface TestClient { @GetMapping("/test/{params}") fun requestTestServer(@PathVariable params: String): String }
@FeignClient로 이름은 test, url은 설정에서 지정한 endpoint, 설정은 TestConfiguration에서 주입받도록 합니다.
interface로 TestClient를 만들어내고 Controller에서 url을 호출하는 것처럼 호출할 서버에 요청할 http를 만들어냅니다.
params에 따라 1이면 정상호출 이외의 값들은 예외를 만들어내기 위해 위처럼 구성하였습니다.
위에 설정시 localhost:9090으로 endpoint가 설정되어있기 때문에 추후에 호출할 url은 다음과 같습니다.
- localhost:9090/test/1 (정상 응답)
- localhost:9090/test/2 (예외 응답)
10. TestConfiguration 설정
class TestConfiguration( private val feignClientProperties: FeignClientProperties, ) { @Bean fun connectionManagerRequestInterceptor(): RequestInterceptor{ return MessageRequestInterceptor(feignClientProperties) } class MessageRequestInterceptor( private val feignClientProperties: FeignClientProperties ) : RequestInterceptor{ override fun apply(template: RequestTemplate) { template.header("Content-Type","application/json") } } }
RequestInterceptor는 공통으로 사용되는 header를 추가하기 위해 사용할 수 있습니다.
apply 메서드를 override하였으며 Content-Type은 application/json 타입으로 받게 합니다.
11. 컨트롤러 구성
@RestController class TestController( private val testClient: TestClient ) { @GetMapping("/test/params}") fun test(@PathVariable params: String): String{ return testClient.requestTestServer(params) } }
이제 testClient를 통해 params를 넣고 호출하면 localhost:9090/test/{params}를 호출할 것입니다.
12. api-external-coupon build.gradle.kts 설정
dependencies { implementation(project(":client")) implementation("org.springframework.boot:spring-boot-starter-web") } dependencyManagement { imports{ mavenBom("org.springframework.cloud:spring-cloud-dependencies:2021.0.1") } }
api 모듈에서 client 모듈을 사용하도록 해야 합니다.
그리고 spring-cloud-dependenceis를 추가해줍니다.
이제 모든 환경 구성이 끝났고 테스트를 위한 서버를 구성하겠습니다.
테스트를 위한 외부 서버 구성
1. settings.gradle.kts 수정
//수정 "api-test-server" 부분 추가됨 include("api-external-coupon", "api-test-server")
2. api-external-coupon의 /src , build.gradle.kts를 그대로 가져옵니다.
3. package명 com.demo.test로 수정, Application이름 TestApplication으로 수정
4. application.properties 또는 application.yml에 server port 9090으로 설정
기본적으로 설정되는 port는 8080으로 다른 서버와 충돌되지 않도록 9090으로 설정하였습니다.
server.port = 9090
5. Application을 한번 실행해보고 localhost:9090으로 접속이 잘 되는 것을 확인할 수 있습니다.
6. Controller 구성
@RestController class TestController { @GetMapping("/test/{params}") fun test(@PathVariable params: String): String{ return when(params){ "1" -> "This is Test Server" else -> throw IllegalArgumentException("error") } } }
localhost:8080/test/1을 호출하면 This is Test Server가 반환됩니다.
localhost:8080/test/2를 호출하면 Exception이 발생합니다.
참고자료
https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
https://www.lesstif.com/java/java-vm-dns-caching-ttl-17105897.html
https://wildeveloperetrain.tistory.com/172
https://javacan.tistory.com/entry/springboot-configuration-properties-class
https://techblog.woowahan.com/2657/
'프로젝트 > 선착순 쿠폰 발급 시스템' 카테고리의 다른 글
kafka 모듈 구성하기 (0) 2023.04.20 Eventual Consistency란? (0) 2023.04.19 서킷 브레이커 구성하기 (0) 2023.02.28 코틀린 멀티모듈 프로젝트 구성하기 (0) 2023.02.23 선착순 쿠폰 발급 프로젝트 개요 (0) 2023.01.16