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주차 기본/심화/생각] 로그인/회원가입 #7

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 4주차/login-signup/.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 },
],
},
}
27 changes: 27 additions & 0 deletions 4주차/login-signup/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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?

# dotenv environment variable files
.env
16 changes: 16 additions & 0 deletions 4주차/login-signup/4주차생각.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
API 통신에 대하여

- 로딩 / 에러 처리를 하는 방법에는 어떤 것들이 있을까?
- 로딩:
- 로딩 스피너 사용
- 사용자에게 메시지 띄우기(ex.로딩중입니다..조금만 기다려주세요 등등)
- 에러:
- input 에러일때: 사용자에게 메시지 띄우기(ex. 이미 사용중인 아이디입니다)
- 에러가 안 뜨는 페이지로 보내고 메시지 띄우기(ex. 일시적 에러가 발생했습니다. 잠시 후 다시 시도해주세요 등등)
- 패칭 라이브러리란 무엇이고 어떤 것들이 있을까?
- "네트워크 요청을 관리하고 처리하기 위한 도구"라고 한다
- axios, fetch,..
- (사실 자바스크립트에는 fetch api가 존재하지만, 더 많은 기능을 지원하기에 개발자들은 서버 통신에 보다 편리한 axios를 선호한다.라네요)
- 패칭 라이브러리를 쓰는 이유는 무엇일까?
- 1. 간편하다! 이미 필요한 코드들을 응집해서 만들어둔 라이브러리로 그것을 사용하기만 하면 되기 때문에. 비동기처리에도 효과적인 대응이 가능하다
- 2. 이건 잘 몰라서 찾아봤는데 인터셉터 및 설정 부분에서 패칭 라이브러리는 요청과 응답을 가로채고 수정할 수 있는 인터셉터를 제공하여 특정 요구사항에 맞게 설정할 수 있다네요..
8 changes: 8 additions & 0 deletions 4주차/login-signup/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
13 changes: 13 additions & 0 deletions 4주차/login-signup/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
Copy link
Member

Choose a reason for hiding this comment

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

아 이거 언석이한테 리뷰 받았는데 우리 같이 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>Vite + React</title>
Copy link
Member

Choose a reason for hiding this comment

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

요기도 프로젝트의 특성에 맞게 바궈줘보기 !

</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions 4주차/login-signup/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "package",
"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": {
"axios": "^1.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.19.0",
"styled-components": "^6.1.1"
},
"devDependencies": {
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"@vitejs/plugin-react": "^4.2.0",
"eslint": "^8.53.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"vite": "^5.0.0"
}
}
Binary file added 4주차/login-signup/public/profile.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions 4주차/login-signup/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
19 changes: 19 additions & 0 deletions 4주차/login-signup/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Route, BrowserRouter, Routes } from "react-router-dom";
import "./App.css";
Copy link
Member

Choose a reason for hiding this comment

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

요거 안쓰는거 아닌가용?

import Login from "./Login";
import SignUp from "./Signup";
import MyPage from "./Mypage";

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/mypage/:userId" element={<MyPage />} />
</Routes>
</BrowserRouter>
);
}

export default App;
118 changes: 118 additions & 0 deletions 4주차/login-signup/src/Login.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { useState, useEffect } from "react";
import styled from "styled-components";
import "./App.css";
import axios from "axios";
import { Link, Navigate, useNavigate } from "react-router-dom";
import { createPortal } from "react-dom";
import {
MainBox,
Header,
Section,
Sections,
SectionInput,
SectionTitle,
} from "./style";

export default function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");

const [showToast, setShowToast] = useState(false);
const [toastMessage, setToastMessage] = useState("");

const navigate = useNavigate();

const handleUserName = (e) => {
setUsername(e.target.value);
};

const handlePassword = (e) => {
setPassword(e.target.value);
};

const handleLogin = () => {
axios
.post(import.meta.env.VITE_BASE_URL + "/api/v1/members/sign-in", {
username: username,
password: password,
})
Comment on lines +35 to +38
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.post(import.meta.env.VITE_BASE_URL + "/api/v1/members/sign-in", {
username: username,
password: password,
})
.post(`${import.meta.env.VITE_BASE_URL}/api/v1/members/sign-in`, {
username,
password,
})

템플릿 리터럴과 단축 프로퍼티 사용하기!

.then((response) => {
navigate(`/mypage/${response.data.id}`);
})
.catch((error) => {
setToastMessage(error.response.data.message);
setShowToast(true);
});
};

useEffect(() => {
// 3초 후 사라짐
const timer = setTimeout(() => {
setShowToast(false);
}, 3000);

return () => clearTimeout(timer);
}, [showToast]);

const toastElement = showToast ? (
<ToastContainer>{toastMessage}</ToastContainer>
) : null;

return (
<MainBox>
<Header>Login</Header>
<Sections>
<Section>
<SectionTitle>ID</SectionTitle>
<SectionInput
value={username}
onChange={handleUserName}
placeholder="아이디를 입력해주세요"
/>
</Section>
<Section>
<SectionTitle>PASSWORD</SectionTitle>
<SectionInput
value={password}
onChange={handlePassword}
placeholder="비밀번호를 입력해주세요"
/>
</Section>
Comment on lines +65 to +80
Copy link
Member

Choose a reason for hiding this comment

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

여기는 사실 거의 똑같은 구조가 반복되고 있는 형태자나!?
중복 코드를 줄일 수 있는 방법을 생각해보면 좋겠다!

나 같은 경우는, 상수파일을 만들어서 id, password 같은 문자열 값을 넣어줬어.
얘네를 map으로 돌려서 각 index 별로 원하는 동작을 수행하게 해줬구 !
다양한 방법이 있겠지만 요런 방법도 있다는거 참고하면 좋을듯 합니당 ~

</Sections>

<Button
type="submit"
Copy link
Member

Choose a reason for hiding this comment

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

헉 야무지게 설정해줬당

Copy link
Member

Choose a reason for hiding this comment

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

잊지 않고 type 설정해주기 좋다! 그런데 button의 type중에 submit은

태그에서 form data가 제출되는 버튼임을 명시하기 위함이라서 type="button'이 맞답니당

onClick={() => {
handleLogin();
}}
>
로그인
</Button>
<Link to="/signup">
<Button>회원가입</Button>
</Link>
{createPortal(toastElement, document.body)}
Copy link
Member

Choose a reason for hiding this comment

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

모달을 띄워주는 부분도 로그인과는 역할이 다른 부분이라 컴포넌트 분리를 해주는게 좋을 것 같아!
예를 들어 모달을 구현한 컴포넌트 이름을 Modal 이라고 가정한다면,
showModal && <Modal /> 이런 식으로 보다 명확하고 직관적인 코드가 될 수 있겠지 ?!

</MainBox>
);
}

const Button = styled.button`
width: 80%;
height: 3rem;

margin: 0.5rem auto;
border: 1px solid black;
`;

const ToastContainer = styled.div`
position: fixed;
bottom: 15rem;
left: 50%;
transform: translateX(-50%);

padding: 1rem;

background-color: gray;
color: white;
border-radius: 0.5rem;
`;
74 changes: 74 additions & 0 deletions 4주차/login-signup/src/Mypage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useEffect, useState } from "react";
import styled from "styled-components";
import "./App.css";
import axios from "axios";
import { useParams, Link } from "react-router-dom";
import { MainBox, Header } from "./style";

export default function MyPage() {
let { userId } = useParams();
Copy link
Member

Choose a reason for hiding this comment

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

요고는 const 쓰면 안되는 이유가 있나?? 아니면 바뀌어야하는 값이니까 혹시몰라서 let 쓴거야??

Copy link
Member

Choose a reason for hiding this comment

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

요고는 한 컴포넌트 내에서 변경되는 값이 아니니까 const로 정의해주는게 더 적절하겠다 !

const [username, setUsername] = useState("");
const [nickname, setNickname] = useState("");

useEffect(() => {
const fetchUserInfo = async () => {
try {
const response = await axios.get(
import.meta.env.VITE_BASE_URL + `/api/v1/members/${userId}`
Copy link
Member

Choose a reason for hiding this comment

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

얘도 매번 타이핑하기 귀찮을텐데 따로 빼서 관리해보쟈

);
setUsername(response.data.username);
setNickname(response.data.nickname);
} catch (error) {
console.log(error.message);
}
};
fetchUserInfo();
});
return (
<MainBox>
<Header>MY PAGE</Header>
<Infos>
<ProfileImg src="/profile.JPG" />
<InfoText>
<Info>ID : {username}</Info>
<Info>NICKNAME : {nickname}</Info>
</InfoText>
</Infos>
<Link to="../../login">
<button>로그아웃</button>
</Link>
</MainBox>
);
}

const Infos = styled.div`
display: flex;
justify-content: center;
align-items: center;

width: 100%;
height: 50%;
`;

const InfoText = styled.div`
display: flex;
flex-direction: column;
`;

const ProfileImg = styled.img`
width: 5rem;
height: 5rem;

border-radius: 50%;
border: 1px solid black;
`;

const Info = styled.div`
height: 3rem;
width: 10rem;
padding: 0 1rem;

text-align: left;
font-weight: bold;
line-height: 3rem;
`;
Loading