Open api를 사용하여 데이터를 불러오기
ReadME.md
# 설치 패키지
# 1) 메뉴 라이브러리 설치
npm i react-router-dom
# 2) 벡엔드 연동 라이브러리 설치
npm i axios
# 3) pre css 컴파일러 : node-sass -> 더이상 안씀 : sass 설치할것
<!-- npm i node-sass -->
npm install sass
# 4) Material Page component 업그레이드
# 과거 v4 -> v5 변경 설치
npm i @mui/material @emotion/react @emotion/styled
# 4-1) 소스에서 임포트 사용법 : <Pagination />
import Pagination from '@mui/material/Pagination';
# 5) typescript jquery, jqueryui type 넣기
# 5-1) typescript jquery 사용
npm i --save-dev @types/jquery
npm i @types/jqueryui
# 6) 공통코드 : 프로젝트 시작 시 코드체계를 정의하고
# 각가의 테이블에서사용하는 목적으로 씀
# 예) 온라인 쇼핑몰 프로젝트 :
# - 공통 코드 대상: 주문 -> 결재 -> 배송 -> 확정 : 상태들
# - 예시 테이블 : 상품명(아우터) - 상태명(결재) : 컬럼명
# - 공통 코드 : 주문 == 10001
# 결재 == 10002
# 배송 == 10003
# 확정 == 10004
# - 관리 개선(공통 코드 적용) : 상품명(아우터) - 상태명(10002)
src - pages - cinema - Cinema.tsx 만들기
Cinema.tsx
// Cinema.tsx : rfce
import React, { useEffect, useState } from "react";
import TitleCom from "../../../components/common/TitleCom";
import { useParams } from "react-router-dom";
import ICinemaDetail from "../../../types/shop/ICinemaDetail";
import CinemaService from "../../../services/shop/CinemaService";
function Cinema() {
// todo: 변수 정의
// 전체조회 페이지에서 전송한 기본키(movieCd)
const { movieCd } = useParams();
// 객체 초기화(상세조회 : 기본키 있음)
const initialCinema = {
movieCd: "", // 영화코드
movieNm: "", // 영화명
prdtYear: "", // 제작년도
showTm: "", // 상영시간
openDt: "", // 개봉연도
actors: [], // 배우들 배열
directors: [], // 감독들 배열
prdtStatNm: "", // 제작상태명
};
// cinemaDetail 객체
const [cinemaDetail, setCinemaDetail] =
useState<ICinemaDetail>(initialCinema);
// todo: 함수 정의
// 상세조회 함수
const getCinema = (movieCd: string) => {
CinemaService.get(movieCd) // 벡엔드로 상세조회 요청
.then((response: any) => {
// todo: 영화 상세정보 받기
const { movieInfo } = response.data.movieInfoResult;
setCinemaDetail(movieInfo);
console.log(response.data.movieInfoResult);
})
.catch((e: Error) => {
console.log(e);
});
};
// 화면이 뜰때 실행되는 이벤트 + movieCd 값이 바뀌면 실행
useEffect(() => {
if (movieCd) getCinema(movieCd);
}, [movieCd]);
return (
// 여기
<>
{/* 제목 start */}
<TitleCom title="Cinema Detail" />
{/* 제목 end */}
<>
{cinemaDetail ? (
<div className="card mb-3">
<div className="row g-0">
<div className="col-md-4 p-2">
<img
className="img-fluid rounded-start"
alt="..."
/>
</div>
<div className="col-md-8">
<div className="card-body">
<span className="badge text-bg-primary">
{cinemaDetail.movieCd}
</span>
<h2 className="card-title mt-3">{cinemaDetail.movieNm}</h2>
<p className="card-text mt-5">
제작년도 : {cinemaDetail.prdtYear} 년
</p>
<p className="card-text">
상영시간 : {cinemaDetail.showTm} 분
</p>
<p className="card-text">개봉일 : {cinemaDetail.openDt}</p>
<p className="card-text">상태 : {cinemaDetail.prdtStatNm}</p>
{/* <span className="badge text-bg-primary">{cinemaDetail.kindCode}</span> */}
<div className="card-text">
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="directors" className="col-form-label">
directors
</label>
</div>
{cinemaDetail.directors &&
cinemaDetail.directors.map((data, index) => (
<div
className="col-3 bg-warning bg-gradient me-2"
key={index}
>
<label htmlFor="actors" className="col-form-label">
{data.peopleNm}
</label>
</div>
))}
</div>
<div className="row g-3 align-items-center mb-3">
<div className="col-3">
<label htmlFor="actors" className="col-form-label">
actors
</label>
</div>
{cinemaDetail.actors &&
cinemaDetail.actors.map((data, index) => (
<div
className="col-3 bg-secondary bg-gradient text-white me-2"
key={index}
>
<label htmlFor="actors" className="col-form-label">
{data.peopleNm}
</label>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
) : (
<div className="col-6 mx-auto">
<br />
<p>Please click on a Cinema...</p>
</div>
)}
</>
</>
);
}
export default Cinema;
src - pages - cinema - CinemaList.tsx 만들기
CinemaList.tsx
// CinemaList.tsx : rfce
import React, { useEffect, useRef, useState } from "react";
import TitleCom from "../../../components/common/TitleCom";
import { Link } from "react-router-dom";
import ICinema from "../../../types/shop/ICinema";
import CinemaService from "../../../services/shop/CinemaService";
// 달력 이미지 경로
import imgCalendar from "../../../assets/img/calendar.png";
function CinemaList() {
// todo: 변수 정의
// cinema 배열 변수
const [cinema, setCinema] = useState<Array<ICinema>>([]);
// nationCode(K : 한국영화, F : 외국영화) 검색어 변수
const [searchNationCode, setSearchNationCode] = useState<string>("");
// todo: 공통 변수 : pageSize(3,6,9 배열)
const [pageSize, setPageSize] = useState<number>(3); // 1페이지당개수
// todo: 공통 pageSizes : 배열 (셀렉트 박스 사용)
const pageSizes = [3, 6, 9];
// todo: 달력변수 정의
// useRef() : html 태그에 직접접근하게 하는 함수
// datepicker.current.value : 그 html 태그의 값
const datepicker = useRef<any>();
// todo: 함수 정의
useEffect(() => {
// todo: 달력(jquery-ui) 초기화
$("#datepicker").datepicker({
dateFormat: "yymmdd",
showOn: "button", // 버튼을 클릭하면 달력보이기
buttonImage: imgCalendar, // 버튼에 달력 이미지 보이기
});
retrieveCinema(); // 전체 조회
}, [pageSize]);
// 전체조회 함수
const retrieveCinema = () => {
// getAll(현재선택된날짜, 영화구분, 페이지크기)
CinemaService.getAll(datepicker.current.value, searchNationCode, pageSize) // 벡엔드 전체조회요청
.then((response: any) => {
const { dailyBoxOfficeList } = response.data.boxOfficeResult;
setCinema(dailyBoxOfficeList);
console.log("response", response.data);
})
.catch((e: Error) => {
console.log(e);
});
};
// 검색어 수동 바인딩 함수
const onChangeSearchNationCode = (e: any) => {
setSearchNationCode(e.target.value);
};
// todo: handlePageSizeChange(공통) : pageSize 값 변경시 실행되는 함수
// select 태그 수동 바인딩 : 화면값 -> 변수에 저장
const handlePageSizeChange = (event: any) => {
setPageSize(event.target.value); // 1페이지당 개수저장(3,6,9)
};
return (
// 여기
<>
{/* 제목 start */}
<TitleCom title="Cinema List" />
{/* 제목 end */}
<div className="row mb-3"></div>
{/* rankOldAndNew start */}
<div className="row mb-1">
<div className="col-1">
<label htmlFor="inputPassword6" className="col-form-label">
영화구분 :
</label>
</div>
<div className="col-2">
<select
className="form-select"
onChange={onChangeSearchNationCode}
value={searchNationCode}
>
<option key="all" value="">
전체
</option>
<option key="korea" value="K">
국내영화
</option>
<option key="foreign" value="F">
외국영화
</option>
</select>
</div>
{/* w-50 : 크기 조정, mx-auto : 중앙정렬(margin: 0 auto), justify-content-center */}
<div className="col-auto w-25 input-group mb-3">
<div className="input-group">
<span className="input-group-addon me-2">
<label htmlFor="inputPassword6" className="col-form-label">
달력 :
</label>
</span>
<input
type="text"
id="datepicker"
className="form-control"
disabled
ref={datepicker}
/>
<button className="btn btn-primary ms-5" onClick={retrieveCinema}>
조회
</button>
</div>
</div>
</div>
{/* rankOldAndNew end */}
{/* paging 시작 */}
<div className="mb-1">
{"Items per Page: "}
<select onChange={handlePageSizeChange} value={pageSize}>
{pageSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
</div>
{/* paging 끝 */}
{/* table start */}
<div className="col-md-12">
{/* table start */}
<table className="table">
<thead className="table-light">
<tr>
<th scope="col">순위</th>
<th scope="col">영화명</th>
<th scope="col">개봉일</th>
<th scope="col">누적관객수</th>
<th scope="col">상영관수</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{cinema &&
cinema.map((data) => (
<tr key={data.movieCd}>
<td>{data.rank}</td>
<td>
{data.movieNm}
<span className="badge rounded-pill text-bg-warning ms-2">
{data.rankOldAndNew == "NEW" ? data.rankOldAndNew : ""}
</span>
</td>
<td>{data.openDt}</td>
<td>{parseInt(data.audiAcc).toLocaleString()}</td>
<td>{parseInt(data.scrnCnt).toLocaleString()}</td>
<td>
<Link to={"/cinema/" + data.movieCd}>
<span className="badge bg-success">Edit</span>
</Link>
</td>
</tr>
))}
</tbody>
</table>
{/* table end */}
</div>
{/* table end */}
</>
);
}
export default CinemaList;
app.tsx 수정 cinema
CinemaList(영화 리스트) Cineam (영화 상세조회) 추가
app.tsx
import React from "react";
// app css import
import "./assets/css/app.css";
import HeaderCom from "./components/common/HeaderCom";
import { Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/auth/Login";
import Register from "./pages/auth/Register";
import ForgotPassword from "./pages/auth/ForgotPassword";
import NotFound from "./pages/common/NotFound";
import DeptList from "./pages/basic/dept/DeptList";
import EmpList from "./pages/basic/emp/EmpList";
import AddDept from "./pages/basic/dept/AddDept";
import AddEmp from "./pages/basic/emp/AddEmp";
import Dept from "./pages/basic/dept/Dept";
import Emp from "./pages/basic/emp/Emp";
import QnaList from "./pages/basic/qna/QnaList";
import CustomerList from "./pages/basic/customer/CustomerList";
import AddQna from "./pages/basic/qna/AddQna";
import AddCustomer from "./pages/basic/customer/AddCustomer";
import Qna from "./pages/basic/qna/Qna";
import Customer from "./pages/basic/customer/Customer";
import FaqList from "./pages/normal/faq/FaqList";
import CinemaFaqList from "./pages/normal/cinema/CinemaFaqList";
import AddFaq from "./pages/normal/faq/AddFaq";
import AddCinemaFaq from "./pages/normal/cinema/AddCinemaFaq";
import Faq from "./pages/normal/faq/Faq";
import CinemaFaq from "./pages/normal/cinema/CinemaFaq";
import ReplyBoardList from "./pages/normal/reply-board/ReplyBoardList";
import ThreadBoardList from "./pages/normal/thread-board/ThreadBoardList";
import AddReplyBoard from "./pages/normal/reply-board/AddReplyBoard";
import AddThreadBoard from "./pages/normal/thread-board/AddThreadBoard";
import ReplyBoard from "./pages/normal/reply-board/ReplyBoard";
import ThreadBoard from "./pages/normal/thread-board/ThreadBoard";
import CodeCategoryList from "./pages/admin/CodeCategoryList";
import AddCodeCategory from "./pages/admin/AddCodeCategory";
import CodeList from "./pages/admin/CodeList";
import AddCode from "./pages/admin/AddCode";
import Code from "./pages/admin/Code";
import SimpleProductList from "./pages/shop/simple-product/SimpleProductList";
import ProductList from "./pages/shop/product/ProductList";
import AddSimpleProduct from "./pages/shop/simple-product/AddSimpleProduct";
import SimpleProduct from "./pages/shop/simple-product/SimpleProduct";
import SimpleCart from "./pages/shop/simple-product/SimpleCart";
import AddProduct from "./pages/shop/product/AddProduct";
import Product from "./pages/shop/product/Product";
import SimpleCartList from "./pages/shop/simple-product/SimpleCartList";
import CinemaList from "./pages/shop/cinema/CinemaList";
import Cinema from "./pages/shop/cinema/Cinema";
import ThemaLoadList from "./pages/shop/thema-load/ThemaLoadList";
import FileDbList from "./pages/advanced/FileDbList";
function App() {
return (
<div className="App">
<HeaderCom />
{/* <!-- 구분 막대 시작 --> */}
<div className="gutter text-center text-muted fade-in-box">
<div>클론 코딩 예제 사이트에 오신 것을 환영합니다.</div>
</div>
{/* <!-- 구분 막대 끝 --> */}
<div id="content-wrapper">
{/* 라우터 정의 시작 */}
<Routes>
{/* login */}
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
{/* dept */}
<Route path="/dept" element={<DeptList />} />
<Route path="/add-dept" element={<AddDept />} />
<Route path="/dept/:dno" element={<Dept />} />
{/* emp(연습) */}
<Route path="/emp" element={<EmpList />} />
<Route path="/add-emp" element={<AddEmp />} />
<Route path="/emp/:eno" element={<Emp />} />
{/* qna */}
<Route path="/qna" element={<QnaList />} />
<Route path="/add-qna" element={<AddQna />} />
<Route path="/qna/:qno" element={<Qna />} />
{/* customer */}
<Route path="/customer" element={<CustomerList />} />
<Route path="/add-customer" element={<AddCustomer />} />
<Route path="/customer/:cid" element={<Customer />} />
{/* faq */}
<Route path="/faq" element={<FaqList />} />
<Route path="/add-faq" element={<AddFaq />} />
<Route path="/faq/:no" element={<Faq />} />
{/* cinema faq */}
<Route path="/cinema-faq" element={<CinemaFaqList />} />
<Route path="/add-cinema-faq" element={<AddCinemaFaq />} />
<Route path="/cinema-faq/:cfno" element={<CinemaFaq />} />
{/* reply-board */}
<Route path="/reply-board" element={<ReplyBoardList />} />
<Route path="/add-reply-board" element={<AddReplyBoard />} />
{/* 정리 : boardParent = 0 이면 부모글을 클릭 */}
{/* 정리 : boardParent = 0 아니면 자식글을 클릭 */}
<Route
path="/reply-board/bid/:bid/boardParent/:boardParent"
element={<ReplyBoard />}
/>
{/* thread-board */}
<Route path="/thread-board" element={<ThreadBoardList />} />
<Route path="/add-thread-board" element={<AddThreadBoard />} />
<Route
path="/thread-board/tid/:tid/tparent/:tparent"
element={<ThreadBoard />}
/>
{/* codeCategory(대분류 공통코드(부모)) */}
<Route path="/code-category" element={<CodeCategoryList />} />
<Route path="/add-code-category" element={<AddCodeCategory />} />
{/* code(소분류 공통코드(자식)) */}
<Route path="/code" element={<CodeList />} />
<Route path="/add-code" element={<AddCode />} />
<Route path="/code/:codeId" element={<Code />} />
{/* simple-product */}
<Route path="/simple-product" element={<SimpleProductList />} />
<Route path="/add-simple-product" element={<AddSimpleProduct />} />
<Route path="/simple-product/:spno" element={<SimpleProduct />} />
{/* 장바구니 상세 */}
<Route path="/simple-cart/:spno" element={<SimpleCart />} />
{/* 장바구니 전체 조회 */}
<Route path="/simple-cart" element={<SimpleCartList />} />
{/* product(연습) */}
<Route path="/product" element={<ProductList />} />
<Route path="/add-product" element={<AddProduct />} />
<Route path="/product/:pno" element={<Product />} />
{/* cinema */}
{/* 박스오피스 순위 전체조회 */}
<Route path="/cinema" element={<CinemaList />} />
{/* 영화 상세조회 */}
<Route path="/cinema/:movieCd" element={<Cinema />} />
{/* NotFound */}
<Route path="*" element={<NotFound />} />
</Routes>
{/* 라우터 정의 끝 */}
</div>
</div>
);
}
export default App;
영화 진흥위원회 오픈 API 회원가입
https://www.kobis.or.kr/kobisopenapi/homepg/main/main.do
영화진흥위원회 오픈API
OPEN API 서비스 영화진흥위원회 영화관입장권통합전산망에서 제공하는 오픈API 서비스로 더욱 풍요롭고 편안한 영화 서비스를 즐겨보세요.
www.kobis.or.kr
키 발급받기
사용 목적 : 학습
관리명 : 아이디
서비스 사용 URL : 비움
src - types - shop - ICinema.ts 생성
영화진흥위원회 제공서비스에 많은 속성 들 중 몇개만 사용
ICinema.ts
// ICinema.ts : 인터페이스
// 영화진흥 위원회 응답필드 중 일부 : 화면에 출력됨
export default interface ICinema {
movieCd:string, // 영화코드
rank: string, // 해당일자의 박스오피스 순위를 출력합니다.
rankOldAndNew: string, // 랭킹에 신규진입여부를 출력합니다.(“OLD” : 기존 , “NEW” : 신규)
movieNm: string, // 영화명(국문)을 출력합니다.
openDt: string, // 영화의 개봉일을 출력합니다.
audiAcc: string, // 누적관객수를 출력합니다.
scrnCnt: string, // 해당일자에 상영한 스크린수를 출력합니다.
}
src - services - shop - CinemaService.ts 생성
// CinemaService.ts
// 영화진흥 위원회의 오픈 API 통신 함수들 정의
// 전체조회 또는 상세조회
import axios from "axios";
import { Console } from "console";
import ICinema from "../../types/shop/ICinema";
// 영화진흥 위원회 기본 주소
// /boxoffice/searchDailyBoxOfficeList.json
// 발급 받은 api key 변수
let apiKey = "004399a654227ec5064e8d748b271196";
// 전체 조회
// 요청필드(조건)
// key : 위의 인증키 넣기(필수)
// targetDt : 조회하고자 하는 날짜를 yyyymmdd 형식(필수)
// repNationCd : “K: : 한국영화 “F” : 외국영화 ("" : 전체)
// itemPerPage : 결과 ROW 의 개수를 지정
// 변수 사용 : 쿼리스트링 방식 (?변수명=값&변수명2=값2...)
const getAll = (targetDt: string, repNationCd: string, itemPerPage:number) => {
// 주소 조합 : 기본주소 + 추가주소 + 변수명
let url = `${baseUrl}/boxoffice/searchDailyBoxOfficeList.json?key=${apiKey}&targetDt=${targetDt}&repNationCd=${repNationCd}&itemPerPage=${itemPerPage}&serviceKey=${apiKey}`;
console.log("url", url);
return axios.get<Array<ICinema>>(url);
}
// 영화 상세조회(1건조회)
// ${baseUrl}/movie/searchMovieInfo.json
// 요청필드(조건) :
// key : 인증키(위에 있음) : 필수
// movieCd (영화코드)
const get = (movieCd: string) => {
// 기본주소 + 추가주소 + 변수명(쿼리스트링)
let url = `${baseUrl}/movie/searchMovieInfo.json?key=${apiKey}&movieCd=${movieCd}`;
console.log("상세조회 url", url);
return axios.get<ICinema>(url);
}
const CinemaService = {
getAll,
get
}
export default CinemaService;
src - types - shop - ICinemaDetail.ts 생성
ICinemaDetail.ts
// ICinemaDetail.ts : 인터페이스
export default interface ICinemaDetail {
movieCd:string, // 영화코드를 출력합니다.
movieNm: string, // 영화명(국문)을 출력합니다.
prdtYear: string, // 제작연도를 출력합니다.
showTm: string, // 상영시간을 출력합니다.
openDt: string, // 개봉연도를 출력합니다.
directors: Array<any>, // 감독을 나타냅니다.
actors: Array<any>, // 배우를 나타냅니다.
prdtStatNm: string, // 제작상태명을 출력합니다.
};
결과
영화 리스트
상세조회
'SI' 카테고리의 다른 글
[SI]공공데이터 포털 오픈 api 사용하여 음식점정보 불러오기 (0) | 2023.11.12 |
---|---|
[SI] 벡엔드 (02_SimpleDMS_Page) 반응형 게시판 (0) | 2023.10.31 |
[SI] 프론트엔드 (02_SimpleDMS_Page) 반응형 게시판 (0) | 2023.10.27 |
[SI] 벡엔드 (02_SimpleDMS_Page) 페이지 기능 추가 (0) | 2023.10.23 |
[SI] 프론트엔드 (02_SimpleDMS_Page) 페이지 기능 추가 (0) | 2023.10.20 |