친구와 토이 프로젝트를 진행하고 있는데, videoId 를 서버 DB에 저장하고 조회 시 해당 id를 통해 youtube API로 상세 정보를 가져온 후 친구의 프론트엔드로 전달하는 코드를 작성했다.
잦은 유튜브 API조회를 피하기 위하여 한번 조회한 아이디는 서버에 저장했다가 내보내주는 방식을 채택했는데 출시 목적의 프로젝트가 아니기 때문에 일단은 그냥 Map 자료구조로 관리를 하고 추후에 공부 겸 레디스로 캐싱하고 mysql 서버로 저장하는 과정까지 추가해볼까 한다.
const youtube = google.youtube({
version: "v3",
auth: config.auth.gkey, // YouTube API 키
});
v3를 사용할 예정이고 api key의 경우 친구한테 받아서 .env로 관리했다.
📦src
┣ 📂config
┃ ┗ 📜config.js
┣ 📂constants
┃ ┗ 📜env.js
그냥 바로 process.env.키명
으로 사용해도 되지만 관리성과 편리성을 위하여 위와 같은 구조로 관리하고 있다.
import dotenv from "dotenv";
dotenv.config();
/* ============================SERVER============================ */
export const PORT = process.env.PORT;
export const HOST = process.env.HOST;
/* ============================ D B ============================ */
export const DB1_NAME = process.env.DB1_NAME;
export const DB1_USER = process.env.DB1_USER;
export const DB1_PASSWORD = process.env.DB1_PASSWORD;
export const DB1_HOST = process.env.DB1_HOST;
export const DB1_PORT = process.env.DB1_PORT;
export const DB2_NAME = process.env.DB2_NAME;
export const DB2_USER = process.env.DB2_USER;
export const DB2_PASSWORD = process.env.DB2_PASSWORD;
export const DB2_HOST = process.env.DB2_HOST;
export const DB2_PORT = process.env.DB2_PORT;
/* ============================REDIS============================= */
export const REDIS_HOST = process.env.REDIS_HOST;
export const REDIS_PORT = process.env.REDIS_PORT;
export const REDIS_PASSWORD = process.env.REDIS_PASSWORD;
export const REDIS_DATABASE = process.env.REDIS_DATABASE;
/* ============================ AUTH ============================ */
export const PEPPER = process.env.PEPPER;
export const SALT = process.env.SALT;
export const SECRET_KEY = process.env.SECRET_KEY;
export const KEY = process.env.KEY;
export const CERT = process.env.CERT;
export const GKEY = process.env.GKEY;
export const config = {
server: {
host: HOST,
port: PORT,
},
database: {
user: {
name: DB1_NAME,
user: DB1_USER,
password: DB1_PASSWORD,
host: DB1_HOST,
port: DB1_PORT,
},
list: {
name: DB2_NAME,
user: DB2_USER,
password: DB2_PASSWORD,
host: DB2_HOST,
port: DB2_PORT,
},
},
redis: {
host: REDIS_HOST,
port: REDIS_PORT,
password: REDIS_PASSWORD,
database: REDIS_DATABASE,
},
auth: {
pepper: PEPPER,
salt: SALT,
secret_key: SECRET_KEY,
key: KEY,
cert: CERT,
gkey: GKEY,
},
};
한번도 환경변수 관리에 대해 적은 적이 없는 것 같아 살짝 끄적여봤다.
export const loadYoutubeData = async (videoId) => {
try {
videoId = videoId.trim();
if (album.has(videoId)) {
return album.get(videoId);
} else {
const response = await youtube.videos.list({
part: ["snippet"],
id: String(videoId),
});
if (response.data.items.length === 0) throw new Error("잘못된 비디오 ID");
album.set(videoId, response.data.items);
return response.data.items;
}
} catch (error) {
console.error(error);
return -1;
}
};
위 코드의 경우에는 레디스 및 DB연동이 되어있지 않아서 아주 간단한 구조로 되어있다.
만약 해당 videoId를 조회했던 이력이 있다면 album
이라는 map에 정보가 담겨있을 것이고 거기서 불러와 반환한다.
비디오 ID가 잘못되었을 경우에는 response.data.items
이 빈 배열로 전달되기 때문에 길이가 0인 상황이라면 잘못된 ID라는 뜻으로 에러를 투척시켜고 catch문에서 -1 리턴시켜 함수가 불린쪽에서 에러처리를 할 수 있게 구현 해 놓았다.
위의 사진처럼 날라오는 정보를 필요한 내용만 반환해주면 서버의 역할은 끝이다.
async getList(email) {
if (!email) throw new CustomErr(ERR_CODES.BAD_REQUEST, "Bad Request");
const lists = await findListById(email);
const listData = await Promise.all(
lists.map(async (list) => {
const listID = list.listId;
const videoId = list.videoId;
const data = await loadYoutubeData(videoId);
const {
kind,
snippet: { title, thumbnails, channelTitle, publishedAt },
} = data[0];
return {
listID,
id: { kind, videoId },
title,
thumbnail: thumbnails.medium,
channelTitle,
publishedAt,
};
})
);
return listData;
}
findListById
함수가 DB에서 자신의 리스트를 불러오고 해당 lists
를 맵을 통해 조회 후 반환한다.
가져온 data 중 필요한 부분만 구조분해할당을 통해 추출하고 반환시키면 Array.map이 결과값을 배열로 반환하고 그것을 다시 컨트롤러로 리턴시켜준다.
처음 저장을 하고 리스트를 조회할 때에는 youtube API를 거치기 때문에 344ms의 시간이 요구되지만, 한번 서버에 등록되고 난 이후엔 Map 자료구조에서 가져오기 때문에 시간이 감소한다.