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

전남대 FE_서민지 - 2주차 과제 #7

Open
wants to merge 49 commits into
base: minji2219
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
76c14ee
Feat: react-router-dom 설치 및 router 구현
minji2219 Jul 1, 2024
fc86fe0
Feat: 각 페이지 폴더로 관리
minji2219 Jul 2, 2024
d5f360b
Feat: Header 생성
minji2219 Jul 2, 2024
2ff3209
Feat: Footer 생성
minji2219 Jul 2, 2024
df1d956
Feat: Layout 구현 및 router 설정
minji2219 Jul 2, 2024
234d4fa
Feat: Header 가운데 정렬
minji2219 Jul 2, 2024
d16c439
Feat: 메인페이지 메인 멘트 컴포넌트 구현
minji2219 Jul 2, 2024
b9a3eb0
Feat: annual category 구현
minji2219 Jul 2, 2024
60bc892
Feat: Badge 컴포넌트 생성
minji2219 Jul 2, 2024
17b2666
Feat: PersonCateogry 컴포넌트 추가
minji2219 Jul 3, 2024
92d27d1
Feat: SituationCategory 컴포넌트 생성
minji2219 Jul 3, 2024
062be3c
Rename: Theme로 파일명 변경
minji2219 Jul 3, 2024
138c9d8
Feat: Rankings 컴포넌트 생성
minji2219 Jul 3, 2024
d746cf3
Feat: 불필요한 텍스트 삭제
minji2219 Jul 3, 2024
63d6111
Feat: Footer 높이 재설정
minji2219 Jul 3, 2024
8c06f86
Feat: Header 상단에 sticky하게 설정
minji2219 Jul 3, 2024
0cce7ca
Feat: cursor pointer 필요한 부분 스타일 추가
minji2219 Jul 4, 2024
31acbda
Feat: theme dynamic route 설정
minji2219 Jul 4, 2024
4c850d2
Feat: a태그 css 리셋
minji2219 Jul 4, 2024
c9f2dc7
Feat: theme Header 섹션 구현
minji2219 Jul 4, 2024
089f877
Feat: theme페이지 goodslist 구현
minji2219 Jul 4, 2024
70c7dd0
Feat: layout Header 스타일 수정
minji2219 Jul 4, 2024
6044e40
Feat: login페이지 구현
minji2219 Jul 4, 2024
e7150ae
Feat: Header 로그인 버튼 로그인 페이지 link연결
minji2219 Jul 4, 2024
1925365
Feat: 로그인 페이지 layout 보여주기 제외
minji2219 Jul 4, 2024
f66c047
Feat: myaccount페이지 구현
minji2219 Jul 4, 2024
cc4bbfb
Feat: a태그 focus될 때 outline 없애기
minji2219 Jul 4, 2024
2ac1119
Feat: Header 선물하기 로고 클릭시 메인페이지로 이동
minji2219 Jul 4, 2024
36a5868
Feat: 불필요한 css 삭제
minji2219 Jul 4, 2024
a95912e
Feat: person category state로 관리
minji2219 Jul 4, 2024
8b5fd07
Feat: situation category state로 관리
minji2219 Jul 4, 2024
159fb93
Feat: key값 없음으로 인한 에러 수정
minji2219 Jul 4, 2024
dc2a457
Merge pull request #1 from minji2219/step1
minji2219 Jul 4, 2024
60d9c05
Docs: step1과제 readme작성
minji2219 Jul 4, 2024
4a72642
Feat: 경로 상수화
minji2219 Jul 4, 2024
83bbc8b
Docs: step2 해야할 일 readme작성
minji2219 Jul 4, 2024
b737072
Feat: UserInfo contextAPI 구현
minji2219 Jul 4, 2024
9dd16a0
Feat: 로그인시 userName storage에 저장 및 context로 공유
minji2219 Jul 4, 2024
cb819c6
Feat: 로그인 여부에 따른 헤더 수정
minji2219 Jul 4, 2024
065b1b0
Feat: my-account는 로그인 된 사람만 접근 가능하게 구현
minji2219 Jul 4, 2024
3771dbb
Feat: my-account에서 로그아웃 구현
minji2219 Jul 4, 2024
363d68b
Feat: 로그인, 내계정 상황에 따라 link수정
minji2219 Jul 4, 2024
0765f4f
Feat: 로그아웃시 메인페이지로 redirect
minji2219 Jul 4, 2024
6f0fec0
Feat: 초기 userName설정 변경
minji2219 Jul 4, 2024
7feb51f
Merge pull request #2 from minji2219/step2
minji2219 Jul 4, 2024
88a5a6d
Docs: step3 질문에 대한 답변 작성
minji2219 Jul 5, 2024
f84c57d
Merge pull request #3 from minji2219/step3
minji2219 Jul 5, 2024
c2290c6
Refactor: 피드백을 기반으로 코드 수정
minji2219 Jul 8, 2024
2142e86
Refactor: 피드백을 기반으로 수정
minji2219 Jul 8, 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
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,52 @@
## Week 2. 1단계 - 페이지 만들기

[🔗 link](https://edu.nextstep.camp/s/hazAC9xa/ls/QzV1ncxk)

---

### Step1 페이지 만들기

1. react-router-dom을 이용하여 router 구현하기
2. Layout: Header구현하기
3. Layout: Footer구현하기
4. Main페이지 구현하기
5. Theme페이지 구현하기
6. 로그인 페이지 구현하기
7. 마이 페이지 구현하기
8. 경로 상수화 하기

---

### Step2 인증 프로세스 구현

1. 로그인 페이지에서 ID, PW를 입력하면 sessionStorage에 authToken key에 저장
2. authToken을 토대로 로그인 여부 판단(contextAPI이용)
3. 로그인 여부에 따른 헤더 수정
4. my-account는 로그인 된 사람만 접근 가능하게 수정
5. my-accout에서 로그아웃 구현

---

### Step3

Q 질문 1. CRA 기반의 SPA프로젝트에서 React Router를 사용하지 않는다면 어떤 문제가 발생하나요?

SPA는 한개의 페이지로 이루어진 어플리케이션으로 렌더링의 역할을 서버가 아닌 브라우저로 처리하게 된다. 즉 `<index.html>`이라는 하나의 html에서 서로 다른 컴포넌트를 보여주는 방식이다.
다른 url로 이동시 전체 페이지가 교체되지 않고 컴포넌트만 변화가 일어나기에 히스토리가 쌓이지 않는 등 불편함이 생긴다.
이러한 단점을 React Router가 보완해줄 수 있는 것이다.

Q 질문 2. 리액트 Context 나 Redux는 언제 사용하면 좋을까요? (로그인을 제외한 예시와 이유를 함께 적어주세요.)

테마(야간 모드 등)를 변경하기 위한 테마 상태를 Context로 관리하면 좋다. 테마를 변경하면 모든 컴포넌트에서 적용이 되어야하기에 props로 테마 상태를 관리하기 보다는 전역 상태로 관리하는 것이 더 좋다고 생각한다.

Q 질문 3. Local Storage, Session Storage 와 Cookies의 차이가 무엇이며 각각 어떨때 사용하면 좋을까요?

- Local Storage: 데이터를 브라우저에 저장하고, 브라우저 종료한 후 다시 들어와도 저장 기록이 남아져 있다.
동일한 origin(동일 프로토콜, 도메인, 포트)를 공유하는 모든 탭/윈도에서 공유가 된다.
- Session Storage: 데이터를 브라우저에 저장하고, 브라우저를 종료할 때 삭제된다.
탭/윈도우 단위로 세션 스토리지가 생성되며, 서로 다른 세션 스토리지는 서로 영향을 주지 않고 독립적으로 동작
- Cookies: 서버와 클라이언트가 지속적으로 데이터 교환을 하기 위해 만들어졌다.
웹 사이트에서 쿠키를 설정하면 모든 웹 요청은 쿠키정보를 포함하여 서버로 전송

Cookies는 로그인 토큰 등을 넣어 사용자의 로그인 상태를 유지할 수 있다.
Local Storage는 "오늘 하루 보지 않기" 탭과 같은 상태를 저장할 수 있다.
41 changes: 40 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.24.0"
},
"devDependencies": {
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"@craco/craco": "^7.1.0",
"@emotion/eslint-plugin": "^11.11.0",
"@storybook/addon-essentials": "^7.6.17",
Expand Down Expand Up @@ -65,8 +64,10 @@
"eslint-plugin-storybook": "^0.8.0",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
"react-scripts": "5.0.1",
"storybook": "^7.6.17",
"tsconfig-paths-webpack-plugin": "^4.1.0",
"typescript": "^4.9.5",
"webpack": "^5.90.3"
},
"overrides": {
Expand Down
11 changes: 2 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import styled from '@emotion/styled';
import { RoutesPage } from './components/Routes';

const App = () => {
const name = 'Josh Perez';

return (
<div>
<Title>Hello, {name}</Title>
<RoutesPage />
</div>
);
};

export default App;

const Title = styled.h1`
font-size: 1.5em;
color: gray;
`;
10 changes: 10 additions & 0 deletions src/components/Layout/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styled from '@emotion/styled';

export const Footer = () => {
return <Wrapper>카카오톡 선물하기</Wrapper>;
};
const Wrapper = styled.div`
background-color: rgb(250, 250, 250);
height: 100px;
padding: 20px;
`;
41 changes: 41 additions & 0 deletions src/components/Layout/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styled from '@emotion/styled';
import { useContext } from 'react';
import { Link } from 'react-router-dom';

import { UserInfo } from '@/providers/UserInfoProviders';
import { breakpoints } from '@/styles/variants';

import { ROUTE_PATHS } from '../Routes/constants';

export const Header = () => {
const { userName } = useContext(UserInfo);
return (
<Wrapper>
<Cotainer>
<Logo>
<Link to={ROUTE_PATHS.ROOT}>선물하기</Link>
</Logo>
<Link to={userName !== '' ? '/my-account' : '/login'}>
{userName !== '' ? '내 계정' : '로그인'}
</Link>
</Cotainer>
</Wrapper>
);
};

const Wrapper = styled.div`
position: sticky;
top: 0;
background-color: #fff;
`;
const Cotainer = styled.div`
max-width: ${breakpoints.md};
margin: 0 auto;
padding: 15px 10px;
display: flex;
justify-content: space-between;
`;
const Logo = styled.div`
font-size: 16px;
font-weight: bold;
`;
14 changes: 14 additions & 0 deletions src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Outlet } from 'react-router-dom';

import { Footer } from './Footer';
import { Header } from './Header';

export const Layout = () => {
return (
<div>
<Header />
<Outlet />
<Footer />
</div>
);
};
9 changes: 9 additions & 0 deletions src/components/Routes/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const ROUTE_PATHS = {
ROOT: '/',
THEME: '/theme/:themekey',
LOGIN: '/login',
MYPAGE: '/my-account',
};
export const getDynamicRoute = {
THEME: (query: string) => ROUTE_PATHS.THEME.replace(':themekey', query),
};
29 changes: 29 additions & 0 deletions src/components/Routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useContext } from 'react';
import { Navigate, Route, Routes } from 'react-router-dom';

import { Home } from '@/pages/Home';
import { Login } from '@/pages/Login';
import { MyAccount } from '@/pages/MyAccount';
import { Theme } from '@/pages/Theme';
import { UserInfo } from '@/providers/UserInfoProviders';

import { Layout } from '../Layout';
import { ROUTE_PATHS } from './constants';

export const RoutesPage = () => {
const { userName } = useContext(UserInfo);
return (
<Routes>
<Route path={ROUTE_PATHS.ROOT} element={<Layout />}>
<Route path={ROUTE_PATHS.ROOT} element={<Home />} />
<Route path={ROUTE_PATHS.THEME} element={<Theme />} />
{userName !== '' ? (
<Route path={ROUTE_PATHS.MYPAGE} element={<MyAccount />} />
) : (
<Route path="*" element={<Navigate replace to={ROUTE_PATHS.LOGIN} />} />
)}
</Route>
<Route path={ROUTE_PATHS.LOGIN} element={<Login />} />
</Routes>
);
};
9 changes: 8 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import '@/styles';

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';

import App from '@/App';

import { UserInfoProvider } from './providers/UserInfoProviders';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<App />
<BrowserRouter>
<UserInfoProvider>
<App />
</UserInfoProvider>
</BrowserRouter>
</React.StrictMode>,
);
31 changes: 31 additions & 0 deletions src/pages/Home/MainMent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled from '@emotion/styled';

import { Image } from '@/components/common/Image';
import { breakpoints } from '@/styles/variants';

export const MainMent = () => {
return (
<Wrapper>
<Ment>
<Image
src="https://gift-s.kakaocdn.net/dn/gift/images/m640/bg_profile_default.png"
radius={20}
width={80}
/>
선물 받을 친구를 선택해주세요.
</Ment>
</Wrapper>
);
};
const Wrapper = styled.div`
background-color: rgb(250, 250, 250);
`;
const Ment = styled.div`
max-width: ${breakpoints.md};
margin: 0 auto;
display: flex;
align-items: center;
gap: 20px;
font-size: 28px;
padding: 40px 0;
`;
76 changes: 76 additions & 0 deletions src/pages/Home/Ranking/PersonCategory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import styled from '@emotion/styled/macro';
import { useState } from 'react';

import { Grid } from '@/components/common/layouts/Grid';

interface Person {
key: 'all' | 'f' | 'm' | 's';
category: string;
ment: string;
}
const personGroup: Person[] = [
{ key: 'all', category: 'ALL', ment: '전체' },
{ key: 'f', category: '👩🏻‍🦰', ment: '여성이' },
{ key: 'm', category: '👨🏻‍🦰', ment: '남성이' },
{ key: 's', category: '🧑🏻‍🦱', ment: '청소년이' },
];

export const PersonCategory = () => {
const [category, setCategory] = useState('all');
const handleCategory = (e: React.ChangeEvent<HTMLInputElement>) => {
setCategory(e.target.value);
};
return (
<Grid columns={4}>
{personGroup.map((p: Person) => (
<Label htmlFor={p.key} key={p.key}>
<RadioInput
type="radio"
id={p.key}
name="person_group"
value={p.key}
onChange={handleCategory}
checked={category === p.key}
/>
<Category>{p.category}</Category>
<CategoryMent>{p.ment}</CategoryMent>
</Label>
))}
</Grid>
);
};
const Label = styled.label`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
cursor: pointer;
`;
const Category = styled.div`
width: 60px;
height: 60px;
background-color: rgb(230, 241, 255);
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 20px;
font-weight: bold;
transition: all 0.3s ease-in-out;
`;
const CategoryMent = styled.div`
font-size: 20px;
transition: all 0.3s ease-in-out;
`;
const RadioInput = styled.input`
display: none;
&:checked + ${Category} {
background-color: rgb(70, 132, 233);
}
&:checked ~ ${CategoryMent} {
color: rgb(70, 132, 233);
font-weight: bold;
}
`;
Loading