콘텐츠 모달
- 지금 바로 보러 가기 클릭 시 로그인 & 구독 여부를 확인해 회차 정보 화면으로 이동
- 작품 DB에 등록된 작품 중 회차 정보도 등록된 작품이라면 에피소드 별 썸네일 노출, 클릭 시 영상 재생
- 작품 DB에 등록된 작품 중 회차 정보가 등록되지 않은 작품이라면 얼럿과 함께 뒤로 가기
작품 DB에 등록된 작품 중 회차 정보도 등록된 작품이라면 에피소드 별 썸네일 노출, 클릭 시 영상 재생
콘텐츠 모달은 어떤 콘텐츠를 눌러도 나와야 한다. 이걸 util로 빼거나 전역으로 관리했어야 했는데 그렇게 못 했다.
아무튼 모달에는 백 단과 미리 이야기해서 만들어 놓은 영화id, 썸네일 이미지, 타이틀, 장르, 생성일, 줄거리가 들어간다.
이 정보들은 어드민 페이지에서 미리 등록을 해놓은 것으로 외부 API를 사용하지 않았다.
영화 정보의 기본값으로 null로 설정한 후 모달id가 바뀔 때마다 해당 영화에 대한 정보를 요청한다.
해당 영화에 대한 정보가 있으면 그 영화 정보를 담아 movieData가 바뀌게 된다.
movieData가 있다면 영화 정보를 그대로 보여주면 된다.
만약 지금 바로 보러 가기를 클릭하면 /list/모달id로 이동하게 된다.
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
const Modal = ({
modalId,
modalImage,
modalTitle,
modalGenre,
modalCreatedDate,
modalExplanation,
closeModal,
}) => {
const [movieData, setMovieData] = useState(null);
const navigate = useNavigate();
useEffect(() => {
axios
.get(
`https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/movies/${modalId}`
)
.then((response) => {
setMovieData(response.data); // 수정: response.data로 변경
})
.catch((error) => {
console.error('Error:', error);
});
}, [modalId]);
// 수정: 버튼의 onClick에는 함수를 전달
const handleButtonClick = () => {
navigate(`/list/${modalId}`);
};
return (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div
className="relative flex flex-col justify-evenly w-3/4 h-4/5 bg-indigo-950 rounded-md shadow-2xl lg:w-1/3 lg:h-3/4 p-3"
onClick={closeModal}
>
<div>
<button
className="absolute top-2 right-2 text-white text-lg"
onClick={closeModal}
>
❌
</button>
<img
src={modalImage}
alt="modal"
className="w-2/3 mx-auto my-5 sm:w-2/5 md:w-1/3 lg:w-2/3"
/>
</div>
{movieData && (
<div className="text-white p-3 text-lg justify-center sm:text-sm md:text-base lg:text-base text-center">
<div className="font-semibold text-xl">{modalTitle}</div>
<div className="">장르: {modalGenre}</div>
<div className="">공개일: {modalCreatedDate}</div>
<div className="py-4 px-3 text-center break-words">
{modalExplanation}
</div>
</div>
)}
<div className="p-1">
<button
className="bg-white text-indigo-950 rounded-sm w-full font-bold py-1 text-sm md:text-base lg:text-xl"
onClick={handleButtonClick} // 수정: 버튼 클릭 핸들러 사용
>
지금 바로 보러 가기
</button>
</div>
</div>
</div>
);
};
export default Modal;
작품 DB에 등록된 작품 중 회차 정보가 등록되지 않은 작품이라면 얼럿과 함께 뒤로 가기
영화 포스터 클릭 > 해당 작품의 에피소드 목록 페이지로 이동 하게 되는데, 이 후 썸네일을 누르면 등록해놓은 영상이 재생된다.
이 때 목록 페이지로 이동할 수 있는 권한은 유저이면서 구독을 한 사람만이 갖게 된다.
따라서 로그인을 했는지 확인하고, 로그인을 했다면 구독 여부를 확인한다.
subscribe = 1로, loginUserResponse.available이 1이라면 구독한 사람이라고 할 수 있다.
즉 구독자에게는 에피소드 목록 페이지를 보여준다.
만약 구독하지 않았다면 구독해달라는 알람과 동시에 구독을 할 수 있는 마이페이지로 이동시킨다.
만약 로그인 자체를 하지 않았다면 로그인을 해달라는 알람과 동시에 로그인 페이지로 이동시킨다.
에피소드 목록 페이지를 보여줄 때 여기에 등록된 회차가 하나도 없다면 === contentResponse.data.length === 0
등록된 회차가 없다는 알람과 함께 뒤로 가게 되고, 그렇지 않다면 회차를 보여주면 된다.
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get(
'https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/api/home',
{
withCredentials: true,
}
);
const login = response.data.login;
if (login) {
const subscribe = response.data.loginUserResponse.available;
if (subscribe === 1) {
const contentResponse = await axios.get(
`https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/contents/${modalId}`
);
if (contentResponse.data.length === 0) {
alert('아직 등록된 회차가 없어요🥲');
navigate(-1);
} else {
setData(contentResponse.data);
}
} else {
alert('구독을 해주세요.');
navigate('/mypage');
}
} else {
alert('로그인을 해주세요.');
navigate('/login');
}
} catch (error) {
console.error(error);
}
};
fetchData();
}, [modalId, navigate]);
data를 전개할 때 유저가 특정 회차의 썸네일을 눌렀다면 그 S3에 저장해놓은 해당 동영상이 새 창에서 재생되도록 했다.
그런데 우리가 최적화를 위해 원본 파일인 mp4가 아니라 m3u8이라는 청크 파일... 이라고 해야 하나
쪼개놓은 파일로 받게 해놨는데 이걸 재생 시키려면 다른 라이브러리가 필요했다. (videojs 같은 거)
그래서 며칠을 백방으로 해당 라이브러리 쓰는 법을 찾아봤지만 이해를 하지 못했고, 결국 확장 프로그램을 설치해 재생하는 방법을 사용하게 되었다. 며칠 내내 찾아봤는데 해결하지 못해서 너무 답답했다...
마이페이지
- 회원 정보 조회
- 구독 결제
로그인 유저 && 구독자만 영상을 재생할 수 있으므로 자신이 구독자인지 확인할 수 있는 페이지가 필요했다.
비밀번호 재설정이나 프로필 사진 같은 건 하지 못했고 (다음 프로젝트에서 했다.)
이번에는 그냥 유저 정보만 확인하고, 구독 결제를 할 수 있고 그걸 확인하는 용도로 마이페이지를 구현했다.
회원 정보 조회
마이페이지는 헤더에서 접근할 수 있는데 애초에 로그인한 사람만 마이페이지 버튼이 뜨게 해두었다.
isLogined 여부로 로그인을 하지 않은 사람의 헤더에는 로그인 만 떠 있고, 로그인한 유저는 마이페이지와 로그아웃이 떠 있다.
아무튼 그래서 마이페이지에 들어가려면 userInfo와 로그인 여부가 필요하니 이걸 받아줬다.
이 프로젝트의 가장 아쉬운 점은 같이 하는 프론트 분의 코드를 제대로 이해하지 못했다는 것이다;
로그인/회원가입을 담당하신 만큼 해당 코드를 잘 알아야 했는데 지금도 잘 이해를 못했다는 걸 체감하고 있다.
프로젝트 회고를 처음해서 그런가 문제점만 많이 보여서 매우 슬프구나 🥲ㅋㅋ...
화면에는 유저의 이름, 이메일, 구독 여부와 구독 기간을 띄워준다.
지금 보니 userInfo 부분... 매우 잘못된 게 보인다...
<div>
<div className="p-5 lg:p-8 font-['Pretendard-Bold']">
<div className="font-black text-xl lg:text-2xl">마이 페이지</div>
<div className="mt-5">
<div className="text-md lg:text-xl font-semibold">회원 정보</div>
<div className="mt-5">user name: {userInfo['nickName']}</div>
<div className="mb-5">user email: {userInfo['email']}</div>
<div>
{userInfo['available'] !== 0 && userInfo['period'] !== 0 ? (
<div className="">구독 잔여일: {userInfo['period']}</div>
) : (
<Payment />
)}
</div>
</div>
</div>
</div>
저렇게 접근하지 말고 const { nickName, email, available, period } = userInfo; 이렇게 구조분해를 하자...
구독 결제
const Payment = () => {
const onClickPayment = () => {
const { IMP } = window;
IMP.init('-');
var today = new Date();
var hours = today.getHours(); // 시
var minutes = today.getMinutes(); // 분
var seconds = today.getSeconds(); // 초
var milliseconds = today.getMilliseconds();
var makeMerchantUid = `${hours}${minutes}${seconds}${milliseconds}`;
const data = {
pg: 'kakaopay',
pay_method: 'card',
merchant_uid: `IMP${makeMerchantUid}`,
name: '테스트 결제',
amount: 14000,
};
IMP.request_pay(data, callback);
};
const callback = (res) => {
const { success, error } = res;
if (success) {
const apiUrl = `https://kwyrmjf86a.execute-api.ap-northeast-2.amazonaws.com/subscribe`;
const requestData = {
available: 1,
period: 365,
};
axios
.post(apiUrl, requestData, { withCredentials: true })
.then((res) => {
console.log('API 요청 성공:', res.data);
navigator(-1);
})
.catch((err) => {
console.error('API 요청 실패:', err);
});
} else {
console.error(error);
}
};
결제를 위해 카카오페이 API와 아임포트 API를 사용했다.
이건 백엔드 분이 알아보라고 하셔서 레퍼런스를 찾아봤는데 거의 그대로 따라하면 된다.
구독 결제 버튼을 누르면 아임포트를 초기화하고 결제에 필요한 data를 설정한다.
merchatUid는 현재 시간을 기반으로 한 고유한 상인 ID다. 이거를 실시간으로 정했다.
카카오 페이로, 카드 결제, 금액은 14000원이다. 테스트용이라 실제로 결제되진 않는데 진짜같다;
결제 요청 이후에 콜백 함수로 결제가 성공했는지, 실패했는지 확인한다.
만약 결제가 성공했다면 post로 유저의 구독 정보를 보내고 뒤로 가기를 해준다.
만약 결제가 실패했다면 에러를 띄워주면 된다.
참고로 결제가 성공했다면 구독 기간은 365일로, 유저는 위에서 말한 것처럼 subscribe 1을 갖게 된다.
결과물은 아래와 같다.
약 세 달 전 프로젝트를 정리하려니 정보도 이미 많이 유실되었고 코드에 대한 기억도 휘발되었다...
이번에 한 프로젝트를 기록하려다가 같이 해봤는데 깨달은 점이 많다.
1. 코드 리뷰는 항상 그때 그때 할 것
2. 코드에 대한 상세한 내용은 꼭 기록할 것
3. 서버가 닫히기 전에 필요한 자료들은 꼼꼼히 정리할 것...
'🛠️ 프로젝트 > ☁️ 구름톤 Hongflix' 카테고리의 다른 글
[Hongfilx] 장르별 콘텐츠 보여주기 & debounce로 실시간 검색 결과 보여주기 (1) | 2023.10.28 |
---|---|
[Hongflix] 조건부 렌더링 헤더 & Slick으로 배너와 캐러셀 만들기 (0) | 2023.10.18 |
[HongsamSNS] 로그인/회원가입 구현하기 (1) | 2023.10.18 |