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

feat: Next / TypeScript / React Query / Zustand / Vanilla Extract를 사용하여 Real World 1차 기능 구현을 완료합니다. #155

Merged
merged 50 commits into from
May 16, 2024
Merged
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
f59fd0f
feat: review-assign-action.yml 깃헙 액션을 추가합니다.
InSeong-So Sep 2, 2023
4d80c9f
Update review-assign-action.yml
InSeong-So Sep 7, 2023
e577cd8
Update review-assign-action.yml
InSeong-So Sep 7, 2023
4bedb3a
Update review-assign-action.yml
InSeong-So Sep 7, 2023
6ea21d9
Update review-assign-action.yml
InSeong-So Sep 7, 2023
daad074
Update review-assign-action.yml
InSeong-So Sep 7, 2023
dab9319
feat: useInfiniteQuery test
hyeon9782 Sep 14, 2023
7681666
Merge branch 'team4/jeong-ho'
hyeon9782 Sep 14, 2023
9419f91
refactor & feat: 효율적인 fetch 사용을 위한 fetch 래핑 및 axios에서 fetch로 전환
hyeon9782 Sep 14, 2023
d1f6b43
feat: ArticleList useIntersectionObserver 커스텀 훅을 통한 무한 스크롤 기능 구현
hyeon9782 Sep 15, 2023
89b8039
refactor: 무한 스크롤 기능 useInfiniteScroll로 비즈니스 로직 분리
hyeon9782 Sep 15, 2023
50f225e
refactor: signup , login , logout 기능 route handler를 사용하여 리팩토링 및 middl…
hyeon9782 Sep 18, 2023
fc4f103
feat & refactor: route handler를 이용한 user api 연동
hyeon9782 Sep 18, 2023
32bceb4
feat: react query를 사용한 로그인
hyeon9782 Sep 26, 2023
1dc77fb
feat: tag로 article 검색 기능 구현
hyeon9782 Sep 26, 2023
209d8ab
refactor: useUserStore로 로그인 로그아웃 기능 수정
hyeon9782 Sep 26, 2023
04c1062
feat: useArticles 커스텀 훅을 통해 Articles 관련 데이터 패칭 처리
hyeon9782 Sep 28, 2023
77d9718
fix: useArticles 데이터가 적을 경우 생기는 에러 해결
hyeon9782 Sep 29, 2023
6f9e355
feat: follow 기능 구현
hyeon9782 Sep 29, 2023
d47e250
feat: Banner 컴포넌트 login 했을 때 home화면에서 안보이게 수정
hyeon9782 Sep 29, 2023
b7d3495
feat: 게시글 작성 기능 구현
hyeon9782 Oct 1, 2023
12f44e4
feat: getArticlesWithAuthorAPI, getArticlesWithFavoritedAPI 추가
hyeon9782 Oct 4, 2023
be02273
ffeat: TagList 컴포넌트 기능 수정 및 useCurrentTab 타입 추가
hyeon9782 Oct 4, 2023
a761449
fix: useUserStore 타입스크립트 에러 수정
hyeon9782 Oct 4, 2023
e140ded
fix: TagInput 컴포넌트 에러 수정
hyeon9782 Oct 4, 2023
eb70a6f
feat: my, favorited articles 조회 기능 추가
hyeon9782 Oct 4, 2023
a37fb97
feat: 게시글 작성 기능 구현
hyeon9782 Oct 4, 2023
c6b0550
feat: 좋아요, 좋아요 취소 기능 구현
hyeon9782 Oct 4, 2023
87fbd0a
feat: 프로필 정보 가져오기 기능 구현
hyeon9782 Oct 4, 2023
e9954cb
feat: profile 페이지 기능 구현
hyeon9782 Oct 4, 2023
1933758
feat: Article 수정 페이지 구현
hyeon9782 Oct 4, 2023
d10e622
feat: Article 삭제 기능 구현
hyeon9782 Oct 4, 2023
ef5e2b2
feat: Article 수정 기능 구현
hyeon9782 Oct 4, 2023
6aad78c
feat: 댓글 달기 기능 구현
hyeon9782 Oct 4, 2023
f6cda8e
feat: 댓글 삭제 기능 구현
hyeon9782 Oct 4, 2023
2819603
feat: 회원 정정보 수정 기능 구현
hyeon9782 Oct 4, 2023
aa6dfaf
feat: Follow 기능 구현
hyeon9782 Oct 5, 2023
b1539e1
feat: LoginForm 컴포넌트 useLogin 커스텀 훅 구현
hyeon9782 Oct 5, 2023
518e466
feat & refactor: Login, Register Page 리팩토링 및 useAuth 구현
hyeon9782 Oct 5, 2023
3f5c018
refactor & feat: SettingForm , LogoutButton 컴포넌트 생성
hyeon9782 Oct 7, 2023
e077855
fix: 회원가입 기능 route handler 수정
hyeon9782 Oct 7, 2023
35edc4f
feat & refactor: useProfile 커스텀 훅 구현
hyeon9782 Oct 7, 2023
2a38d12
refactor: favorite, unfavorite 기능 useArticles에 포함
hyeon9782 Oct 7, 2023
a0c45ce
fix: favorite articles에 token값 추가
hyeon9782 Oct 7, 2023
50dcc3e
refactor: ProfileBox 컴포넌트 구현
hyeon9782 Oct 7, 2023
d98a93d
fix: follow 기능 에러 해결
hyeon9782 Oct 8, 2023
33b5c7a
feat & refactor: EditForm으로 수정 페이지와 작성 페이지 수정
hyeon9782 Oct 8, 2023
0720993
fix: ArticlesList 에러 해결
hyeon9782 Oct 8, 2023
ec99a30
feat: Type 세분화 및 적용
hyeon9782 Oct 8, 2023
7f488cc
Update README.md
hyeon9782 Oct 18, 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
Prev Previous commit
Next Next commit
feat: Type 세분화 및 적용
hyeon9782 committed Oct 8, 2023

Verified

This commit was signed with the committer’s verified signature.
yanmxa Meng Yan
commit ec99a309a987473aa37f7b81287245080c08c22d
Empty file removed api/articles.ts
Empty file.
15 changes: 0 additions & 15 deletions app/api/comments/[slug]/route.ts
Original file line number Diff line number Diff line change
@@ -13,8 +13,6 @@ async function GET(req: NextRequest, route: { params: { slug: string } }) {
},
});

console.log(res);

return NextResponse.json({ message: 'Comment Get Success', success: true, data: res });
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 400 });
@@ -26,10 +24,6 @@ async function POST(req: NextRequest, route: { params: { slug: string } }) {
const body = await req.json();
const slug = route.params.slug;
const token = req.cookies.get('token')?.value || '';
console.log(body);

console.log(slug);
console.log(token);

const res = await http.post(`/articles/${slug}/comments`, body, {
headers: {
@@ -38,8 +32,6 @@ async function POST(req: NextRequest, route: { params: { slug: string } }) {
},
});

console.log(res);

return NextResponse.json({ message: 'Comment Create Success', success: true, data: res });
} catch (error: any) {
return NextResponse.json({ error: error.message }, { status: 400 });
@@ -55,19 +47,12 @@ async function DELETE(req: NextRequest, route: { params: { slug: string } }) {

const token = req.cookies.get('token')?.value || '';

console.log(slug);
console.log(token);
console.log('삭제 전');

const res = await http.delete(`/articles/${slug}/comments/${id}`, {
headers: {
'Content-Type': 'application/json; charset=utf-8',
Authorization: `Token ${token}`,
},
});
console.log('삭제 후');

console.log(res);

return NextResponse.json({ message: 'Comment Delete Success', success: true, data: res });
} catch (error: any) {
10 changes: 4 additions & 6 deletions app/api/user/route.ts
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@ import { NextRequest, NextResponse } from 'next/server';
import { getUserAPI, updateUserAPI } from '@/services/users';

export async function GET(req: NextRequest) {
const { value } = req.cookies.get('token');
const token = req.cookies.get('token')?.value || '';

const { user } = await getUserAPI(value);
const { user } = await getUserAPI(token);

return NextResponse.json({
message: 'Login successfull',
@@ -14,10 +14,8 @@ export async function GET(req: NextRequest) {
}

export async function PUT(req: NextRequest) {
const { value } = req.cookies.get('token');
const token = req.cookies.get('token')?.value || '';
const user = await req.json();
console.log('Route');
console.log(user);

return updateUserAPI(user, value);
return updateUserAPI(user, token);
}
2 changes: 0 additions & 2 deletions app/loading.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';

const HomeLoading = () => {
return <div>HomeLoading...</div>;
};
9 changes: 5 additions & 4 deletions components/account/SignInForm.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@ import useUserStore from '@/stores/useUserStore';
import { form } from '@/styles/account.css';
import { fillGreenButton, input } from '@/styles/common.css';
import { buttonBox } from '@/styles/layout.css';
import { LoginUser, UserAction } from '@/types';
import { LoginUser, UserResponse } from '@/types/api/users';
import { UserAction } from '@/types/store/userStore';
import { useRouter } from 'next/navigation';
import { ChangeEvent, FormEvent, useState } from 'react';

@@ -17,13 +18,13 @@ const SignInForm = () => {
password: '',
});

const loginSuccess = (res: any) => {
const loginSuccess = (res: UserResponse) => {
saveUserInfo({ ...res.user });
router.push('/');
};

const loginError = (err: any) => {
console.log(err.message);
const loginError = (err: Error) => {
console.error(err.message);
alert('이메일 또는 비밀번호가 잘못되었습니다.');
};

8 changes: 3 additions & 5 deletions components/account/SignUpForm.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,9 @@ import useUserStore from '@/stores/useUserStore';
import { form } from '@/styles/account.css';
import { fillGreenButton, input } from '@/styles/common.css';
import { buttonBox } from '@/styles/layout.css';
import { NewUser, UserAction } from '@/types';
import { NewUser } from '@/types/api/users';
import { UserAction } from '@/types/store/userStore';

import { useRouter } from 'next/navigation';
import { ChangeEvent, FormEvent, useState } from 'react';

@@ -19,10 +21,6 @@ const SignUpForm = () => {
});

const signupSuccess = (res: any) => {
console.log('Client');

console.log(res);

saveUserInfo({
...res.user,
});
3 changes: 1 addition & 2 deletions components/article/ArticleDeleteButton.tsx
Original file line number Diff line number Diff line change
@@ -5,8 +5,7 @@ import { useRouter } from 'next/navigation';
const ArticleDeleteButton = ({ slug }: { slug: string }) => {
const router = useRouter();
const handleButtonClick = async () => {
const res = await fetch(`/api/articles/${slug}`, { method: 'DELETE' }).then(res => res.json());
console.log(res);
await fetch(`/api/articles/${slug}`, { method: 'DELETE' }).then(res => res.json());
router.push('/');
};
return (
3 changes: 2 additions & 1 deletion components/article/ArticleList.tsx
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import React, { useRef } from 'react';
import { flexCenter } from '@/styles/common.css';
import useCurrentTab from '@/stores/useCurrentTab';
import useArticles from '@/hooks/useArticles';
import { Article } from '@/types/api/articles';
type Props = {
username?: string;
};
@@ -21,7 +22,7 @@ const ArticleList = ({ username }: Props) => {
<div>
{articlesData?.pages.map((group, i) => (
<div key={i}>
{group?.articles?.map(article => <ArticlePreview key={article.slug} article={article} />)}
{group?.articles?.map((article: Article) => <ArticlePreview key={article.slug} article={article} />)}
</div>
))}
</div>
6 changes: 2 additions & 4 deletions components/article/ArticlePreview.tsx
Original file line number Diff line number Diff line change
@@ -20,14 +20,12 @@ const ArticlePreview = ({

const queryClient = useQueryClient();

const onSuccess = (res: any) => {
console.log(res);
const onSuccess = () => {
queryClient.invalidateQueries({ queryKey: ['articles', tab] });
};

const onError = (err: any) => {
const onError = () => {
// 권한이 없을 경우 login 페이지로 이동
console.log(err);
router.push('/login');
};

2 changes: 1 addition & 1 deletion components/article/ArticleTab.tsx
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
import useCurrentTab from '@/stores/useCurrentTab';
import useUserStore from '@/stores/useUserStore';
import { articleTab, articleTabItem, articleTabItemActivate, articleTabItemDisable } from '@/styles/article.css';
import { User } from '@/types';
import { User } from '@/types/api/users';
import { usePathname } from 'next/navigation';
import { useEffect } from 'react';

7 changes: 3 additions & 4 deletions components/comments/CommentBox.tsx
Original file line number Diff line number Diff line change
@@ -5,8 +5,9 @@ import React from 'react';
import CommentForm from './CommentForm';
import CommentCard from './CommentCard';
import { flexCenter, flexRow, textCenter } from '@/styles/common.css';
import { User } from '@/types';
import { useQuery } from '@tanstack/react-query';
import { User } from '@/types/api/users';
import { Comment } from '@/types/api/comment';

const CommentBox = ({ slug }: { slug: string }) => {
const { email } = useUserStore() as User;
@@ -16,15 +17,13 @@ const CommentBox = ({ slug }: { slug: string }) => {
select: res => res.data.comments,
});

console.log(comments);

return (
<div>
{email ? (
<div className={`${flexRow} ${flexCenter}`}>
<CommentForm slug={slug} />
<div className={flexRow}>
{comments.map(comment => (
{comments.map((comment: Comment) => (
<CommentCard key={comment.id} comment={comment} slug={slug} />
))}
</div>
6 changes: 4 additions & 2 deletions components/comments/CommentCard.tsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@ import { TrashIcon } from '@/composables/icons';
import useUserStore from '@/stores/useUserStore';
import { commentContent, commentFormFooter, commnetCard } from '@/styles/comments.css';
import { circle, flexCenter } from '@/styles/common.css';
import { Comment, User } from '@/types';
import { Comment } from '@/types/api/comment';
import { User } from '@/types/api/users';

import { formatDate } from '@/utils';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import Image from 'next/image';
@@ -37,7 +39,7 @@ const CommentCard = ({ comment, slug }: Props) => {
height={20}
alt="iamge"
/>
&nbsp; {comment.author.username} {formatDate(comment.createAt)}
&nbsp; {comment.author.username} {formatDate(comment.createdAt)}
</div>
{comment.author.username === username && <TrashIcon onClick={() => handleTrashClick(slug)} />}
</div>
2 changes: 1 addition & 1 deletion components/comments/CommentForm.tsx
Original file line number Diff line number Diff line change
@@ -24,7 +24,6 @@ const CommentForm = ({ slug }: { slug: string }) => {
});
const handleSubmit = (e: any) => {
e.preventDefault();
console.log(comment);

mutate(e.target.comment.value);
setComment('');
@@ -36,6 +35,7 @@ const CommentForm = ({ slug }: { slug: string }) => {
name="comment"
className={commentTextarea}
placeholder="Write a comment..."
value={comment}
onChange={e => setComment(e.target.value)}
></textarea>
<div className={commentFormFooter}>
9 changes: 3 additions & 6 deletions components/editor/EditForm.tsx
Original file line number Diff line number Diff line change
@@ -6,8 +6,9 @@ import { editorButton, editorForm } from '@/styles/editor.css';
import TagInput from './TagInput';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { NewArticle } from '@/types';

import { useRouter } from 'next/navigation';
import { NewArticle } from '@/types/api/articles';

const EditForm = ({ slug }: { slug?: string }) => {
const queryClient = useQueryClient();
@@ -39,15 +40,11 @@ const EditForm = ({ slug }: { slug?: string }) => {
);
}
},
onSuccess: data => {
console.log('등록 성공');

console.log(data);
onSuccess: () => {
queryClient.invalidateQueries(['articles', 'global']);
router.push('/');
},
onError: (error: any) => {
console.log('에러 발생');
console.error(error);
},
});
2 changes: 0 additions & 2 deletions components/editor/TagInput.tsx
Original file line number Diff line number Diff line change
@@ -21,8 +21,6 @@ const TagInput = ({ appendTag }: Props) => {
};

const handleTagClick = (tag: string) => {
console.log('쿨릭');

setTags((prevTags: string[]) => prevTags.filter(prevTag => prevTag !== tag));
};

7 changes: 5 additions & 2 deletions components/layouts/Banner.tsx
Original file line number Diff line number Diff line change
@@ -2,7 +2,8 @@
import useUserStore from '@/stores/useUserStore';
import { backgroundBlack, backgroundGreen, container } from '@/styles/common.css';
import { banner } from '@/styles/home.css';
import { User } from '@/types';
import { User } from '@/types/api/users';

import { ReactNode } from 'react';
type Props = {
children: ReactNode;
@@ -11,7 +12,9 @@ type Props = {
const Banner = ({ children, background }: Props) => {
const { email } = useUserStore() as User;
return email && background === 'green' ? (
<div></div>
<div>
<div></div>
</div>
) : (
<div className={`${banner} ${background === 'green' ? backgroundGreen : backgroundBlack}`}>
<div className={container}>{children}</div>
2 changes: 1 addition & 1 deletion components/layouts/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import { userImageSm } from '@/styles/profile.css';
import Image from 'next/image';
import { EditIcon, SettingIcon } from '@/composables/icons';
import useUserStore from '@/stores/useUserStore';
import { User } from '@/types';
import { User } from '@/types/api/users';

const NAVS = [
{
2 changes: 1 addition & 1 deletion components/profile/ProfileBox.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SettingIcon } from '@/composables/icons';
import useUserStore from '@/stores/useUserStore';
import { settingButton, userBlock, userImage, userInfo, userName } from '@/styles/profile.css';
import { User } from '@/types';
import Image from 'next/image';
import Link from 'next/link';
import FollowButton from '../user/FollowButton';
import { User } from '@/types/api/users';
type Props = {
image: string;
username: string;
7 changes: 4 additions & 3 deletions components/settings/LogoutButton.tsx
Original file line number Diff line number Diff line change
@@ -3,18 +3,19 @@ import useAuth from '@/hooks/useAuth';
import useUserStore from '@/stores/useUserStore';
import { flex } from '@/styles/common.css';
import { logoutButton } from '@/styles/settings.css';
import { UserAction } from '@/types';
import { UserAction } from '@/types/store/userStore';

import { useRouter } from 'next/navigation';

const LogoutButton = () => {
const router = useRouter();
const { logout } = useUserStore() as UserAction;
const signoutSuccess = (res: any) => {
const signoutSuccess = () => {
logout();
router.push('/login');
};

const signoutError = (err: any) => {
const signoutError = (err: Error) => {
console.error(err.message);
};

4 changes: 1 addition & 3 deletions components/settings/SettingForm.tsx
Original file line number Diff line number Diff line change
@@ -24,9 +24,7 @@ const SettingForm = () => {
const { mutate, isLoading } = useMutation({
mutationFn: (formData: any) =>
fetch('/api/auth/user', { method: 'PUT', body: JSON.stringify(formData) }).then(res => res.json()),
onSuccess: data => {
console.log(data);
console.log('성공');
onSuccess: () => {
updateUser({
...formData,
});
6 changes: 2 additions & 4 deletions components/user/FollowButton.tsx
Original file line number Diff line number Diff line change
@@ -13,13 +13,11 @@ const FollowButton = ({ author: { username, following }, slug }: Props) => {
const router = useRouter();
const queryClient = useQueryClient();

const onSuccess = (res: any) => {
console.log(res);
const onSuccess = () => {
queryClient.invalidateQueries(['article', slug]);
};

const onError = (err: any) => {
console.error(err);
const onError = () => {
router.push('/login');
};

Loading