Coding History/project

스포티파이 플레이어 임베드하기. (원하는 재생목록도 가져오기)

BlackBirdIT 2024. 9. 22. 23:00

메인 페이지? 대충 완성했으니까 이제 써야지 스포티파이 API

    <!-- Spotify 플레이어 -->
    <div style="width: 100%; text-align: center; margin-top: 20px;">
        <iframe src="https://open.spotify.com/embed/playlist/37i9dQZF1DXcBWIGoYBM5M" width="100%" height="80" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
    </div>

jsp에 해당 코드 작성해서 일단은 플레이어가 불러와지는가 체크했다.

아무 곡이나 불러와지네.

그럼 이제 이걸 Lofi 장르만 가져오도록 해야되는데 이제부터가 어떻게 해야되는가이다.

우선 방법을 대충 알아왔다.

순서는 이렇다.

OAuth2 인증 받기.
엑세스 토큰 발급.
시큐리티에서 토큰이 관리되니까 여기서 가져와야함.(OAuth2AuthorizedClientService을 사용해서 가져옴.)
여기서 가져온 토큰 값을 다시 메인 페이지로 전달.
JSP에서 템플릿에 엑세스 토큰을 js로 전달.
클라이언트 측에서 전달받은 엑세스토큰을 Spotify API에게 요청.

약간 이런 단계인데 대충 요약해서 말해보자면 로그인할 때 토큰 받아서 이걸 다시 API측에 이런 일을 처리해주세요 하고 다시 전달하고 다시 전달 받으면 된다.

그러니까 index.js로 관리하는게 아니고 서버에서 직접 다 처리하는 방식으로 간다는 뜻이다.

OAuth2인증은 로그인 하면서 다 처리 했으니까 컨트롤러로 토큰을 가져오는 것 부터 해보자.

    private final OAuth2AuthorizedClientService authorizedClientService;

    public ursHomeController(OAuth2AuthorizedClientService authorizedClientService) {
        this.authorizedClientService = authorizedClientService;
    }

    @RequestMapping("/usr/home/main")
    public String showMain(@AuthenticationPrincipal OAuth2User principal, Model model, OAuth2AuthenticationToken authentication) {
        System.err.println("===================메인페이지 접근=====================");

        // OAuth2AuthorizedClientService를 사용하여 Access Token을 가져옴
        OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient(
                authentication.getAuthorizedClientRegistrationId(), authentication.getName());

        if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
            String accessToken = authorizedClient.getAccessToken().getTokenValue();
            model.addAttribute("accessToken", accessToken);
        } else {
            System.err.println("Authorized client or access token is null.");
        }
        return "usr/home/main"; // 메인 페이지 뷰
    }

OAuth2AuthorizedClientService를 통해 현재 인증된 사용자의 Access Token을 가져옴.
가져온 Access Token을 Model에 추가하여 JSP로 전달함.
JSP에서 이 Access Token을 활용해 클라이언트 측에서 API 호출을 할 수 있음.

이런 로직?

만들고 또 이리저리 알아보니까 토큰이 1시간마다 사라진다고 해서 자동으로 갱신해주는 방법을 찾아서 구조 개선을 했다.

@Configuration
public class OAuth2ClientConfig {

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .authorizationCode()
                        .refreshToken()  // 토큰 만료 시 자동 갱신
                        .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientRepository);

        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}
@Controller
public class ursHomeController {

    @Autowired
    private OAuth2AuthorizedClientManager authorizedClientManager;


    @RequestMapping("/usr/home/main")
    public String showMain(@AuthenticationPrincipal OAuth2User principal, Model model, OAuth2AuthenticationToken authentication) {
        System.err.println("===================메인페이지 접근=====================");

        // 토큰을 관리하고 자동으로 갱신 처리
        OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(
                OAuth2AuthorizeRequest.withClientRegistrationId("spotify")
                        .principal(authentication)
                        .build());

        if (authorizedClient != null && authorizedClient.getAccessToken() != null) {
            String accessToken = authorizedClient.getAccessToken().getTokenValue();
            model.addAttribute("accessToken", accessToken);
        } else {
            System.err.println("Authorized client or access token is null.");
        }
        return "usr/home/main"; // 메인 페이지 뷰
    }
}

이제 JSP로 가보자.

    <!-- JavaScript에서 사용할 Access Token 전달 -->
    <script type="text/javascript">
        var accessToken = "${accessToken}";  // Controller에서 전달된 Access Token
        console.log("Access Token: ", accessToken);
    </script>

    <!-- API 호출을 위해 자바스크립트에서 Access Token을 활용 -->
    <script type="text/javascript">
        // 예시: Spotify API에 Access Token을 사용한 요청
        fetch('https://api.spotify.com/v1/me/playlists', {
            method: 'GET',
            headers: {
                'Authorization': 'Bearer ' + accessToken
            }
        })
            .then(response => response.json())
            .then(data => {
                console.log('Playlist Data: ', data);
                // 플레이리스트 데이터를 처리하고 페이지에 표시
            })
            .catch(error => console.error('Error fetching data:', error));
    </script>

이렇게 해주고 콘솔 확인해보면 된다.

모두 다 잘 가져왔다! 이제 플레이어를 제대로 구현만 해주면 될듯.

플레이어에서 좀 애를 먹고 있는데, 지금 코드가 좀 문제인 것 같다.

    <!-- JavaScript에서 사용할 Access Token 전달 -->
    <script type="text/javascript">
        var accessToken = "${accessToken}";  // Controller에서 전달된 Access Token
        console.log("Access Token: ", accessToken);
    </script>

    <!-- API 호출을 위해 자바스크립트에서 Access Token을 활용 -->
    <script type="text/javascript">
        // Spotify API로 Lofi 트랙 검색
        function searchLofiTracks() {
            const query = "lofi";  // 기본 검색어 'lofi' 설정
            fetch(`https://api.spotify.com/v1/search?q=${query}&type=track&limit=10`, {
                method: 'GET',
                headers: {
                    'Authorization': 'Bearer ' + accessToken
                }
            })
                .then(response => {
                    if (!response.ok) {
                        // API 요청이 실패한 경우 처리
                        throw new Error(`HTTP error! status: ${response.status}, message: ${response.statusText}`);
                    }
                    return response.json();
                })
                .then(data => {
                    console.log('Lofi Tracks: ', data);

                    // API 응답에 tracks와 items가 있는지 확인
                    if (data.tracks && data.tracks.items) {
                        const tracks = data.tracks.items;
                        let trackListHTML = '';

                        // 트랙 목록을 생성
                        tracks.forEach(track => {
                            trackListHTML += `
                    <div>
                        <p><strong>${track.name}</strong> by ${track.artists[0].name}</p>
                        <button onclick="playTrack('${track.id}')">Play</button>
                    </div>
                `;
                        });

                        // 트랙 목록을 페이지에 표시
                        document.getElementById('track-list').innerHTML = trackListHTML;
                    } else {
                        console.error('No tracks found in the API response.');
                        document.getElementById('track-list').innerHTML = "<p>No tracks found</p>";
                    }
                })
                .catch(error => {
                    console.error('Error fetching Lofi tracks:', error);
                });
        }

        // 선택한 트랙을 재생하는 함수
        function playTrack(trackId) {
            document.getElementById('spotify-player').innerHTML = `
            <iframe src="https://open.spotify.com/embed/track/${trackId}" width="100%" height="80" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
        `;
        }

        // 페이지 로드 시 기본적으로 Lofi 트랙 검색
        window.onload = function() {
            searchLofiTracks();  // 'lofi' 검색어로 트랙 검색
        };
    </script>

여기서 ${query}이게 빈 문자열로 반환된다. 이유가 뭔지를 모르겠네.

콘솔로그 찍어보니까 검색어는 확실하게 잘 되어있다.

그래서 토큰의 만료 문제인가 싶어서 내부로 더 들어가봤다.

는 문제가 없어서

가만 생각해보니까 검색값은 어차피 lofi 고정이라 그냥 고정값으로 쓰니까

가져오는거 오류없이 성공은 했는데 뭔가 제대로 된걸 가져오는 것 같지 않았다.

우선은 artists 데이터가 배열로 반환되는데, 이를 잘못 처리해서 아티스트 이름이 제대로 표시되지 않는 것 같아서 이를 처리하는 로직을 명확하게 했다. 첫 번째 아티스트만 표시하려면 artists[0]을 사용해야 하고, 여러 아티스트를 표시하려면 map이나 join을 통해 이름을 처리해야되서 그렇게 해봤다.

배열 문제를 해결하니까 값을 제대로 가져왔다. 이제 플레이어만 재생시키면 성공인데 어째선지 되지 않았다.(화면에 보이는 데이터도 제대로 안보여줌. 위 사진이랑 같은 화면.) 이 문제를 해결해야된다!!

데이터가 아예 뜨지 않는 것을 보고 뭔가 누락되는 것 같아서 플레이를 클릭할 때 제대로된 아이디 값을 가져오나 확인해보니까

안가져오는 것 같다.


코드에 트랙을 찍는 로그를 다 넣으니까 가져오는 것 같다. 재생 버튼 클릭할 때 전달 하는 값이 잘못된건가?

그래서 아이디 까지 찍어봤는데

음.. 잘 가져오는데?

그래서 여길 까봤는데 여기서 변수 저장을 제대로 못하고 있는 것 같았다.

그래서 코드를 다시 자세히 봤따

여기서 변수를 변수로 인식을 못하는 것 같았다.

JavaScript 내에서 템플릿 리터럴을 사용할 때 IDE가 템플릿 리터럴 안의 변수를 JSP의 서버 사이드 변수로 인식하고 있는 것으로 보입니다. 이 경우 JavaScript의 변수를 JSP 내부의 서버 사이드 변수가 아닌 클라이언트 사이드에서 처리되는 변수로 이해해야 합니다.

라는 GPT 답변.

그래서 일단 콘솔에 다시 다 찍어봤는데 다 잘뜬다.

그런데 여전히 div 안에는 값이 없다.

템플릿 리터럴이라는 기능을 사용하고 싶은데 이 기능을 사용하면서 생긴 이슈다.

// 템플릿 리터럴을 쓰지 않고 문자열을 추가하는 방식으로 변경
                trackListHTML += '<div>';
                trackListHTML += '<p><strong>' + trackName + '</strong> by ' + artistNames + '</p>';
                trackListHTML += '<button onclick="playTrack(\'' + trackId + '\')">Play</button>';
                trackListHTML += '</div>';
            });

이렇게 하나하나 다 써주니까.

값 불러오기는 성공 근데 재생은 안된다.

재생도 보니까 또 빈 값인데 템플릿 리터럴(${})문제 같다. 그냥 근본적인 해결 방법을 찾아봐야겠다.

근본적인 해결 방법 찾았다.

해당글 참고 했다.

${'${trackId}'}한번 더 감싸주면 됨. 위에 저것도 다시 이렇게 수정해서 써야겠다.

ㅇㅇ 해결 함 내가 원하는 플레이 리스트 가져왔고 음원 재생도 된다.

그럼 이제 저 위에 리시트를 조금 더 이쁘게 만들면 될듯 .. 그건 다음에.