Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/#511 프론트의 토큰 만료 검증로직을 삭제하고 Axios를 도입한다. #554

Merged
merged 24 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3ec2b5f
config: axios 설치
Creative-Lee Nov 10, 2023
895be60
feat: axios 인스턴스 및 인터셉터 구현
Creative-Lee Nov 12, 2023
69605c1
refactor: 엑세스 토큰 refresh remote 함수 분리
Creative-Lee Nov 12, 2023
89bdb5f
refactor: 분리되지 않은 type, remote 함수 분리
Creative-Lee Nov 12, 2023
e10bfbe
refactor: remote 함수의 fetcher 의존성 제거 및 인스턴스 적용
Creative-Lee Nov 13, 2023
f2c79ee
refactor: 로그인 remote 함수 분리
Creative-Lee Nov 13, 2023
3ecd234
feat: 메인 페이지 장르별 fetch msw 구현
Creative-Lee Nov 13, 2023
db04ff0
chore: 불필요한 파일 제거
Creative-Lee Nov 13, 2023
a2bbcee
chore: remote 함수 중복 래핑 hook 삭제 및 코드 이동
Creative-Lee Nov 13, 2023
c287cfe
refactor: remote 함수 query parameter 처리 방식 통일
Creative-Lee Nov 13, 2023
d06887a
chore: import 방식 변경
Creative-Lee Nov 13, 2023
a4a83df
chore: auth 관련 remote함수 auth/하위로 이동
Creative-Lee Nov 13, 2023
4f858e9
fix: refresh 요청 API 명세에 맞게 로직 수정
Creative-Lee Nov 13, 2023
09cf9f3
refactor: 최종 만료시 로그인 페이지 리다이렉트 처리
Creative-Lee Nov 14, 2023
e6bfcfe
refactor: 타입 분리
Creative-Lee Nov 14, 2023
744fbff
refactor: 인터셉터 refresh 중복 요청 방지 기능 추가
Creative-Lee Nov 14, 2023
2166a72
refactor: 에러응답 타입 분리
Creative-Lee Nov 14, 2023
8e0a22f
chore: fetcher 및 토큰 만료 검증 파일 제거
Creative-Lee Nov 14, 2023
0fbcfde
refactor: 함수 네이밍 개선
Creative-Lee Nov 15, 2023
6755702
chore: 주석 수정
Creative-Lee Nov 15, 2023
0f87b75
refactor: promise 변수 null 초기화 코드 위치 이동
Creative-Lee Nov 28, 2023
dbf9655
style: promise 변수 라인 변경
Creative-Lee Nov 28, 2023
500cae1
refactor: config type 변경 및 린트 주석 제거
Creative-Lee Dec 9, 2023
ea5e89b
Merge branch 'main' into refactor/#511
Creative-Lee Dec 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@types/styled-components": "^5.1.26",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"axios": "^1.6.1",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.31.1",
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/features/auth/remotes/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { client, clientBasic } from '@/shared/remotes/axios';
import type { AccessTokenRes } from '../types/auth.type';

export const getAccessToken = async (platform: string, code: string) => {
const { data } = await client.get<AccessTokenRes>(`/login/${platform}`, {
params: { code },
});

return data;
};

export const postRefreshAccessToken = async () => {
const { data } = await clientBasic.post<AccessTokenRes>('/reissue');

return data;
};
3 changes: 3 additions & 0 deletions frontend/src/features/auth/types/auth.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface AccessTokenRes {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Res가 Response인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞아요 Res, Req!

accessToken: string;
}
14 changes: 7 additions & 7 deletions frontend/src/features/comments/components/CommentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import Avatar from '@/shared/components/Avatar';
import useModal from '@/shared/components/Modal/hooks/useModal';
import useToastContext from '@/shared/components/Toast/hooks/useToastContext';
import { useMutation } from '@/shared/hooks/useMutation';
import fetcher from '@/shared/remotes';
import { postComment } from '../remotes/comments';

interface CommentFormProps {
getComment: () => Promise<void>;
getComments: () => Promise<void>;
songId: number;
partId: number;
}

const CommentForm = ({ getComment, songId, partId }: CommentFormProps) => {
const CommentForm = ({ getComments, songId, partId }: CommentFormProps) => {
const [newComment, setNewComment] = useState('');
const { isOpen, closeModal: closeLoginModal, openModal: openLoginModal } = useModal();
const { user } = useAuthContext();

const isLoggedIn = !!user;

const { mutateData } = useMutation(() =>
fetcher(`/songs/${songId}/parts/${partId}/comments`, 'POST', { content: newComment.trim() })
const { mutateData: postNewComment } = useMutation(() =>
postComment(songId, partId, newComment.trim())
);

const { showToast } = useToastContext();
Expand All @@ -38,11 +38,11 @@ const CommentForm = ({ getComment, songId, partId }: CommentFormProps) => {
const submitNewComment: React.FormEventHandler<HTMLFormElement> = async (event) => {
event.preventDefault();

await mutateData();
await postNewComment();

showToast('댓글이 등록되었습니다.');
resetNewComment();
getComment();
await getComments();
};

return (
Expand Down
17 changes: 5 additions & 12 deletions frontend/src/features/comments/components/CommentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,24 @@
import Spacing from '@/shared/components/Spacing';
import SRHeading from '@/shared/components/SRHeading';
import useFetch from '@/shared/hooks/useFetch';
import fetcher from '@/shared/remotes';
import { getComments } from '../remotes/comments';
import Comment from './Comment';
import CommentForm from './CommentForm';

interface Comment {
id: number;
content: string;
createdAt: string;
writerNickname: string;
}

interface CommentListProps {
songId: number;
partId: number;
}

const CommentList = ({ songId, partId }: CommentListProps) => {
const { isOpen, openModal, closeModal } = useModal(false);
const { data: comments, fetchData: getComment } = useFetch<Comment[]>(() =>
fetcher(`/songs/${songId}/parts/${partId}/comments`, 'GET')
const { data: comments, fetchData: refetchComments } = useFetch(() =>
getComments(songId, partId)
);

useEffect(() => {
getComment();
refetchComments();
}, [partId]);

Check warning on line 26 in frontend/src/features/comments/components/CommentList.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useEffect has a missing dependency: 'refetchComments'. Either include it or remove the dependency array

if (!comments) {
return null;
Expand Down Expand Up @@ -73,7 +66,7 @@
))}
</Comments>
<Spacing direction="vertical" size={8} />
<CommentForm getComment={getComment} songId={songId} partId={partId} />
<CommentForm getComments={refetchComments} songId={songId} partId={partId} />
</BottomSheet>
</>
);
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/features/comments/remotes/comments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { client } from '@/shared/remotes/axios';
import type { Comment } from '../types/comment.type';

export const postComment = async (songId: number, partId: number, content: string) => {
await client.post(`/songs/${songId}/parts/${partId}/comments`, { content });
};

export const getComments = async (songId: number, partId: number) => {
const { data } = await client.get<Comment[]>(`/songs/${songId}/parts/${partId}/comments`);

return data;
};
6 changes: 6 additions & 0 deletions frontend/src/features/comments/types/comment.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Comment {
id: number;
content: string;
createdAt: string;
writerNickname: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import { useAuthContext } from '@/features/auth/components/AuthProvider';
import useCollectingPartContext from '@/features/killingParts/hooks/useCollectingPartContext';
import { usePostKillingPart } from '@/features/killingParts/remotes/usePostKillingPart';
import useVideoPlayerContext from '@/features/youtube/hooks/useVideoPlayerContext';
import useModal from '@/shared/components/Modal/hooks/useModal';
import Modal from '@/shared/components/Modal/Modal';
import Spacing from '@/shared/components/Spacing';
import { useMutation } from '@/shared/hooks/useMutation';
import { toPlayingTimeText } from '@/shared/utils/convertTime';
import { postKillingPart } from '../remotes/killingPart';

const RegisterPart = () => {
const { isOpen, openModal, closeModal } = useModal();
const { user } = useAuthContext();
const { interval, partStartTime, songId } = useCollectingPartContext();
const video = useVideoPlayerContext();
const { createKillingPart } = usePostKillingPart();
const { mutateData: createKillingPart } = useMutation(postKillingPart);
const navigate = useNavigate();

// 현재 useMutation 훅이 response 객체를 리턴하지 않고 내부적으로 처리합니다.
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/features/killingParts/hooks/killingPart.ts

This file was deleted.

18 changes: 18 additions & 0 deletions frontend/src/features/killingParts/remotes/killingPart.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { client } from '@/shared/remotes/axios';
import type { KillingPartPostRequest } from '@/shared/types/killingPart';
import type { SongInfo } from '@/shared/types/song';

// PartCollectingPage에 존재하던 remote 함수입니다.
// useFetch<SongInfo>(() => fetcher(`/songs/${songId}`, 'GET')) 로직에서 분리하였습니다.
// SongInfo type에는 killingPart[] 필드가 있는데, 마이파트 수집용 `노래 1개` 조회에서 해당 타입이 사용되고 있습니다.
// 추후 수정되어야 합니다.

export const getSong = async (songId: number) => {
const { data } = await client.get<SongInfo>(`/songs/${songId}`);

return data;
};

export const postKillingPart = async (songId: number, body: KillingPartPostRequest) => {
await client.post(`/songs/${songId}/member-parts`, body);
};

This file was deleted.

4 changes: 2 additions & 2 deletions frontend/src/features/member/components/MyPartList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export type LikeKillingPart = Pick<
const MyPartList = () => {
const [tab, setTab] = useState<MyPageTab>('Like');

const { data: likes } = useFetch<LikeKillingPart[]>(getLikeParts);
const { data: myParts } = useFetch<LikeKillingPart[]>(getMyParts);
const { data: likes } = useFetch(getLikeParts);
const { data: myParts } = useFetch(getMyParts);
Comment on lines -22 to +23
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 제네릭이 필요 없어졌다면 useFetch도 수정해야되지 않을까요?

Copy link
Collaborator Author

@Creative-Lee Creative-Lee Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 식으로 수정이 되어야할까요? 생각나는 방향이 있으시면 같이 알려주세용
제네릭 없이도 useFetch data 타입이 추론 될지 모르겠네용


if (!likes || !myParts) {
return null;
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/features/member/remotes/member.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';

export const deleteMember = (memberId: number | undefined) => () => {
return fetcher(`/members/${memberId}`, 'DELETE');
export const deleteMember = async (memberId: number) => {
await client.delete(`/members/${memberId}`);
};
6 changes: 4 additions & 2 deletions frontend/src/features/member/remotes/memberParts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';

export const deleteMemberParts = (partId: number) => fetcher(`/member-parts/${partId}`, 'DELETE');
export const deleteMemberParts = async (partId: number) => {
await client.delete(`/member-parts/${partId}`);
};
15 changes: 12 additions & 3 deletions frontend/src/features/member/remotes/myPage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';
import type { LikeKillingPart } from '../components/MyPartList';

export const getLikeParts = () => fetcher('/my-page/like-parts', 'GET');
export const getLikeParts = async () => {
const { data } = await client.get<LikeKillingPart[]>('/my-page/like-parts');

export const getMyParts = () => fetcher('/my-page/my-parts', 'GET');
return data;
};

export const getMyParts = async () => {
const { data } = await client.get<LikeKillingPart[]>('/my-page/my-parts');

return data;
};
26 changes: 19 additions & 7 deletions frontend/src/features/search/remotes/search.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';
import type { SingerDetail } from '../../singer/types/singer.type';
import type { SingerSearchPreview } from '../types/search.type';

export const getSingerSearchPreview = async (query: string): Promise<SingerSearchPreview[]> => {
const encodedQuery = encodeURIComponent(query);
return await fetcher(`/search?keyword=${encodedQuery}&type=singer`, 'GET');
export const getSingerSearchPreview = async (query: string) => {
const { data } = await client.get<SingerSearchPreview[]>(`/search`, {
params: {
keyword: query,
type: 'singer',
},
});

return data;
};

export const getSingerSearch = async (query: string): Promise<SingerDetail[]> => {
const encodedQuery = encodeURIComponent(query);
return await fetcher(`/search?keyword=${encodedQuery}&type=singer&type=song`, 'GET');
export const getSingerSearch = async (query: string) => {
const params = new URLSearchParams();
params.append('keyword', query);
params.append('type', 'singer');
params.append('type', 'song');

Comment on lines +17 to +20
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

axios는 params 값으로 URLSearchParams 인스턴스도 받아용
중복되는 param는 바로 위 remote 함수와 같은 방식으로 넣을 수 없기에 사용했습니다~

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중복되는 param는 바로 위 remote 함수와 같은 방식으로 넣을 수 없기에 사용했습니다~

💬 안되는건가요? 아래와 같은 방법으로 충분히 되는 것 같아요.

// 1 string
const params = new URLSearchParams(`keyword=${query}&type=singer&type=song`);

// 2 array
const params = new URLSearchParams([
  ['keyword', query],
  ['type', 'singer'],
  ['type', 'song'],
]);

Copy link
Collaborator Author

@Creative-Lee Creative-Lee Dec 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바로 위에 있는 remote함수와 같은 방식으로 넣을 수 없어서 URLSearchParams 를 썻다~ 가 핵심입니다 ㅋㅋ
제시해주신 방법도 충분히 가능합니다 ㅋㅋ

const { data } = await client.get<SingerDetail[]>(`/search`, { params });

return data;
};
8 changes: 5 additions & 3 deletions frontend/src/features/singer/remotes/singer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';
import type { SingerDetail } from '../types/singer.type';

export const getSingerDetail = async (singerId: number): Promise<SingerDetail> => {
return await fetcher(`/singers/${singerId}`, 'GET');
export const getSingerDetail = async (singerId: number) => {
const { data } = await client.get<SingerDetail>(`/singers/${singerId}`);

return data;
};
4 changes: 2 additions & 2 deletions frontend/src/features/songs/components/SongItem.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import popularSongs from '@/mocks/fixtures/popularSongs.json';
import highLikedSongs from '@/mocks/fixtures/highLikedSongs.json';
import SongItem from './SongItem';
import type { Meta, StoryObj } from '@storybook/react';

Expand All @@ -11,7 +11,7 @@ export default meta;

type Story = StoryObj<typeof SongItem>;

const { title, singer, albumCoverUrl, totalLikeCount } = popularSongs[0];
const { title, singer, albumCoverUrl, totalLikeCount } = highLikedSongs[0];

export const Default: Story = {
args: {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/features/songs/remotes/likes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';

export const putKillingPartLikes = async (songId: number, partId: number, likeStatus: boolean) => {
return await fetcher(`/songs/${songId}/parts/${partId}/likes`, 'PUT', { likeStatus });
await client.put(`/songs/${songId}/parts/${partId}/likes`, { likeStatus });
};
18 changes: 11 additions & 7 deletions frontend/src/features/songs/remotes/song.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import fetcher from '@/shared/remotes';
import { client } from '@/shared/remotes/axios';
import type { Genre, Song } from '../types/Song.type';
import type { RecentSong } from '@/shared/types/song';

// 메인 케러셀 최신순 노래 n개 조회 api - 쿼리파람 없는경우, 응답 기본값은 5개입니다.
export const getRecentSongs = async (songCount?: number): Promise<RecentSong[]> => {
const query = songCount ? `?size=${songCount}` : '';
export const getRecentSongs = async (songCount?: number) => {
const { data } = await client.get<RecentSong[]>(`/songs/recent`, {
params: { size: songCount },
});

return await fetcher(`/songs/recent${query}`, 'GET');
return data;
};

export const getHighLikedSongs = async (genre: Genre): Promise<Song[]> => {
const query = genre === 'ALL' ? '' : `?genre=${genre}`;
export const getHighLikedSongs = async (genre: Genre) => {
const { data } = await client.get<Song[]>(`/songs/high-liked`, {
params: { genre: genre === 'ALL' ? null : genre },
});

return await fetcher(`/songs/high-liked${query}`, 'GET');
return data;
};
Loading
Loading