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

[ 4주차 기본/심화/생각 과제 ] #9

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9977483
4차 세미나 실습
se0jinYoon Nov 11, 2023
d33c6dd
feat: 프로젝트 초기세팅
se0jinYoon Nov 16, 2023
4dd918a
style: global style, theme 적용
se0jinYoon Nov 16, 2023
6d065ee
feat: 로그인 틀 ContentWrapper 초기구현
se0jinYoon Nov 16, 2023
394cb26
feat: input 공통 컴포넌트 구현
se0jinYoon Nov 16, 2023
5ccae6c
feat: InputWrapper 레이아웃 구현
se0jinYoon Nov 16, 2023
846929a
feat: 로그인 컴포넌트 초기구현
se0jinYoon Nov 16, 2023
79aa725
feat: 회원가입 컴포넌트, 중복체크 버튼 생성
se0jinYoon Nov 16, 2023
ba78f23
feat: 마이페이지 컴포넌트 초기구현
se0jinYoon Nov 16, 2023
c31aa8c
feat: 라우팅 경로 설정
se0jinYoon Nov 16, 2023
fb88b2b
feat: 아이디 중복확인 구현, 상수파일 분리
se0jinYoon Nov 16, 2023
f5a0392
feat: 중복에 따른 색상 연결
se0jinYoon Nov 16, 2023
5eaeb50
feat: 중복체크 후 ID 값을 변경하면 중복체크가 되지 않은 상태로 돌아가기
se0jinYoon Nov 16, 2023
59a67e9
feat: 비밀번호 확인에 따른 회원가입 활성화
se0jinYoon Nov 16, 2023
10343d3
feat: 모든필드 입력여부, 중복 버튼 클릭 여부, 중복여부에 따른 비활성화
se0jinYoon Nov 16, 2023
ddceb9d
feat: 회원가입 버튼 활성화 구현 완료
se0jinYoon Nov 16, 2023
36ba054
feat: 회원가입 form 전송 연결
se0jinYoon Nov 16, 2023
37af037
feat: 로그인 input값 저장, 서버 연결
se0jinYoon Nov 16, 2023
fe80b8b
feat: context API로 유저 정보 저장하기
se0jinYoon Nov 16, 2023
1cd830d
feat: 마이페이지 유저아이디GET, contextAPI의 사용에서 변경
se0jinYoon Nov 16, 2023
b41cf34
feat: 로그아웃 구현
se0jinYoon Nov 16, 2023
1f12360
fix: 과제 구현사항에 맞게 myPage 데이터 변경 (contextAPI 삭제)
se0jinYoon Nov 16, 2023
4f23d7b
feat: 에러 토스트 모달 구현
se0jinYoon Nov 17, 2023
90aae04
style: 버튼 색 변경, myPage 마진 조절
se0jinYoon Nov 17, 2023
8e23ce6
fix: 회원가입 reducer deps에 nickname 추가
se0jinYoon Nov 17, 2023
c9b54cb
style: 로그아웃 버튼 스타일링
se0jinYoon Nov 17, 2023
20d5cfb
style: 로그인,회원가입 색상 수정
se0jinYoon Nov 17, 2023
387f6d7
chore: 필요없는 주석 삭제
se0jinYoon Nov 17, 2023
f22976b
생각과제 제출
se0jinYoon Nov 17, 2023
29e5df9
chore: 필요없는 파일 삭제
se0jinYoon Nov 17, 2023
9099750
style: 회원가입 비활성화 커서 변경
se0jinYoon Nov 17, 2023
a898769
chore: button 컴포넌트 삭제
se0jinYoon Nov 17, 2023
0624f24
fix: toast modal state 업데이트
se0jinYoon Nov 17, 2023
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
20 changes: 20 additions & 0 deletions week4/SignupLogin/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
26 changes: 26 additions & 0 deletions week4/SignupLogin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
8 changes: 8 additions & 0 deletions week4/SignupLogin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# React + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
14 changes: 14 additions & 0 deletions week4/SignupLogin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>❄️서진로그</title>
</head>
<body>
<div id="modal"></div>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
28 changes: 28 additions & 0 deletions week4/SignupLogin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "react",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.1",
"styled-reset": "^4.5.1"
},
"devDependencies": {
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@vitejs/plugin-react": "^4.0.3",
"eslint": "^8.45.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.3",
"vite": "^4.4.5"
}
}
Binary file added week4/SignupLogin/public/img/profileImg.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions week4/SignupLogin/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import styled, { ThemeProvider } from 'styled-components';

import GlobalStyle from './styles/GlobalStyle';
import theme from './styles/theme';
import Router from './components/Router';

function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Router />
</ThemeProvider>
);
}

export default App;
7 changes: 7 additions & 0 deletions week4/SignupLogin/src/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import axios from "axios";

const API = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_URL,
});

export default API;
16 changes: 16 additions & 0 deletions week4/SignupLogin/src/assets/constants/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@


const SIGNUP_LABEL = ['ID', '비밀번호', '비밀번호 확인', '닉네임'];

const SIGNUP_PLACEHOLDER = [
'아이디를 입력해주세요',
'비밀번호를 입력해주세요',
'비밀번호를 다시 한 번 입력해주세요',
'닉네임을 입력해주세요',
];

const LOGIN_LABEL = ['ID', 'PASSWORD'];

const LOGIN_PLACEHOLDER = ['아이디를 입력해주세요', '비밀번호를 입력해주세요'];

export {SIGNUP_LABEL, SIGNUP_PLACEHOLDER, LOGIN_LABEL, LOGIN_PLACEHOLDER};
Comment on lines +3 to +16

Choose a reason for hiding this comment

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

항상 서진언니 코드 보면 상수파일 따로 분리해주고 아래를 모듈화 해주어서 진짜 구조 보기가 진짜 편하고 효율적이라고 느껴용..!

22 changes: 22 additions & 0 deletions week4/SignupLogin/src/components/Layout/BtnWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import styled from 'styled-components';

const BtnWrapper = (props) => {
return(
<Wrapper>{props.children}</Wrapper>
)
}

export default BtnWrapper;

const Wrapper = styled.section`
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem 0;

margin-top: 1rem;

width: 100%;
`

32 changes: 32 additions & 0 deletions week4/SignupLogin/src/components/Layout/ContentWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import styled from 'styled-components';

const ContentWrapper = (props, {children}) => {
return (
<Wrapper onSubmit={props.onSubmit}>
<Header>{props.header}</Header>
{props.children}
</Wrapper>
)
}

export default ContentWrapper;

const Wrapper = styled.form`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 2rem;

width: 34rem;
padding: 3rem 2rem;

border: 1px solid ${({theme}) => theme.colors.gray};
border-radius: 1rem;
background-color: ${({theme}) => theme.colors.white};
`

const Header = styled.header`
font-size: 2rem;
`
19 changes: 19 additions & 0 deletions week4/SignupLogin/src/components/Layout/InputWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import styled from 'styled-components';

import Input from "../UI/Input";

const InputWrapper = (props) => {
return <Wrapper>{props.children}</Wrapper>
}

export default InputWrapper;

const Wrapper = styled.section`
display: flex;
flex-direction: column;
gap: 1.5rem;

width: 100%;
margin-top: 1rem;
`
115 changes: 115 additions & 0 deletions week4/SignupLogin/src/components/Pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useState, useReducer, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { createPortal } from 'react-dom';
import styled from 'styled-components';

import { LOGIN_LABEL, LOGIN_PLACEHOLDER } from '../../assets/constants/constants';
import API from '../../api';

import ContentWrapper from '../Layout/ContentWrapper';
import InputWrapper from '../Layout/InputWrapper';
import BtnWrapper from '../Layout/BtnWrapper';
import Input from '../UI/Input';
import ErrorToast from '../UI/ErrorToast';
Comment on lines +1 to +13

Choose a reason for hiding this comment

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

이렇게 import 하는 요소들 개행해주니까 너무 깔끔하고 좋다!!🥹


const initialState = {
id: '',
password: '',
};

const reducerFn = (state, action) => {
switch (action.type) {
case 'ID':
return {
...state,
id: action.value,
};
case 'PASSWORD':
return {
...state,
password: action.value,
};
}
};

const portalElement = document.getElementById('modal');

const Login = () => {
// 로그인 폼 입력값
const [inputVal, dispatch] = useReducer(reducerFn, initialState);
// 토스트 모달
const [toastState, setToastState] = useState({
message: '',
flag: false,
});
const navigate = useNavigate();

// 회원가입으로 이동
const onClickSignup = () => {
navigate('/signup');
};

// 아이디, 비밀번호 업데이트
const onChangeHandler = (e) => {
dispatch({ type: e.target.name, value: e.target.value });
};

// 로그인 요청
const onLoginSubmit = async (e) => {
e.preventDefault();
try {
const response = await API.post(
`api/v1/members/sign-in`,
{
username: `${inputVal.id}`,
password: `${inputVal.password}`,
},
{
header: {
'Content-Type': 'application/json',
},
}
);
const userInfo = response.data;
navigate(`/mypage/${userInfo.id}`);
} catch (error) {
setToastState({ message: error.response.data.message, flag: true });
}
};

return (
<ContentWrapper header={'로그인'} onSubmit={onLoginSubmit}>
<InputWrapper>
{LOGIN_LABEL.map((label, idx) => (
<Input key={idx} label={label} placeholder={LOGIN_PLACEHOLDER[idx]} onChange={onChangeHandler} />
))}
</InputWrapper>

<BtnWrapper>
<LoginBtn $login={true} type="submit">로그인</LoginBtn>
<LoginBtn $login={false} type="button" onClick={onClickSignup}>
회원가입
</LoginBtn>
</BtnWrapper>
{toastState.flag
? createPortal(<ErrorToast setToastState={setToastState}>{toastState.message}</ErrorToast>, portalElement)
: null}
</ContentWrapper>
);
};

export default Login;

const LoginBtn = styled.button`
display: flex;
justify-content: center;
align-items: center;

padding: 0.8rem;
width: 100%;

font-size: 17px;
font-weight: bold;
background-color: ${({ $login, theme }) => ($login? theme.colors.darkGreen : theme.colors.darkIvory)};
color: ${({ $login, theme }) => ($login? theme.colors.ivory : theme.colors.brown)};
`;
Loading