Interceptor 로 외부 API 요청 받기

Hyun-jin Won·2021년 8월 31일

어허 스타 이야기 아니야

Interceptor란?

In the field of software development, an interceptor pattern is a software design pattern that is used when software systems or frameworks want to offer a way to change, or augment, their usual processing cycle.
출처 : 위키피디아 Interceptor pattern

Interceptor는 어떤 특정 Frame에 국한된 기술이 아닌 디자인 패턴 중 하나이다.
소프트웨어 시스템이나 프레임워크에서 특정 프로세스 사이클을 변경하거나 추가적인 작업을 할 때 사용한다. 여러 상황이 있겠지만 이번에 내가 적용한 프로젝트는 외부의 REST API 를 사용할 때 였다.

REST API 요청에서의 공통작업

모든 사용자들에게 오픈되어 있는 API요청도 있겠지만 많은 API들이 OAuth2 인증 후 발급받는 Access token 을 Http Header에 담아 API에 접근을 하도록 요구한다. 그렇다면 일일히 API를 요청할 때 일일히 Access token를 담는 작업을 해야하는 상황이 오게 된다. (Intra 42 api document)
이러한 상황에 적용할 수 있는 Pattern이 바로 Intercepter 다.

Spring 에서는 어떻게 지원하는가?

Spring framework 에서는 해당 기능을 ClientHttpRequestInterceptor와 RestTemplate class가 이를 사용할 수 있게 만들어두었다. ClientHttpRequestInterceptor interface로 intercetper가 해야할 일을 구현하며, RestTemplate에 해당 intercepter를 추가하고 해당 객체로 REST 요청을 보냄으로서 이러한 디자인 패턴을 간편히 사용할 수 있다.

하단에는 intra42 Api에서 "GET /me" 요청을 구현한 방식을 서술하였다.
단 OAuth2 인증과 관련된 부분은 해당 주제와 부합하지 않아 제외했다. 해당 예제는 맨 하단에 첨부한 Spring.io 사이트에서 제공한 예제를 사용하여 구현하였다.

ClientHttpRequestInterceptor

첫번째로 API에 요청을 보내는 Request Http Header에 Access Token 를 넣어주어야 한다.
Spring 에서 이러한 기능을 도와주는 녀석이 ClientHttpRequestInterceptor interface 다. RestTemplate에 해당 interface를 구현함으로서 Access Token를 담아주는 역활을 담당하게 된다.

//ApiBinding class
/* token 요구됨 */
private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {
	return new ClientHttpRequestInterceptor() {
		@Override
		public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
			request.getHeaders().add("Authorization", "Bearer " + accessToken);
			return execution.execute(request, bytes);
		}
	};
}

/* token 요구 되지 않음 */
private ClientHttpRequestInterceptor getNoTokenInterceptor() {
	return new ClientHttpRequestInterceptor() {
		@Override
		public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
			throw new IllegalStateException("Can't access the Intra42 API without an access token");
		}
	};
}
    
    

해당 코드는 2가지로 존재하는데

RestTemplate

두번째로 API에 요청을 보내고 그 결과를 받는 기능이 필요하다.
Spring에서는 RestTemplate Class를 사용하여 이러한 기능을 구현할 수 있다.
해당 클래스는 REST 명령을 보내고 그 결과를 사용자가 정의한 Class Object로 반환하는 기능을 구현한 클래스이다. 또한 위에서 구현한 intercepter를 해당 RestTemplate에 추가할 수 있다.

//ApiBinding class
protected RestTemplate restTemplate;

public ApiBinding(String accessToken) {
	this.restTemplate = new RestTemplate();
	if (accessToken != null) {
		this.restTemplate.getInterceptors().add(getBearerTokenInterceptor(accessToken));
	} else {
		this.restTemplate.getInterceptors().add(getNoTokenInterceptor());
	}
}

해당 코드를 보면 restTemplate에 생성자에서 받은 accessToken를 intercepter와 함께 담는 것을 확인할 수 있다. 이후 restTemplate를 사용하여 요청을 보낼 때 interceptor를 통해 Token이 자동으로 담기게 된다.

실제 구현

public class Intra42 extends ApiBinding {
    private static final String GRAPH_API_BASE_URL = "https://api.intra.42.fr/v2";

    public Intra42(String accessToken) {
        super(accessToken);
    }

    public Profile getProfile() {
        return restTemplate.getForObject(GRAPH_API_BASE_URL + "/me", Profile.class);
    }
}

실제로 ApiBinding class를 상속받아 구현한 API 요청 Class 이다.
Intra42 api의 BASE_URL를 설정하고 restTemplate객체에 요청을 보낼 주소와 반환받은 값을 저장할 객체를 넣고 요청을 보낸다.

후기

OAuth2를 통한 로그인 기능은 상당히 많은 자료가 있으나 해당 자료는 많이 부족하며, 실제로 해본 경험이 없어 직접 구현하게 되었다. 처음에는 좀 어려웠으나 Spring.io tutorial 에 예제와 과정이 적혀있어 짧은시간에 테스트를 해볼 수 있었다. 다음부터는 책에 의존하기 보다는 이러한 공식 tutorial 문서를 애용할 것이며, 차후 조금만 해당 기능을 추가하여 42Seoul GitHub에 올릴 수 있도록 만들어 볼 예정이다.

예제 링크 : https://spring.io/blog/2018/03/06/using-spring-security-5-to-integrate-with-oauth-2-secured-services-such-as-facebook-and-github

예제 GitHub : https://github.com/Belnut/facebook-security5 (변경 예정)

profile
삽질을 주체하지 못하는 잉간

0개의 댓글