-
Notifications
You must be signed in to change notification settings - Fork 7
[3주차] 고은 미션 제출합니다. #4
base: master
Are you sure you want to change the base?
Changes from all commits
0bc3e51
086863f
66f470a
7ed4324
ae509f6
17258a2
6b8ec7d
3b08979
9134d24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,4 +28,5 @@ yarn-error.log* | |
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
.env* | ||
.env* | ||
.now |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,4 @@ | ||
# react-vote-11th | ||
|
||
## 실행 방법 | ||
|
||
``` | ||
npm install | ||
npm run dev | ||
``` | ||
|
||
- npm install : 필요한 모든 패키지를 설치합니다. 처음 1번만 실행하면 됩니다. | ||
- npm run dev : react(next) 웹서버를 localhost:3000에서 실행합니다. | ||
vote-form을 구현하는데 시간을 많이 썼던것같습니다 | ||
axios.put에서 url로 각 후보별 id에 해당하는 인자를 어떻게 넘겨줄지가 어려웠고, 투표 버튼을 클릭해서 투표 완료시 화면에 업데이트 하는점도 어려웠습니다 로그인 오류시 폼 초기화하는걸 getElementById로 하느라 input에 props 전달할때 name으로 안하고 id로 해서 [e.target.id]: e.target.value로 했는데 맞는지 모르겠어요.. | ||
useEffect에 대한 이해도 아직 부족하고 axios사용도 많이 미숙한것같습니다ㅠㅠ! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"version": 2, | ||
"public": false, | ||
"builds": [{ "src": "next.config.js", "use": "@now/next" }], | ||
"build": { | ||
"env": { | ||
"NODE_ENV": "@react-vote-11th-node-env", | ||
"PORT": "@react-vote-11th-port", | ||
"API_HOST": "@react-vote-11th-api-host" | ||
} | ||
} | ||
} | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,21 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
import React from 'react'; | ||
import styled from 'styled-components'; | ||
|
||
import LoginForm from "../src/components/login-form"; | ||
import LoginForm from '../src/components/login-form'; | ||
|
||
export default function Home() { | ||
return ( | ||
<Wrapper> | ||
리액트 투-표 | ||
<LoginForm /> | ||
</Wrapper> | ||
); | ||
return ( | ||
<Wrapper> | ||
<Title>리액트 투-표</Title> | ||
<LoginForm /> | ||
</Wrapper> | ||
); | ||
} | ||
|
||
const Title = styled.h1` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. semantic tag 좋습니다 ㅎㅎ |
||
font-size: 40px; | ||
`; | ||
const Wrapper = styled.div` | ||
min-height: 100vh; | ||
padding: 10rem 40rem; | ||
background-color: Azure; | ||
min-height: 100vh; | ||
padding: 10rem 40rem; | ||
background-color: Azure; | ||
`; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,109 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
import React, { useState } from 'react'; | ||
import styled from 'styled-components'; | ||
import axios from 'axios'; | ||
|
||
import VoteForm from './vote-form'; | ||
|
||
export default function LoginForm() { | ||
return <Wrapper>안녕 나는 로그인 폼!</Wrapper>; | ||
const [LoginForm, setLoginForm] = useState({ email: '', password: '' }); | ||
const [isloggedIn, setloggedIn] = useState(false); | ||
|
||
const erase = (name) => () => { | ||
setLoginForm({ | ||
...loginForm, | ||
[name]: '', | ||
}); | ||
}; | ||
|
||
const handleFormChange = (e) => { | ||
setLoginForm({ ...LoginForm, [e.target.name]: e.target.value }); | ||
}; | ||
|
||
const handleSubmit = () => { | ||
const { email, password } = LoginForm; | ||
if (email === '' || password === '') { | ||
alert('모든 항목을 입력해주세요!'); | ||
return false; | ||
} | ||
|
||
axios | ||
.post(process.env.API_HOST + '/auth/signin/', LoginForm) | ||
.then((response) => { | ||
console.log(response); | ||
setloggedIn(true); | ||
alert('로그인 성공!'); | ||
}) | ||
.catch((error) => { | ||
if (error.response.status === 404) { | ||
alert('이메일이 존재하지 않습니다.'); | ||
erase('email')(); | ||
erase('password')(); | ||
} | ||
if (error.response.status === 422) { | ||
alert('비밀번호가 일치하지 않습니다!'); | ||
erase('email')(); | ||
} | ||
return Promise.reject(error.response); | ||
}); | ||
}; | ||
|
||
return ( | ||
<div> | ||
{!isloggedIn && ( | ||
<Wrapper> | ||
<Title>로그인</Title> | ||
<Row> | ||
<Label>EMAIL</Label> | ||
<Input type="text" name="email" onChange={handleFormChange}></Input> | ||
</Row> | ||
<Row> | ||
<Label>PASSWORD</Label> | ||
<Input | ||
type="password" | ||
onChange={handleFormChange} | ||
name="password" | ||
></Input> | ||
</Row> | ||
<Button onClick={() => handleSubmit()}>로그인</Button> | ||
</Wrapper> | ||
)} | ||
|
||
{isloggedIn && <VoteForm />} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. VoteForm이 LoginForm 내부에 있네요 😅 |
||
</div> | ||
); | ||
} | ||
const Title = styled.h1` | ||
font-size: 3rem; | ||
margin-bottom: 4rem; | ||
margin-top: 25px; | ||
`; | ||
const Row = styled.div` | ||
display: flex; | ||
flex-direction: row; | ||
margin-bottom: 2rem; | ||
`; | ||
const Button = styled.button` | ||
display: block; | ||
margin-left: auto; | ||
font-size: 1.8rem; | ||
padding: 0.5rem 1rem; | ||
border-style: none; | ||
border-radius: 1rem; | ||
`; | ||
|
||
const Label = styled.label` | ||
font-size: 20px; | ||
margin-right: auto; | ||
`; | ||
const Input = styled.input` | ||
width: 75%; | ||
padding: 0.5rem 1rem; | ||
border: 1px solid grey; | ||
`; | ||
const Wrapper = styled.div` | ||
width: 100%; | ||
min-height: 30rem; | ||
background-color: white; | ||
font-size: 18px; | ||
padding: 3rem 4rem; | ||
width: 100%; | ||
min-height: 30rem; | ||
background-color: white; | ||
font-size: 18px; | ||
padding: 3rem 4rem; | ||
`; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,14 +1,115 @@ | ||||||
import React from "react"; | ||||||
import styled from "styled-components"; | ||||||
import React, { useState, useEffect } from 'react'; | ||||||
import axios from 'axios'; | ||||||
|
||||||
export default function VoteForm() { | ||||||
return <Wrapper>안녕 나는 투표 폼!</Wrapper>; | ||||||
import styled from 'styled-components'; | ||||||
|
||||||
function VoteForm() { | ||||||
const [candidates, setCandidates] = useState([]); | ||||||
const [voteCount, setVoteCount] = useState(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 state가 사용되지 않고 있어요!, vote시 paramter로 넘겨줄 필요가 없기 때문에 지워주시면 될 것 같아요!
Suggested change
|
||||||
|
||||||
useEffect(() => { | ||||||
getCandidates(); | ||||||
}, []); | ||||||
|
||||||
const getCandidates = async () => { | ||||||
await axios | ||||||
.get(process.env.API_HOST + '/candidates/', candidates) | ||||||
.then(({ data }) => { | ||||||
setCandidates(data); | ||||||
}) | ||||||
.catch((error) => { | ||||||
console.log(error); | ||||||
}); | ||||||
}; | ||||||
|
||||||
const voteCandidates = async (candidate) => { | ||||||
const newUrl = `${process.env.API_HOST}/candidates/${candidate._id}/vote/`; | ||||||
await axios | ||||||
.put(newUrl, voteCount) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
request body schema가 명시되지 않은 경우, 안 넘겨주셔도 됩니다! |
||||||
.then(({ data }) => { | ||||||
setVoteCount(data); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
삭제해주시면 됩니다 ㅎㅎ |
||||||
alert(candidate.name + '님에게 투표 완료!'); | ||||||
getCandidates(); | ||||||
}) | ||||||
.catch(function (error) { | ||||||
console.log(error); | ||||||
alert('투표 실패!'); | ||||||
}); | ||||||
}; | ||||||
|
||||||
return ( | ||||||
<Wrapper> | ||||||
<Title> | ||||||
<RedTitle>프론트엔드 인기쟁이</RedTitle>는 누구? | ||||||
</Title> | ||||||
<SubTitle>CEOS 프론트엔드 개발자 인기 순위 및 투표 창입니다.</SubTitle> | ||||||
<VoteArea> | ||||||
{candidates | ||||||
.sort((person1, person2) => person2.voteCount - person1.voteCount) | ||||||
.map((person, index) => ( | ||||||
<Row key={person._id}> | ||||||
<Rank>{index + 1}위:</Rank> | ||||||
<CandiName> | ||||||
{person.name} | ||||||
<br />[{person.voteCount}표] | ||||||
</CandiName> | ||||||
<Button onClick={() => voteCandidates(person)}>투표</Button> | ||||||
</Row> | ||||||
))} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. candidate 한명 한명에 대한 component를 따로 분리해서 map 해보는건 어떨까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 엇 이게 map안에서 적용되고 있는거 아닌가요..?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 앗 넵 다른 파일로 분리해보시면 어떨까해서 드린 말씀이었어요! |
||||||
</VoteArea> | ||||||
</Wrapper> | ||||||
); | ||||||
} | ||||||
export default React.memo(VoteForm); | ||||||
const Row = styled.div` | ||||||
display: flex; | ||||||
flex-direction: row; | ||||||
align-items: center; | ||||||
`; | ||||||
const Button = styled.button` | ||||||
background-color: navy; | ||||||
color: white; | ||||||
font-size: 1.5rem; | ||||||
padding: 0.5rem 1rem; | ||||||
border-style: none; | ||||||
border-radius: 1rem; | ||||||
`; | ||||||
const Rank = styled.strong` | ||||||
font-size: 1.5rem; | ||||||
margin: 0rem 4rem 1rem 0rem; | ||||||
`; | ||||||
const CandiName = styled.p` | ||||||
font-size: 1.5rem; | ||||||
display: block; | ||||||
margin: 0rem auto 1rem 0rem; | ||||||
`; | ||||||
const VoteArea = styled.div` | ||||||
width: 100%; | ||||||
padding: 5rem 10rem; | ||||||
border: 1px solid black; | ||||||
display: flex; | ||||||
flex-direction: column; | ||||||
justify-content: space-between; | ||||||
`; | ||||||
const Title = styled.span` | ||||||
font-size: 3rem; | ||||||
color: black; | ||||||
display: inline-block; | ||||||
font-weight: bold; | ||||||
`; | ||||||
const RedTitle = styled.span` | ||||||
Comment on lines
+64
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 용도 별로 다른 태그 사용하신거 너무 잘하셨어요~~ 💯 |
||||||
font-size: 3rem; | ||||||
color: crimson; | ||||||
`; | ||||||
const SubTitle = styled.h1` | ||||||
font-size: 2.5rem; | ||||||
color: grey; | ||||||
`; | ||||||
|
||||||
const Wrapper = styled.div` | ||||||
width: 100%; | ||||||
min-height: 30rem; | ||||||
background-color: white; | ||||||
font-size: 18px; | ||||||
padding: 3rem 4rem; | ||||||
width: 100%; | ||||||
min-height: 30rem; | ||||||
background-color: white; | ||||||
font-size: 18px; | ||||||
padding: 3rem 4rem 10rem; | ||||||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
굿입니다 👍