RestTemplate vs WebClient
둘은 Rest 방식 API를 호출할 수 있는 클래스이다.
RestTemplate = Blocking, WebClient = Non Blokcing 방식이라고 한다. 물론 WebClient도 Blocking 방식은 지원한다.
여러 이유 중에서 특정 임계점을 넘게 되면 RestTemplate는 급격하게 속도가 저하된다. 그러므로 규모가 있는 프로젝트에서 WebClient를 쓰는게 더 좋다.
요즘은 WebClient를 쓰는 추세라고 한다.
물론 모아리움 프로젝트는 소규모라 Blocking 방식으로 트래픽을 감당할 수 있다. 그러므로 RestTemplate를 채택했다.
API 개발 시 마주한 문제들
이슈 2. CamelCase로 인한 Google API Bad Request 문제
Google API 요청은 확인했으나 access_token이 필요하다며 bad request를 보여줬다. 문제가 무엇이나 고민하던 찰나 공식 문서를 봤더니, request, response가 snake case였다. 그래서 Request, Response DTO는 JsonNaming을 활용를 Snake Case로 변경시켰다.
1
2
3
4
5
6
7
8
9
10
11
12
| @AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) // camel case -> snake case
public class GoogleOAuthLoginReqDto{
private String clientId;
private String redirectUri;
private String clientSecret;
private String grantType;
private String code;
}
|
이슈 2. [[Spring - @Value]] getter가 null이 되는 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| @Component
@Data
public class GoogleOAuthLoginApiClient implements OAuthLoginApiClient<GoogleOAuthProfile>{
@Value("${google.auth.url}")
private String googleAuthUrl;
@Value("${google.login.url}")
private String googleLoginUrl;
@Value("${google.auth.scope}")
private String scopes;
private RestTemplate restTemplate = new RestTemplateBuilder().errorHandler(new RestTemplateResponseErrorHandler()).build();
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public String getTokenURI() {
System.out.println("Auth: " +getGoogleAuthUrl());
return UriComponentsBuilder.fromHttpUrl(getGoogleAuthUrl()+"/token").toUriString();
}
|
getGoogleAuthUrl()
가 계속 null을 출력해서 해결책을 모색하고자 했다.
- Properties가 잘 적용되었는가? -> Yes
- @Value를 쓰는 클래스에 Bean을 잘 등록했는가? -> Yes
- GoogleOAuthLoginApiClient의 의존성 주입을 잘 수행했는가? -> No
결국 3번의 문제였다. 바로 의존성 주입하지 않고 new 생성자를 만든 것이었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| @Service
public class OAuthLoginService {
public GoogleOAuthProfile requestGoogleOAuthLogin(String authCode){
OAuthLoginApiClient<GoogleOAuthProfile> client = new GoogleOAuthLoginApiClient();
try {
String accessToken = client.requestOAuthClientAccessToken(authCode);
return client.requestOauthInfo(accessToken);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
|
Spring에서 싱글톤으로 관리되는 빈이 아닌 새로운 객체로 생성하게 되면 @Value 애노테이션으로 설정 파일을 읽어들이는 변수는 null로 값이 저장되지 않습니다.
출처 - gyucheolk 티스토리, [Spring] @Value 애노테이션 null 점검
그래서 새로운 객체가 아닌 의존성 주입으로 코드를 수정했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| @Service
public class OAuthLoginService {
private final GoogleOAuthLoginApiClient authLoginApiClient;
public OAuthLoginService(GoogleOAuthLoginApiClient authLoginApiClient) {
this.authLoginApiClient = authLoginApiClient;
}
public GoogleOAuthProfile requestGoogleOAuthLogin(String authCode){
OAuthLoginApiClient<GoogleOAuthProfile> client = authLoginApiClient;
try {
String accessToken = client.requestOAuthClientAccessToken(authCode);
return client.requestOauthInfo(accessToken);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
|
팀원에게 조언을 얻었는데, 서비스 컨트롤러가 아닌 것을 주입할때 명시하는 것이 좋다고 한다.