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

[Feature] - 검색 구현 #328

Merged
merged 37 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2e06458
feat: 여행기 검색 무한스크롤 query 구현
0jenn0 Aug 15, 2024
78e3899
chore: 검색 아이콘 추가
0jenn0 Aug 16, 2024
7477740
feat: 헤더에 검색 추가
0jenn0 Aug 16, 2024
d1f89a2
feat: SearchFallback 컴포넌트 추가
0jenn0 Aug 16, 2024
cdc3846
feat: 검색 결과 페이지 구현
0jenn0 Aug 16, 2024
c5be9e8
feat: 검색 페이지 라우트 추가
0jenn0 Aug 16, 2024
82b6d13
feat: home icon 추가
0jenn0 Aug 16, 2024
73c46dd
refactor: Header 컴포넌트를 기본 Header의 basic으로 사용할 수 있도록 리팩토링
0jenn0 Aug 16, 2024
325d2fe
feat: HomeHeader 구현
0jenn0 Aug 16, 2024
db1c2bb
feat: LogoHeader 구현
0jenn0 Aug 16, 2024
08acd6d
feat: NavigationHeader 구현
0jenn0 Aug 16, 2024
9ac1612
feat: SearchHeader 구현
0jenn0 Aug 16, 2024
f65f958
fix: storybook preview rootStyle이 common/Header에는 다르게 적용되도록 수정
0jenn0 Aug 16, 2024
3d31541
test: Header storybook 추가
0jenn0 Aug 16, 2024
396d2c8
feat: AppLayout에서 Header를 동적으로 추가
0jenn0 Aug 16, 2024
4b0f840
refactor: HomeHeader -> HomePageHeader로 이름 수정
0jenn0 Aug 17, 2024
fe7154f
refactor: 스타일드 컴포넌트 이름 수정 (wrapper -> container)
0jenn0 Aug 17, 2024
a541d82
refactor: Header Props명 수정
0jenn0 Aug 17, 2024
f7fc33a
refactor: 검색어 최소 글자수 상수화
0jenn0 Aug 17, 2024
e01a908
refactor: AppLayout에서 pathname 확인 부분 상수 사용
0jenn0 Aug 17, 2024
0dde08b
refactor: h1 태그 대신 Text 컴포넌트를 사용해서 리팩토링
0jenn0 Aug 17, 2024
3b09549
refactor: gap 값에 theme spacing을 사용
0jenn0 Aug 17, 2024
f0eced9
refactor: gap 값에 theme spacing 사용
0jenn0 Aug 17, 2024
4fbf8bf
chore: 코드 사이에 개행 추가
0jenn0 Aug 17, 2024
103a485
chore: 개행추가
0jenn0 Aug 17, 2024
b73b0a2
refactor: Icon 대신 IconButton을 사용
0jenn0 Aug 17, 2024
303f563
refactor: #fff 대신 primitive white 사용
0jenn0 Aug 17, 2024
202bd15
chore: 빈 파일 삭제
0jenn0 Aug 17, 2024
2228a7a
refactor: dom으로 직접 접근하는 부분 ref를 사용하도록 수정
0jenn0 Aug 17, 2024
31715b6
fix: 검색어에 trim 추가 및 useInfiniteSearchTravelogues 가 keyword가 빈값이 아닐때만…
0jenn0 Aug 17, 2024
db4a5ac
refactor: NavigationHeader -> SearchResultPageHeader로 이름 수정
0jenn0 Aug 17, 2024
003c4e0
fix: Header 이름 수정 꼬인것 수정
0jenn0 Aug 17, 2024
2e980c9
refactor: Header 관련 컴포넌트 인덱싱
0jenn0 Aug 17, 2024
c378863
fix: 검색 페이지에서 뒤로가기 누를 때, query는 그전 검색어지만 Input value는 그전 검색어가 아니던 이슈 수정
0jenn0 Aug 17, 2024
1088e6a
storybook: styled의 Input이 아닌 진짜 Input.tsx 사용하도록 수정
0jenn0 Aug 17, 2024
5b7a74a
Merge branch 'develop/fe' into feature/fe/#306
0jenn0 Aug 19, 2024
0af07b5
fix: build 에러 잡음
0jenn0 Aug 19, 2024
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: 5 additions & 5 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ initialize(
handlers,
);

const rootStyle = {
const getRootStyle = (storyId: string) => ({
width: "48rem",
padding: "1.6rem",
padding: storyId.startsWith("common-header--") ? 0 : "1.6rem",
Copy link
Contributor

Choose a reason for hiding this comment

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

이 친구는 추가하게 된 이유가 있을까요 .. ?! id가 common-header인 것이 검색했을 때는 안나와서요 ㅜㅜ..!

Copy link
Contributor Author

@0jenn0 0jenn0 Aug 17, 2024

Choose a reason for hiding this comment

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

image

previewd의 padding이 1.6rem으로 되어있어서 Header storybook 확인 할 때 위와 같이 padding만큼 밀려서 보였었습니다
따라서 common/Header일때는 padding이 0이 되도록하여

image

위처럼 안밀리도록 하기 위함이었습니다!

display: "flex",
alignItems: "center",
justifyContent: "center",
};
});

const preview: Preview = {
parameters: {
Expand All @@ -39,14 +39,14 @@ const preview: Preview = {
},
},
decorators: [
(Story) => {
(Story, context) => {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<Global styles={globalStyle} />
<div id="root">
<div style={rootStyle}>
<div style={getRootStyle(context.id)}>
<Story />
</div>
</div>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/assets/svg/home-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/assets/svg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export { default as KaKao } from "./kakao.svg";
export { default as KoreanLogo } from "./korean-logo.svg";
export { default as Plus } from "./plus.svg";
export { default as tturiUrl } from "./tturi.svg?url";
export { default as HomeIcon } from "./home-icon.svg";
3 changes: 3 additions & 0 deletions frontend/src/assets/svg/search-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions frontend/src/components/common/Header/Header.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { css } from "@emotion/react";
Copy link
Contributor

Choose a reason for hiding this comment

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

image

스토리북 들어갔을 때 오른쪽에 Drawer가 보여지는데 현재 코드에서 제가 이해하는데 시간이 좀 걸려서(ㅜㅜ) 일단 이미지 올려놔봅니다!

Copy link
Contributor Author

@0jenn0 0jenn0 Aug 17, 2024

Choose a reason for hiding this comment

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

image

위 스샷을 보면 Drawer가 root 밖 오른쪽에 딱 붙어있는걸 볼 수 있는데요

export const DrawerContainer = styled.div<{ isOpen: boolean }>`
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  right: ${({ isOpen }) => (isOpen ? "0" : "-210px")}; // isOpen값이 0일 때 Drawer 컨테이너가 화면밖으로 자기 width 만큼 나가있음

Drawer 애니메이션 구현이 위와같이 되어있어서 어쩔 수 없는 부분이라고 생각합니다~

Copy link
Contributor

Choose a reason for hiding this comment

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

악 이런 이유였군요,,, 감사합니다 ㅎㅎ


import type { Meta, StoryObj } from "@storybook/react";

import Icon from "../Icon/Icon";
import IconButton from "../IconButton/IconButton";
import { Input } from "../Input/Input.styled";
import Header from "./Header";
import * as S from "./SearchHeader/SearchHeader.styled";

const rightContentOptions = {
None: null,
HomeIcon: <IconButton iconType="home-icon" size="20" />,
SearchIcon: <IconButton iconType="search-icon" size="18" />,
SearchForm: (
<S.FormWrapper>
<Input
autoFocus
placeholder="검색해주세요"
css={css`
height: 4rem;
`}
/>
<S.ButtonContainer>
<S.DeleteButton title="delete keyword button" type="button">
<Icon iconType="x-icon" size="8" />
</S.DeleteButton>
<button title="search button" type="submit">
<Icon iconType="search-icon" size="18" />
</button>
</S.ButtonContainer>
</S.FormWrapper>
),
};

const meta = {
title: "common/Header",
component: Header,
argTypes: {
useHamburger: { control: "boolean" },
useLogo: { control: "boolean" },
rightContent: {
options: Object.keys(rightContentOptions),
mapping: rightContentOptions,
Copy link
Contributor

Choose a reason for hiding this comment

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

mapping 속성 한 수 배워갑니다!

control: {
type: "select",
},
},
$isRightContentFull: { control: "boolean" },
},
decorators: [
(Story, context) => {
return (
<div style={{ padding: 0, width: "48rem", position: "relative" }}>
<Story args={{ ...context.args }} />
</div>
);
},
],
tags: ["autodocs"],
} satisfies Meta<typeof Header>;

export default meta;

type Story = StoryObj<typeof meta>;

export const HomeHeader: Story = {
Copy link
Contributor

Choose a reason for hiding this comment

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

얘는 마이페이지나 상세보기 헤더 정도로 이름이 들어가면 더 좋을 것 같네요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

얘가 좀 애메한데요, 사실 특수한 상황 (로그인, 검색하는 경우, 메인 페이지) 제외하고서는 다 이 헤더를 사용하기때문에
DefaultHeader로 수정해보았습니다..!
사실 헤더 이름이 지금도 뭔가 애메하다고 생각해서 더 얘기해보면 좋을거같기도합니다

Copy link
Contributor

Choose a reason for hiding this comment

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

인정합니다 저도 얘 이름을 말할 때는 고민이 많이 되더라고요!

args: {
useHamburger: true,
rightContent: "HomeIcon",
},
};

export const LogoHeader: Story = {
Copy link
Contributor

Choose a reason for hiding this comment

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

얘가 HomePageHeader 면 좀 더 자연스러울 거 같아요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정했습니다!

args: {
useLogo: true,
useHamburger: true,
rightContent: "SearchIcon",
},
};

export const NavigationHeader: Story = {
Copy link
Contributor

Choose a reason for hiding this comment

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

얘는 검색결과 헤더면 좋을 것 같구요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SearchResultPageHeader로 수정해보았습니다!

args: {
useHamburger: true,
rightContent: "SearchIcon",
},
};

export const SearchHeader: Story = {
args: {
rightContent: "SearchForm",
$isRightContentFull: true,
},
};
18 changes: 11 additions & 7 deletions frontend/src/components/common/Header/Header.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const HeaderLayout = styled.header`
z-index: ${({ theme }) => theme.zIndex.header};
Copy link
Contributor

Choose a reason for hiding this comment

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

여기 line은 아닌데 19번 line의 배경색을 fff가 아닌 primitive color의 white를 사용해주면 좋을거같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이것도 수정했는데 코멘트깜빡했네요 !


width: 100%;
height: fit-content;
height: 6rem;
max-width: 48rem;
padding: 1.6rem;
border-bottom: 0.1rem solid ${({ theme }) => theme.colors.border};
Expand Down Expand Up @@ -41,12 +41,16 @@ export const MenuList = styled.ul`
background-color: ${PRIMITIVE_COLORS.white};
`;

export const HeaderTitle = styled.span`
${({ theme }) => theme.typography.mobile.bodyBold}
color: ${({ theme }) => theme.colors.text.primary};
export const LeftWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;

export const HiddenDiv = styled.div`
width: 2.4rem;
height: 2.4rem;
export const RightWrapper = styled.div<{ $isRightContentFull?: boolean }>`
display: flex;
gap: ${({ theme }) => theme.spacing.m};
justify-content: center;
align-items: center;
${({ $isRightContentFull = false }) => $isRightContentFull && "flex: 1;"}
`;
61 changes: 32 additions & 29 deletions frontend/src/components/common/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { useLocation, useNavigate } from "react-router-dom";

import IconButton from "@components/common/IconButton/IconButton";

import useUser from "@hooks/useUser";

import { ROUTE_PATHS_MAP } from "@constants/route";
Expand All @@ -12,53 +10,61 @@ import { PRIMITIVE_COLORS } from "@styles/tokens";
import { DoubleRightArrow } from "@assets/svg";

import Drawer from "../Drawer/Drawer";
import IconButton from "../IconButton/IconButton";
import * as S from "./Header.styled";

const Header = () => {
interface HeaderProps {
useLogo?: boolean;
rightContent: React.ReactNode;
$isRightContentFull?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

이 친구는 $ 표시가 없어도 될 듯 합니다

Copy link
Contributor Author

Choose a reason for hiding this comment

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

정말 css에만 관여할 때 props명 앞에 $ 붙이는거 아니었나용??

Copy link
Contributor

Choose a reason for hiding this comment

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

styled-component에 prop을 넘겨주는 경우에만 $ 표시를 하는걸로 알고 있어요!

https://styled-components.com/docs/api#transient-props

근데 v5.1 이상 부턴 shouldForwardProp로 표현이 가능하다고 하긴 하네요 .. !

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 보통 스타일 컴포넌트에 넘겨줄 때만 $ 표시를 쓴다고 알아서 저렇게 말했습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아아 글쿤용 수정하겠습니다!

useHamburger?: boolean;
}

const Header = ({
useLogo = false,
rightContent,
$isRightContentFull = false,
useHamburger = false,
}: HeaderProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

useLogo 랑 useHamburger가 처음에 커스텀 훅인줄 알고 헷갈렸었네요! 네이밍 수정이 있으면 더 좋을 것 같아요 보통 boolean 값을 판단할 때 is 혹은 has로 시작하도록 많이 쓰는데 isLogoUsed 랑 isHamburgerUsed 정도로 수정되면 좋겠습니다

Copy link
Contributor

Choose a reason for hiding this comment

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

useLogo 랑 useHamburger가 처음에 커스텀 훅인줄 알고 헷갈렸었네요! 네이밍 수정이 있으면 더 좋을 것 같아요 보통 boolean 값을 판단할 때 is 혹은 has로 시작하도록 많이 쓰는데 isLogoUsed 랑 isHamburgerUsed 정도로 수정되면 좋겠습니다

저도 이 부분은 꼭 반영되었으면 좋겠어요!!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정했습니다!

const { user, saveUser } = useUser();
const location = useLocation();
const pathName = location.pathname;

const navigate = useNavigate();

const handleClickButton =
pathName === ROUTE_PATHS_MAP.root || pathName === ROUTE_PATHS_MAP.login
? () => navigate(ROUTE_PATHS_MAP.root)
: () => navigate(ROUTE_PATHS_MAP.back);

const handleClickLogout = () => {
if (
pathName.includes(ROUTE_PATHS_MAP.travelPlan().split("/").shift()!) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분 의미가 좀 더 명확했으면 좋겠네요 한 번에 확 와닿지가 않아서요!

pathName.includes(ROUTE_PATHS_MAP.my)
) {
navigate(ROUTE_PATHS_MAP.login);
}

saveUser({ accessToken: "", memberId: 0 });
};

const handleClickMyPage = () => navigate(ROUTE_PATHS_MAP.my);

const handleClickHome = () => navigate(ROUTE_PATHS_MAP.root);

return (
<Drawer>
<S.HeaderLayout>
<IconButton
color={pathName === ROUTE_PATHS_MAP.root ? theme.colors.primary : PRIMITIVE_COLORS.black}
onClick={handleClickButton}
iconType={pathName === ROUTE_PATHS_MAP.root ? "korean-logo" : "back-icon"}
/>
{pathName === ROUTE_PATHS_MAP.login ? (
<>
<S.HeaderTitle>로그인</S.HeaderTitle>
<S.HiddenDiv />
</>
) : (
<Drawer.Trigger>
<IconButton iconType={"hamburger"} />
</Drawer.Trigger>
)}
<S.LeftWrapper>
<IconButton
color={useLogo ? theme.colors.primary : PRIMITIVE_COLORS.black}
iconType={useLogo ? "korean-logo" : "back-icon"}
onClick={
useLogo ? () => navigate(ROUTE_PATHS_MAP.root) : () => navigate(ROUTE_PATHS_MAP.back)
}
/>
</S.LeftWrapper>

<S.RightWrapper $isRightContentFull={$isRightContentFull}>
{rightContent}
{useHamburger && (
<Drawer.Trigger>
<IconButton iconType="hamburger" />
</Drawer.Trigger>
)}
</S.RightWrapper>
Copy link
Contributor

Choose a reason for hiding this comment

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

wrapper는 하나의 요소만 담을 때 쓰면 더 좋을 것 같네요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

나중에 요소 추가하고 container로 바꾸는걸 깜박했네요~ 수정했습니다

</S.HeaderLayout>

<Drawer.Header>
Expand All @@ -70,9 +76,6 @@ const Header = () => {
</Drawer.Header>
<Drawer.Content>
<S.MenuList>
<Drawer.Trigger>
<S.MenuItem onClick={handleClickHome}>홈</S.MenuItem>
</Drawer.Trigger>
<Drawer.Trigger>
<S.MenuItem onClick={handleClickMyPage}>마이페이지</S.MenuItem>
</Drawer.Trigger>
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

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

빈 파일은 지워주시면 감사드리겠습니다 :)

(SearchHeader를 제외한 나머지 Header 모두 동일합니다!)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

빈 파일들이 있었네요 😮
컴포넌트 만들때 바로 styled.ts 파일까지 바로 미리 만들다보니 이런 일도 생기네요 😅
삭제했습니다!

Empty file.
25 changes: 25 additions & 0 deletions frontend/src/components/common/Header/HomeHeader/HomeHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useNavigate } from "react-router-dom";

import IconButton from "@components/common/IconButton/IconButton";

import { ROUTE_PATHS_MAP } from "@constants/route";

import Header from "../Header";

const HomeHeader = () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

함수명도 마찬가집니다! home을 가지고 있음을 나타내기보다 페이지에 관한 헤더면 이름이 더 좋을 것 같아요

const navigation = useNavigate();
return (
<Header
rightContent={
<IconButton
iconType="home-icon"
size="20"
onClick={() => navigation(ROUTE_PATHS_MAP.root)}
/>
}
useHamburger
/>
);
};

export default HomeHeader;
Empty file.
27 changes: 27 additions & 0 deletions frontend/src/components/common/Header/LogoHeader/LogoHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useNavigate } from "react-router-dom";

import IconButton from "@components/common/IconButton/IconButton";

import { ROUTE_PATHS_MAP } from "@constants/route";

import Header from "../Header";

const LogoHeader = () => {
const navigation = useNavigate();

return (
<Header
useLogo
rightContent={
<IconButton
iconType="search-icon"
size="18"
onClick={() => navigation(ROUTE_PATHS_MAP.searchMain)}
/>
}
useHamburger
/>
);
};

export default LogoHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useNavigate } from "react-router-dom";

import IconButton from "@components/common/IconButton/IconButton";

import { ROUTE_PATHS_MAP } from "@constants/route";

import Header from "../Header";

const NavigationHeader = () => {
const navigation = useNavigate();

return (
<Header
rightContent={
<IconButton
iconType="search-icon"
size="18"
onClick={() => navigation(ROUTE_PATHS_MAP.searchMain)}
/>
}
useHamburger
/>
);
};

export default NavigationHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import styled from "@emotion/styled";

export const FormWrapper = styled.form`
flex: 1;
position: relative;
padding-left: 1.6rem;
`;

export const ButtonContainer = styled.div`
display: flex;
gap: 1.2rem;
position: absolute;
top: 50%;
right: 1.6rem;
transform: translateY(-50%);
`;

export const DeleteButton = styled.button`
display: flex;
justify-content: center;
align-items: center;
padding: 0.8rem;
border-radius: 50%;
background: rgb(0 0 0/ 10%);
`;
Loading