오늘은 2부에 이어서 가볍게 2.5부로 리팩토링을 진행해보도록 하겠습니다. 원래 다음 포스팅은 3부로 기분 / 날씨 / 장르 등 키워드를 선택하여 노래 추천받는 기능 및 디자인까지 마무리 지으려고 했는데, 중간점검할 겸 조금이나마 정리된 코드를 공유하려고 합니다. 2부 포스팅은 아래를 클릭하면 볼 수 있습니다.
리팩토링
지금 모든 코드가 App.js 에 들어가 있기 때문에 더 복잡해지지 않기 위해서는 중간에 리팩토링이 한번 들어가야 할 것 같습니다. 따라서 Api와 Hooks로 로직과 컴포넌트를 나누어주도록 하겠습니다.
호출하고 있는 Api는 두 개이며, Api 폴더를 만든 뒤 하위에 OpenAI 관련 폴더 하나, 음악 검색 관련 폴더 하나 만들어주겠습니다.
API
src/api/MusicSearchAPI 추가
export default class MusicSearchAPI {
// maniaDB 음악 키워드 검색
static fetchMusicSearch(params) {
const { keyword, sr, display, v } = params;
return fetch(`/api/search/${keyword}/?sr=${sr}&display=${display}&v=${v}`);
}
}
src/api/OpenAIAPI 추가
import { Configuration, OpenAIApi } from 'openai';
export default class OpenAIAPI {
// OpenAI Api
static fetchOpenAI(params) {
const { apiKey, prompt } = params;
const configuration = new Configuration({ apiKey });
return new OpenAIApi(configuration).createCompletion({
model: 'text-davinci-003',
prompt: prompt,
temperature: 0,
max_tokens: 150,
});
}
}
Api에 넘길 params도 interface를 만들어주어 types.ts로 관리해줍시다.
src/api/types 추가
export interface MusicSearchParams {
keyword: string;
sr: string;
display: number;
v: number;
}
export interface OpenAIParams {
apiKey: string;
prompt: string;
}
Custom Hooks
그럼 Api 세팅까지는 완료되었으니, Api 호출 로직을 따로 커스텀 훅을 만들어서 분리해주겠습니다.
이 커스텀 훅의 역할은 호출했을 때 1. OpenAI API 호출 > 2. 응답(노래 데이터) 을 음악 검색 API에 파라미터로 넘겨서 호출 > 3. 응답 파싱 > 4. 노래 제목, 가수, 앨범 커버를 리턴하도록 합니다.
src/hooks/useMusicSearch 추가
import { useCallback, useState } from 'react';
import MusicSearchAPI from 'api/MusicSearchAPI';
import OpenAIAPI from 'api/OpenAIAPI';
import XMLParser from 'react-xml-parser';
const OPENAI_API_KEY = process.env.REACT_APP_OPENAI_API_KEY;
function useMusicSearch() {
const [music, setMusic] = useState({ title: '', artist: '', img: '' });
const fetchMusicSearch = useCallback(async (keyword: string) => {
const params = { keyword, sr: 'album', display: 1, v: 0.5 };
const regExp = /[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]/g; // eslint-disable-line
try {
await MusicSearchAPI.fetchMusicSearch(params)
.then((response) => response.text())
.then((str) => new XMLParser().parseFromString(str))
.then((data) => {
const { children } = data.children[0];
const item = children.filter((child) => child.name === 'item')[0]
.children;
const text = item.filter((el) => el.name === 'title')[0].value;
const img = item
.filter((el) => el.name === 'image')[0]
.value.replace('>', '');
const [artist, title] = text.split(regExp);
setMusic({ title, artist, img });
});
} catch (error) {
console.log(`Error: ${error}`);
}
}, []);
const fetchOpenAI = useCallback(
async (prompt: string) => {
try {
await OpenAIAPI.fetchOpenAI({
apiKey: OPENAI_API_KEY,
prompt,
}).then((res) => {
const { choices } = res.data;
const [title] = choices[0].text.split('by');
fetchMusicSearch(title);
});
} catch (error) {
console.log(`Error: ${error}`);
}
},
[fetchMusicSearch]
);
return { fetchOpenAI, music } as const;
}
export default useMusicSearch;
로직을 분리했으니 App.js에서는 해당 로직들을 모두 제거하고, 호출해서 결과값만 받아오도록 수정합니다.
src/App.js 수정
import { useCallback, useEffect } from 'react';
import './App.css';
import useMusicSearch from 'hooks/useMusicSearch';
function App() {
const { fetchOpenAI, music } = useMusicSearch();
const recommendMusic = useCallback(() => {
fetchOpenAI('recommend me one male indie song');
}, [fetchOpenAI]);
useEffect(() => {
recommendMusic(); // Mount 시 호출한다.
}, []);
const { title, artist, img } = music;
return (
<div className="App">
<img className="Thumbnail" src={img} alt="Thumbnail" />
{title} {artist}
</div>
);
}
export default App;
현재까지 App.js에 넣어주었던 Api와 호출부, 관련 로직을 Api, types, custom hooks로 분리해주어 가독성도 높이고 다른 페이지에서도 필요하다면 재사용할 수 있도록 수정하는 작업까지 해보았습니다.
그럼 이제 다음 3부에서는 기분, 날씨, 장르 등 원하는 키워드를 선택하는 기능을 만들어서, 디자인까지 진행해보도록 하겠습니다. 그럼 읽어주셔서 감사하고 잘 마무리해서 다음 포스팅까지 올려보겠습니다!
'Programming > FrontEnd' 카테고리의 다른 글
[CSS] Vanilla Extract 사용방법 (2) | 2023.07.31 |
---|---|
[OpenAI] 음악 추천 서비스 만들어보기 (React) 2부 (2) | 2023.02.22 |
[OpenAI] 음악 추천 서비스 만들어보기 (React) 1부 (0) | 2023.02.18 |
[CSS] Flexbox의 여러 활용법 정리 (0) | 2021.08.31 |
[HTML] <table>의 row index와 cell index 구하는 법 (0) | 2021.07.23 |
댓글