> For the complete documentation index, see [llms.txt](https://tech.x2bee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tech.x2bee.com/dev-guide/dev-start/markdown/restapi.md).

# RestAPI 통신

본 문서는 Spring의 REST API HTTP 통신에 대한 구현 및 X2BEE 환경에서의 적용 방식을 설명합니다.

***

## Spring REST API 지원의 변화

Spring에서 REST API 통신을 지원하는 방식은 버전에 따라 다음과 같이 진화했습니다:

* Spring 3.0
  * RestTemplate: REST API 호출을 위한 표준으로 사용되었으나, Spring 6.0부터는 사용 중단 예정입니다.
  * 주의: RestTemplate은 신규 프로젝트에서는 사용하지 않는 것이 권장됩니다.
* Spring 5.0
  * WebClient: 비동기 및 블로킹 통신 모두를 지원하며, WebFlux 모듈에 포함되어 있습니다.
  * X2BEE에서는 WebClient를 내부 통신 모듈로 활용 중입니다.
* Spring 6.0
  * HTTP Interface:
    * WebClient를 내부적으로 사용하여 REST API와의 통신을 인터페이스로 정의합니다.
    * FeignClient와 유사하지만, 명시적으로 WebClient와 Bean 등록이 필요합니다.
    * X2BEE에서는 이와 유사한 RestApiInterface 어노테이션을 구현하여 외부 통신에 활용하고 있습니다.

{% hint style="info" %}
참고: RestTemplate은 과거 표준이었으나, 현재는 WebClient / HTTP Interface 기반 사용이 권장됩니다.
{% endhint %}

## HTTP Interface 설정

HTTP Interface는 WebClient를 사용해 REST API의 엔드포인트를 설정하고 이를 Spring Bean으로 등록합니다.

설정 예제:

{% code title="HttpConfig.java" %}

```java
@Configuration
public class HttpConfig {
    @Bean
    RepositoryService repositoryService() {
        WebClient client = WebClient.create("https://jsonplaceholder.typicode.com");
        HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(WebClientAdapter.forClient(client)).build();
        return factory.createClient(RepositoryService.class);
    }
}
```

{% endcode %}

* WebClient.create: Base URL 설정.
* HttpServiceProxyFactory: HTTP 인터페이스 Bean 생성을 위한 팩토리 클래스

## X2BEE `RestApiInterface` (공통 커스텀 어노테이션)

X2BEE는 REST API 엔드포인트 관리의 편리성을 위해 `@RestApiInterface`라는 커스텀 어노테이션을 제공합니다.

어노테이션 정의 예제:

{% code title="RestApiInterface.java" %}

```java
/**
 * Rest Api Interface 용 커스텀 어노테이션
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RestApiInterface {
    String baseUrl() default "";
    String defaultPath() default "";
    int connectionTimeoutSeconds() default 10;
    int readTimeoutSeconds() default 60;
    int writeTimeoutSeconds() default 60;
    boolean enableTokenAuth() default true;
    boolean useMemberToken() default false;
}
```

{% endcode %}

### 주요 속성

| 속성명                        | 설명                                                     | 기본값   |
| -------------------------- | ------------------------------------------------------ | ----- |
| `baseUrl`                  | API 엔드포인트 URL (필수). 환경변수와 연동 가능: `${app.apiUrl.event}` | -     |
| `defaultPath`              | 기본 경로. `baseUrl`와 결합하여 최종 URL 형성.                      | -     |
| `connectionTimeoutSeconds` | 연결 대기 시간 설정 (초).                                       | 10    |
| `readTimeoutSeconds`       | 응답 대기 시간 설정 (초).                                       | 60    |
| `writeTimeoutSeconds`      | 요청 데이터 전송 시간 설정 (초).                                   | 60    |
| `enableTokenAuth`          | 인증 토큰을 헤더에 포함 여부 설정.                                   | true  |
| `useMemberToken`           | 사용자 인증 토큰 포함 여부 설정 (`enableTokenAuth=true`일 때 동작).     | false |

## HTTP Interface 구현

REST API Interface 예제:

{% code title="TestInterface.java" %}

```java
@RestApiInterface(baseUrl = "${app.apiUrl.common}", defaultPath = "/api/common", enableTokenAuth = false, useMemberToken = false)
public interface TestInterface {
    @Headers({"Content-Type: application/json; charset=utf-8"})
    @GET("/interface/test1")
    Call<Response> commonTest1();

    @Headers({"Content-Type: application/json; charset=utf-8"})
    @GET("/interface/test2")
    Call<Response> commonTest2();
}
```

{% endcode %}

* `@RestApiInterface`: Bean 등록 시 인터페이스를 자동 검색 및 설정.
* `@Headers`: API 호출 시 필요한 헤더 값 설정.

HTTP 요청 호출 예 (HTTPSample.java):

{% code title="HTTPSample.java" %}

```java
private final TestInterface testInterfaceService;

public ResponseEntity<Response> http1SearchSamples1() {
    ResponseEntity<Response> result1 = Http.requestEntitySync(testInterfaceService.commonTest1(), new TypeReference<Response>() {});
    // -> 동기식 ResponseEntity 반환

    Response<String> result2 = Http.requestSync(testInterfaceService.commonTest1(), new TypeReference<Response>() {});
    // -> 동기식 payload 반환

    Mono<ResponseEntity<Response>> result3 = Http.requestEntityAsync(testInterfaceService.commonTest1(), new TypeReference<Response>() {});
    // -> 비동기식 ResponseEntity 반환
    ResponseEntity<Response> result33 = result3.block();
    // -> 비동기적인 작업의 결과를 동기적으로 변환

    Mono<Response> result4 = Http.requestAsync(testInterfaceService.commonTest1(), new TypeReference<Response>() {});
    // -> 비동기식 payload 반환
    Response result44 = result4.block();
    // -> 비동기적인 작업의 결과를 동기적으로 변환

    return result1;
}
```

{% endcode %}

## RestApiUtil (X2BEE 공통 Http 통신)

X2BEE 내부 통신 모듈인 `RestApiUtil`은 REST API 호출을 간소화합니다. 해당 유틸의 경우 내부통신에서만 사용되기 때문에 반환값이 Response으로 고정되어 있습니다.

JAVA 사용 예:

{% code title="RestApiUtil 사용 예.java" %}

```java
@Value("${app.apiUrl.common}")
private String commonApiUrl;

private final RestApiUtil restApiUtil;

public Response<String> http1SearchSamples1() {
    return restApiUtil.get(
        commonApiUrl + "/interface/test1",
        null,
        new ParameterizedTypeReference<Response<String>>() {}
    );
}
```

{% endcode %}

## React 연동 예제

React에서 HTTP 요청 헬퍼를 구성하여 간편하게 REST API를 호출할 수 있습니다.

React 사용 예:

{% code title="RestApi.ts" %}

```typescript
const RestApi = (() => {
    class RestApi {
        private static instance: RestApi // Singleton 패턴 구현
        static getInstance() {
            if (RestApi.instance) {
                return this.instance
            }
            this.instance = new RestApi()
            return this.instance
        }

        // HTTP 요청 메서드
        async http(
            url: string,
            method: string,
            options?: RequestOptions
        ): Promise<ResponseEntity | null> {
            // Method 분기 처리 ...
            return this.fetch(url, method, options)
        }

        // 내부 fetch 메서드
        private async fetch(
            url: string,
            method: string,
            options?: RequestOptions,
            accessToken: string = '',
            isAuth: boolean = true
        ): Promise<ResponseEntity | null> {
            ...
            switch (method) {
                case 'GET':
                    response = await restApiUtil
                        .get(url, options)
                        .then((response) => response)
                        .catch((errorResponse) => errorResponse)
                    break
                ...
            }
            return response
        }
    }
})()

// GET 요청을 위한 헬퍼 함수
function httpGet<T = object>(
    url: string,
    options?: RequestOptions
): Promise<T> {
    return RestApi.getInstance().http(url, 'GET', options) as Promise<T>
}

// restApi 객체 내보내기
export const restApi = {
    get: httpGet
}
```

{% endcode %}

API 호출 예:

{% code title="sampleApi.ts" %}

```typescript
import { restApi } from './RestApi'

export const fetchSampleList = async (
    params: SampleSchemaType
): Promise<SampleApiResponse> => {
    const response = (await restApi.get(
        '/api/common/interface/test1',
        { params }
    )) as GridResponse<SampleResponseDetail[]>
    return response.payload
}
```

{% endcode %}

***

##


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://tech.x2bee.com/dev-guide/dev-start/markdown/restapi.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
