Programming/Spring framework

@Value 스프링 빈 사이클에 따른 적용불가 오류 해결

최동훈1 2024. 1. 26. 01:58

이전포스트의 문제해결을 다른 관점으로 해결해 보았다.

 

해당 부분을 yml을 이용해서 @Value 애너테이션 을 이용하여서 숨길려고 시도해 보았지만 spotifyAPI.clientCredentials().build() 부분에서 로그상에 정상적으로 토큰이 존재하는데도 불구하고 옳지 않은 키 값으로 나왔다. 아무래도 스프링이로 등록될때 절차상에 제가 놓친부분이 있는거 같았다 ! 

@Slf4j
@Component
public class SpotifyConfig {
private static String CLIENT\_ID;  

private static String CLIENT\_SECRET;  


@Value("${spotify.registration.client-id}")  
public void setClientId(String clientId) {  
    CLIENT\_ID=clientId;  
}  

@Value("${spotify.registration.client-secret}")  
public void setClientSecret(String clientSecret) {  
    CLIENT\_SECRET=clientSecret;  
}  
private SpotifyApi spotifyApi = new SpotifyApi.Builder().setClientId(CLIENT\_ID).setClientSecret(CLIENT\_SECRET).build();  

private  String accessToken;  
private  Instant accessTokenExpiration;  

public String getAccessToken(){  
    if (accessToken == null || Instant.now().isAfter(accessTokenExpiration)) {  
        refreshAccessToken();  
    }  
    return accessToken;  
}  

private  void refreshAccessToken() {  
    ClientCredentialsRequest clientCredentialsRequest = spotifyApi.clientCredentials().build();  
    log.info("새로운 리프레쉬 토큰 발급을 위한 과정 시작");  
    log.info("현재 정의된 Client-id : "+CLIENT\_ID);  
    log.info("현재 정의된 Client-secret : "+CLIENT\_SECRET);  

    try {  

        final ClientCredentials clientCredentials = clientCredentialsRequest.execute();  

        log.info("0");  
        accessToken = clientCredentials.getAccessToken();  
        log.info("현재 발급된 토큰 : "+accessToken);  
        // 토큰의 유효 시간을 계산하여 저장합니다.  
        accessTokenExpiration = Instant.now().plusSeconds(clientCredentials.getExpiresIn());  
        log.info("2");  

        spotifyApi.setAccessToken(accessToken);  
        log.info("3");  

    } catch (IOException | SpotifyWebApiException | org.apache.hc.core5.http.ParseException e) {  
        System.out.println("Error: " + e.getMessage());  
        accessToken = "error";  
    }  
}

}

다시 @Value애너테이션의 변수 주입시점을 스프링 빈 등록 과정에서 어떤 곳에서 이루어지는지 파악을 해본 결과 문제의 원인은 생성자에서 SpotifyApi 객체를 초기화할 때 CLIENT_IDCLIENT_SECRET의 값이 초기에 할당되지 않아 null로 들어가게 되는 것이였다. Spring의 라이프사이클에서 @Value를 사용한 주입은 빈이 생성될 때 수행되므로, SpotifyApi를 초기화하는 생성자에서는 해당 값들이 초기화되지 않은 상태였다.

이것을 해결하기 위해서 SpotifyApi의 초기화를 위한 작업을 @PostConstruct 메서드나 InitializingBean 인터페이스를 구현한 afterPropertiesSet() 메서드 등을 사용하여 빈이 생성된 후에 수행하도록 변경하였습니다. 이렇게 하면 CLIENT_IDCLIENT_SECRET이 이미 주입된 상태에서 SpotifyApi를 초기화할 수 있었고, 테스트 결과 Client-id가 잘못되었다는 위의 오류가 더이상 나오지 않았다.

아래는 위의 두가지 해결방법중에 @PostConstruct 을 이용하여서 빈이 생성될때 @Value 주입이 수행되고 난 뒤에 Spotifyapi를 초기화 하도록 수정하였다.

import javax.annotation.PostConstruct;

@Component
@Slf4j
public class SpotifyConfig {

    @Value("${spotify.registration.client-id}")
    private String CLIENT_ID;

    @Value("${spotify.registration.client-secret}")
    private String CLIENT_SECRET;

    private SpotifyApi spotifyApi;

    private static String accessToken;
    private static Instant accessTokenExpiration;

    @PostConstruct
    public void initialize() {
        log.info("CLIENT_ID = {}", CLIENT_ID);
        log.info("CLIENT_SECRET = {}", CLIENT_SECRET);
        this.spotifyApi = new SpotifyApi.Builder().setClientId(CLIENT_ID).setClientSecret(CLIENT_SECRET).build();
        refreshAccessToken();
    }
}

이렇게 하면 SpotifyApi 객체 초기화는 @PostConstruct 어노테이션이 붙은 메서드에서 수행되며, 이 메서드는 스프링 빈이 생성된 후에 호출된다. 따라서 CLIENT_IDCLIENT_SECRET 값이 이미 주입된 상태에서 SpotifyApi를 초기화할 수 있다. 해당 코드대로 수정하였고, 정상적으로 동작되었다.

해당 과정을 논의하고 해결한 과정이 담긴 PR

https://github.com/Playlist-pack/Server/pull/66

 

♻️ [refactor] : spotify API 성능 개선 by ulsandonghun · Pull Request #66 · Playlist-pack/Server

기존의 순수 자바코드로 동작하던 AccessToken을 받는 과정이 담긴SpotifyConfig를 스프링 빈 컨테이너로 등록하여 싱글톤으로 관리되게 하였습니다. 매번 SpotifyApi의 기능을 사용할때마다 반복 호출

github.com