Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 1 addition & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,4 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
.next/
4 changes: 2 additions & 2 deletions components/AllItemsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useEffect, useState } from 'react';
import { getProducts } from '../lib/api';
import ItemCard from './ItemCard';
import ItemCard from '@components/ItemCard';
import '../style/Items.css';
import SearchIcon from '../public/svgs/ic_search.svg';
import SearchIcon from '@public/svgs/ic_search.svg';
import {
Product,
ProductListResponse,
Expand Down
4 changes: 2 additions & 2 deletions components/BestItemsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { getProducts } from '../lib/api';
import ItemCard from './ItemCard';
import { getProducts } from '@lib/api';
import ItemCard from '@components/ItemCard';
import '../style/Items.css';
import {
Product,
Expand Down
6 changes: 3 additions & 3 deletions components/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import SortIcon from '../public/svgs/ic_sort.svg';
import styles from '../styles/DropdownMenu.module.css';
import { ArticleOrderBy } from './types/articleTypes';
import SortIcon from '@public/svgs/ic_sort.svg';
import styles from '@styles/DropdownMenu.module.css';
import { ArticleOrderBy } from '@components/types/articleTypes';
Comment on lines +2 to +4
Copy link
Collaborator

Choose a reason for hiding this comment

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

리뷰 반영 굳 입니다! 👍


interface DropdownMenuProps {
onSelection: (Option: ArticleOrderBy) => void;
Expand Down
9 changes: 5 additions & 4 deletions components/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import styles from '../styles/FileInput.module.css';
import PlusIcon from '../public/svgs/ic_plus.svg';
import DeleteIcon from '../public/svgs/ic_X.svg';
import styles from '@styles/FileInput.module.css';
import PlusIcon from '@public/svgs/ic_plus.svg';
import DeleteIcon from '@public/svgs/ic_X.svg';

interface FileInputProps {
name: string;
Expand Down Expand Up @@ -56,6 +56,7 @@ function FileInput({ name, value, initialPreview, onChange }: FileInputProps) {
onChange={handleChange}
ref={inputRef}
disabled={isImageValid}
style={{ display: 'none' }}
/>
<label htmlFor="file" className={styles['filelabel']}>
<PlusIcon />
Expand All @@ -64,7 +65,7 @@ function FileInput({ name, value, initialPreview, onChange }: FileInputProps) {
{value && (
<div className={styles['preview-container']}>
<img
className="image-preview"
className={styles['image-preview']}
src={preview || ''}
alt="이미지 미리보기"
/>
Expand Down
4 changes: 2 additions & 2 deletions components/ItemCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import '../style/Items.css';
import Heart from '../images/ic_heart.svg';
import { Product } from './types/productTypes';
import Heart from '@pubilc/svgs/ic_heart.svg';
import { Product } from '@components/types/productTypes';
import Link from 'next/link';

interface ItemCardProps {
Expand Down
10 changes: 5 additions & 5 deletions components/ItemComment.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import axiosInstance from '../lib/axiosInstance';
import Profile from '../public/svgs/ic_profile.svg';
import EmptyComment from '../pubilc/svgs/Img_inquiry_empty.svg';
import axiosInstance from '@lib/axiosInstance';
import Profile from '@public/svgs/ic_profile.svg';
import EmptyComment from '@pubilc/svgs/Img_inquiry_empty.svg';
import '../style/ItemComment.css';
import DropdownMenu from './DropdownMenu';
import { Product } from './types/productTypes';
import DropdownMenu from '@components/DropdownMenu';
import { Product } from '@components/types/productTypes';

interface Comment {
id: number;
Expand Down
6 changes: 3 additions & 3 deletions components/Layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import styles from '../../styles/Header.module.css';
import styles from '@styles/Header.module.css';
import Image from 'next/image';

const Header = () => {
Expand All @@ -19,7 +19,7 @@ const Header = () => {
/>
</Link>
<div className={styles['nav-link']}>
<Link className={styles['nav-list']} href="/boards">
<Link className={styles['nav-list']} href="/board">
자유게시판
</Link>
<Link
Expand All @@ -34,7 +34,7 @@ const Header = () => {
</Link>
</div>
</div>
<Link className={styles['login']} href="/signin">
<Link className={styles['login']} href="/login">
로그인
</Link>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import Header from './Header';
import Header from '@components/Layout/Header';
import { useRouter } from 'next/router';

interface LayoutProps {
Expand Down
4 changes: 2 additions & 2 deletions components/ProductCreateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import '../style/FileInput.css';
import FileInput from './FileInput';
import FileInput from '@components/FileInput';
import { useState, useEffect, ChangeEvent } from 'react';
import TagInput from './TagInput';
import TagInput from '@components/TagInput';

interface ProductCreateFormProps {
initialValues?: {
Expand Down
4 changes: 2 additions & 2 deletions components/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SearchIcon from '../public/svgs/ic_search.svg';
import SearchIcon from '@public/svgs/ic_search.svg';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import styles from '../styles/SearchBar.module.css';
import styles from '@styles/SearchBar.module.css';

interface SearchBarProps {
onSearch: (keyword: string) => void;
Expand Down
6 changes: 1 addition & 5 deletions components/TagInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import '../style/ProductCreateForm.css';
import DeleteIcon from '../public/svgs/ic_X.svg';
import DeleteIcon from '@public/svgs/ic_X.svg';
import '../style/TagInput.css';

interface TagInputProps {
Expand All @@ -27,10 +27,6 @@ function TagInput({ value, onChange }: TagInputProps) {
onChange(newTags); // 태그 삭제 시 부모 컴포넌트에 알림
};

useEffect(() => {
console.log('Tags updated:', tags);
}, [tags]);

return (
<div className="ProductTag section-title">
태그
Expand Down
22 changes: 13 additions & 9 deletions components/boards/AllArticlesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useEffect, useRef, useState } from 'react';
import styles from '../../styles/AllArticlesSection.module.css';
import { getAllArticles } from '../../pages/api/boardApi';
import styles from '@styles/AllArticlesSection.module.css';
import { getAllArticles } from '@pages/api/boardApi';
import Link from 'next/link';
import Image from 'next/image';
import Heart from '../../public/svgs/ic_heart.svg';
import SearchBar from '../SearchBar';
import Heart from '@public/svgs/ic_heart.svg';
import SearchBar from '@components/SearchBar';
import router, { useRouter } from 'next/router';
import DropdownMenu from '../DropdownMenu';
import { ArticleList, ArticleOrderBy } from '../types/articleTypes';
import DropdownMenu from '@components/DropdownMenu';
import { ArticleList, ArticleOrderBy } from '@components/types/articleTypes';
import React from 'react';

interface AllArticlesSectionProps {
Expand Down Expand Up @@ -148,13 +148,17 @@ export default function AllArticlesSection({
{articles.length > 0 ? (
<div className={styles['allarticle-list']}>
{articles.map((article) => (
<div key={article.id} className={styles['allarticle-item']}>
<Link
href={`/board/${article.id}`}
key={article.id}
className={styles['allarticle-item']}
>
<div className={styles['allarticle-body']}>
<h2 className={styles['allarticle-title']}>{article.title}</h2>
<Image
width={46}
height={46}
src={article.image || '/images/Img_home_02.png'}
src={article.image || '/pngs/Img_home_02.png'}
alt="게시글 이미지"
className={styles['allarticle-image']}
/>
Expand All @@ -173,7 +177,7 @@ export default function AllArticlesSection({
{article.likeCount}
</span>
</div>
</div>
</Link>
))}
</div>
) : (
Expand Down
46 changes: 46 additions & 0 deletions components/boards/ArticleContentSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import styles from '@styles/ArticleContentSection.module.css';
import { ArticleList } from '@/components/types/articleTypes';
import Heart from '@public/svgs/ic_heart.svg';
import Profile from '@public/svgs/ic_profile.svg';

interface ArticlesSectionProps {
article: ArticleList;
}

export default function ArticleContentSection({
article,
}: ArticlesSectionProps) {
const formatDate = (isoDate: string) => {
const date = new Date(isoDate);
return date
.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
.replace(/\. /g, '. ')
.slice(0, -1); // 공백 제거
};

return (
<div className={styles['article-section']}>
<div className={styles['article-title']}>{article.title}</div>
<div className={styles['article-info']}>
<div className={styles['article-userinfo']}>
<Profile />
<div className={styles['article-nickname']}>
{article.writer.nickname}
</div>
<div className={styles['article-createdAt']}>
{formatDate(article.createdAt)}
</div>
</div>
<div className={styles['article-likecount']}>
<Heart />
{article.likeCount}
</div>
</div>
<div className={styles['article-content']}>{article.content}</div>
</div>
);
}
10 changes: 5 additions & 5 deletions components/boards/BestArticlesSection.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { getBestArticles } from '../../pages/api/boardApi';
import useDimensions from '../../hooks/useDimensions';
import styles from '../../styles/BestArticlesSection.module.css';
import { getBestArticles } from '@pages/api/boardApi';
import useDimensions from '@/hooks/useDimensions';
import styles from '@styles/BestArticlesSection.module.css';
import Image from 'next/image';
import Heart from '../../public/svgs/ic_heart.svg';
import Medal from '../../public/svgs/ic_medal.svg';
import Heart from '@public/svgs/ic_heart.svg';
import Medal from '@public/svgs/ic_medal.svg';

interface ArticleList {
id: number;
Expand Down
49 changes: 49 additions & 0 deletions contexts/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createContext, useContext, useState, ReactNode } from 'react';
import axiosInstance from '@lib/axiosInstance';

interface AuthContextType {
login: (credentials: { email: string; password: string }) => Promise<void>;
}

const AuthContext = createContext<AuthContextType>({
login: async () => {},
});

interface AuthProviderProps {
children: ReactNode;
}

export function AuthProvider({ children }: AuthProviderProps) {
async function login(credentials: { email: string; password: string }) {
try {
const response = await axiosInstance.post('/auth/signIn', credentials);
const { accessToken } = response.data;

localStorage.setItem('accessToken', accessToken);
console.log('로그인 성공');
} catch (error) {
console.error('로그인 실패:', error);
throw new Error('로그인에 실패했습니다.');
}
}

return (
<AuthContext.Provider
value={{
login,
}}
>
{children}
</AuthContext.Provider>
);
}

export function useAuth() {
const context = useContext(AuthContext);

if (!context) {
throw new Error('반드시 AuthProvider 안에서 사용해야 합니다.');
}

return context;
}
Comment on lines +16 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

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

context API를 사용할 때는, value를 useMemo로 감싸서 불필요한 리렌더링 방지 처리를 꼭 해줘야해요.
라이브러리(redux, zustand 등) 을 사용 안하게 될 땐, 리랜더링 최적화를 저희가 직접 챙겨야하거든요.

const value = useMemo(() => ({
    login,
  }), [login]);

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );

Copy link
Collaborator

Choose a reason for hiding this comment

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

useMemo뿐 아니라, useCallback도 추가할 수 있습니다.

const login = useCallback(async (credentials: { email: string; password: string }) => {
    try {
      const response = await axiosInstance.post('/auth/signIn', credentials);
      const { accessToken } = response.data;

      localStorage.setItem('accessToken', accessToken);
      console.log('로그인 성공');
    } catch (error) {
      console.error('로그인 실패:', error);
      throw new Error('로그인에 실패했습니다.');
    }
  }, []);

https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-constructed-context-values.md

1 change: 0 additions & 1 deletion lib/axiosInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import axios from 'axios';

const axiosInstance = axios.create({
baseURL: 'https://panda-market-api.vercel.app',
timeout: 2000,
});

export default axiosInstance;
1 change: 1 addition & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const nextConfig = {
'via.placeholder.com',
'example.com',
'sprint-fe-project.s3.ap-northeast-2.amazonaws.com',
'ibb.co',
],
},
reactStrictMode: true,
Expand Down
2 changes: 1 addition & 1 deletion pages/AddItems.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ProductCreateForm from '../components/ProductCreateForm';
import ProductCreateForm from '@components/ProductCreateForm';

function AddItems() {
return (
Expand Down
11 changes: 5 additions & 6 deletions pages/ItemDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useLocation, useNavigate } from 'react-router-dom';
import '../style/ItemDetail.css';
import { ReactComponent as BackIcon } from '../images/ic_back.svg';
import { ReactComponent as Heart } from '../images/ic_heart.svg';
import { ReactComponent as Profile } from '../images/ic_profile.svg';
import BackIcon from '@public/svgs/ic_back.svg';
import Heart from '@public/svgs/ic_heart.svg';
import Profile from '@public/svgs/ic_profile.svg';
import { useState, useEffect, ChangeEvent } from 'react';
import ItemComment from '../components/ItemComment';
import axiosInstance from '../lib/axiosInstance';
import ItemComment from '@components/ItemComment';
import axiosInstance from '@lib/axiosInstance';

const INITIAL_VALUES = {
tags: [],
Expand Down Expand Up @@ -33,7 +33,6 @@ function ItemDetail({ initialValues = INITIAL_VALUES }) {
limit: 10, // 요청에 'limit' 값을 쿼리 파라미터로 추가
},
});
console.log(response.data);
setItemDetail(response.data); // 서버에서 받은 데이터를 상태에 저장
} catch (error) {
console.error('Error fetching comments:', error);
Expand Down
4 changes: 2 additions & 2 deletions pages/Items.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import AllItemsSection from '../components/AllItemsSection';
import BestItemSection from '../components/BestItemsSection';
import AllItemsSection from '@components/AllItemsSection';
import BestItemSection from '@components/BestItemsSection';

function Items() {
return (
Expand Down
Loading
Loading