Feign μ‚¬μš© (1)

· β˜• 2 min read · πŸ‘€... views

이번 API 톡신을 ν•˜λ©΄μ„œ Feign 을 μ‚¬μš©ν•΄λ΄€λŠ”λ°, μ–΄λ–»κ²Œ μ‚¬μš©ν–ˆλŠ”μ§€ μ μ–΄λ³΄κ³ μž 함.

Feign μ΄λž€?

이라고 κ°„λ‹¨ν•˜κ²Œλ§Œ,


API 톡신을 μ΄μš©ν•΄μ„œ κ°œλ°œν•  ν•„μš”κ°€ μƒκ²ΌλŠ”λ°, API λ₯Ό ν†΅ν•˜μ—¬ κ°œλ°œν•΄ λ³Έ 적이 없기에

κ·Έλž˜μ„œ λ¬΄μž‘μ • ꡬ글링 μ‹œμž‘
-> httpClient λ₯Ό μ΄μš©ν•΄μ„œ ν†΅μ‹ ν•˜λŠ” 것을 μ•Œκ²Œ 됨 -> κ΄€λ ¨ μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ 뢄석 μ‹œμž‘

-> Spring μ—μ„œλŠ” restTemplate λ₯Ό μ΄μš©ν•œλ‹€λŠ” μ–˜κΈ°λ₯Ό λ“€μŒ -> restTemplate ꡬ글링 및 ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„±

-> Feign μ΄λΌλŠ” 것을 λ“€μŒ -> Feign μ μš©ν•΄λ³΄μž

λΌλŠ” νλ¦„μœΌλ‘œ 진행. (무렀 ν•˜λ£¨λ§Œμ— μ§„ν–‰λœ 흐름..)
ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄μ„œ wireMock, MockMvc, Mockito 등도 μ‚¬μš©ν–ˆλŠ”λ°, μ΄λŠ” 좔후에 λ”°λ‘œ..

1. μ˜μ‘΄μ„± μΆ”κ°€

  • maven 을 μ‚¬μš©ν•˜λ―€λ‘œ pom.xml 에 μ˜μ‘΄μ„± μΆ”κ°€
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    <dependency>
          <groupId>io.github.openfeign</groupId>
          <artifactId>feign-jackson</artifactId>
          <version>10.2.3</version>
    </dependency>
    <dependency>
          <groupId>io.github.openfeign</groupId>
          <artifactId>feign-okhttp</artifactId>
          <version>10.2.3</version>
    </dependency>
    <dependency>
          <groupId>io.github.openfeign</groupId>
          <artifactId>feign-slf4j</artifactId>
          <version>10.2.3</version>
    </dependency>

2. Feign μΈν„°νŽ˜μ΄μŠ€ μž‘μ„±

FeignClient Annotation이 μžˆμœΌλ‚˜ κΉƒ readme 에 μžˆλŠ” basic 방법을 μ‚¬μš©.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public interface AAAClient {

    @RequestLine("GET /list")
    ApiResponse<Data> getList(@QueryMap SearchRequest SearchRequest);
    
    @RequestLine("GET /{boardId}")
    ApiResponse<BoardDetail> getDetail(@Param(value = "boardId") long boardId);
    
    @RequestLine("PUT /{boardId}")
    @Headers("Content-Type: application/json")
    ApiResponse<String> putBoard(@Param("boardId") long boardId, BoardDetail boardDetail);
}
  • Annotation μ‚¬μš©μ— λŒ€ν•΄
    1. @RequestLine - requestMapping κ³Ό 같은 κ°œλ…, url μ•žμ— λΆ™μ—¬μ£ΌλŠ” GET, POST, PUT, DELETE 으둜 Rest 적용
    2. @Param - λ„˜κΈΈ νŒŒλΌλ―Έν„°
    3. @QueryMap - Paramκ³Ό κ°™μœΌλ‚˜, param 의 νŒŒλΌλ―Έν„°κ°€ 3개 이상이 μ•ˆλ˜λ―€λ‘œ 3κ°œμ΄μƒμ˜ 경우 QueryMap μ‚¬μš©
      • ex) param -> list?start=1 , QueryMap -> list?start=1&end=2&limit=3
    4. @Headers - 해더λ₯Ό μΆ”κ°€ν•΄μ€Œ, μ‘λ‹΅μΈ‘μ—μ„œ requestBody λ₯Ό μ‚¬μš©ν•  경우 Content-Type: application/json κ³Ό 같이 μΆ”κ°€ν•΄μ€˜μ•Ό 함.
    5. 이외에도 @Body 와 @HeaderMap 이 있음.

3. ClientConfig μž‘μ„±

2μ—μ„œ λ§Œλ“  μΈν„°νŽ˜μ΄μŠ€μ— λŒ€ν•΄ config μž‘μ„±

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Configuration
public class AAAClientConfig {
    @Value("${url}")
    private String url;
    
    @Bean
    public AAAClient aAAClient() {
        return Feign.builder()
                .retryer(new Retryer.Default())
                .client(new OkHttpClient())
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .logger(new Slf4jLogger(AAAClient.class))
                .target(AAAClient.class, url);
    }
}
  • λΉŒλ” νŒ¨ν„΄μ„ μ‚¬μš©ν•œ 것을 μ•Œ 수 있음.
  • μ΅μˆ™ν•œ(?) httpClient κ°€ λ³΄μž„.

4. Service 생성

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
public class BoardService {

    private final AAAClient aAAClient;

    public static final Logger LOGGER = LoggerFactory.getLogger(BoardService.class);

    @Autowired
    public BoardService(AAAClient aAAClient) {
        this.aAAClient = aAAClient;
    }
    
    public ApiResponse<Data> getList(SurveySearchRequest vo) {
        ApiResponse<Data> response;
        try {
            response = aAAClient.getList(vo);
        } catch (FeignException ex) {
            LOGGER.error("FeignException {}", ex);
        }
        return response;
    }
    // μƒλž΅
}

5. Test μ½”λ“œ 생성

  1. API 와 직접 톡신할 ν•„μš”λŠ” μ—†μŒ
  2. ν•΄λ‹Ή μ„œλΉ„μŠ€ 둜직이 μ •μƒμ μœΌλ‘œ μž‘λ™ν•˜λŠ”μ§€ μ—¬λΆ€λ§Œ 확인
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ServiceTest {

    @Rule
    public WireMockRule wireMockRule = new WireMockRule();    
    @Rule
    public MockitoRule rule = MockitoJUnit.rule();
    private BoardServiceService service;
    
    @Before
    public void setUp() {
        AAAClient aAAClient = Feign.builder()
                .client(new OkHttpClient())
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .logger(new Slf4jLogger(SurveyClient.class))
                .logLevel(Logger.Level.FULL)
                .target(AAAClient.class, "http://localhost:8080");
        service = new AAASurveyService(aAAClient);
    }
    
    @Test
    public void λͺ©λ‘_test() {
        SearchRequest req = new SearchRequest();
        req.setStart(0);
        req.setLimit(10);
        StubMapping accept = stubFor(
                get(urlPathMatching("/list"))
                        .withQueryParam("start", equalTo(req.getStart().toString()))
                        .withQueryParam("limit", equalTo(req.getLimit().toString()))
                        .willReturn(aResponse()
                                .withStatus(200)
                                .withHeader("content-type", "application/json")
                                .withBody("{}")
                        )
        );
        ApiResponse<Data> ret = service.getList(req);
        assertEquals("200", ret.getCode());
        assertEquals("OK", ret.getMessage());
    }
    // μƒλž΅
}

Feign μ„€μ • 및 μ μš©μ€ μ΄λ ‡κ²Œ ν•΄μ„œ 끝.
μΆ”ν›„ 컨트둀러λ₯Ό 톡해 μ‹€μ œ API 와 μ—°κ²°λ˜λŠ”μ§€ ν…ŒμŠ€νŠΈκ°€ ν•„μš”. (MockMvc μ‚¬μš©)

Share on

snack
WRITTEN BY
snack
Web Programmer


What's on this Page