From ff26beea608ce1e01c8ec405ca279b8ca97965f2 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 13 Sep 2023 18:04:30 +0900
Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?=
 =?UTF-8?q?=EB=A6=AC=20=ED=95=84=ED=84=B0=20ui=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 .../src/components/Controls/SelectBox.tsx     |  4 +-
 frontend/src/pages/ArticleListPage/index.tsx  | 55 +++++++++++++++++--
 2 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/frontend/src/components/Controls/SelectBox.tsx b/frontend/src/components/Controls/SelectBox.tsx
index 7a0fa1edb..4e73a86c2 100644
--- a/frontend/src/components/Controls/SelectBox.tsx
+++ b/frontend/src/components/Controls/SelectBox.tsx
@@ -46,6 +46,7 @@ interface SelectBoxProps {
 }
 
 const SelectBox: React.VFC<SelectBoxProps> = ({
+  isClearable = true,
   isMulti = false,
   options,
   placeholder,
@@ -75,7 +76,7 @@ const SelectBox: React.VFC<SelectBoxProps> = ({
     `}
   >
     <Select
-      isClearable={true}
+      isClearable={isClearable}
       isMulti={isMulti}
       options={options}
       placeholder={placeholder}
@@ -83,7 +84,6 @@ const SelectBox: React.VFC<SelectBoxProps> = ({
       styles={selectStyles}
       defaultValue={defaultOption}
       value={value}
-      // theme={(theme) => ({ ...theme, colors: { ...theme.colors, primary: 'transparent' } })}
     />
   </div>
 );
diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index 4c4f8cc2c..d1db4802b 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -4,23 +4,50 @@ import ArticleList from '../../components/Article/ArticleList';
 import { Button } from '../../components';
 import PencilIcon from '../../assets/images/pencil_icon.svg';
 import { useHistory } from 'react-router-dom';
-import { PATH } from '../../constants';
+import { COLOR, PATH } from '../../constants';
 import styled from '@emotion/styled';
 import { MainContentStyle } from '../../PageRouter';
+import SelectBox from '../../components/Controls/SelectBox';
+import { useState } from 'react';
+import { css } from '@emotion/react';
+
+const CATEGORY_OPTIONS = [
+  { value: '전체보기', label: '전체보기' },
+  { value: '프론트엔드', label: '프론트엔드' },
+  { value: '백엔드', label: '백엔드' },
+  { value: '안드로이드', label: '안드로이드' },
+];
+
+type CategoryOptions = typeof CATEGORY_OPTIONS[number];
 
 const ArticleListPage = () => {
   const history = useHistory();
   const goNewArticlePage = () => history.push(PATH.NEW_ARTICLE);
+  const [selectedOption, setSelectedOption] = useState<CategoryOptions>(CATEGORY_OPTIONS[0]);
+
+  const changeFilterOption: (option: { value: string; label: string }) => void = (option) => {
+    setSelectedOption(option);
+  };
 
   return (
     <div css={[MainContentStyle]}>
       <Container>
+        <SelectBoxWrapper>
+          <SelectBox
+            isClearable={false}
+            value={selectedOption}
+            defaultOption={selectedOption}
+            options={CATEGORY_OPTIONS}
+            onChange={changeFilterOption}
+          />
+        </SelectBoxWrapper>
         <Button
           type="button"
-          size="SMALL"
+          size="X_SMALL"
           icon={PencilIcon}
           alt="새 아티클 쓰기 아이콘"
           onClick={goNewArticlePage}
+          cssProps={WriteButtonStyle}
         >
           글쓰기
         </Button>
@@ -34,7 +61,27 @@ export default ArticleListPage;
 
 export const Container = styled.div`
   display: flex;
-  flex-direction: column;
-  align-items: flex-end;
+  flex-direction: row;
+  justify-content: space-between;
   margin-bottom: 20px;
+  gap: 15px;
+`;
+
+const SelectBoxWrapper = styled.div`
+  width: 200px;
+`;
+
+export const WriteButtonStyle = css`
+  width: 100px;
+  height: 42px;
+  padding: 0.2rem 0.8rem;
+
+  border-radius: 0.6rem;
+  font-size: 1.6rem;
+
+  color: ${COLOR.WHITE};
+
+  :hover {
+    background-color: ${COLOR.DARK_BLUE_600};
+  }
 `;

From c4e1f9885f9516608f63e959be55e0a1df383648 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Fri, 15 Sep 2023 16:50:31 +0900
Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=EC=8A=A4=ED=81=AC=EB=9E=A9=20?=
 =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80=EC=99=80=20=EB=AF=B8?=
 =?UTF-8?q?=EC=84=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A1=B0=EC=A0=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 .../src/components/Article/Article.style.tsx  | 61 +++++++++++++------
 frontend/src/components/Article/Article.tsx   | 20 +++++-
 .../components/Article/ArticleList.style.tsx  | 12 ++--
 .../src/components/Reaction/Scrap.styles.ts   |  2 +-
 frontend/src/components/Reaction/Scrap.tsx    |  8 ++-
 frontend/src/hooks/queries/article.ts         |  1 -
 frontend/src/pages/ArticleListPage/index.tsx  |  2 +-
 7 files changed, 74 insertions(+), 32 deletions(-)

diff --git a/frontend/src/components/Article/Article.style.tsx b/frontend/src/components/Article/Article.style.tsx
index 049d4cba4..a7ffa518b 100644
--- a/frontend/src/components/Article/Article.style.tsx
+++ b/frontend/src/components/Article/Article.style.tsx
@@ -1,11 +1,12 @@
 import styled from '@emotion/styled';
+import { css } from '@emotion/react';
 import { COLOR } from '../../constants';
 
 export const Container = styled.li`
   width: 100%;
-  height: 340px;
-  padding: 20px;
-  border-radius: 15px;
+  height: 100%;
+  padding: 10px;
+  border-radius: 8px;
   background-color: #ffffff;
   list-style: none;
 
@@ -20,16 +21,16 @@ export const ThumbnailWrapper = styled.div`
   display: flex;
   justify-content: center;
   align-items: center;
+  aspect-ratio: 16/9;
   width: 100%;
-  height: 154px;
   border-radius: 15px;
-  margin-bottom: 20px;
+  margin-bottom: 10px;
 `;
 
 export const Thumbnail = styled.img`
   width: 100%;
-  height: 154px;
-  border-radius: 15px;
+  height: 100%;
+  border-radius: 8px;
   object-fit: cover;
 `;
 
@@ -41,34 +42,58 @@ export const ArticleInfoContainer = styled.div`
   padding: 10px;
 `;
 
-export const UserName = styled.p`
-  width: 250px;
-  margin: 0;
-  color: ${COLOR.DARK_GRAY_400};
-  font-size: 14px;
+export const ArticleInfoWrapper = styled.div`
+  display: flex;
+  width: 100%;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
   word-break: break-all;
+  font-size: 14px;
+`;
+
+export const UserName = styled.p`
+  margin: 0;
+  color: ${COLOR.DARK_GRAY_400};
 `;
 
 export const Title = styled.p`
-  width: 250px;
+  width: 100%;
+  height: 50px;
   margin: 0;
   color: ${COLOR.BLACK_900};
   font-size: 16px;
   font-weight: 700;
-  overflow: hidden;
   text-overflow: ellipsis;
-  white-space: nowrap;
-  word-break: break-all;
+  overflow: hidden;
+  word-break: break-word;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+`;
+
+export const ScrapButtonWrapper = styled.div``;
+
+export const ArticleScrapButtonStyle = css`
+  flex-direction: column;
+  padding: 0;
+  width: fit-content;
+  font-size: 1.4rem;
+
+  background-color: transparent;
+  color: ${COLOR.BLACK_800};
+
+  & > img {
+    margin-right: 0;
+    width: 1.8rem;
+    height: 1.8rem;
+  }
 `;
 
 export const CreatedAt = styled.span`
   width: 100%;
-  margin-top: 16px;
   color: ${COLOR.DARK_GRAY_400};
   text-align: right;
-  font-size: 16px;
+  font-size: 12px;
   font-weight: 700;
 `;
diff --git a/frontend/src/components/Article/Article.tsx b/frontend/src/components/Article/Article.tsx
index c610c38af..eccc0722d 100644
--- a/frontend/src/components/Article/Article.tsx
+++ b/frontend/src/components/Article/Article.tsx
@@ -1,7 +1,18 @@
 import * as Styled from './Article.style';
 import type { ArticleType } from '../../models/Article';
+import Scrap from '../Reaction/Scrap';
+import { useState } from 'react';
 
 const Article = ({ title, userName, url, createdAt, imageUrl }: ArticleType) => {
+  const [scrap, setScrap] = useState(false);
+
+  const toggleScrap: React.MouseEventHandler<HTMLButtonElement> = (e) => {
+    e.preventDefault();
+    setScrap((prev) => !prev);
+
+    // api
+  };
+
   return (
     <Styled.Container>
       <Styled.Anchor href={url} target="_blank" rel="noopener noreferrer">
@@ -9,9 +20,14 @@ const Article = ({ title, userName, url, createdAt, imageUrl }: ArticleType) =>
           <Styled.Thumbnail src={imageUrl} />
         </Styled.ThumbnailWrapper>
         <Styled.ArticleInfoContainer>
-          <Styled.UserName>{userName}</Styled.UserName>
+          <Styled.ArticleInfoWrapper>
+            <Styled.UserName>{userName}</Styled.UserName>
+            <Styled.CreatedAt>{createdAt.split(' ')[0]}</Styled.CreatedAt>
+          </Styled.ArticleInfoWrapper>
           <Styled.Title>{title}</Styled.Title>
-          <Styled.CreatedAt>{createdAt}</Styled.CreatedAt>
+          <Styled.ScrapButtonWrapper>
+            <Scrap scrap={scrap} onClick={toggleScrap} cssProps={Styled.ArticleScrapButtonStyle} />
+          </Styled.ScrapButtonWrapper>
         </Styled.ArticleInfoContainer>
       </Styled.Anchor>
     </Styled.Container>
diff --git a/frontend/src/components/Article/ArticleList.style.tsx b/frontend/src/components/Article/ArticleList.style.tsx
index b7d21cadd..e941cfc56 100644
--- a/frontend/src/components/Article/ArticleList.style.tsx
+++ b/frontend/src/components/Article/ArticleList.style.tsx
@@ -3,19 +3,19 @@ import MEDIA_QUERY from '../../constants/mediaQuery';
 
 export const Container = styled.ul`
   display: grid;
-  grid-template-columns: repeat(3, 1fr);
+  grid-template-columns: repeat(4, 1fr);
   grid-auto-rows: 1fr;
-  gap: 30px 64px;
+  gap: 10px;
 
-  ${MEDIA_QUERY.xl} {
-    gap: 30px 40px;
+  ${MEDIA_QUERY.lg} {
+    grid-template-columns: repeat(3, 1fr);
   }
 
-  ${MEDIA_QUERY.lg} {
+  ${MEDIA_QUERY.md} {
     grid-template-columns: repeat(2, 1fr);
   }
 
-  ${MEDIA_QUERY.md} {
+  ${MEDIA_QUERY.sm} {
     grid-template-columns: repeat(1, 1fr);
   }
 `;
diff --git a/frontend/src/components/Reaction/Scrap.styles.ts b/frontend/src/components/Reaction/Scrap.styles.ts
index 2335d252a..976533905 100644
--- a/frontend/src/components/Reaction/Scrap.styles.ts
+++ b/frontend/src/components/Reaction/Scrap.styles.ts
@@ -1,7 +1,7 @@
 import { css } from '@emotion/react';
 import { COLOR } from '../../enumerations/color';
 
-export const ScrapButtonStyle = css`
+export const DefaultScrapButtonStyle = css`
   flex-direction: column;
   padding: 0;
   width: fit-content;
diff --git a/frontend/src/components/Reaction/Scrap.tsx b/frontend/src/components/Reaction/Scrap.tsx
index 08bfad860..9a30aa9ec 100644
--- a/frontend/src/components/Reaction/Scrap.tsx
+++ b/frontend/src/components/Reaction/Scrap.tsx
@@ -3,14 +3,16 @@ import { Button, BUTTON_SIZE } from '..';
 
 import scrappedIcon from '../../assets/images/scrap_filled.svg';
 import unScrapIcon from '../../assets/images/scrap.svg';
-import { ScrapButtonStyle } from './Scrap.styles';
+import { DefaultScrapButtonStyle } from './Scrap.styles';
+import { SerializedStyles } from '@emotion/react';
 
 interface Props {
   scrap: boolean;
   onClick: MouseEventHandler<HTMLButtonElement>;
+  cssProps?: SerializedStyles;
 }
 
-const Scrap = ({ scrap, onClick }: Props) => {
+const Scrap = ({ scrap, onClick, cssProps }: Props) => {
   const scrapIcon = scrap ? scrappedIcon : unScrapIcon;
   const scrapIconAlt = scrap ? '스크랩 취소' : '스크랩';
 
@@ -20,7 +22,7 @@ const Scrap = ({ scrap, onClick }: Props) => {
       size={BUTTON_SIZE.X_SMALL}
       icon={scrapIcon}
       alt={scrapIconAlt}
-      cssProps={ScrapButtonStyle}
+      cssProps={cssProps ?? DefaultScrapButtonStyle}
       onClick={onClick}
     />
   );
diff --git a/frontend/src/hooks/queries/article.ts b/frontend/src/hooks/queries/article.ts
index 4e6780cbf..54fd5f804 100644
--- a/frontend/src/hooks/queries/article.ts
+++ b/frontend/src/hooks/queries/article.ts
@@ -1,5 +1,4 @@
 import { useMutation, useQuery, useQueryClient } from 'react-query';
-import { UserContext } from '../../contexts/UserProvider';
 import { requestGetArticles, requestPostArticles } from '../../apis/articles';
 import { ArticleType } from '../../models/Article';
 import { ERROR_MESSAGE } from '../../constants';
diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index d1db4802b..e4f55745c 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -68,7 +68,7 @@ export const Container = styled.div`
 `;
 
 const SelectBoxWrapper = styled.div`
-  width: 200px;
+  width: 150px;
 `;
 
 export const WriteButtonStyle = css`

From af831690cf2edb352300a0c9e2d7c7e21fda6c4f Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Fri, 15 Sep 2023 17:22:51 +0900
Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=EC=95=84=ED=8B=B0=ED=81=B4=20?=
 =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?=
 =?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/apis/articles.ts                 | 11 +++++++-
 .../src/components/Article/Article.style.tsx  |  4 +--
 frontend/src/components/Article/Article.tsx   | 28 +++++++++++++------
 frontend/src/hooks/queries/article.ts         | 13 ++++++++-
 frontend/src/mocks/db/articles.json           | 16 +++++++++++
 frontend/src/mocks/handlers/articles.ts       |  4 +++
 frontend/src/models/Article.ts                |  5 ++++
 7 files changed, 67 insertions(+), 14 deletions(-)

diff --git a/frontend/src/apis/articles.ts b/frontend/src/apis/articles.ts
index f45c3bd81..6982ceddb 100644
--- a/frontend/src/apis/articles.ts
+++ b/frontend/src/apis/articles.ts
@@ -1,5 +1,10 @@
 import { client } from '.';
-import { ArticleRequest, MetaOgRequest, MetaOgResponse } from '../models/Article';
+import {
+  ArticleBookmarkPutRequest,
+  ArticleRequest,
+  MetaOgRequest,
+  MetaOgResponse,
+} from '../models/Article';
 
 export const requestGetArticles = () => client.get(`/articles`);
 
@@ -8,3 +13,7 @@ export const requestPostArticles = (body: ArticleRequest) => client.post('/artic
 export const requestGetMetaOg = ({ url }: MetaOgRequest) => {
   return client.get<MetaOgResponse>(`/meta-og?url=${url}`);
 };
+
+export const requestPutArticleBookmark = ({ articleId, bookmark }: ArticleBookmarkPutRequest) => {
+  return client.put(`/articles/${articleId}/bookmark`, { checked: bookmark });
+};
diff --git a/frontend/src/components/Article/Article.style.tsx b/frontend/src/components/Article/Article.style.tsx
index a7ffa518b..14e03d311 100644
--- a/frontend/src/components/Article/Article.style.tsx
+++ b/frontend/src/components/Article/Article.style.tsx
@@ -72,9 +72,7 @@ export const Title = styled.p`
   -webkit-box-orient: vertical;
 `;
 
-export const ScrapButtonWrapper = styled.div``;
-
-export const ArticleScrapButtonStyle = css`
+export const ArticleBookmarkButtonStyle = css`
   flex-direction: column;
   padding: 0;
   width: fit-content;
diff --git a/frontend/src/components/Article/Article.tsx b/frontend/src/components/Article/Article.tsx
index eccc0722d..4211117b4 100644
--- a/frontend/src/components/Article/Article.tsx
+++ b/frontend/src/components/Article/Article.tsx
@@ -1,16 +1,24 @@
 import * as Styled from './Article.style';
 import type { ArticleType } from '../../models/Article';
 import Scrap from '../Reaction/Scrap';
-import { useState } from 'react';
+import { useRef, useState } from 'react';
+import { usePutArticleBookmarkMutation } from '../../hooks/queries/article';
+import debounce from '../../utils/debounce';
 
-const Article = ({ title, userName, url, createdAt, imageUrl }: ArticleType) => {
-  const [scrap, setScrap] = useState(false);
+const Article = ({ id, title, userName, url, createdAt, imageUrl }: ArticleType) => {
+  const bookmarkRef = useRef(false);
+  const [bookmark, setBookmark] = useState(false);
+  const { mutate: putBookmark } = usePutArticleBookmarkMutation();
 
-  const toggleScrap: React.MouseEventHandler<HTMLButtonElement> = (e) => {
+  const toggleBookmark: React.MouseEventHandler<HTMLButtonElement> = (e) => {
     e.preventDefault();
-    setScrap((prev) => !prev);
 
-    // api
+    bookmarkRef.current = !bookmarkRef.current;
+    setBookmark((prev) => !prev);
+
+    debounce(() => {
+      putBookmark({ articleId: id, bookmark: bookmarkRef.current });
+    }, 300);
   };
 
   return (
@@ -25,9 +33,11 @@ const Article = ({ title, userName, url, createdAt, imageUrl }: ArticleType) =>
             <Styled.CreatedAt>{createdAt.split(' ')[0]}</Styled.CreatedAt>
           </Styled.ArticleInfoWrapper>
           <Styled.Title>{title}</Styled.Title>
-          <Styled.ScrapButtonWrapper>
-            <Scrap scrap={scrap} onClick={toggleScrap} cssProps={Styled.ArticleScrapButtonStyle} />
-          </Styled.ScrapButtonWrapper>
+          <Scrap
+            scrap={bookmark}
+            onClick={toggleBookmark}
+            cssProps={Styled.ArticleBookmarkButtonStyle}
+          />
         </Styled.ArticleInfoContainer>
       </Styled.Anchor>
     </Styled.Container>
diff --git a/frontend/src/hooks/queries/article.ts b/frontend/src/hooks/queries/article.ts
index 54fd5f804..cc4eded62 100644
--- a/frontend/src/hooks/queries/article.ts
+++ b/frontend/src/hooks/queries/article.ts
@@ -1,5 +1,9 @@
 import { useMutation, useQuery, useQueryClient } from 'react-query';
-import { requestGetArticles, requestPostArticles } from '../../apis/articles';
+import {
+  requestGetArticles,
+  requestPostArticles,
+  requestPutArticleBookmark,
+} from '../../apis/articles';
 import { ArticleType } from '../../models/Article';
 import { ERROR_MESSAGE } from '../../constants';
 import { SUCCESS_MESSAGE } from '../../constants/message';
@@ -29,3 +33,10 @@ export const usePostArticlesMutation = () => {
     },
   });
 };
+
+export const usePutArticleBookmarkMutation = () => {
+  return useMutation(requestPutArticleBookmark, {
+    onSuccess: () => {},
+    onError: () => {},
+  });
+};
diff --git a/frontend/src/mocks/db/articles.json b/frontend/src/mocks/db/articles.json
index 9105fbb0a..cac0d4f16 100644
--- a/frontend/src/mocks/db/articles.json
+++ b/frontend/src/mocks/db/articles.json
@@ -14,5 +14,21 @@
     "url": "https://pgccoding.tistory.com/66",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 3,
+    "userName": "패트릭",
+    "title": "CORS",
+    "url": "https://pgccoding.tistory.com/66",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 4,
+    "userName": "패트릭",
+    "title": "CORS",
+    "url": "https://pgccoding.tistory.com/66",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
   }
 ]
diff --git a/frontend/src/mocks/handlers/articles.ts b/frontend/src/mocks/handlers/articles.ts
index c9b74676e..9419615fb 100644
--- a/frontend/src/mocks/handlers/articles.ts
+++ b/frontend/src/mocks/handlers/articles.ts
@@ -31,4 +31,8 @@ export const articlesHandler = [
     articles.push(newArticle);
     return res(ctx.status(200), ctx.json(newArticle));
   }),
+
+  rest.put(`${BASE_URL}/articles/:articleId/bookmark`, (req, res, ctx) => {
+    return res(ctx.status(200));
+  }),
 ];
diff --git a/frontend/src/models/Article.ts b/frontend/src/models/Article.ts
index 742bcfd9b..1f194f7a5 100644
--- a/frontend/src/models/Article.ts
+++ b/frontend/src/models/Article.ts
@@ -21,3 +21,8 @@ export interface MetaOgResponse {
   imageUrl: string;
   title: string;
 }
+
+export interface ArticleBookmarkPutRequest {
+  articleId: number;
+  bookmark: boolean;
+}

From 144006655214c5a133cdf765455cfe420def1c7d Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Fri, 15 Sep 2023 18:08:48 +0900
Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=ED=95=84=ED=84=B0=EB=A7=81?=
 =?UTF-8?q?=EA=B3=BC=20=EC=97=B0=EA=B4=80=EB=90=9C=20=EB=B6=81=EB=A7=88?=
 =?UTF-8?q?=ED=81=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/apis/articles.ts                 |  5 ++++
 .../Article/ArticleBookmarkFIlter.tsx         | 28 +++++++++++++++++++
 .../src/components/Article/ArticleList.tsx    | 11 ++++----
 frontend/src/hooks/queries/article.ts         | 14 +++++-----
 frontend/src/mocks/db/articles.json           |  4 +++
 frontend/src/mocks/handlers/articles.ts       | 10 +++++++
 frontend/src/models/Article.ts                |  5 ++++
 frontend/src/pages/ArticleListPage/index.tsx  | 27 ++++++++++++++----
 8 files changed, 85 insertions(+), 19 deletions(-)
 create mode 100644 frontend/src/components/Article/ArticleBookmarkFIlter.tsx

diff --git a/frontend/src/apis/articles.ts b/frontend/src/apis/articles.ts
index 6982ceddb..4f4f8bd46 100644
--- a/frontend/src/apis/articles.ts
+++ b/frontend/src/apis/articles.ts
@@ -2,6 +2,7 @@ import { client } from '.';
 import {
   ArticleBookmarkPutRequest,
   ArticleRequest,
+  CourseFilter,
   MetaOgRequest,
   MetaOgResponse,
 } from '../models/Article';
@@ -17,3 +18,7 @@ export const requestGetMetaOg = ({ url }: MetaOgRequest) => {
 export const requestPutArticleBookmark = ({ articleId, bookmark }: ArticleBookmarkPutRequest) => {
   return client.put(`/articles/${articleId}/bookmark`, { checked: bookmark });
 };
+
+export const requestGetFilteredArticle = (course: string, bookmark: boolean) => {
+  return client.get(`/articles/filter?course=${course}&bookmark=${bookmark}`);
+};
diff --git a/frontend/src/components/Article/ArticleBookmarkFIlter.tsx b/frontend/src/components/Article/ArticleBookmarkFIlter.tsx
new file mode 100644
index 000000000..82a451f71
--- /dev/null
+++ b/frontend/src/components/Article/ArticleBookmarkFIlter.tsx
@@ -0,0 +1,28 @@
+import styled from '@emotion/styled';
+
+interface ArticleBookmarkFilterProps {
+  checked: boolean;
+  handleCheckBookmark: React.ChangeEventHandler<HTMLInputElement>;
+}
+
+const ArticleBookmarkFilter = ({ checked, handleCheckBookmark }: ArticleBookmarkFilterProps) => {
+  return (
+    <ArticleBookmarkFilterContainer>
+      <input
+        type="checkbox"
+        checked={checked}
+        onChange={handleCheckBookmark}
+        value="북마크한 아티클"
+      />
+    </ArticleBookmarkFilterContainer>
+  );
+};
+
+export default ArticleBookmarkFilter;
+
+const ArticleBookmarkFilterContainer = styled.div`
+  width: 100px;
+  border: 1px solid black;
+  border-radius: 5px;
+  font-size: 1.5rem;
+`;
diff --git a/frontend/src/components/Article/ArticleList.tsx b/frontend/src/components/Article/ArticleList.tsx
index 5aebbfe49..59fd386ce 100644
--- a/frontend/src/components/Article/ArticleList.tsx
+++ b/frontend/src/components/Article/ArticleList.tsx
@@ -1,13 +1,12 @@
 import * as Styled from './ArticleList.style';
 import Article from './Article';
-import { useGetRequestArticleQuery } from '../../hooks/queries/article';
+import { ArticleType } from '../../models/Article';
 
-const ArticleList = () => {
-  const { data: articles, isLoading, isError } = useGetRequestArticleQuery();
-
-  if (isLoading) return <div>loading...</div>;
-  if (isError) return <div>error...</div>;
+interface ArticleListProps {
+  articles: ArticleType[];
+}
 
+const ArticleList = ({ articles }: ArticleListProps) => {
   return (
     <Styled.Container>
       {articles?.map((article) => (
diff --git a/frontend/src/hooks/queries/article.ts b/frontend/src/hooks/queries/article.ts
index cc4eded62..f62cd49f8 100644
--- a/frontend/src/hooks/queries/article.ts
+++ b/frontend/src/hooks/queries/article.ts
@@ -1,20 +1,20 @@
 import { useMutation, useQuery, useQueryClient } from 'react-query';
 import {
-  requestGetArticles,
+  requestGetFilteredArticle,
   requestPostArticles,
   requestPutArticleBookmark,
 } from '../../apis/articles';
-import { ArticleType } from '../../models/Article';
+import { ArticleType, CourseFilter } from '../../models/Article';
 import { ERROR_MESSAGE } from '../../constants';
 import { SUCCESS_MESSAGE } from '../../constants/message';
 
 const QUERY_KEY = {
-  articles: 'articles',
+  filteredArticles: 'filteredArticles',
 };
 
-export const useGetRequestArticleQuery = () => {
-  return useQuery<ArticleType[]>([QUERY_KEY.articles], async () => {
-    const response = await requestGetArticles();
+export const useGetFilteredArticleQuery = (course: string, bookmark: boolean) => {
+  return useQuery<ArticleType[]>([QUERY_KEY.filteredArticles], async () => {
+    const response = await requestGetFilteredArticle(course, bookmark);
 
     return response.data;
   });
@@ -25,7 +25,7 @@ export const usePostArticlesMutation = () => {
 
   return useMutation(requestPostArticles, {
     onSuccess: () => {
-      queryClient.invalidateQueries([QUERY_KEY.articles]);
+      queryClient.invalidateQueries([QUERY_KEY.filteredArticles]);
       alert(SUCCESS_MESSAGE.CREATE_ARTICLE);
     },
     onError: () => {
diff --git a/frontend/src/mocks/db/articles.json b/frontend/src/mocks/db/articles.json
index cac0d4f16..182d5e558 100644
--- a/frontend/src/mocks/db/articles.json
+++ b/frontend/src/mocks/db/articles.json
@@ -3,6 +3,7 @@
     "id": 1,
     "userName": "해온",
     "title": "Axios",
+    "isBookMarked": true,
     "url": "https://hae-on.tistory.com/104",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
@@ -11,6 +12,7 @@
     "id": 2,
     "userName": "패트릭",
     "title": "CORS",
+    "isBookMarked": false,
     "url": "https://pgccoding.tistory.com/66",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
@@ -19,6 +21,7 @@
     "id": 3,
     "userName": "패트릭",
     "title": "CORS",
+    "isBookMarked": true,
     "url": "https://pgccoding.tistory.com/66",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
@@ -27,6 +30,7 @@
     "id": 4,
     "userName": "패트릭",
     "title": "CORS",
+    "isBookMarked": false,
     "url": "https://pgccoding.tistory.com/66",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
diff --git a/frontend/src/mocks/handlers/articles.ts b/frontend/src/mocks/handlers/articles.ts
index 9419615fb..4ea38b898 100644
--- a/frontend/src/mocks/handlers/articles.ts
+++ b/frontend/src/mocks/handlers/articles.ts
@@ -24,6 +24,7 @@ export const articlesHandler = [
       title: '직렬화, 역직렬화는 무엇일까?',
       url: 'https://think0wise.tistory.com/107',
       createdAt: '2023-07-08 16:48',
+      isBookMarked: false,
       imageUrl:
         'https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60',
     };
@@ -35,4 +36,13 @@ export const articlesHandler = [
   rest.put(`${BASE_URL}/articles/:articleId/bookmark`, (req, res, ctx) => {
     return res(ctx.status(200));
   }),
+
+  rest.get(`${BASE_URL}/articles/filter`, (req, res, ctx) => {
+    const course = req.url.searchParams.get('course');
+    const bookmark = req.url.searchParams.get('bookmark');
+
+    const filteredArticle = articles.filter((article) => bookmark === String(article.isBookMarked));
+
+    return res(ctx.status(200), ctx.json(filteredArticle));
+  }),
 ];
diff --git a/frontend/src/models/Article.ts b/frontend/src/models/Article.ts
index 1f194f7a5..4bce9855f 100644
--- a/frontend/src/models/Article.ts
+++ b/frontend/src/models/Article.ts
@@ -8,6 +8,7 @@ export interface ArticleType {
   id: number;
   userName: string;
   title: string;
+  isBookMarked: false;
   url: string;
   createdAt: string;
   imageUrl: string;
@@ -26,3 +27,7 @@ export interface ArticleBookmarkPutRequest {
   articleId: number;
   bookmark: boolean;
 }
+
+export type Course = '프론트엔드' | '백엔드' | '안드로이드';
+
+export type CourseFilter = Course | '전체보기';
diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index e4f55745c..2987f7870 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -8,8 +8,10 @@ import { COLOR, PATH } from '../../constants';
 import styled from '@emotion/styled';
 import { MainContentStyle } from '../../PageRouter';
 import SelectBox from '../../components/Controls/SelectBox';
-import { useState } from 'react';
+import { ChangeEventHandler, useState } from 'react';
 import { css } from '@emotion/react';
+import ArticleBookmarkFilter from '../../components/Article/ArticleBookmarkFIlter';
+import { useGetFilteredArticleQuery } from '../../hooks/queries/article';
 
 const CATEGORY_OPTIONS = [
   { value: '전체보기', label: '전체보기' },
@@ -23,10 +25,22 @@ type CategoryOptions = typeof CATEGORY_OPTIONS[number];
 const ArticleListPage = () => {
   const history = useHistory();
   const goNewArticlePage = () => history.push(PATH.NEW_ARTICLE);
-  const [selectedOption, setSelectedOption] = useState<CategoryOptions>(CATEGORY_OPTIONS[0]);
+  const [selectedCourse, setSelectedCourse] = useState<CategoryOptions>(CATEGORY_OPTIONS[0]);
+  const [checked, setChecked] = useState(false);
+
+  const { data: filteredArticles = [], refetch: getFilteredArticles } = useGetFilteredArticleQuery(
+    selectedCourse.value,
+    checked
+  );
 
   const changeFilterOption: (option: { value: string; label: string }) => void = (option) => {
-    setSelectedOption(option);
+    setSelectedCourse(option);
+    // getFilteredArticles();
+  };
+
+  const handleCheckBookmark: React.ChangeEventHandler<HTMLInputElement> = (e) => {
+    setChecked(e.currentTarget.checked);
+    // getFilteredArticles();
   };
 
   return (
@@ -35,12 +49,13 @@ const ArticleListPage = () => {
         <SelectBoxWrapper>
           <SelectBox
             isClearable={false}
-            value={selectedOption}
-            defaultOption={selectedOption}
+            value={selectedCourse}
+            defaultOption={selectedCourse}
             options={CATEGORY_OPTIONS}
             onChange={changeFilterOption}
           />
         </SelectBoxWrapper>
+        <ArticleBookmarkFilter checked={checked} handleCheckBookmark={handleCheckBookmark} />
         <Button
           type="button"
           size="X_SMALL"
@@ -52,7 +67,7 @@ const ArticleListPage = () => {
           글쓰기
         </Button>
       </Container>
-      <ArticleList />
+      <ArticleList articles={filteredArticles} />
     </div>
   );
 };

From 694c7b1e669c7cca70642a7543d0aa5e6aaafc78 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Mon, 18 Sep 2023 15:53:38 +0900
Subject: [PATCH 05/16] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?=
 =?UTF-8?q?=20=EB=AA=85=EC=84=B8=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 frontend/src/apis/articles.ts           | 3 +--
 frontend/src/mocks/handlers/articles.ts | 6 ++++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/frontend/src/apis/articles.ts b/frontend/src/apis/articles.ts
index 4f4f8bd46..b2f1bace5 100644
--- a/frontend/src/apis/articles.ts
+++ b/frontend/src/apis/articles.ts
@@ -2,7 +2,6 @@ import { client } from '.';
 import {
   ArticleBookmarkPutRequest,
   ArticleRequest,
-  CourseFilter,
   MetaOgRequest,
   MetaOgResponse,
 } from '../models/Article';
@@ -20,5 +19,5 @@ export const requestPutArticleBookmark = ({ articleId, bookmark }: ArticleBookma
 };
 
 export const requestGetFilteredArticle = (course: string, bookmark: boolean) => {
-  return client.get(`/articles/filter?course=${course}&bookmark=${bookmark}`);
+  return client.get(`/articles/filter?course=${course}&onlyBookmarked=${bookmark}`);
 };
diff --git a/frontend/src/mocks/handlers/articles.ts b/frontend/src/mocks/handlers/articles.ts
index 4ea38b898..441dae3e9 100644
--- a/frontend/src/mocks/handlers/articles.ts
+++ b/frontend/src/mocks/handlers/articles.ts
@@ -39,9 +39,11 @@ export const articlesHandler = [
 
   rest.get(`${BASE_URL}/articles/filter`, (req, res, ctx) => {
     const course = req.url.searchParams.get('course');
-    const bookmark = req.url.searchParams.get('bookmark');
+    const onlyBookmarked = req.url.searchParams.get('onlyBookmarked');
 
-    const filteredArticle = articles.filter((article) => bookmark === String(article.isBookMarked));
+    const filteredArticle = articles.filter(
+      (article) => onlyBookmarked === String(article.isBookMarked)
+    );
 
     return res(ctx.status(200), ctx.json(filteredArticle));
   }),

From 443bfcf8f2ed990a724c7814ef7a79daeaf4aac0 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Mon, 18 Sep 2023 15:54:43 +0900
Subject: [PATCH 06/16] =?UTF-8?q?refactor:=20useEffect=EB=A1=9C=20?=
 =?UTF-8?q?=EB=B6=81=EB=A7=88=ED=81=AC,=20=ED=95=84=ED=84=B0=20=EC=98=B5?=
 =?UTF-8?q?=EC=85=98=20api=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8F=84=EB=A1=9D?=
 =?UTF-8?q?=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 frontend/src/pages/ArticleListPage/index.tsx | 34 ++++++++++++--------
 1 file changed, 21 insertions(+), 13 deletions(-)

diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index 2987f7870..bc0ec7f49 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -8,7 +8,7 @@ import { COLOR, PATH } from '../../constants';
 import styled from '@emotion/styled';
 import { MainContentStyle } from '../../PageRouter';
 import SelectBox from '../../components/Controls/SelectBox';
-import { ChangeEventHandler, useState } from 'react';
+import { useEffect, useState } from 'react';
 import { css } from '@emotion/react';
 import ArticleBookmarkFilter from '../../components/Article/ArticleBookmarkFIlter';
 import { useGetFilteredArticleQuery } from '../../hooks/queries/article';
@@ -35,27 +35,31 @@ const ArticleListPage = () => {
 
   const changeFilterOption: (option: { value: string; label: string }) => void = (option) => {
     setSelectedCourse(option);
-    // getFilteredArticles();
   };
 
   const handleCheckBookmark: React.ChangeEventHandler<HTMLInputElement> = (e) => {
     setChecked(e.currentTarget.checked);
-    // getFilteredArticles();
   };
 
+  useEffect(() => {
+    getFilteredArticles();
+  }, [checked, selectedCourse]);
+
   return (
     <div css={[MainContentStyle]}>
       <Container>
-        <SelectBoxWrapper>
-          <SelectBox
-            isClearable={false}
-            value={selectedCourse}
-            defaultOption={selectedCourse}
-            options={CATEGORY_OPTIONS}
-            onChange={changeFilterOption}
-          />
-        </SelectBoxWrapper>
-        <ArticleBookmarkFilter checked={checked} handleCheckBookmark={handleCheckBookmark} />
+        <FilteringWrapper>
+          <SelectBoxWrapper>
+            <SelectBox
+              isClearable={false}
+              value={selectedCourse}
+              defaultOption={selectedCourse}
+              options={CATEGORY_OPTIONS}
+              onChange={changeFilterOption}
+            />
+          </SelectBoxWrapper>
+          <ArticleBookmarkFilter checked={checked} handleCheckBookmark={handleCheckBookmark} />
+        </FilteringWrapper>
         <Button
           type="button"
           size="X_SMALL"
@@ -82,6 +86,10 @@ export const Container = styled.div`
   gap: 15px;
 `;
 
+const FilteringWrapper = styled.div`
+  display: flex;
+`;
+
 const SelectBoxWrapper = styled.div`
   width: 150px;
 `;

From 4cdd4fb081d9b50d36cb3778e0eeb059cddb11ea Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Mon, 18 Sep 2023 15:54:57 +0900
Subject: [PATCH 07/16] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?=
 =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98?=
 =?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 .../src/components/Article/Article.style.tsx  | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/frontend/src/components/Article/Article.style.tsx b/frontend/src/components/Article/Article.style.tsx
index 14e03d311..13980b9b4 100644
--- a/frontend/src/components/Article/Article.style.tsx
+++ b/frontend/src/components/Article/Article.style.tsx
@@ -72,19 +72,20 @@ export const Title = styled.p`
   -webkit-box-orient: vertical;
 `;
 
-export const ArticleBookmarkButtonStyle = css`
-  flex-direction: column;
-  padding: 0;
-  width: fit-content;
-  font-size: 1.4rem;
+export const BookmarkWrapper = styled.div`
+  width: 100%;
+  display: flex;
+  justify-content: flex-end;
+`;
 
+export const ArticleBookmarkButtonStyle = css`
+  width: initial;
   background-color: transparent;
-  color: ${COLOR.BLACK_800};
+  text-align: right;
 
   & > img {
-    margin-right: 0;
-    width: 1.8rem;
-    height: 1.8rem;
+    width: 2.3rem;
+    height: 2.3rem;
   }
 `;
 

From 1744871d5c1a0cc57cc06f9cf2f6da9eea93b0b4 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Mon, 18 Sep 2023 15:55:15 +0900
Subject: [PATCH 08/16] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?=
 =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20wrapper?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 frontend/src/components/Article/Article.tsx | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/frontend/src/components/Article/Article.tsx b/frontend/src/components/Article/Article.tsx
index 4211117b4..7b9069414 100644
--- a/frontend/src/components/Article/Article.tsx
+++ b/frontend/src/components/Article/Article.tsx
@@ -33,11 +33,13 @@ const Article = ({ id, title, userName, url, createdAt, imageUrl }: ArticleType)
             <Styled.CreatedAt>{createdAt.split(' ')[0]}</Styled.CreatedAt>
           </Styled.ArticleInfoWrapper>
           <Styled.Title>{title}</Styled.Title>
-          <Scrap
-            scrap={bookmark}
-            onClick={toggleBookmark}
-            cssProps={Styled.ArticleBookmarkButtonStyle}
-          />
+          <Styled.BookmarkWrapper>
+            <Scrap
+              scrap={bookmark}
+              onClick={toggleBookmark}
+              cssProps={Styled.ArticleBookmarkButtonStyle}
+            />
+          </Styled.BookmarkWrapper>
         </Styled.ArticleInfoContainer>
       </Styled.Anchor>
     </Styled.Container>

From 3291400c4b4475bc767300feef087886e5a7d2b2 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Mon, 18 Sep 2023 15:55:30 +0900
Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?=
 =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20label=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: 이도현 <Creative-Lee@users.noreply.github.com>
---
 .../Article/ArticleBookmarkFIlter.tsx          | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/frontend/src/components/Article/ArticleBookmarkFIlter.tsx b/frontend/src/components/Article/ArticleBookmarkFIlter.tsx
index 82a451f71..9fb48f7d9 100644
--- a/frontend/src/components/Article/ArticleBookmarkFIlter.tsx
+++ b/frontend/src/components/Article/ArticleBookmarkFIlter.tsx
@@ -8,12 +8,10 @@ interface ArticleBookmarkFilterProps {
 const ArticleBookmarkFilter = ({ checked, handleCheckBookmark }: ArticleBookmarkFilterProps) => {
   return (
     <ArticleBookmarkFilterContainer>
-      <input
-        type="checkbox"
-        checked={checked}
-        onChange={handleCheckBookmark}
-        value="북마크한 아티클"
-      />
+      <label>
+        <input type="checkbox" checked={checked} onChange={handleCheckBookmark} />
+        북마크한 아티클
+      </label>
     </ArticleBookmarkFilterContainer>
   );
 };
@@ -21,8 +19,10 @@ const ArticleBookmarkFilter = ({ checked, handleCheckBookmark }: ArticleBookmark
 export default ArticleBookmarkFilter;
 
 const ArticleBookmarkFilterContainer = styled.div`
-  width: 100px;
-  border: 1px solid black;
-  border-radius: 5px;
+  display: flex;
+  align-items: center;
+  margin-left: 10px;
+  width: 150px;
+  height: 100%;
   font-size: 1.5rem;
 `;

From fce77c66cd3f8aa8dc909fc50fa32d903263bc2b Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:49:59 +0900
Subject: [PATCH 10/16] =?UTF-8?q?docs:=20=EA=B8=B0=EC=A1=B4=20=EC=BD=94?=
 =?UTF-8?q?=EB=93=9C=20=EB=AC=B8=EC=A0=9C=EC=97=90=20=EA=B4=80=ED=95=9C=20?=
 =?UTF-8?q?=EC=A3=BC=EC=84=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/Controls/SelectBox.tsx     | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/frontend/src/components/Controls/SelectBox.tsx b/frontend/src/components/Controls/SelectBox.tsx
index 4e73a86c2..168875962 100644
--- a/frontend/src/components/Controls/SelectBox.tsx
+++ b/frontend/src/components/Controls/SelectBox.tsx
@@ -34,6 +34,25 @@ interface SelectOption {
   label: string;
 }
 
+/**
+  FIXME: value props type SelectOption['value'] 로 변경되어야 함.
+  아래 예시처럼 type을 좁힐 수 없는 문제가 있음.
+
+  const CATEGORY_OPTIONS = [
+    { value: '', label: '전체보기' },
+    { value: 'frontend', label: '프론트엔드' },
+    { value: 'backend', label: '백엔드' },
+    { value: 'android', label: '안드로이드' },
+  ];
+
+  type CategoryOptions = typeof CATEGORY_OPTIONS[number]; 
+
+  ->type CategoryOptions = {
+      value: string;
+      label: string;
+    }
+  위 처럼 value type을 좁힐 수 없음.
+ */
 interface SelectBoxProps {
   isMulti?: boolean;
   options: SelectOption[];

From 7a9584883e90ec37cdfb696742a866091a93f8f7 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:50:32 +0900
Subject: [PATCH 11/16] =?UTF-8?q?refactor:=20bookmark=20=EC=B4=88=EA=B8=B0?=
 =?UTF-8?q?=EA=B0=92=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/components/Article/Article.tsx | 4 ++--
 frontend/src/models/Article.ts              | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/frontend/src/components/Article/Article.tsx b/frontend/src/components/Article/Article.tsx
index 7b9069414..0d55d5f71 100644
--- a/frontend/src/components/Article/Article.tsx
+++ b/frontend/src/components/Article/Article.tsx
@@ -5,9 +5,9 @@ import { useRef, useState } from 'react';
 import { usePutArticleBookmarkMutation } from '../../hooks/queries/article';
 import debounce from '../../utils/debounce';
 
-const Article = ({ id, title, userName, url, createdAt, imageUrl }: ArticleType) => {
+const Article = ({ id, title, userName, url, createdAt, imageUrl, isBookMarked }: ArticleType) => {
   const bookmarkRef = useRef(false);
-  const [bookmark, setBookmark] = useState(false);
+  const [bookmark, setBookmark] = useState(isBookMarked);
   const { mutate: putBookmark } = usePutArticleBookmarkMutation();
 
   const toggleBookmark: React.MouseEventHandler<HTMLButtonElement> = (e) => {
diff --git a/frontend/src/models/Article.ts b/frontend/src/models/Article.ts
index 4bce9855f..dc7540160 100644
--- a/frontend/src/models/Article.ts
+++ b/frontend/src/models/Article.ts
@@ -8,7 +8,7 @@ export interface ArticleType {
   id: number;
   userName: string;
   title: string;
-  isBookMarked: false;
+  isBookMarked: boolean;
   url: string;
   createdAt: string;
   imageUrl: string;

From f566aaa389fe48f0bc9e1876b336815e527b6791 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:50:59 +0900
Subject: [PATCH 12/16] =?UTF-8?q?chore:=20url=EC=97=90=20filter=20?=
 =?UTF-8?q?=EC=82=AD=EC=A0=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/mocks/handlers/articles.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/mocks/handlers/articles.ts b/frontend/src/mocks/handlers/articles.ts
index 441dae3e9..2302996cc 100644
--- a/frontend/src/mocks/handlers/articles.ts
+++ b/frontend/src/mocks/handlers/articles.ts
@@ -37,7 +37,7 @@ export const articlesHandler = [
     return res(ctx.status(200));
   }),
 
-  rest.get(`${BASE_URL}/articles/filter`, (req, res, ctx) => {
+  rest.get(`${BASE_URL}/articles/`, (req, res, ctx) => {
     const course = req.url.searchParams.get('course');
     const onlyBookmarked = req.url.searchParams.get('onlyBookmarked');
 

From 974c165ec4c9fed4667f4d144c254af8a5628054 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:53:22 +0900
Subject: [PATCH 13/16] =?UTF-8?q?refactor:=20url=20filter=20=EC=82=AD?=
 =?UTF-8?q?=EC=A0=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/apis/articles.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/frontend/src/apis/articles.ts b/frontend/src/apis/articles.ts
index b2f1bace5..e3f223ea0 100644
--- a/frontend/src/apis/articles.ts
+++ b/frontend/src/apis/articles.ts
@@ -19,5 +19,5 @@ export const requestPutArticleBookmark = ({ articleId, bookmark }: ArticleBookma
 };
 
 export const requestGetFilteredArticle = (course: string, bookmark: boolean) => {
-  return client.get(`/articles/filter?course=${course}&onlyBookmarked=${bookmark}`);
+  return client.get(`/articles?course=${course}&onlyBookmarked=${bookmark}`);
 };

From cd493ba09205d00866c567aadb3ce471f18d81fe Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:53:41 +0900
Subject: [PATCH 14/16] =?UTF-8?q?refactor:=20select=20value=20=EC=98=81?=
 =?UTF-8?q?=EC=96=B4=EB=A1=9C=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/pages/ArticleListPage/index.tsx | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index bc0ec7f49..91bd8aacf 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -14,10 +14,10 @@ import ArticleBookmarkFilter from '../../components/Article/ArticleBookmarkFIlte
 import { useGetFilteredArticleQuery } from '../../hooks/queries/article';
 
 const CATEGORY_OPTIONS = [
-  { value: '전체보기', label: '전체보기' },
-  { value: '프론트엔드', label: '프론트엔드' },
-  { value: '백엔드', label: '백엔드' },
-  { value: '안드로이드', label: '안드로이드' },
+  { value: 'all', label: '전체보기' },
+  { value: 'frontend', label: '프론트엔드' },
+  { value: 'backend', label: '백엔드' },
+  { value: 'android', label: '안드로이드' },
 ];
 
 type CategoryOptions = typeof CATEGORY_OPTIONS[number];

From 19dcb2fb23f31fdba035329abf6ddac27c1ad8d6 Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 16:58:06 +0900
Subject: [PATCH 15/16] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?=
 =?UTF-8?q?=ED=95=9C=20=EC=9C=A0=EC=A0=80=EC=97=90=EA=B2=8C=EB=A7=8C=20?=
 =?UTF-8?q?=EB=B2=84=ED=8A=BC=EA=B3=BC=20=EB=B6=81=EB=A7=88=ED=81=AC=20?=
 =?UTF-8?q?=ED=95=84=ED=84=B0=20=EB=B3=B4=EC=9D=B4=EB=8F=84=EB=A1=9D=20?=
 =?UTF-8?q?=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/pages/ArticleListPage/index.tsx | 32 ++++++++++++--------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/frontend/src/pages/ArticleListPage/index.tsx b/frontend/src/pages/ArticleListPage/index.tsx
index 91bd8aacf..9f6882c0e 100644
--- a/frontend/src/pages/ArticleListPage/index.tsx
+++ b/frontend/src/pages/ArticleListPage/index.tsx
@@ -8,10 +8,11 @@ import { COLOR, PATH } from '../../constants';
 import styled from '@emotion/styled';
 import { MainContentStyle } from '../../PageRouter';
 import SelectBox from '../../components/Controls/SelectBox';
-import { useEffect, useState } from 'react';
+import { useContext, useEffect, useState } from 'react';
 import { css } from '@emotion/react';
 import ArticleBookmarkFilter from '../../components/Article/ArticleBookmarkFIlter';
 import { useGetFilteredArticleQuery } from '../../hooks/queries/article';
+import { UserContext } from '../../contexts/UserProvider';
 
 const CATEGORY_OPTIONS = [
   { value: 'all', label: '전체보기' },
@@ -28,6 +29,9 @@ const ArticleListPage = () => {
   const [selectedCourse, setSelectedCourse] = useState<CategoryOptions>(CATEGORY_OPTIONS[0]);
   const [checked, setChecked] = useState(false);
 
+  const { user } = useContext(UserContext);
+  const { isLoggedIn } = user;
+
   const { data: filteredArticles = [], refetch: getFilteredArticles } = useGetFilteredArticleQuery(
     selectedCourse.value,
     checked
@@ -58,18 +62,22 @@ const ArticleListPage = () => {
               onChange={changeFilterOption}
             />
           </SelectBoxWrapper>
-          <ArticleBookmarkFilter checked={checked} handleCheckBookmark={handleCheckBookmark} />
+          {isLoggedIn && (
+            <ArticleBookmarkFilter checked={checked} handleCheckBookmark={handleCheckBookmark} />
+          )}
         </FilteringWrapper>
-        <Button
-          type="button"
-          size="X_SMALL"
-          icon={PencilIcon}
-          alt="새 아티클 쓰기 아이콘"
-          onClick={goNewArticlePage}
-          cssProps={WriteButtonStyle}
-        >
-          글쓰기
-        </Button>
+        {isLoggedIn && (
+          <Button
+            type="button"
+            size="X_SMALL"
+            icon={PencilIcon}
+            alt="새 아티클 쓰기 아이콘"
+            onClick={goNewArticlePage}
+            cssProps={WriteButtonStyle}
+          >
+            글쓰기
+          </Button>
+        )}
       </Container>
       <ArticleList articles={filteredArticles} />
     </div>

From d9501c1c4b430581cfc514aea0552043f449523b Mon Sep 17 00:00:00 2001
From: hae-on <solbi2004@naver.com>
Date: Wed, 20 Sep 2023 17:43:19 +0900
Subject: [PATCH 16/16] =?UTF-8?q?refactor:=20msw=20filtering=20=EA=B8=B0?=
 =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 frontend/src/mocks/db/articles-android.json  | 29 +++++++++++
 frontend/src/mocks/db/articles-backend.json  | 29 +++++++++++
 frontend/src/mocks/db/articles-frontend.json | 29 +++++++++++
 frontend/src/mocks/db/articles.json          | 51 ++++++++++++++++++--
 frontend/src/mocks/handlers/articles.ts      | 32 ++++++++----
 5 files changed, 158 insertions(+), 12 deletions(-)
 create mode 100644 frontend/src/mocks/db/articles-android.json
 create mode 100644 frontend/src/mocks/db/articles-backend.json
 create mode 100644 frontend/src/mocks/db/articles-frontend.json

diff --git a/frontend/src/mocks/db/articles-android.json b/frontend/src/mocks/db/articles-android.json
new file mode 100644
index 000000000..865954a4c
--- /dev/null
+++ b/frontend/src/mocks/db/articles-android.json
@@ -0,0 +1,29 @@
+[
+  {
+    "id": 7,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": false,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 8,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": true,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 9,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": false,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  }
+]
diff --git a/frontend/src/mocks/db/articles-backend.json b/frontend/src/mocks/db/articles-backend.json
new file mode 100644
index 000000000..f3fdaae4c
--- /dev/null
+++ b/frontend/src/mocks/db/articles-backend.json
@@ -0,0 +1,29 @@
+[
+  {
+    "id": 4,
+    "userName": "패트릭",
+    "title": "CORS",
+    "isBookMarked": false,
+    "url": "https://pgccoding.tistory.com/66",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 5,
+    "userName": "패트릭",
+    "title": "CORS",
+    "isBookMarked": true,
+    "url": "https://pgccoding.tistory.com/66",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 6,
+    "userName": "패트릭",
+    "title": "CORS",
+    "isBookMarked": true,
+    "url": "https://pgccoding.tistory.com/66",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  }
+]
diff --git a/frontend/src/mocks/db/articles-frontend.json b/frontend/src/mocks/db/articles-frontend.json
new file mode 100644
index 000000000..d7da0c5e8
--- /dev/null
+++ b/frontend/src/mocks/db/articles-frontend.json
@@ -0,0 +1,29 @@
+[
+  {
+    "id": 1,
+    "userName": "해온",
+    "title": "Axios",
+    "isBookMarked": true,
+    "url": "https://hae-on.tistory.com/104",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 2,
+    "userName": "해온",
+    "title": "Axios",
+    "isBookMarked": true,
+    "url": "https://hae-on.tistory.com/104",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 3,
+    "userName": "해온",
+    "title": "Axios",
+    "isBookMarked": true,
+    "url": "https://hae-on.tistory.com/104",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  }
+]
diff --git a/frontend/src/mocks/db/articles.json b/frontend/src/mocks/db/articles.json
index 182d5e558..35b46c204 100644
--- a/frontend/src/mocks/db/articles.json
+++ b/frontend/src/mocks/db/articles.json
@@ -10,6 +10,24 @@
   },
   {
     "id": 2,
+    "userName": "해온",
+    "title": "Axios",
+    "isBookMarked": true,
+    "url": "https://hae-on.tistory.com/104",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 3,
+    "userName": "해온",
+    "title": "Axios",
+    "isBookMarked": true,
+    "url": "https://hae-on.tistory.com/104",
+    "createdAt": "2023-07-24 18:18",
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 4,
     "userName": "패트릭",
     "title": "CORS",
     "isBookMarked": false,
@@ -18,7 +36,7 @@
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
   },
   {
-    "id": 3,
+    "id": 5,
     "userName": "패트릭",
     "title": "CORS",
     "isBookMarked": true,
@@ -27,12 +45,39 @@
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
   },
   {
-    "id": 4,
+    "id": 6,
     "userName": "패트릭",
     "title": "CORS",
-    "isBookMarked": false,
+    "isBookMarked": true,
     "url": "https://pgccoding.tistory.com/66",
     "createdAt": "2023-07-24 18:18",
     "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 7,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": false,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 8,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": true,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
+  },
+  {
+    "id": 9,
+    "userName": "도밥",
+    "title": "직렬화, 역직렬화는 무엇일까?",
+    "url": "https://think0wise.tistory.com/107",
+    "createdAt": "2023-07-08 16:48",
+    "isBookMarked": false,
+    "imageUrl": "https://plus.unsplash.com/premium_photo-1682088845396-1b310a002302?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8JUVDJTk1JTg0JUVEJThCJUIwJUVEJTgxJUI0fGVufDB8fDB8fHww&auto=format&fit=crop&w=500&q=60"
   }
 ]
diff --git a/frontend/src/mocks/handlers/articles.ts b/frontend/src/mocks/handlers/articles.ts
index 2302996cc..6571fa4fa 100644
--- a/frontend/src/mocks/handlers/articles.ts
+++ b/frontend/src/mocks/handlers/articles.ts
@@ -1,15 +1,18 @@
 import { rest } from 'msw';
 import { BASE_URL } from '../../configs/environment';
 import articles from '../db/articles.json';
+import articlesFrontend from '../db/articles-frontend.json';
+import articlesBackend from '../db/articles-backend.json';
+import articlesAndroid from '../db/articles-android.json';
 import metaOg from '../db/metaog.json';
 import { ArticleType } from '../../models/Article';
 
 const articleUrl = 'https://think0wise.tistory.com/107';
 
 export const articlesHandler = [
-  rest.get(`${BASE_URL}/articles`, (req, res, ctx) => {
-    return res(ctx.status(200), ctx.json(articles));
-  }),
+  // rest.get(`${BASE_URL}/articles`, (req, res, ctx) => {
+  //   return res(ctx.status(200), ctx.json(articles));
+  // }),
 
   rest.get(`${BASE_URL}/meta-og?url=${articleUrl}`, async (req, res, ctx) => {
     const data = metaOg;
@@ -37,13 +40,24 @@ export const articlesHandler = [
     return res(ctx.status(200));
   }),
 
-  rest.get(`${BASE_URL}/articles/`, (req, res, ctx) => {
-    const course = req.url.searchParams.get('course');
-    const onlyBookmarked = req.url.searchParams.get('onlyBookmarked');
+  rest.get(`${BASE_URL}/articles`, (req, res, ctx) => {
+    const course = req.url.searchParams.get('course') ?? 'all';
+    const onlyBookmarked = req.url.searchParams.get('onlyBookmarked') as string;
+
+    const filteredCourse = (course: string) => {
+      if (course === 'all') return articles;
+      if (course === 'frontend') return articlesFrontend;
+      if (course === 'backend') return articlesBackend;
+      if (course === 'android') return articlesAndroid;
+    };
+
+    if (onlyBookmarked === 'false') {
+      return res(ctx.status(200), ctx.json(filteredCourse(course)));
+    }
 
-    const filteredArticle = articles.filter(
-      (article) => onlyBookmarked === String(article.isBookMarked)
-    );
+    const filteredArticle = filteredCourse(course)?.filter((article) => {
+      return onlyBookmarked === String(article.isBookMarked) ? true : false;
+    });
 
     return res(ctx.status(200), ctx.json(filteredArticle));
   }),