Skip to content

[김수영] sprint9 #118

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

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
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["next/babel"],
"plugins": ["babel-plugin-styled-components"]
}
4 changes: 4 additions & 0 deletions .bash_profile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\u@\h \W\[\033[32m\]\$(parse_git_branch)\[\033[00m\] $ "
17 changes: 15 additions & 2 deletions .github/workflows/delete-merged-branch-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,20 @@ jobs:
delete-branch:
runs-on: ubuntu-latest
steps:
- name: delete branch
- name: Checkout repository
uses: actions/checkout@v3

- name: Check if branch exists
id: check_branch
run: |
if git show-ref --verify --quiet refs/heads/${{ github.head_ref }}; then
echo "branch_exists=true" >> $GITHUB_ENV
else
echo "branch_exists=false" >> $GITHUB_ENV
fi

- name: Delete branch
if: env.branch_exists == 'true'
uses: SvanBoxel/delete-merged-branch@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 changes: 24 additions & 0 deletions components/_styled/boardStyled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from "styled-components";

export const BoardPage = styled.div`
width:100%;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
`;
export const BoardPageContainer = styled.div`
max-width:1200px;
width:100%;
margin-top:24px;
display:flex;
gap:40px;
flex-direction:column;

@media(max-width:1199px){
max-width:696px;
}
@media(max-width:767px){
max-width:343px;
}
`;
11 changes: 11 additions & 0 deletions components/_styled/mainStyled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from "styled-components";

export const MainWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
align-items: center;
`;
export const MainContainer = styled.div`
width: 100%;
`;
63 changes: 63 additions & 0 deletions components/boards/BestPost.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState, useEffect, useCallback} from 'react';
import * as BS from './Styled';
import BestPostCard from './BestPostCard';
import { Item, ItemList } from './PostList';
import { getArticles } from '@/pages/api/api';

interface ItemCardProps {
item: Item;
}

interface ItemListProps {
itemList : ItemList;
}

export default function BestPost() {
const [itemList, setItemList] = useState<ItemList>({ totalCount: 0, list: [] });
const [pageSize, setPageSize] = useState(4);
const [order, setOrder] = useState('like');
const fetchSortedData = useCallback(async () => {
try {
const articles = await getArticles({ orderBy: order, pageSize });
setItemList(articles);
console.log(articles);
} catch (error) {
console.error('Error fetching articles:', error);
}
}, [order, pageSize]);

useEffect(() => {
fetchSortedData();
}, [fetchSortedData]);

useEffect(() => {
const handleResize = () => {
const width = window.innerWidth;
if (width < 767) {
setPageSize(1);
} else if (width < 1280) {
setPageSize(2);
} else {
setPageSize(4);
}
};

window.addEventListener('resize', handleResize);
handleResize();

return () => {
window.removeEventListener('resize', handleResize);
};
}, []);

return (
<BS.BestPostConainer>
<BS.Title>베스트 게시글</BS.Title>
<BS.BestCardListContainer>
{itemList.list.map((item) => (
<BestPostCard key={item.id} item={item} />
))}
</BS.BestCardListContainer>
</BS.BestPostConainer>
);
}
41 changes: 41 additions & 0 deletions components/boards/BestPostCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';
import * as BS from './Styled';
import MedalIcon from '../common/images/ic_medal.png';
import LikeIcon from '../common/images/ic_heart.png';
import DefaultImg from '../common/images/default.png';
import { Item } from './PostList';
import formatDate from '../common/function/formatDate';

interface ItemCardProps {
item:Item;
}


const BestPostCard:React.FC<ItemCardProps> = ({ item }) => {
return (
<BS.BestCardContainer>
<BS.BestBadgeContainer>
<BS.MedalIcon src={MedalIcon} alt="메달아이콘" />
<BS.BestText>Best</BS.BestText>
</BS.BestBadgeContainer>
<BS.BestPostContentContainer>
<BS.BestPostContentText>{item.title}</BS.BestPostContentText>
<BS.BestPostImg
src={item.image ? item.image : DefaultImg}
width={72}
height={72}
alt="게시물 이미지" >
</BS.BestPostImg>
</BS.BestPostContentContainer>
<BS.SubContainer>
<BS.InfoContainer>
<BS.Writer>{item.writer.nickname}</BS.Writer>
<BS.LikeIcon src={LikeIcon} alt="좋아요 아이콘"></BS.LikeIcon>
<BS.LikeCount>{item.likeCount}</BS.LikeCount>
</BS.InfoContainer>
<BS.PostDate>{formatDate(item.createdAt)}</BS.PostDate>
</BS.SubContainer>
</BS.BestCardContainer>
);
}
export default BestPostCard;
47 changes: 47 additions & 0 deletions components/boards/PostCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import * as PS from './Styled';
import DefaultImg from '../common/images/default.png';
import LikeIcon from '../common/images/ic_heart.png';
import ProfileImg from '../common/images/profile.png';
import { Item } from './PostList';
import formatDate from '../common/function/formatDate';

interface ItemCardProps {
item: Item;
}

const PostCard: React.FC<ItemCardProps> = ({ item }) => {
return (
<PS.PostCardContainer>
<PS.PostContentContainer>
<PS.BestPostContentText>{item.title}</PS.BestPostContentText>
<PS.BestPostImg
src={item.image ? item.image : DefaultImg}
width={72}
height={72}
alt="게시물 이미지"
/>
</PS.PostContentContainer>
<PS.SubContainer>
<PS.InfoContainer>
<PS.ProfileImg
src={ProfileImg}

alt="프로필 이미지"
/>
<PS.Writer>{item.writer.nickname}</PS.Writer>
<PS.PostDate>{formatDate(item.createdAt)}</PS.PostDate>
</PS.InfoContainer>
<PS.InfoContainer>
<PS.LikeIcon
src={LikeIcon}
alt="좋아요 아이콘"
/>
<PS.LikeCount>{item.likeCount}</PS.LikeCount>
</PS.InfoContainer>
</PS.SubContainer>
</PS.PostCardContainer>
);
}

export default PostCard;
98 changes: 98 additions & 0 deletions components/boards/PostList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/router';
import * as BS from './Styled';
import PostCard from './PostCard';
import { getArticles } from '@/pages/api/api';

export interface Item {
id: number;
title: string;
content: string;
image: string;
writer: Writer;
likeCount: number;
updatedAt: string;
createdAt: string;
}

interface Writer {
nickname: string;
id: number;
}

export interface ItemList {
totalCount: number;
list: Item[];
}

export default function PostList() {
const [itemList, setItemList] = useState<ItemList>({ totalCount: 0, list: [] });
const [pageSize, setPageSize] = useState(4);
Copy link
Collaborator

Choose a reason for hiding this comment

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

P2:
pageSize는 number보다는 더 좁은 타입인 1 | 2 | 3이라고 말할 수 있습니다.
이 경우에는 타입을 좁힐 필요가 크게 없지만 일반적으로는 타입은 좁힐 수 있다면 좁히는 것이 좋습니다.

enum PAGE_SIZE_BY_SCREEN {
  PC = 4,
  TABLET = 2,
  MOBILE = 1,
}

const getPageSize = (width: number): PAGE_SIZE_BY_SCREEN => {
  if (width < 768) return PAGE_SIZE_BY_SCREEN.MOBILE;
  if (width < 1200) return PAGE_SIZE_BY_SCREEN.TABLET;

  return PAGE_SIZE_BY_SCREEN.PC;
};

const [pageSize, setPageSize] = useState<PAGE_SIZE_BY_SCREEN>();

위의 예시처럼 enum으로 어떤 경우 4이 되는지를 명시해주셔도 좋습니다.
이렇게 하시면 코드의 의도가 더 명확해집니다.

const [order, setOrder] = useState('recent');
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [search, setSearch] = useState('');

const router = useRouter();
const{ q } = router.query;

const toggleDropdown = () => {
setIsDropdownOpen(!isDropdownOpen);
};

const handleNewestClick = () => {
setOrder('recent');
setIsDropdownOpen(false);
};

const handleLikeClick = () => {
setOrder('like');
setIsDropdownOpen(false);
};

const handleChange = (e:React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value);
const fetchSortedData = useCallback(async () => {
try {
const articles = await getArticles({ orderBy: order, pageSize: 1, keyword:search});
const totalCount = articles.totalCount;
Comment on lines +55 to +56
Copy link
Collaborator

Choose a reason for hiding this comment

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

P2:
totalCount가 필요한 이유가 무엇일까요?
article을 전부 가지고 올 이유가 없는 걸로 알아서요~ 만약 sort를 위해서라면 orderBy를 통해서 해결하시는 것을 추천드립니다~

const allArticles = await getArticles({ orderBy: order, pageSize: totalCount, keyword:search});
setItemList(allArticles);
console.log(allArticles);
} catch (error) {
console.error('Error fetching articles:', error);
}
}, [order, search]);

useEffect(() => {
fetchSortedData();
}, [fetchSortedData]);

return (
<BS.PostListContainer>
<BS.PostListHeader>
<BS.Title>게시물</BS.Title>
<BS.PostButton>글쓰기</BS.PostButton>
</BS.PostListHeader>
<BS.SearchContainer>
<BS.SearchBox
type='text'
placeholder='검색할 상품을 입력해주세요'
onChange={handleChange}
value={search}
>
</BS.SearchBox>
<BS.DropdownButtonContainer>
<BS.DropdownButton onClick={toggleDropdown}>
{order === 'recent' ? '최신순' : '좋아요순'} <BS.DropdownArrow>▾</BS.DropdownArrow>
</BS.DropdownButton>
<BS.DropdownMenu $isOpen={isDropdownOpen}>
<BS.DropdownOption onClick={handleNewestClick}>최신순</BS.DropdownOption>
<BS.DropdownOption onClick={handleLikeClick}>좋아요순</BS.DropdownOption>
</BS.DropdownMenu>
</BS.DropdownButtonContainer>
Comment on lines +83 to +91
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3:
드롭다운은 따로 컴포넌트로 분리하면 더 좋을 것 같아요~
관련 로직들도 많아서 분리하면 가독성이 더 좋아질 것 같습니다

</BS.SearchContainer>
{itemList.list.map((item) => (
<PostCard key={item.id} item={item} />
))}
</BS.PostListContainer>
);
}
Loading
Loading