본문 바로가기
팁 & 노하우/C#

영화진흥위원회 오픈API 활용 C#, .net 8.0, MVC, Vue.js

by 대디동동 2024. 2. 20.
728x90

이번 게시물에서는 백엔드(Backend) C# .net 8.0 MVC 패턴과

프론트엔드(Frontend) Vue.js 3.0 버전을 활용한 영화진흥위원회 오픈 API 활용 예제를 설명하겠습니다.

네이버에서 영화 관련 API 를 제공하였으나 2023년 3월 31일에 종료되어서

영화진흥위원회에서 제공하는 API가 좋은 대안이 될 수 있습니다.

 

영화진흥위원회 오픈API 활용

1. 영화진흥위원회 API 신청

https://www.kobis.or.kr/kobisopenapi

 

https://www.kobis.or.kr/kobisopenapi/

 

www.kobis.or.kr

우선, 영화진흥위원회 API 사이트 회원 가입 후, 상단의 "키 발급/관리"를 통해서 API에 사용할 키를 발급받아야 합니다.

현재, 1계정당 2개까지 발급이 가능하며 1일 3,000회 사용이 가능합니다.

 

OPEN API 메뉴에 개발에 참고할 사항이 있으니 먼저 확인하시길 추천 합니다.

 

2. 백엔드(Backend) 개발

이 강좌에서는 C# .net 8.0 (.netcore)의 MVC 패턴과 Razor 뷰 페이지를 사용하여 설명하겠습니다.

기본적인 프로젝트 생성 및 패턴에 대한 설명은 추후 다른 게시물로 설명을 드리겠으며,

이번 강좌에서는 대략적인 설명만 드리며, 실제 프로그램 실력 향상을 위해서는 Ctrl + C, Ctrl + V 보다는

타이핑하고 생각해야 실력이 향상되니 기본적인 팁 위주로 설명드립니다.

 

발급받은 API 키가 웹상에서 노출되지 않도록 하기 위해 보안상 반드시 Backend  형태로 개발해야 합니다.

 

MVC 패턴이지만 APIController를 활용한 API 호출로 개발하겠습니다.

설명의 편의상 모델(Model)과 컨트롤러(Controller)를 하나의 파일로 작업하였습니다.

API Controller 파일 생성 - KobisController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;

namespace Kobis.API.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class KobisController : ControllerBase
    {
        private readonly string? KOBIS_API_KEY = "발급받은 API Key";
        private readonly string MOVIE_HOST = "http://www.kobis.or.kr";
        private readonly string DAILY_BOXOFFICE_URI = "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList";
        private readonly string WEEKLY_BOXOFFICE_URI = "/kobisopenapi/webservice/rest/boxoffice/searchWeeklyBoxOfficeList";
        private readonly string COM_CODE_LIST_URI = "/kobisopenapi/webservice/rest/code/searchCodeList";
        private readonly string MOVIE_LIST_URI = "/kobisopenapi/webservice/rest/movie/searchMovieList";
        private readonly string MOVIE_INFO_URI = "/kobisopenapi/webservice/rest/movie/searchMovieInfo";
        private readonly string COMPANY_LIST_URI = "/kobisopenapi/webservice/rest/company/searchCompanyList";
        private readonly string COMPANY_INFO_URI = "/kobisopenapi/webservice/rest/company/searchCompanyInfo";
        private readonly string PEOPLE_LIST_URI = "/kobisopenapi/webservice/rest/people/searchPeopleList";
        private readonly string PEOPLE_INFO_URI = "/kobisopenapi/webservice/rest/people/searchPeopleInfo";

        [HttpPost]
        public async Task<object> GetKobisMovieInfo([FromBody] KobisSearch model)
        {
            if (ModelState.IsValid)
            {
                string callUrl = string.Empty;

                if (model.BoxOfficeOpt == "DAILY_BOXOFFICE")
                {
                    callUrl = DAILY_BOXOFFICE_URI;
                }
                else if (model.BoxOfficeOpt == "WEEKLY_BOXOFFICE")
                {
                    callUrl = WEEKLY_BOXOFFICE_URI;
                }
                else if (model.BoxOfficeOpt == "COM_CODE_LIST")
                {
                    callUrl = COM_CODE_LIST_URI;
                }
                else if (model.BoxOfficeOpt == "MOVIE_LIST")
                {
                    callUrl = MOVIE_LIST_URI;
                }
                else if (model.BoxOfficeOpt == " MOVIE_INFO")
                {
                    callUrl = MOVIE_INFO_URI;
                }
                else if (model.BoxOfficeOpt == "COMPANY_LIST")
                {
                    callUrl = COMPANY_LIST_URI;
                }
                else if (model.BoxOfficeOpt == "COMPANY_INFO")
                {
                    callUrl = COMPANY_INFO_URI;
                }
                else if (model.BoxOfficeOpt == "PEOPLE_LIST")
                {
                    callUrl = PEOPLE_LIST_URI;
                }
                else if (model.BoxOfficeOpt == "PEOPLE_INFO")
                {
                    callUrl = PEOPLE_INFO_URI;
                }

                string searchOpt = string.Empty;
                foreach (var item in model.SearchOpts)
                {
                    searchOpt += $"&{item.Key}={item.Value}";
                }

                using (var client = new HttpClient())
                {
                    client.BaseAddress = new Uri(MOVIE_HOST);
                    HttpResponseMessage response = await client.GetAsync($"{callUrl}.json?key={KOBIS_API_KEY}{searchOpt}").ConfigureAwait(false);
                    if (response.IsSuccessStatusCode)
                    {
                        var jsonResponse = response.Content.ReadAsStringAsync().Result;

                        return Ok(jsonResponse);
                    }
                    else
                    {
                        return BadRequest();
                    }
                }
            }
            else
            {
                return BadRequest();
            }
        }
    }

    public class KobisSearch
    {
        public string BoxOfficeOpt { get; set; }
        public List<KeyValueParmeter> SearchOpts { get; set; }
    }

    public class KeyValueParmeter
    {
        public string Key { get; set; }
        public string Value { get; set; }
    }
}

 

프로그램 처리 로직은 간단합니다.

  • HttpClient() 통해서 영화진흥위원회 API 호출
  • 처리 결과를 Content.ReadAsStringAsync().Result() 로 받고
  • 그대로 Ok() 파라미터로 전달
  • 영화진흥위원회  API 호출을 위한 Parameter는  Key, Value 모델을 통해서 여러개 생성가능하도록 구성

3. 프론트엔드 (Frontend) - Vue.js, Razor 뷰 페이지를 통해 구현

@section Scripts {
    <script type="module">
        const { createApp, ref, reactive, onMounted } = Vue;

        createApp({
            setup() {
                const isLoading = ref(true);
                const movieData = ref({ 'boxOfficeResult': { 'boxofficeType': '', 'showRange': '', 'yearWeekTime': '', 'weeklyBoxOfficeList': [] } });
                const searchDate = ref({ 'dateS': '20240219' });

                function getBoxOffice(searchOpts) {
                    isLoading.value = true;
                    axios.post('/api/Kobis/GetKobisMovieInfo', searchOpts)
                        .then(function (response) {
                            if (response.status === 200) {
                                let data = response.data;
                                movieData.value = data;
                            }
                        })
                        .catch(function (error) {

                        })
                        .finally(function () {
                            isLoading.value = false;
                        });
                }

                function changeDate(opt, e) {
                    e.preventDefault();

                    if (opt === 'prev') {
                        searchDate.value.dateS = moment(searchDate.value.dateS, "YYYYMMDD").add(-7, 'days').format('YYYYMMDD');
                    } else if (opt === 'next') {
                        searchDate.value.dateS = moment(searchDate.value.dateS, "YYYYMMDD").add(7, 'days').format('YYYYMMDD');
                    }

                    let search = {
                        'boxOfficeOpt': 'WEEKLY_BOXOFFICE',
                        'searchOpts': [{ 'key': 'targetDt', 'value': searchDate.value.dateS }, { 'key': 'weekGb', 'value': '0' }]
                    }

                    getBoxOffice(search);
                }

                onMounted(() => {
                    let search = {
                        'boxOfficeOpt': 'WEEKLY_BOXOFFICE',
                        'searchOpts': [{ 'key': 'targetDt', 'value': searchDate.value.dateS }, { 'key': 'weekGb', 'value': '0' }]
                    }

                    getBoxOffice(search);
                })

                return {
                    movieData,
                    searchDate,
                    getBoxOffice,
                    changeDate,
                    isLoading
                }
            }
        }).mount('#app')
    </script>
}

<div id="app">
    <section class="content-section bg-primary text-white text-center">
        <div class="container px-4 px-lg-5 text-center">
            <div class="pb-2">
                <h1>{{ movieData.boxOfficeResult.boxofficeType }}</h1>
                <h2><button class="btn btn-light" v-on:click="changeDate('prev',$event)"><i class="fas fa-angle-left"></i></button> {{ movieData.boxOfficeResult.showRange }} <button class="btn btn-light" v-on:click="changeDate('next',$event)"><i class="fas fa-angle-right"></i></button></h2>
            </div>
            <div class="loader" v-if="isLoading">Loading ...</div>
            <div class="row" v-else>
                <div class="col-12 pt-2 pb-2" v-for="item in movieData.boxOfficeResult.weeklyBoxOfficeList" v-bind:key="item.rnum">
                    <div class="card text-black text-start">
                        <div class="card-header bg-light">
                            <span class="badge text-bg-danger">{{ item.rank }}</span>&nbsp;{{ item.movieNm }}
                        </div>
                        <div class="card-body">
                            <dl class="row">
                                <dt class="col-sm-3">개봉일</dt>
                                <dd class="col-sm-9">{{ item.openDt }}</dd>

                                <dt class="col-sm-3">상영관수</dt>
                                <dd class="col-sm-9">{{ item.scrnCnt }}</dd>

                                <dt class="col-sm-3">해당 기간 관객수</dt>
                                <dd class="col-sm-9">{{ item.audiCnt }}</dd>

                                <dt class="col-sm-3">전체 누적 관객수</dt>
                                <dd class="col-sm-9">{{ item.audiAcc }}</dd>
                            </dl>
                        </div>
                    </div>
                </div>
                <div v-show="movieData.boxOfficeResult.weeklyBoxOfficeList.length === 0">
                    <div class="card">
                        <div class="card-body text-black">
                            영화 정보가 아직 업데이트 되지 않았습니다.
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
</div>
  • _Layout 페이지
    • Vue.js  3, moment, Bootstrap 5.x 로딩이 되어 있어야 합니다.
    • Vue.js  isLoading 파라미터를 통해서 로딩 화면이 표시되도록 구성하였습니다.
    • searchDate, search, searchOpts 변수를 통해서 검색 조건을 변경 할 수 있도록 구성하였습니다.

이번 강좌에서는 간단하게 C# API, Vue.js 활용한 영화정보 검색 기능을 설명드렸습니다.

이번 샘플 소스에는 일부러 자세한 주석을 달지 않았으니,

천천히 분석하면서 실력을 늘려가는 Junior 개발자에게 도움이 되었으면 합니다.

반응형