** ์ด๋ฒ ๊ธ์์๋ ํ ์คํธ์์ WireMock์ ํ์ฉํ์ฌ ์ธ๋ถ ์๋ฒ๋ฅผ ๋ชจํนํ๋ ๊ณผ์ ์ ์ค๋ช ํ๋ฉฐ, ๋ค์ ๊ธ์์๋ INFO ๋ ๋ฒจ ๋ก๊ทธ ์ค์ ๋ฐฉ๋ฒ์ ๋ค๋ฃฐ ์์ ์ ๋๋ค. **
Clova AI์ ๋ฐ๋ณต์ ์ธ ์๋ต ์ค๋ฅ ๋ฌธ์
๊ณ ๋ฏผ ์๋ด ํ๋ซํผ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ๋ค์ด๋ฒ Clova AI์ ์์ฒญ์ ๋ณด๋ด๋ ์์ ์ ์ํํ์ต๋๋ค. ํ์ง๋ง Clova AI๋ก๋ถํฐ ์ค๋ ์๋ต์ด ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ๋ง์ง ์์ ๋น๋ฒํ๊ฒ ์ค๋ฅ๋ฅผ ๋ฐ์์์ผฐ์ต๋๋ค. ์ด์ ๋ฐ๋ผ, Clova AI ๊ฐ ์ด๋ค ์๋ต์ ์ฃผ๊ณ , ์ด๋ค ์ค๋ฅ๋ฅผ ์ ๋ฐํ๋์ง ์ ํํ ํ์ธํ ํ์๊ฐ ์์์ต๋๋ค.
์ด ๊ณผ์ ์์ OpenFeign์ ๋ก๊น ๊ธฐ๋ฅ์ ํ์ฉํ๋ ค ํ์ง๋ง, ๊ธฐ์กด ๋ฐฉ์์ผ๋ก๋ ๋ช ๊ฐ์ง ๋ถํธํจ์ด ์์์ต๋๋ค.
logger-level : FULL ์ ์ฌ์ฉํ์ง ์๋ ์ด์
1. ์ค์ ์ด์ ์๋ฒ์์ Feign ๊ด๋ จ ๋ก๊ทธ๋ฅผ ํ์ธํ๋ ค๋ฉด docker logs๋ฅผ ์ฌ์ฉํด ๋ก๊ทธ๋ฅผ ์๋์ผ๋ก ๊ฒ์ํด์ผ ํ์ต๋๋ค.
2. OpenFeign ๋ก๊ทธ ์ถ๋ ฅ์ ์ํด ์๋ฒ์ ๋ก๊ทธ ๋ ๋ฒจ์ DEBUG๋ก ์ค์ ํด์ผ ํ์ต๋๋ค.
3. ์๋ฒ์์ logback-spring.xml์ ์ฌ์ฉํด INFO ๋ ๋ฒจ๊ณผ ERROR ๋ ๋ฒจ ๋ก๊ทธ๋ง ๋ณ๋ ํด๋๋ก ๊ด๋ฆฌํ๊ณ ์์ด, DEBUG ๋ก๊ทธ๋ฅผ ํ์ฑํํ๋ ๊ฒ์ด ๋ถ์ ํฉํ์ต๋๋ค.
INFO ๋ ๋ฒจ ๋ก๊ทธ ์ถ๋ ฅ์ ์ํ Feign ์ปค์คํฐ๋ง์ด์ง ๊ณํ
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด OpenFeign ๋ก๊ทธ๋ฅผ INFO ๋ ๋ฒจ์์๋ ์ถ๋ ฅํ ์ ์๋๋ก ์ปค์คํฐ๋ง์ด์ง์ ๊ณํํ์ต๋๋ค. ๊ทธ๋ฌ๋, ์ค์ Clova AI ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ฉฐ ์์ ํ ๊ฒฝ์ฐ ๋น์ฉ์ด ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ํ ์คํธ ํ๊ฒฝ์์ Clova AI ์๋ฒ๋ฅผ ๋ชจํนํ๊ณ , @FeignClient๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ์งํํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
1. WireMock์ ํ์ฉํด Clova AI ์๋ฒ๋ฅผ ๋ชจํน
2. INFO ๋ ๋ฒจ ๋ก๊ทธ ์ค์ ํ, WireMock์ ํธ์ถํ์ฌ @FeignClient ํธ์ถ ์ ๋ก๊ทธ๊ฐ INFO ๋ ๋ฒจ์์๋ ์ถ๋ ฅ๋๋์ง ํ์ธ
WireMock vs MockRestServiceServer
OpenFeign์ ์ฌ์ฉํ์ฌ ์ธ๋ถ ์๋ฒ๋ฅผ ๋ชจํน(Mock)ํ๋ ค๋ฉด ์์ฃผ ๋ฑ์ฅํ๋ ๋๊ตฌ ์ค ํ๋๊ฐ WireMock์ ๋๋ค.
ํํธ, ๊ฐ์ธ ํ๋ก์ ํธ์์๋ RestTemplate์ ์ฌ์ฉํ์ฌ ์ธ๋ถ ์๋ฒ๋ฅผ ํธ์ถํ ๊ฒฝํ์ด ์์๊ณ , ์ด๋ฅผ ํ ์คํธํ ๋ MockRestServiceServer๋ฅผ ์ฌ์ฉํ์ต๋๋ค. ์ด ๊ฒฝํ์์ ์ถ๋ฐํด WireMock๊ณผ MockRestServiceServer์ ์ฐจ์ด๋ฅผ ์์๋ณด๊ณ ์ ํฉ๋๋ค.
- MockRestServiceServer
MockRestServiceServer์ Javadoc์ ๋ฐ๋ฅด๋ฉด, ์ด ๋๊ตฌ๋ RestTemplate์ ํตํด ์ํ๋ ์์ ์์ฒญ๊ณผ ์๋ต์ ์ค์ ํ๋ฉฐ, ์ค์ ์๋ฒ ์์ด๋ ํ ์คํธ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ์ฆ, HTTP ์๋ฒ๋ฅผ ์คํํ์ง ์๊ณ ๋ RestTemplate ์์ฒญ์ ๋ชจ์(Mock)๋ก ์ค์ ํ๊ณ , ์ด์ ๋ํ ์๋ต์ ์ ์ํ ์ ์๋ ๋๊ตฌ์ ๋๋ค.
- WireMock
๋ฐ๋ฉด, WireMock์ ์ค์ ์น ์๋น์ค์ ์ฐ๊ฒฐํ๋ ๊ฒ์ฒ๋ผ ๋์ํ๋ HTTP ์๋ฒ๋ฅผ ๊ตฌ์ฑํฉ๋๋ค. WireMock์ ํ ์คํธ ์ ๋ณ๋์ ํฌํธ๋ฅผ ์ง์ ํด ์๋ฒ๋ฅผ ์คํํ ์ ์์ผ๋ฉฐ, ํฌํธ๋ฅผ ์ง์ ํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ธ 8080 ํฌํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. (๋ง์ฝ, ์ง์ ํ์ง ์๋๋ค๋ฉด 8080ํฌํธ๋ฅผ ์ฌ์ฉํ๋, ๋ถํธ์ ๊ธฐ๋ณธ ํฌํธ์ ์ถฉ๋๋์ง ์๋๋ก ๋ฐ๊พธ๋ ๊ฒ์ด ์ข์๋ณด์ ๋๋ค.) ๊ทธ๋์, WireMock ์ ์ฌ์ฉํ๋ค๋ฉด ์ค์ ๋คํธ์ํฌ ์์ฒญ๊ณผ ์ ์ฌํ ๋์์ ํ ์คํธํ ๋ ์ฅ์ ์ด ์์ ๊ฒ ๊ฐ์ต๋๋ค.
- ์ ๋ฆฌ
MockRestServiceServer
- RestTemplate์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ ํฉ.
- ์ค์ HTTP ์๋ฒ ์์ด ์์ฒญ๊ณผ ์๋ต์ ๋ชจ์ํ์ฌ ๋จ์ ํ ์คํธ๋ฅผ ์ํ.
WireMock
- OpenFeign ๋ฐ ์ค์ HTTP ์๋ฒ์์ ์ํธ์์ฉ์ ํ ์คํธํ๋ ๊ฒฝ์ฐ ์ ํฉ.
- HTTP ์๋ฒ๋ฅผ ์คํํ์ฌ ์ค์ ๋คํธ์ํฌ ์์ฒญ๊ณผ ์ ์ฌํ ํตํฉ ํ ์คํธ๋ฅผ ์ํ.
๋ณธ๋ก : ํ ์คํธ์์ Clova AI(์ธ๋ถ) ์๋ฒ ๋ชจํน ๊ณผ์
๋ณธ๋ก ์ผ๋ก ๋์์, WireMock ์ ํ ์คํธ์ ์ฌ์ฉํ ์ ์๋๋ก @OpenFeign ๊ณผ Test Code ๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
- ์์กด์ฑ ์ถ๊ฐ
Spring ์์ ์ ๊ณตํ๋ openfeign ๊ณผ wiremock ์ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
WireMock์ ๋ฒ์ ๊ด๋ฆฌ๋ Spring Boot์ dependency-management์๊ฒ ๋งก๊ธฐ๋๋ก ํ๊ฒ ์ต๋๋ค.
testImplementation 'org.springframework.cloud:spring-cloud-contract-wiremock'
- OpenFeign ํด๋ผ์ด์ธํธ ์ค๋น
@FeignClient๋ฅผ ์ค์ ํ์ฌ ์ธ๋ถ ์๋ฒ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. URL์ ์ธ๋ถ ์ค์ ํ์ผ(.yml)์์ ์ฝ์ด์ค๋๋ก ๊ตฌ์ฑํฉ๋๋ค.
ํ
์คํธ ํ๊ฒฝ์์๋ ์ค์ Clova AI ์๋ฒ๋ก ์์ฒญ์ ๋ณด๋ด์ง ์๋๋ก, ํ
์คํธ ์์ ์ URL์ ์ฃผ์
ํ ์ ์๊ฒ ์ค์ ํ์ต๋๋ค.
@FeignClient(name = "clova-service", url = "${feign.clova.url}")
public interface ClovaFeignClient {
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
ClovaResponseDto sendToClova(
@RequestHeader("X-NCP-CLOVASTUDIO-API-KEY") String apiKey,
@RequestHeader("X-NCP-APIGW-API-KEY") String apigwKey,
@RequestHeader("X-NCP-CLOVASTUDIO-REQUEST-ID") String requestId,
@RequestBody ClovaRequestDto clovaRequestDto
);
}
- ํ ์คํธ ์ฝ๋ ์์ฑ
@ActiveProfiles("test")
@SpringBootTest // ํฐ์บฃ๊ณผ ๊ฐ์ HTTP ์๋ฒ๋ฅผ ๋์ธ ํ์๊ฐ ์๋ค๊ณ ํ๋จํด์, Mock ์์ฑ์ ์ฌ์ฉํ์ต๋๋ค.
@TestPropertySource(properties = "feign.clova.url=http://localhost:${wiremock.server.port}")
@AutoConfigureWireMock(port = 0)
class ClovaFeignClientTest {
@Autowired
private ClovaService clovaService;
@DisplayName("WireMock์ ์ฌ์ฉํ์ฌ @FeignClient ํธ์ถ ์ logger-Level : full์ ํ์ธํฉ๋๋ค.")
@Test
void test() {
// given, #import static com.github.tomakehurst.wiremock.client.WireMock.*;
stubFor(post("/")
.willReturn(aResponse()
.withStatus(200)));
// when
ClovaResponseDto result = clovaService.send("message");
// then
assertThat(result).isNull();
}
}
@ActiveProfiles("test")
- ํ ์คํธ ํ๊ฒฝ์ ๋ง๋ test.yml ์ค์ ํ์ผ์ ์ฌ์ฉํฉ๋๋ค.
@TestPropertySource
- @FeignClient์ URL ํ๊ฒฝ ๋ณ์๋ฅผ http://localhost:${wiremock.server.port}๋ก ์ฃผ์ ํ๊ธฐ ์ํ ์ค์ ์ ๋๋ค. ์ฌ๊ธฐ์ ${wiremock.server.port}๋ ๋ณ๋๋ก .yml ์ ์์ฑํ์ง ์์๋, WireMock์ด ์คํ๋ ๋ ๋ชจํน๋ ์๋ฒ์ ํฌํธ๊ฐ ์๋์ผ๋ก "wiremock.server.port" ์์ฑ์ ๋ฐ์ธ๋ฉ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ํ ์คํธ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ์์ ํด๋น ์์ฑ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
@AutoConfigureWireMock(port=0)
- WireMock ์๋ฒ๋ฅผ ๋๋ค ํฌํธ์์ ์คํํฉ๋๋ค.
- Spring Application Context์ ์ผ๋ถ๋ก WireMock ์๋ฒ๋ฅผ ์์ํ๋ ค๋ ํ ์คํธ ํด๋์ค์ ๋ํ ์ด๋ ธํ ์ด์ ์ ๋๋ค.
ํด๋น ์ด๋ ธํ ์ด์ ๋ค์ ์ ์ฌ์ฉํ๋์ง๋ Spring Cloud Contract WireMock Docs๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
ํด๋น ํ ์คํธ๋ฅผ ์คํํ์๊ณ , OpenFeign์ logger-level: FULL ์ค์ ์ผ๋ก ์ธํด DEBUG ๋ ๋ฒจ์ ์์ธํ ๋ก๊ทธ๊ฐ ์ถ๋ ฅ๋จ์ ํ์ธํ ์ ์์์ต๋๋ค.
* feign log ๋ฅผ debug ๋ก ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ -> feign logging Docs
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] ---> POST http://localhost:12410 HTTP/1.1
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] Accept: application/json
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] Content-Length: 5571
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] Content-Type: application/json
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova]
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] 5571์ ํด๋นํ๋ body
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] ---> END HTTP (5571-byte body)
...(์ค๋ต)
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] <--- HTTP/1.1 200 OK (196ms)
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] matched-stub-id: f1b4d2e2-83eb-4138-9554-4fa7934d42d8
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] transfer-encoding: chunked
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] vary: Accept-Encoding, User-Agent
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova]
2024-12-21 DEBUG 16948 --- [ClovaFeignClient#sendToClova] <--- END HTTP (0-byte body)
์ ๋ฆฌ
์ด๋ฒ ๊ธ์์๋ ํ ์คํธ ํ๊ฒฝ์์ WireMock์ ์ฌ์ฉํ์ฌ Clova AI(์ธ๋ถ) ์๋ฒ๋ฅผ ๋ชจํนํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ค์ต๋๋ค. ์ด๋ฅผ ํตํด Feign ํด๋ผ์ด์ธํธ๋ฅผ ์์ ํ๊ฒ ํ ์คํธํ๊ณ , logger-level: FULL ์ค์ ์ผ๋ก DEBUG ๋ก๊ทธ๋ฅผ ํ์ธํ ์ ์์์ต๋๋ค.
๋ค์ ๊ธ์์๋ INFO ๋ ๋ฒจ์์๋ Feign ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๋๋ก ์ปค์คํฐ๋ง์ด์งํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํ๊ฒ ์ต๋๋ค.
์ถ์ฒ
- Spring Cloud Contract WireMock Docs
- Baeldung ์ Introduction WireMock
- ์ฐ์ํ๊ธฐ์ ๋ธ๋ก๊ทธ : ์ผ, ๋๋ WireMock ์ผ๋ก ํ ์คํธํ ์ ์์ด
- https://devnm.tistory.com/34
์ถ๊ฐ๋ก, WireMock์ ํ์ฉํ๋ฉฐ ํ ์คํธ๋ฅผ ์์ฑํ๋ ๊ณผ์ ์์ ๋ค์ํ ํ ์คํธ ์ด๋ ธํ ์ด์ ์ ์ ํ๊ฒ ๋์๊ณ , ์ด๋ฅผ ์ ๋ฆฌํ์ฌ ๊ณต์ ํ๊ณ ์ ํฉ๋๋ค.
@TestInstance
@TestInstance๋ JUnit 5์์ ํ
์คํธ ํด๋์ค์ ์ธ์คํด์ค ๋ผ์ดํ์ฌ์ดํด์ ์ค์ ํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
๋ค์ ๋ ๊ฐ์ง ์ต์
์ด ์์ต๋๋ค.
PER_METHOD (DEFAULT)
- ํ ์คํธ ๋ฉ์๋๋ง๋ค ์๋ก์ด ํด๋์ค ์ธ์คํด์ค๋ฅผ ์์ฑ
- ๊ฐ ํ ์คํธ๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ
- ๋จ์ ํ ์คํธ์ ์ ํฉ
PER_CLASS
- ํ ์คํธ ํด๋์ค๋น ํ๋์ ์ธ์คํด์ค๋ง ์์ฑ
- ํ ์คํธ ๋ฉ์๋๊ฐ ํด๋์ค์ ์ํ๋ฅผ ๊ณต์ ํด์ผ ํ ๋.
- ํ ์คํธ๊ฐ ์์ฐจ์ ์ผ๋ก ์คํ๋๋ฉฐ ์ํ๋ฅผ ์ ๋ฐ์ดํธ
- @BeforeAll๊ณผ @AfterAll์ ์ฌ์ฉํ ๋ static์ ๋ถ์ด์ง ์์๋ ๋ฉ๋๋ค.
์ฐธ๊ณ : @TestInstance Annotation in JUnit 5
@SpringBootTest
WebEnvironment.MOCK (DEFAULT)
- ๋ชจ์(Mock) ์๋ธ๋ฆฟ ํ๊ฒฝ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ๋ก๋ํฉ๋๋ค.
- ์ค์ HTTP ์๋ฒ๋ฅผ ์คํํ์ง ์๊ณ , ์์ฒญ๊ณผ ์๋ต์ MockMvc์ ๊ฐ์ ๋๊ตฌ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
- ์ปจํธ๋กค๋ฌ ๋จ์ ํ ์คํธ ๋๋ ํน์ ์น ๊ณ์ธต ๋์๋ง ํ ์คํธํ ๋ ์ ํฉ.
WebEnvironment.RANDOM_PORT
- ์ค์ HTTP ์๋ฒ๋ฅผ ์คํํ์ง๋ง, ์ถฉ๋์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋๋ค ํฌํธ์์ ๊ตฌ๋ํฉ๋๋ค.
- Spring Boot์ ๋ด์ฅ ์๋ฒ(Tomcat, Jetty ๋ฑ)๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํฉ๋๋ค.
- TestRestTemplate์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํด HTTP ์์ฒญ ๋ฐ ์๋ต์ ํ ์คํธ.
- Mock๋ณด๋ค ์คํ ์๊ฐ์ด ๊ธธ ์ ์์.
WebEnvironment.DEFINED_PORT
- ์ค์ HTTP ์๋ฒ๋ฅผ ์คํํ๋ฉฐ, application.properties ๋๋ application.yml์ ์ ์๋ ๊ณ ์ ํฌํธ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํฉ๋๋ค.
- ์ธ๋ถ ํด๋ผ์ด์ธํธ๋ ์๋น์ค๊ฐ ํน์ ํฌํธ๋ก ํ ์คํธ ์๋ฒ์ ์ํธ์์ฉํด์ผ ํ ๋.
- ํ ์คํธ ํ๊ฒฝ์์ ํฌํธ ์ถฉ๋ ๊ฐ๋ฅ์ฑ ์กด์ฌ.
WebEnvironment.NONE
- ์น ํ๊ฒฝ์ ๋นํ์ฑํํ๊ณ , ์์ํ ApplicationContext๋ง ๋ก๋ํฉ๋๋ค.
- ๋ด์ฅ HTTP ์๋ฒ๋ฅผ ์คํํ์ง ์์ผ๋ฉฐ, ์น ๊ด๋ จ ๋น(@Controller, @RestController ๋ฑ)์ ์์ฑ๋์ง ์์ต๋๋ค.
- Spring์ ์น ๊ณ์ธต ๋น์ ํ ์คํธ ์ปจํ ์คํธ์ ํฌํจ๋์ง ์์.
์ฐธ๊ณ : Difference between webEnvironment = RANDOM_PORT and webEnvironment = MOCK