티스토리 뷰

오늘은 SpringBoot 3.2.5 + Kotlin + Thymeleaf를 이용하여 유튜브 API 사용방법에 대해 작성하고자 합니다. 프로젝트 생성 및 유튜브 API 키 생성은 아래 글을 참고해 주세요.

 - [IntelliJ] SpringBoot + Kotlin 프로젝트 생성하는 방법 
 - [기타] Youtube Data API v3 사용방법

 

1. api-key 입력

  • application.yml 파일에 api-key 값을 입력합니다.
youtube:
  api-key: 발급받은 API Key



2. 라이브러리 추가

  • build.gradle.kts로 관련 API를 다운로드합니다. 버전은 본인 상황에 알맞게 설치하시면 됩니다.
implementation("com.google.api-client:google-api-client:2.2.0")
implementation("com.google.oauth-client:google-oauth-client-jetty:1.34.1")
implementation("com.google.apis:google-api-services-youtube:v3-rev20230816-2.0.0")
implementation("com.google.http-client:google-http-client-jackson2:1.39.2")



3. DTO 작성

package com.cyb.youtubeplayer.contoller.dto

import lombok.Getter
import lombok.Setter

@Getter
@Setter
class SearchDto {
    var keyword: String = ""
}



4. VO 작성

  • 결과를 담은 VO를 작성합니다.
package com.cyb.youtubeplayer.model

import lombok.Getter
import lombok.Setter

@Getter
@Setter
class SearchResultVO {
    var title: String = ""
    var thumbnail: String = ""
    var publishTime: String = ""
    var videoUrl: String = ""
}



5. 컨트롤러 작성

package com.cyb.youtubeplayer.contoller;

import com.cyb.youtubeplayer.contoller.dto.SearchDto
import com.cyb.youtubeplayer.model.SearchResultVO
import com.cyb.youtubeplayer.service.YoutubeService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class YoutubeController(private val youtubeService: YoutubeService) {

    @PostMapping("/youtube/search")
    fun youtubeSearch(@RequestBody searchDto: SearchDto): ResponseEntity<ArrayList<SearchResultVO>> {
        return ResponseEntity.ok(youtubeService.youtubeSearch(searchDto))
    }

}



6. 서비스코드 작성

package com.cyb.youtubeplayer.service;

import com.cyb.youtubeplayer.common.util.DateUtil
import com.cyb.youtubeplayer.contoller.dto.SearchDto
import com.cyb.youtubeplayer.model.SearchResultVO
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.youtube.YouTube
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.util.*
import kotlin.collections.ArrayList

@Service
class YoutubeService(
    @Value("\${youtube.api-key}")
    private val apiKey: String) {

    fun youtubeSearch(searchDto: SearchDto): ArrayList<SearchResultVO> {
        var result = ""

        val jsonFactory = JacksonFactory()

        val youtube = YouTube.Builder(
            NetHttpTransport(),
            jsonFactory,
            HttpRequestInitializer { }
        ).build()

        val search = youtube.search().list(Collections.singletonList("id,snippet"))

        search.setKey(apiKey)

        search.setQ(searchDto.keyword)

        val searchResponse = search.execute()

        val searchResultList = searchResponse.items

        var searchResultVO: ArrayList<SearchResultVO> = ArrayList()

        if(searchResultList != null && searchResultList.size > 0){
            for(i in searchResultList.indices){
                val searchResult = searchResultList[i]
                var title = searchResult.snippet.title
                var thumbnails = searchResult.snippet.thumbnails.medium.url
                var publishTime = searchResult.snippet.publishedAt
                var videoId = searchResult.id.videoId

                var searchData : SearchResultVO = SearchResultVO()
                searchData.title = title
                searchData.thumbnail = thumbnails
                searchData.publishTime = DateUtil.formatDateTime(publishTime)
                searchData.videoUrl = "https://www.youtube.com/watch?v=" + videoId

                searchResultVO.add(searchData)
            }
        }

        return searchResultVO
    }
}



7. 화면 컨트롤러 작성

  • 화면 맵핑을 위한 Controller를 작성합니다.
package com.cyb.youtubeplayer.contoller

import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping

@Controller
class MainController {

    @GetMapping("/", "/index", "")
    fun index(): String {
        return "index"
    }

}



8. 화면 작성

  • index.html 화면을 작성합니다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<th:block th:replace="~{fragments/head.html :: headFragment}"/>
<body>
<div class="container">

  <div class="well well-sm">
    <div>
      <strong>Search </strong>
      <div class="btn-group">
        <input type="text" id="keyword" placeholder="Search Keyword...">
        <button onclick="searchVideo()">Search...</button>
      </div>
    </div>
    <hr/>
    <div>
      <strong>Display </strong>
      <div class="btn-group">
        <a href="#" id="list" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-th-list">
        </span>List</a> <a href="#" id="grid" class="btn btn-default btn-sm"><span
        class="glyphicon glyphicon-th"></span>Grid</a>
      </div>
    </div>
  </div>

  <div id="products" class="row list-group">

  </div>
</div>
<script>
  $(document).ready(function () {
    $('#list').click(function (event) {
      event.preventDefault();
      $('#products .item').addClass('list-group-item');
    });
    $('#grid').click(function (event) {
      event.preventDefault();
      $('#products .item').removeClass('list-group-item');
      $('#products .item').addClass('grid-group-item');
    });
  });

  function searchVideo() {
    var $keyword = $('#keyword');

    if(!$keyword || $keyword.val() === ''){
      alert("Please enter keyword");
      $keyword.focus();
    } else {
      const params = {
        keyword : $keyword.val()
      }
      postApi('/youtube/search', params, onSuccess, onApiError);
    }

  }

  function onSuccess(data){
    var jsonString = JSON.stringify(data);

    var parsedData = JSON.parse(jsonString);

    // 파싱된 데이터를 사용하여 작업 수행
    var str = '';
    for (var i = 0; i < parsedData.length; i++) {
      var item = parsedData[i];
      str += '<div class="item  col-xs-4 col-lg-4" style="height: 400px">';
      str += '  <div class="thumbnail" style="height: 100%">';
      str += '    <img class="group list-group-image" src="'+item.thumbnail+'" alt=""/>';
      str += '    <div class="caption">';
      str += '      <h4 class="group inner list-group-item-heading">'+item.title+'</h4>';
      str += '      <div class="row">';
      str += '        <div class="col-xs-12 col-md-6">';
      str += '          <p class="lead" style="font-size: 12pt">'+item.publishTime+'</p>';
      str += '        </div>';
      str += '      </div>';
      str += '      <div class="row">';
      str += '        <div class="col-xs-12 col-md-6">';
      str += '          <a class="btn btn-success" href="'+item.videoUrl+'">Go to video</a>';
      str += '        </div>';
      str += '      </div>';
      str += '    </div>';
      str += '  </div>';
      str += '</div>';
    }

    $("#products").html(str);
  }

</script>
</body>
</html>

 

전체 소스는 Github를 참고 부탁드립니다.


감사합니다.

최근에 올라온 글
Total
Today
Yesterday