Skip to content

Commit

Permalink
[Feat] head 작업 (#121)
Browse files Browse the repository at this point in the history
* Feat/#112: 타이틀 태그 동적 생성

* Feat/#112: 메타 태그 설정

* Chore/#112: 스토리북 provider 추가
  • Loading branch information
imddoy authored Jan 22, 2025
1 parent 1e79752 commit 46c737a
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 135 deletions.
7 changes: 5 additions & 2 deletions .storybook/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import theme from '../src/styles/theme';
import GlobalStyle from '../src/styles/GlobalStyles';
import React, { ReactNode, useState } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

interface ProviderProps {
Expand All @@ -16,8 +17,10 @@ export const Provider = ({ children }: ProviderProps) => {
return (
<ThemeProvider theme={theme}>
<QueryClientProvider client={queryClient}>
<GlobalStyle />
<BrowserRouter>{children}</BrowserRouter>
<HelmetProvider>
<GlobalStyle />
<BrowserRouter>{children}</BrowserRouter>
</HelmetProvider>
</QueryClientProvider>
</ThemeProvider>
);
Expand Down
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta property="og:title" content="daruda" />
<meta property="og:description" content="대학생활에 필요한 툴을 다루다" />
<meta name="keywords" content="다루다, daruda, 대학생, 과제, 툴" />
<meta property="og:image" content="${import.meta.env.VITE_CLIENT_URL}/og_img.png" />
<meta property="og:url" content="${import.meta.env.VITE_CLIENT_URL}" />
<link rel="icon" href="/logo.svg" type="image/svg+xml" />
<title>다루다(daruda)</title>
</head>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"lottie-react": "^2.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^2.0.5",
"react-intersection-observer": "^9.15.1",
"react-lottie": "^1.2.10",
"react-lottie-player": "^2.1.0",
Expand Down
Binary file added public/og_img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/og_img1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/og_img2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/components/common/title/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Helmet } from 'react-helmet-async';

const Title = ({ title, tool }: { title: string; tool?: string }) => {
return (
<Helmet>
<title>{title}</title>
<meta property="og:tool" content={tool ? `${tool} 툴을 다루다` : '대학생활에 필요한 툴을 다루다'} />
</Helmet>
);
};

export default Title;
2 changes: 2 additions & 0 deletions src/components/layout/MyPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Footer from '@components/footer/Footer';
import Header from '@components/header/Header';
import Title from '@components/title/Title';
import { HEADER_STATE, HeaderState } from '@constants/headerState';
import styled from '@emotion/styled';
import MyPageTab from '@pages/myPage/components/tab/MyPageTab';
Expand All @@ -14,6 +15,7 @@ const MyPageLayout = () => {
return (
<>
<Header headerState={headerState} forOnboarding={false} />
<Title title="마이 페이지" />
<MyPageContainer>
<Outlet />
</MyPageContainer>
Expand Down
11 changes: 7 additions & 4 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HelmetProvider } from 'react-helmet-async';

import App from './App';

Expand All @@ -15,10 +16,12 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<GlobalStyle />
<ThemeProvider theme={theme}>
<QueryClientProvider client={queryClient}>
<App />
<div style={{ fontSize: '1.6rem' }}>
<ReactQueryDevtools initialIsOpen={false} />
</div>
<HelmetProvider>
<App />
<div style={{ fontSize: '1.6rem' }}>
<ReactQueryDevtools initialIsOpen={false} />
</div>
</HelmetProvider>
</QueryClientProvider>
</ThemeProvider>
</React.StrictMode>,
Expand Down
2 changes: 2 additions & 0 deletions src/pages/CommunityDetail/CommunityDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IcCommentGray24, IcBookmark } from '@assets/svgs';
import SquareButton from '@components/button/squareButton/SquareButton';
import Card from '@components/postCard/PostCard';
import Title from '@components/title/Title';
import { handleScrollDown } from '@utils';
import { useRef, useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
Expand Down Expand Up @@ -37,6 +38,7 @@ const CommunityDetail = () => {
const comments = CommentData?.pages.flatMap((page) => page.commentList) || [];
return (
<>
<Title title={data?.title as string} />
<S.PageWrapper>
<S.PageHeader>
<h1>글 상세보기</h1>
Expand Down
40 changes: 22 additions & 18 deletions src/pages/community/Community.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IcPlusWhite20, IcChevron } from '@assets/svgs';
import ToolListBanner from '@components/banner/ToolListBanner';
import CircleButton from '@components/button/circleButton/CircleButton';
import Title from '@components/title/Title';
import { handleScrollUp } from '@utils';
import { useEffect, useState } from 'react';
import { useInView } from 'react-intersection-observer';
Expand Down Expand Up @@ -31,24 +32,27 @@ const Community = () => {
setIsNoTopic(toolId === null);
};
return (
<S.CommunityWrapper>
<Banner />
<S.CommunityContainer>
<ToolListBanner forCommunity={true} onToolSelect={handleToolSelect} />
<S.CardList>
{postList?.map((post) => <Card key={`community-post-${post.boardId}`} post={post} />)}
{hasNextPage ? <div ref={ref} /> : null}
</S.CardList>
</S.CommunityContainer>
<S.FollowingBtns>
<CircleButton size="small" shadow={true} icon={<IcPlusWhite20 />}>
글쓰기
</CircleButton>
<S.TopBtn type="button" onClick={handleScrollUp}>
<IcChevron />
</S.TopBtn>
</S.FollowingBtns>
</S.CommunityWrapper>
<>
<Title title="커뮤니티" />
<S.CommunityWrapper>
<Banner />
<S.CommunityContainer>
<ToolListBanner forCommunity={true} onToolSelect={handleToolSelect} />
<S.CardList>
{postList?.map((post) => <Card key={`community-post-${post.boardId}`} post={post} />)}
{hasNextPage ? <div ref={ref} /> : null}
</S.CardList>
</S.CommunityContainer>
<S.FollowingBtns>
<CircleButton size="small" shadow={true} icon={<IcPlusWhite20 />}>
글쓰기
</CircleButton>
<S.TopBtn type="button" onClick={handleScrollUp}>
<IcChevron />
</S.TopBtn>
</S.FollowingBtns>
</S.CommunityWrapper>
</>
);
};

Expand Down
50 changes: 27 additions & 23 deletions src/pages/communityWrite/CommunityWrite.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ToolListBanner from '@components/banner/ToolListBanner';
import CircleButton from '@components/button/circleButton/CircleButton';
import Title from '@components/title/Title';
import Toast from '@components/toast/Toast';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
Expand Down Expand Up @@ -47,29 +48,32 @@ const CommunityWrite = () => {
};

return (
<S.WriteWrapper>
<S.WriteTitle>글쓰기</S.WriteTitle>
<S.WriteContainer>
<S.WriteBox>
<WritingTitle setTitle={setTitle} />
<WritingBody setBody={setBody} />
<WritingImg onImageUpload={setImages} />
</S.WriteBox>
<S.SideBanner>
<ToolListBanner onToolSelect={handleToolSelect} />
<CircleButton onClick={handlePostSubmit} size="large" disabled={isButtonDisabled}>
글 게시하기
</CircleButton>
</S.SideBanner>
</S.WriteContainer>
{isToastVisible && (
<S.ToastBox>
<Toast isVisible={true} isWarning={true}>
{toastMessage}
</Toast>
</S.ToastBox>
)}
</S.WriteWrapper>
<>
<Title title="글쓰기" />
<S.WriteWrapper>
<S.WriteTitle>글쓰기</S.WriteTitle>
<S.WriteContainer>
<S.WriteBox>
<WritingTitle setTitle={setTitle} />
<WritingBody setBody={setBody} />
<WritingImg onImageUpload={setImages} />
</S.WriteBox>
<S.SideBanner>
<ToolListBanner onToolSelect={handleToolSelect} />
<CircleButton onClick={handlePostSubmit} size="large" disabled={isButtonDisabled}>
글 게시하기
</CircleButton>
</S.SideBanner>
</S.WriteContainer>
{isToastVisible && (
<S.ToastBox>
<Toast isVisible={true} isWarning={true}>
{toastMessage}
</Toast>
</S.ToastBox>
)}
</S.WriteWrapper>
</>
);
};

Expand Down
32 changes: 18 additions & 14 deletions src/pages/login/KakaoAuth.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { ImgDarudalogo40 } from '@assets/svgs';
import Title from '@components/title/Title';

import SvgKakaoVector from './assets/KakaoVector';
import * as S from './KakaoAuth.styled';

const KakaoAuth = () => {
return (
<S.LogintWrapper>
<S.Container>
<S.LogoSection>
<ImgDarudalogo40 width={407} height={120} />
</S.LogoSection>
<span>모든 대학생이 찾는 솔루션, 다루다에서 만나보세요.</span>
<S.LoginButtonContainer>
<S.LoginButton aria-label="카카오 로그인">
<SvgKakaoVector />
<p>카카오 로그인</p>
</S.LoginButton>
</S.LoginButtonContainer>
</S.Container>
</S.LogintWrapper>
<>
<Title title="로그인" />
<S.LogintWrapper>
<S.Container>
<S.LogoSection>
<ImgDarudalogo40 width={407} height={120} />
</S.LogoSection>
<span>모든 대학생이 찾는 솔루션, 다루다에서 만나보세요.</span>
<S.LoginButtonContainer>
<S.LoginButton aria-label="카카오 로그인">
<SvgKakaoVector />
<p>카카오 로그인</p>
</S.LoginButton>
</S.LoginButtonContainer>
</S.Container>
</S.LogintWrapper>
</>
);
};

Expand Down
92 changes: 48 additions & 44 deletions src/pages/signUp/SignUp.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ImgModalcheck } from '@assets/svgs';
import CircleButton from '@components/button/circleButton/CircleButton';
import { AlterModal } from '@components/modal';
import Title from '@components/title/Title';
import React, { useState } from 'react';

import AffiliationBtn from './components/affiliationButton/AffiliationBtn';
Expand Down Expand Up @@ -43,50 +44,53 @@ const SignUp = () => {
};

return (
<S.SignUpWrapper>
<S.Container>
<S.LeftContainer>
<S.LeftBox>
<S.TitleBox>
쉬운 대학생활, <br />
앞으로 한 걸음 남았어요.
</S.TitleBox>
<S.CommentBox>
공부, 과제, 팀플, 동아리, 대외활동 <br />
복잡하고 어렵기만 했던 툴에 대한 고민은 <br />
다루다가 해결해 드릴게요.
</S.CommentBox>
</S.LeftBox>
</S.LeftContainer>
<S.RightContainer>
<h1>회원가입</h1>
<S.AffiliationBox>
<h2>소속을 선택해주세요.</h2>
<S.AffiliationBtnBox>
{AFFILIATION_OPTIONS.map((label) => (
<AffiliationBtn
key={label}
label={label}
isSelected={selectedAffiliation === label}
onClick={() => setSelectedAffiliation(label)}
/>
))}
</S.AffiliationBtnBox>
</S.AffiliationBox>
<S.NicknameInputBox>
{/* TODO: 중복확인 상태에 따른 로직 구현 */}
<NamingInput value={nickname} onChange={handleNicknameChange} />
</S.NicknameInputBox>
<S.SignUpBtn>
{/* TODO: 중복확인 되었을 때만 작동되도록 */}
<CircleButton size="mini" disabled={!isCircleBtnActive} onClick={handleCircleBtnClick}>
회원가입 하기
</CircleButton>
</S.SignUpBtn>
</S.RightContainer>
<AlterModal {...modalProps} />
</S.Container>
</S.SignUpWrapper>
<>
<Title title="회원가입" />
<S.SignUpWrapper>
<S.Container>
<S.LeftContainer>
<S.LeftBox>
<S.TitleBox>
쉬운 대학생활, <br />
앞으로 한 걸음 남았어요.
</S.TitleBox>
<S.CommentBox>
공부, 과제, 팀플, 동아리, 대외활동 <br />
복잡하고 어렵기만 했던 툴에 대한 고민은 <br />
다루다가 해결해 드릴게요.
</S.CommentBox>
</S.LeftBox>
</S.LeftContainer>
<S.RightContainer>
<h1>회원가입</h1>
<S.AffiliationBox>
<h2>소속을 선택해주세요.</h2>
<S.AffiliationBtnBox>
{AFFILIATION_OPTIONS.map((label) => (
<AffiliationBtn
key={label}
label={label}
isSelected={selectedAffiliation === label}
onClick={() => setSelectedAffiliation(label)}
/>
))}
</S.AffiliationBtnBox>
</S.AffiliationBox>
<S.NicknameInputBox>
{/* TODO: 중복확인 상태에 따른 로직 구현 */}
<NamingInput value={nickname} onChange={handleNicknameChange} />
</S.NicknameInputBox>
<S.SignUpBtn>
{/* TODO: 중복확인 되었을 때만 작동되도록 */}
<CircleButton size="mini" disabled={!isCircleBtnActive} onClick={handleCircleBtnClick}>
회원가입 하기
</CircleButton>
</S.SignUpBtn>
</S.RightContainer>
<AlterModal {...modalProps} />
</S.Container>
</S.SignUpWrapper>
</>
);
};

Expand Down
Loading

0 comments on commit 46c737a

Please sign in to comment.