diff --git a/.gitignore b/.gitignore
index a5ac825..f014060 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,5 @@ yarn-error.log*
.env.development.local
.env.test.local
.env.production.local
-.env*
\ No newline at end of file
+.env*
+.now
\ No newline at end of file
diff --git a/README.md b/README.md
index 461c3ec..d9fcd15 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
# react-vote-11th
-## 실행 방법
+## 구현 방법
-```
-npm install
-npm run dev
-```
+1. login form과 vote form을 로그인 결과에 따라 보여주는 페이지 index를 만든다.
+2. 로그인시 서버에 리퀘스트를 보내 이메일과 비밀번호를 확인한다
+3. vote form 내부 리스트 정렬을 vote list를 구현하여 진행한다
-- npm install : 필요한 모든 패키지를 설치합니다. 처음 1번만 실행하면 됩니다.
-- npm run dev : react(next) 웹서버를 localhost:3000에서 실행합니다.
+## 어려웠던 점
+
+개인적으로 시간을 너무 촉박하게 잡고 과제를 진행해서 아쉬운 부분이 많이 남습니다. 그리고 이상한 부분에서 계속 삽질을 해서,, 흑흑 시간이 조금 많이 모자랐던 것 같습니다. 서버 통신을 axios로 하는게 되게 간단하고 효율적인 것 같아 좋았습니다! memo는 어느 곳에 써야할지 아직은 감이 잘 안잡혀서 이부분도 더 공부하고 싶습니다.
diff --git a/package-lock.json b/package-lock.json
index a439258..136f0db 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1920,6 +1920,37 @@
}
}
},
+ "axios": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+ "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+ "requires": {
+ "follow-redirects": "1.5.10"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "=3.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
diff --git a/package.json b/package.json
index 0e22c09..817c4b9 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"start": "node server"
},
"dependencies": {
+ "axios": "^0.19.2",
"compression": "^1.7.4",
"dotenv": "^8.2.0",
"express": "^4.17.1",
diff --git a/pages/index.js b/pages/index.js
index 6474ebb..2fae33e 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -1,13 +1,18 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import styled from "styled-components";
+import axios from "axios";
import LoginForm from "../src/components/login-form";
+import VoteForm from "../src/components/vote-form";
export default function Home() {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+
return (
- 리액트 투-표
-
+ 리액트 투-표
+ {!isLoggedIn && }
+ {isLoggedIn && }
);
}
@@ -17,3 +22,6 @@ const Wrapper = styled.div`
padding: 10rem 40rem;
background-color: Azure;
`;
+const Title = styled.h1`
+ font-size: 4rem;
+`;
diff --git a/src/components/login-form.js b/src/components/login-form.js
index 418d945..a0188f0 100644
--- a/src/components/login-form.js
+++ b/src/components/login-form.js
@@ -1,14 +1,106 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import styled from "styled-components";
+import axios from "axios";
-export default function LoginForm() {
- return 안녕 나는 로그인 폼!;
-}
+export default function LoginForm({ setIsLoggedIn }) {
+ const [form, setForm] = useState({ email: "", password: "" });
+
+ const handleFormChange = (e) => {
+ setForm({
+ ...form,
+ [e.target.name]: e.target.value,
+ });
+ };
+ const resetForm = () => {
+ setForm({ email: "", password: "" });
+ };
+ const validCheck = () => {
+ if (form.email.length === 0 || form.password.length === 0) {
+ alert("모든 항목을 입력하세요");
+ return false;
+ }
+ return true;
+ };
+ const handleSubmit = () => {
+ console.log(form);
+ if (!validCheck) return;
+ axios
+ .post(process.env.API_HOST + "/auth/signin/", form)
+ .then(function (response) {
+ console.log(response);
+ alert("로그인 성공!");
+ setIsLoggedIn(true);
+ })
+ .catch(function (error) {
+ resetForm();
+ alert("로그인 실패!");
+ console.log(error);
+ });
+ };
+ return (
+
+ 로그인
+
+
+
+
+
+
+
+
+
+
+ 로그인
+
+ );
+}
+export const MemoizedLoginForm = React.memo(LoginForm);
const Wrapper = styled.div`
- width: 100%;
+ width: 100
min-height: 30rem;
background-color: white;
font-size: 18px;
padding: 3rem 4rem;
+ display: flex;
+ flex-direction: column;
+`;
+const Title = styled.p`
+ font-weight: bold;
+`;
+const InputWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+`;
+const Row = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+`;
+const Input = styled.input`
+ width: 50rem;
+ height: 3rem;
+`;
+const Label = styled.label`
+ font-size: 12px;
+`;
+const SubmitButton = styled.button`
+ display: block;
+ margin-left: auto;
+ font-size: 1.8rem;
+ cursor: pointer;
+ padding: 0.5rem 1rem;
+ border: 1rem none;
+ outline: none;
`;
diff --git a/src/components/vote-form.js b/src/components/vote-form.js
index 65bc549..f8f9d79 100644
--- a/src/components/vote-form.js
+++ b/src/components/vote-form.js
@@ -1,10 +1,55 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import styled from "styled-components";
+import axios from "axios";
+
+import CandidateCard from "./vote-list";
+import { useCandidates } from "../hooks/Candidates";
export default function VoteForm() {
- return 안녕 나는 투표 폼!;
+ const { candidates, loading, error, fetchData } = useCandidates();
+ console.log(candidates);
+
+ return (
+
+
+ 프론트엔드 인기쟁이는 누구?
+
+ CEOS 프론트엔드 개발자 인기순위 및 투표창입니다.
+
+ {candidates &&
+ candidates
+ .sort((a, b) => {
+ return b.voteCount - a.voteCount;
+ })
+ .map((candidate, index) => {
+ const { _id: id } = candidate;
+ return (
+
+ );
+ })}
+
+
+ );
}
+export const MemoizedVoteForm = React.memo(VoteForm);
+const Title = styled.h2``;
+const Red = styled.strong`
+ color: red;
+`;
+const SubTitle = styled.h3`
+ color: grey;
+`;
+const VoteSection = styled.div`
+ width: 100%;
+ padding: 5rem 10rem;
+ border: 1px solid black;
+`;
const Wrapper = styled.div`
width: 100%;
min-height: 30rem;
diff --git a/src/components/vote-list.js b/src/components/vote-list.js
new file mode 100644
index 0000000..69241b7
--- /dev/null
+++ b/src/components/vote-list.js
@@ -0,0 +1,55 @@
+import React, { useState, useEffect } from "react";
+import styled from "styled-components";
+import axios from "axios";
+
+export default function VoteList({
+ name,
+ voteCount,
+ _id: id,
+ rank,
+ getCandidateList,
+}) {
+ const handleVote = () => {
+ axios
+ .put(process.env.API_HOST + `/candidates/${id}/vote/`, {
+ params: {},
+ })
+ .then(function (response) {
+ console.log(response);
+ getCandidateList();
+ alert(name + "님께 투표 했습니다!");
+ })
+ .catch(function (error) {
+ console.log(error);
+ alert("투표 실패했습니다!");
+ });
+ };
+
+ return (
+
+
+ {rank}위: {name} [{voteCount}]표
+
+ 투표
+
+ );
+}
+const Wrapper = styled.div`
+ width: 100%;
+ background-color: white;
+ font-size: 18px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+`;
+const Rank = styled.strong``;
+const Candidate = styled.div``;
+const VoteButton = styled.button`
+ background-color: navy;
+ color: white;
+ font-size: 2rem;
+ padding: 0.5rem 1rem;
+ border-radius: 1rem;
+ display: block;
+ margin-left: auto;
+`;
diff --git a/src/hooks/Candidates.js b/src/hooks/Candidates.js
new file mode 100644
index 0000000..72d9ddf
--- /dev/null
+++ b/src/hooks/Candidates.js
@@ -0,0 +1,32 @@
+import React, { useState, useEffect } from "react";
+import styled from "styled-components";
+import axios from "axios";
+
+export const useCandidates = () => {
+ const [candidates, setCandidateList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(false);
+ const fetchData = async () => {
+ setError(false);
+ setLoading(true);
+
+ try {
+ await axios
+ .get(process.env.API_HOST + "/candidates/", candidates)
+ .then(({ data }) => {
+ setCandidateList(data);
+ })
+ .catch(function (error) {
+ console.log(error);
+ });
+ } catch (error) {
+ setError(true);
+ }
+ setLoading(false);
+ };
+ useEffect(() => {
+ fetchData();
+ }, []);
+
+ return { candidates, loading, error, fetchData };
+};