김건희

[Merge] 'frontend' into 'master'

Showing 45 changed files with 1713 additions and 19 deletions
This diff is collapsed. Click to expand it.
1 +Stack trace:
2 +Frame Function Args
3 +000005FF340 0018006286E (001802901B2, 0018026DE3E, 0000000005E, 000005FAEA0)
4 +000005FF340 0018004846A (00000000000, 00000000000, 7FFE00000000, 00000001000)
5 +000005FF340 001800484A2 (00000000000, 0000000005A, 0000000005E, 00000000000)
6 +000005FF340 001800D3E58 (00000000000, 00100000000, 0018026D9F2, 000005FBFEC)
7 +000005FF340 0018012C857 (00000000000, 00180223E20, 00180223E10, 000005FDDE0)
8 +000005FF340 001800488B4 (0018030F940, 000005FDDE0, 00000000000, 00000000000)
9 +000005FF340 0018004A01F (0007FFE0384, 00000000000, 00000000000, 00000000000)
10 +000005FF340 001800D4E98 (00000000000, 00000000000, 00000000000, 00000000000)
11 +000005FF5E0 7FFE062E9A1D (00180040000, 00000000001, 00000000000, 000005FF528)
12 +000005FF5E0 7FFE0633C1E7 (7FFE0630DB00, 000007F3901, 7FFE00000001, 00000000001)
13 +000005FF5E0 7FFE0633BF7A (000007F3900, 000005FF5E0, 000007F4270, 00000070000)
14 +000005FF5E0 7FFE0633C000 (00000000010, 00000000000, 7FFE06401A90, 000005FF678)
15 +00000000000 7FFE063A3C2A (00000000000, 00000000000, 00000000001, 00000000000)
16 +00000000000 7FFE06344CDB (7FFE062D0000, 00000000000, 00000347000, 00000000000)
17 +00000000000 7FFE06344B63 (00000000000, 00000000000, 00000000000, 00000000000)
18 +00000000000 7FFE06344B0E (00000000000, 00000000000, 00000000000, 00000000000)
19 +End of stack trace
This diff could not be displayed because it is too large.
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
6 "@testing-library/jest-dom": "^5.16.4", 6 "@testing-library/jest-dom": "^5.16.4",
7 "@testing-library/react": "^13.2.0", 7 "@testing-library/react": "^13.2.0",
8 "@testing-library/user-event": "^13.5.0", 8 "@testing-library/user-event": "^13.5.0",
9 + "axios": "^0.27.2",
9 "cors": "^2.8.5", 10 "cors": "^2.8.5",
10 "express": "^4.18.1", 11 "express": "^4.18.1",
11 "http-proxy-middleware": "^2.0.6", 12 "http-proxy-middleware": "^2.0.6",
...@@ -13,7 +14,12 @@ ...@@ -13,7 +14,12 @@
13 "nodemon": "^2.0.16", 14 "nodemon": "^2.0.16",
14 "react": "^18.1.0", 15 "react": "^18.1.0",
15 "react-dom": "^18.1.0", 16 "react-dom": "^18.1.0",
17 + "react-redux": "^8.0.2",
18 + "react-router-dom": "^6.3.0",
16 "react-scripts": "5.0.1", 19 "react-scripts": "5.0.1",
20 + "redux": "^4.1.2",
21 + "redux-promise-middleware": "^6.1.2",
22 + "redux-thunk": "^2.4.1",
17 "web-vitals": "^2.1.4" 23 "web-vitals": "^2.1.4"
18 }, 24 },
19 "scripts": { 25 "scripts": {
...@@ -39,5 +45,8 @@ ...@@ -39,5 +45,8 @@
39 "last 1 firefox version", 45 "last 1 firefox version",
40 "last 1 safari version" 46 "last 1 safari version"
41 ] 47 ]
48 + },
49 + "devDependencies": {
50 + "redux-devtools-extension": "^2.13.9"
42 } 51 }
43 } 52 }
......
1 -import logo from './logo.svg';
2 import './App.css'; 1 import './App.css';
2 +import RegisterPage from './component/views/RegisterPage/RegisterPage';
3 +import LoginPage from './component/views/LoginPage/LoginPage';
4 +import MainPage from './component/views/MainPage/MainPage';
5 +import RecommandPage from './component/views/RecommandPage/RecommandPage';
6 +import { Route, Routes } from 'react-router-dom';
7 +import WeatherPage from './component/views/WeatherPage/WeatherPage';
3 8
4 function App() { 9 function App() {
5 return ( 10 return (
6 - <div className="App"> 11 + <div>
7 - <header className="App-header"> 12 + <Routes>
8 - <img src={logo} className="App-logo" alt="logo" /> 13 + <Route exact path = "/login" element = {<LoginPage/>}/>
9 - <p> 14 + <Route exact path = "/" element = {<RegisterPage/>}/>
10 - Edit <code>src/App.js</code> and save to reload. 15 + <Route exact path = "/main" element = {<MainPage/>}/>
11 - </p> 16 + <Route exact path = "/weather" element = {<WeatherPage/>}/>
12 - <a 17 + <Route exact path = "/recommand" element = {<RecommandPage/>}/>
13 - className="App-link" 18 + </Routes>
14 - href="https://reactjs.org"
15 - target="_blank"
16 - rel="noopener noreferrer"
17 - >
18 - Learn React
19 - </a>
20 - </header>
21 </div> 19 </div>
22 ); 20 );
23 } 21 }
......
1 +import React, { useCallback, useEffect, useState } from "react";
2 +import { useDispatch, useSelector } from "react-redux";
3 +import { useNavigate } from "react-router-dom";
4 +import { login } from "../../../modules/user";
5 +import { address } from "../../../modules/weather";
6 +import "../style/LoginPage.scss"
7 +
8 +function LoginPage(props) {
9 + const dispatch = useDispatch();
10 + const navigate = useNavigate();
11 +
12 + const loginResult = useSelector((state) => state.user.loginData);
13 +
14 + const [Id, setId] = useState("");
15 + const [Password, setPassword] = useState("");
16 +
17 + const [checkLogin, setCheckLogin] = useState(false);
18 + const [checkIdError, setCheckIdError] = useState(false);
19 + const [checkPasswordError, setCheckPasswordError] = useState(false);
20 + const [checkLoginError, setCheckLoginError] = useState(false);
21 +
22 + const idRegex = /^(?=.*[a-zA-Z])(?=.*[0-9]).{6,14}$/;
23 + const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-?])(?=.*[0-9]).{8,25}$/;
24 +
25 + useEffect(() => {
26 + if (checkLogin === true) {
27 + loginResult.then((result) => {
28 + if (result.loginSuccess === true) {
29 + alert('로그인에 성공하였습니다.');
30 + navigate('/main');
31 + }
32 + else {
33 + alert('로그인에 실패하였습니다.');
34 + }
35 + })
36 + }
37 + }, [loginResult])
38 +
39 + const onIdHandler = useCallback((event) => {
40 + setId(event.currentTarget.value);
41 +
42 + // 아이디 유효성 검사
43 + if (!idRegex.test(event.currentTarget.value)) {
44 + setCheckIdError(true);
45 + }
46 + else {
47 + setCheckIdError(false);
48 + }
49 +
50 + }, [checkIdError]);
51 +
52 + const onPasswordHandler = useCallback((event) => {
53 + setPassword(event.currentTarget.value);
54 +
55 + // 비밀번호 유효성 검사
56 + if (!passwordRegex.test(event.currentTarget.value)) {
57 + setCheckPasswordError(true);
58 + }
59 + else {
60 + setCheckPasswordError(false);
61 + }
62 + }, [checkPasswordError]);
63 +
64 + const onSubmitHandler = useCallback((event) => {
65 + event.preventDefault();
66 +
67 + if (checkIdError || Id === "") {
68 + setCheckLoginError(true);
69 + }
70 + else if (checkPasswordError || Password === "") {
71 + setCheckLoginError(true);
72 + }
73 + else {
74 + setCheckLoginError(false);
75 + }
76 +
77 + // login
78 + if (!checkLoginError) {
79 + const UserData = {
80 + id: Id,
81 + password: Password
82 + };
83 +
84 + dispatch(login(UserData));
85 + dispatch(address());
86 + setCheckLogin(true);
87 +
88 + }
89 +
90 + }, [checkIdError, checkPasswordError, Password, dispatch, loginResult]);
91 +
92 + return (
93 + <div id = "body">
94 + <div className="login-box">
95 + <h2>로그인</h2>
96 + <div className="input-area">
97 + <input
98 + placeholder="아이디"
99 + type="text"
100 + value={Id}
101 + onChange={onIdHandler}
102 + />
103 + </div>
104 + <div className="check-variable">
105 + {checkIdError && <div style={{color : 'red'}}>아이디는 6자리 이상 14자리 이하 소문자와 숫자로 입력해주세요.</div>}
106 + </div>
107 + <div className="input-area">
108 + <input
109 + placeholder="비밀번호"
110 + type="text"
111 + value={Password}
112 + onChange={onPasswordHandler}
113 + />
114 + </div>
115 + <div className="check-variable">
116 + {checkPasswordError && <div style={{color : 'red'}}>알파벳과 숫자, 특수문자를 포함하여 8자리 이상 입력해주세요.</div>}
117 + </div>
118 + <div className="btn-area" onClick={onSubmitHandler}>
119 + <button
120 + className="login-btn"
121 + >
122 + 로그인
123 + </button>
124 + </div>
125 + <div className="check-variable">
126 + {checkLoginError && <div style={{color : 'red'}}>정보를 제대로 입력해주세요.</div>}
127 + </div>
128 + </div>
129 + </div>
130 + );
131 +
132 +}
133 +
134 +export default LoginPage;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useCallback, useEffect, useState } from "react";
2 +import { useDispatch, useSelector } from "react-redux";
3 +import { useNavigate } from "react-router-dom";
4 +import { logout } from "../../../modules/user";
5 +import { todayInformation, tommorrowInformation } from "../../../modules/weather";
6 +import "../style/MainPage.scss"
7 +
8 +function MainPage(props) {
9 +
10 + const dispatch = useDispatch();
11 + const navigate = useNavigate();
12 +
13 + const addressResult = useSelector((state) => state.weather.address);
14 + const user = useSelector((state) => state.user.loginData);
15 +
16 + //이름, 성별, 시구동주소
17 + const [Name, setName] = useState("");
18 + const [Sex, setSex] = useState("");
19 + const [CityAdd, setCityAdd] = useState("");
20 + const [GuAdd, setGuAdd] = useState("");
21 + const [DongAdd, setDongAdd] = useState("");
22 +
23 + // const [checkNameError, setCheckNameError] = useState(false);
24 + // const [checkSexError, setCheckSexError] = useState(true);
25 + const [checkCityAddError, setCheckCityAddError] = useState(true);
26 + const [checkGuAddError, setCheckGuAddError] = useState(true);
27 + const [checkDongAddError, setCheckDongAddError] = useState(true);
28 + const [checkSubmitError, setCheckSubmitError] = useState(false);
29 +
30 + const CityAddSelectList = ["시/도 선택", "강원도", "경기도", "경상북도", "경상남도", "광주광역시", "대구광역시", "대전광역시", "부산광역시", "서울특별시", "울산광역시", "인천광역시", "전라북도", "전라남도", "제주특별자치도", "충청북도", "충청남도"];
31 +
32 + const [GuAddSelectList, setGuAddselectList] = useState(["시/군/구 선택"]);
33 + const [DongAddSelectList, setDongAddselectList] = useState(["읍/면/동 선택"]);
34 +
35 + const [Time, setTime] = useState("00:00:00");
36 +
37 + const currentTime = () => {
38 + const date = new Date();
39 + const hours = String(date.getHours()).padStart(2, "0");
40 + const minutes = String(date.getMinutes()).padStart(2, "0");
41 + const seconds = String(date.getSeconds()).padStart(2, "0");
42 + setTime(hours+" : "+minutes+" : "+seconds);
43 + }
44 +
45 + const startTimer = () => {
46 + setInterval(currentTime, 1000)
47 + }
48 +
49 + startTimer()
50 +
51 + useEffect(() => {
52 + user.then((result) => {
53 + setName(result.logData.name);
54 + setSex(result.logData.gender);
55 + })
56 + }, [user])
57 +
58 + // 시/군/구 주소
59 + useEffect(() => {
60 + const tempList = [];
61 +
62 + addressResult.then((result) => {
63 +
64 + for (let i = 0; i < result.length; i++) {
65 + if (result[i].address1 === CityAdd) {
66 + if (tempList[tempList.length - 1] !== result[i].address2){
67 + tempList.push(result[i].address2);
68 + }
69 + }
70 + }
71 + tempList[0] = "시/군/구 선택";
72 + setGuAddselectList(tempList);
73 + setDongAddselectList(["읍/면/동 선택"]);
74 + });
75 + }, [CityAdd]);
76 +
77 + // 읍/면/동 주소
78 + useEffect(() => {
79 + const tempList = [];
80 +
81 + addressResult.then((result) => {
82 +
83 + for (let i = 0; i < result.length; i++) {
84 + if (result[i].address2 === GuAdd) {
85 + if ((tempList[tempList.length - 1] !== result[i].address3) && tempList[0] !== result[i].address3){
86 + tempList.push(result[i].address3);
87 + }
88 + }
89 + }
90 + tempList[0] = "읍/면/동 선택";
91 + setDongAddselectList(tempList);
92 + });
93 + }, [GuAdd]);
94 +
95 + const onCityAddhandler = useCallback((event) => {
96 + setCityAdd(event.currentTarget.value);
97 +
98 + if (event.currentTarget.value === "시/도 선택") {
99 + setCheckCityAddError(true);
100 + }
101 + else {
102 + setCheckCityAddError(false);
103 + }
104 + }, [checkCityAddError]);
105 +
106 + const onGuAddhandler = useCallback((event) => {
107 + setGuAdd(event.currentTarget.value);
108 +
109 + if (event.currentTarget.value === "시/군/구 선택") {
110 + setCheckGuAddError(true);
111 + }
112 + else {
113 + setCheckGuAddError(false);
114 + }
115 +
116 + }, [checkGuAddError]);
117 +
118 + const onDongAddhandler = useCallback((event) => {
119 + setDongAdd(event.currentTarget.value);
120 +
121 + if (event.currentTarget.value === "읍/면/동 선택") {
122 + setCheckDongAddError(true);
123 + }
124 + else {
125 + setCheckDongAddError(false);
126 + }
127 + }, [checkDongAddError]);
128 +
129 + const onClickTitle = useCallback((event) => {
130 + navigate('/main')
131 + })
132 +
133 + const onClickLogout = useCallback((event) => {
134 + dispatch(logout());
135 + navigate('/login');
136 + })
137 +
138 + const onClickRegister = useCallback((event) => {
139 + navigate('/');
140 + })
141 +
142 + const onSubmitHandler = useCallback((event) => { //제출 전 오류 확인 함수
143 + event.preventDefault(); //체크박스 미리 클릭 방지
144 +
145 + if (checkCityAddError || CityAdd === "") {
146 + setCheckSubmitError(true);
147 + }
148 + else if (checkGuAddError || GuAdd === "") {
149 + setCheckSubmitError(true);
150 + }
151 + else if (checkDongAddError || DongAdd === "") {
152 + setCheckSubmitError(true);
153 + }
154 + else {
155 + setCheckSubmitError(false);
156 + }
157 +
158 +
159 + if (!checkSubmitError) {
160 +
161 + addressResult.then((result) => {
162 + for (let i = 0; i<result.length; i++) {
163 +
164 + if (result[i].address1 === CityAdd && result[i].address2 === GuAdd && result[i].address3 === DongAdd) {
165 +
166 + const dotData = {
167 + address1 : CityAdd,
168 + address2 : GuAdd,
169 + address3 : DongAdd,
170 + dotX : result[i].dotX,
171 + dotY : result[i].dotY,
172 + }
173 +
174 + dispatch(todayInformation(dotData));
175 + dispatch(tommorrowInformation(dotData));
176 +
177 + navigate('/weather');
178 + break;
179 + }
180 + }
181 + })
182 + }
183 +
184 + }, [checkCityAddError, checkDongAddError, checkGuAddError, checkSubmitError, Name, Sex, CityAdd, GuAdd, DongAdd]);
185 +
186 + return (
187 + <>
188 + <dir id = "header">
189 + <dir className="header_title" onClick = {onClickTitle}>
190 + <h1>Weather_Briefing</h1>
191 + </dir>
192 + <dir className="header_choice_box">
193 + <button type="button" onClick = {onClickLogout}>Logout</button>
194 + <button type="button" onClick = {onClickRegister}>Register</button>
195 + </dir>
196 + </dir>
197 +
198 + <div id = "body">
199 + <div className="info-box">
200 + <p className="info">정보를 입력해주세요.</p>
201 + <div className="main-input-area" readOnly>
202 + <li>이름</li>
203 + <input
204 + placeholder={Name}
205 + type="text"
206 + value={Name}
207 + />
208 + </div>
209 + <hr/>
210 + <div className="main-input-area" readOnly>
211 + <li>성별</li>
212 + <p>남자</p>
213 + <input
214 + type="radio" //라디오 버튼 타입
215 + value = "0"
216 + checked = {Sex === "0"}
217 + />
218 + <p>여자</p>
219 + <input
220 + type="radio"
221 + value = "1"
222 + checked = {Sex === "1"}
223 + />
224 + </div>
225 + <hr/>
226 + <div className="main-input-area">
227 + <li>지역</li>
228 + <div className="CityAddSelect">
229 + <select onChange={onCityAddhandler} value={CityAdd}>
230 + {CityAddSelectList.map((item) => (
231 + <option value={item} key={item}>
232 + {item}
233 + </option>
234 + ))}
235 + </select>
236 + </div>
237 + <div className="GuAddSelect">
238 + <select onChange={onGuAddhandler} value={GuAdd}>
239 + {GuAddSelectList.map((item) => (
240 + <option value={item} key={item}>
241 + {item}
242 + </option>
243 + ))}
244 + </select>
245 + </div>
246 + <div className="DongAddSelect">
247 + <select onChange={onDongAddhandler} value={DongAdd}>
248 + {DongAddSelectList.map((item) => (
249 + <option value={item} key={CityAdd+GuAdd+item}>
250 + {item}
251 + </option>
252 + ))}
253 + </select>
254 + </div>
255 + </div>
256 + <hr/>
257 + <div className="main-btn-area" onClick={onSubmitHandler}>
258 + <button className="submit-btn">
259 + 날씨 정보
260 + </button>
261 + </div>
262 + <div className="main-check-variable">
263 + {checkSubmitError && <div style={{color : 'red'}}>정보를 제대로 입력해주세요.</div>}
264 + </div>
265 + </div>
266 + </div>
267 + </>
268 + );
269 +}
270 +
271 +export default MainPage;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useCallback, useEffect, useState } from "react";
2 +import { useDispatch, useSelector } from "react-redux";
3 +import { useNavigate } from "react-router-dom";
4 +import { logout } from "../../../modules/user";
5 +import "../style/RecommandPage.scss"
6 +
7 +function RecommandPage(props) {
8 +
9 + const clothesResult = useSelector((state) => state.clothes.clothesRecommend);
10 +
11 + const [IsRain, setIsRain] = useState("");
12 + const [TopPath, setTopPath] = useState('');
13 + const [BottomPath, setBottomPath] = useState('');
14 +
15 + const dispatch = useDispatch();
16 +
17 + useEffect(() => {
18 + clothesResult.then((result) => {
19 +
20 + if (result.umbrella == 1) {
21 + setIsRain("비 예보가 있습니다. 우산을 꼭 챙겨주세요!");
22 + }
23 + else {
24 + setIsRain("비 예보가 없습니다!");
25 + }
26 +
27 + setTopPath(result.top);
28 + setBottomPath(result.bottom);
29 + })
30 + }, [clothesResult])
31 +
32 + const navigate = useNavigate();
33 +
34 + const onClickLogout = useCallback((event) => {
35 + dispatch(logout());
36 + navigate('/login');
37 + })
38 +
39 + const onClickRegister = useCallback((event) => {
40 + navigate('/');
41 + })
42 +
43 + const onClickTitle = useCallback((event) => {
44 + navigate('/main')
45 + })
46 +
47 + return (
48 + <>
49 + <dir id = "header">
50 + <dir className="header_title" onClick = {onClickTitle}>
51 + <h1>Weather_Briefing</h1>
52 + </dir>
53 + <dir className="header_choice_box">
54 + <button type="button" onClick={onClickLogout}>Logout</button>
55 + <button type="button" onClick={onClickRegister}>Register</button>
56 + </dir>
57 + </dir>
58 +
59 + <div id = "recommand_body">
60 + <dir className="fashion_recommand">
61 + <dir className="rainOrnot">{IsRain}</dir>
62 + <dir className="clothes">
63 + <dir className="Top">
64 + <h1>TOP</h1>
65 + <img src={TopPath} className='Top_Image' />
66 + </dir>
67 + <dir className="Bottom">
68 + <h1>BOTTOM</h1>
69 + <img src={BottomPath} className='Bottom_Image' />
70 + </dir>
71 + </dir>
72 + </dir>
73 + </div>
74 + </>
75 + );
76 +};
77 +
78 +export default RecommandPage;
79 +
1 +import React, { useCallback, useEffect, useState } from "react";
2 +import { register } from "../../../modules/user.js";
3 +import "../style/RegisterPage.scss";
4 +import { useDispatch, useSelector } from "react-redux";
5 +import { useNavigate } from "react-router-dom";
6 +
7 +function RegisterPage(props) {
8 + const dispatch = useDispatch();
9 + const navigate = useNavigate();
10 +
11 + const registerResult = useSelector((state) => state.user.registerSuccess);
12 +
13 + const [Name, setName] = useState("");
14 + const [Sex, setSex] = useState("");
15 + const [Id, setId] = useState("");
16 + const [Password, setPassword] = useState("");
17 + const [PasswordCheck, setPasswordCheck] = useState("");
18 + const [PasswordError, setPasswordError] = useState(false);
19 +
20 + const [checkRegister, setCheckRegister] = useState(false);
21 + const [checkNameError, setCheckNameError] = useState(false);
22 + const [checkSexError, setCheckSexError] = useState(true);
23 + const [checkIdError, setCheckIdError] = useState(false);
24 + const [checkPasswordError, setCheckPasswordError] = useState(false);
25 + const [checkRegisterError, setCheckRegisterError] = useState(false);
26 +
27 + const idRegex = /^(?=.*[a-zA-Z])(?=.*[0-9]).{6,14}$/;
28 + const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[!@#$%^*+=-?])(?=.*[0-9]).{8,25}$/;
29 +
30 + useEffect(() => {
31 + if (checkRegister === true) {
32 + registerResult.then((result) => {
33 + if (result.registerSuccess === '1') {
34 + alert('회원 가입에 성공하였습니다.');
35 + navigate('/login');
36 + }
37 + else if (result.registerSuccess === '0') {
38 + alert('중복된 아이디가 존재합니다.');
39 + }
40 + else {
41 + alert('회원 가입에 실패하였습니다.');
42 + }
43 + })
44 + }
45 + }, [registerResult])
46 +
47 + const onIdHandler = useCallback((event) => {
48 + setId(event.currentTarget.value);
49 +
50 + // 아이디 유효성 검사
51 + if (!idRegex.test(event.currentTarget.value)) {
52 + setCheckIdError(true);
53 + }
54 + else {
55 + setCheckIdError(false);
56 + }
57 +
58 + }, [checkIdError]);
59 +
60 + const onNameHandler = useCallback((event) => {
61 + setName(event.currentTarget.value);
62 +
63 + // 이름 유효성 검사
64 + if (event.currentTarget.value.length < 2) {
65 + setCheckNameError(true);
66 + }
67 + else {
68 + setCheckNameError(false);
69 + }
70 + }, [checkNameError]);
71 +
72 + const onSexHandler = useCallback((event) => {
73 + setSex(event.currentTarget.value);
74 + setCheckSexError(false);
75 + }, [checkSexError]);
76 +
77 + const onPasswordHandler = useCallback((event) => {
78 + setPassword(event.currentTarget.value);
79 +
80 + // 비밀번호 유효성 검사
81 + if (!passwordRegex.test(event.currentTarget.value)) {
82 + setCheckPasswordError(true);
83 + }
84 + else {
85 + setCheckPasswordError(false);
86 + }
87 + }, [checkPasswordError]);
88 +
89 + const onClickLogIn = useCallback((event) => {
90 + navigate('../login');
91 + })
92 +
93 + const onPasswordCheckHandler = useCallback((event) => {
94 + //비밀번호를 입력할때마다 password 를 검증하는 함수
95 + setPasswordError(event.currentTarget.value !== Password);
96 + setPasswordCheck(event.currentTarget.value);
97 + }, [PasswordError]);
98 +
99 + const onSubmitHandler = useCallback((event) => {
100 + event.preventDefault();
101 +
102 + if (checkIdError || Id === "") {
103 + setCheckRegisterError(true);
104 + }
105 + else if (checkNameError || Name === "") {
106 + setCheckRegisterError(true);
107 + }
108 + else if (checkSexError || Sex === "") {
109 + setCheckRegisterError(true);
110 + }
111 + else if (checkPasswordError || Password === "") {
112 + setCheckRegisterError(true);
113 + }
114 + else if (Password !== PasswordCheck) {
115 + setCheckRegisterError(true);
116 + }
117 + else {
118 + setCheckRegisterError(false);
119 + }
120 +
121 + if (!checkRegisterError) {
122 + const UserData = {
123 + name: Name,
124 + id: Id,
125 + password: Password,
126 + gender: Sex,
127 + };
128 +
129 + // 액션생성함수
130 + dispatch(register(UserData));
131 + setCheckRegister(true);
132 +
133 + };
134 +
135 + }, [checkIdError, checkNameError, checkPasswordError, checkRegisterError, checkSexError, Password, PasswordCheck, Sex, dispatch, registerResult, checkRegister]);
136 +
137 + return (
138 + <>
139 + <div id="body">
140 + <div className="register-box">
141 + <h2>회원가입</h2>
142 + <div className="input-area">
143 + <input
144 + placeholder="이름"
145 + type="text"
146 + value={Name}
147 + onChange={onNameHandler}
148 + />
149 + </div>
150 + <div className="check-variable">
151 + {checkNameError && <div style={{ color: 'red' }}>이름을 두글자 이상 입력해 주세요.</div>}
152 + </div>
153 + <div className="input-area">
154 + <input
155 + placeholder="아이디"
156 + type="text"
157 + value={Id}
158 + onChange={onIdHandler}
159 + />
160 + </div>
161 + <div className="check-variable">
162 + {checkIdError && <div style={{ color: 'red' }}>아이디는 6자리 이상 14자리 이하 소문자와 숫자로 입력해주세요.</div>}
163 + </div>
164 + <div className="input-area">
165 + <input
166 + placeholder="비밀번호"
167 + type="text"
168 + value={Password}
169 + onChange={onPasswordHandler}
170 + />
171 + </div>
172 + <div className="check-variable">
173 + {checkPasswordError && <div style={{ color: 'red' }}>알파벳과 숫자, 특수문자를 포함하여 8자리 이상 입력해주세요.</div>}
174 + </div>
175 + <div className="input-area">
176 + <input
177 + placeholder="비밀번호 재입력"
178 + type="text"
179 + value={PasswordCheck}
180 + onChange={onPasswordCheckHandler}
181 + />
182 + </div>
183 + <div className="check-variable">
184 + {PasswordError && <div style={{ color: 'red' }}>비밀번호가 일치하지 않습니다.</div>}
185 + </div>
186 + <div className="input-area">
187 + <input
188 + type="radio"
189 + value="0"
190 + checked={Sex === "0"}
191 + onChange={onSexHandler}
192 + />
193 + <input
194 + type="radio"
195 + value="1"
196 + checked={Sex === "1"}
197 + onChange={onSexHandler}
198 + />
199 + </div>
200 + <div className="btn-area" onClick={onSubmitHandler}>
201 + <button
202 + className="register-btn"
203 + >
204 + 가입하기
205 + </button>
206 + </div>
207 + </div>
208 + {checkRegisterError && <div style={{ color: 'red' }}>정보를 제대로 입력해주세요.</div>}
209 + </div>
210 + </>
211 + );
212 +
213 +}
214 +
215 +export default RegisterPage;
...\ No newline at end of file ...\ No newline at end of file
1 +import React, { useCallback, useEffect, useState } from "react";
2 +import { useDispatch, useSelector } from "react-redux";
3 +import { useNavigate } from "react-router-dom";
4 +import { recommend } from "../../../modules/clothes";
5 +import { logout } from "../../../modules/user";
6 +import "../style/WeatherPage.scss"
7 +
8 +function WeatherPage(props) {
9 + const dispatch = useDispatch();
10 + const navigate = useNavigate();
11 + const user = useSelector((state) => state.user.loginData);
12 + const todayWeatherResult = useSelector((state) => state.weather.todayInformation);
13 + const tommorrowWeatherResult = useSelector((state) => state.weather.tommorrowInformation);
14 + const today = new Date();
15 + const detailWeather = [];
16 + let currentHour;
17 +
18 + const today_year = today.getFullYear();
19 + const today_month = today.getMonth();
20 + const today_date = today.getDate();
21 + const [todayHighTemperature, setTodayHighTemperature] = useState(-100);
22 + const [todayLowTemperature, setTodayLowTemperature] = useState(100);
23 + const [todayWeatherSymbol, setTodayWeatherSymbol] = useState('☀️');
24 + const [nowWeatherSymbol, setNowWeatherSymbol] = useState('');
25 + const [nowTemperature, setNowTemperature] = useState("");
26 + const [tommorrowHighTemperature, setTommorrowHighTemperature] = useState(-100);
27 + const [tommorrowLowTemperature, setTommorrowLowTemperature] = useState(100);
28 + const [tommorrowWeatherSymbol, setTommorrowWeatherSymbol] = useState('');
29 +
30 + const [cityAdd, setCityAdd] = useState('');
31 + const [guAdd, setGuAdd] = useState('');
32 + const [dongAdd, setDongAdd] = useState('');
33 + const [Time, setTime] = useState("00:00:00");
34 +
35 + let todayWeatherLevel = 0;
36 + let userGender;
37 +
38 + const currentTime = () => {
39 + const date = new Date();
40 + const hours = String(date.getHours()).padStart(2, "0");
41 + const minutes = String(date.getMinutes()).padStart(2, "0");
42 + const seconds = String(date.getSeconds()).padStart(2, "0");
43 + setTime(hours+" : "+minutes+" : "+seconds);
44 + }
45 +
46 + const startTimer = () => {
47 + setInterval(currentTime, 1000)
48 + }
49 +
50 + startTimer()
51 +
52 + useEffect(() => {
53 + todayWeatherResult.then((result) => {
54 + let highTemperature = -100;
55 + let lowTemperature = 100;
56 + let symbol = '';
57 +
58 + currentHour = Time[0] + Time[1];
59 +
60 + // 주소 설정
61 + setCityAdd(result[24].address1);
62 + setGuAdd(result[24].address2);
63 + setDongAdd(result[24].address3);
64 +
65 + for (let i = 0; i<24; i++) {
66 + if (i === 13) {
67 + if (result[i].weather === 0) {
68 + todayWeatherLevel = 0;
69 + }
70 + else if (result[i].weather === 1) {
71 + todayWeatherLevel = 1;
72 + }
73 + else if (result[i].weather === 2) {
74 + todayWeatherLevel = 2;
75 + }
76 + else if (result[i].weather === 3) {
77 + todayWeatherLevel = 3;
78 + }
79 + else if (result[i].weather === 4) {
80 + todayWeatherLevel = 4;
81 + }
82 +
83 + }
84 + // 세부 시간 정보
85 + if (i > Number(currentHour)) {
86 + if (result[i].rainPer >= 50) {
87 + symbol = '🌧️';
88 + setTodayWeatherSymbol('🌧️');
89 + }
90 + else if (i > 18 || i < 6) {
91 + symbol = '🌙';
92 + }
93 + else {
94 + symbol = '☀️';
95 + }
96 + const tempData = {
97 + time : result[i].time,
98 + temperature : result[i].temperature,
99 + symbol : symbol,
100 + }
101 + detailWeather.push(tempData);
102 + }
103 + // 현재 시간 정보 다루는 부분
104 + if (i === Number(currentHour)) {
105 + if (result[i].rainPer >= 50) {
106 + setNowWeatherSymbol('🌧️');
107 + }
108 + else if (i >= 18 || i < 6) {
109 + setNowWeatherSymbol('🌙');
110 + }
111 + else {
112 + setNowWeatherSymbol('☀️');
113 + }
114 +
115 + setNowTemperature(result[i].temperature);
116 + }
117 + // 하루 온도 정보 다루는 부분
118 + if (result[i].temperature < lowTemperature) {
119 + lowTemperature = result[i].temperature;
120 + }
121 + if (result[i].temperature > highTemperature) {
122 + highTemperature = result[i].temperature;
123 + }
124 + }
125 +
126 + setTodayHighTemperature(highTemperature);
127 + setTodayLowTemperature(lowTemperature);
128 + })
129 + }, [todayWeatherResult, Time])
130 +
131 + // 내일의 날씨
132 + useEffect(() => {
133 + tommorrowWeatherResult.then((result) => {
134 +
135 + let highTemperature = -100;
136 + let lowTemperature = 100;
137 + let symbol = '☀️';
138 +
139 + for (let i = 0; i < 24; i++) {
140 + // symbol 설정
141 + if (result[i].rainPer >= 50) {
142 + symbol = '🌧️';
143 + }
144 + // 내일 온도 정보 다루는 부분
145 + if (result[i].temperature < lowTemperature) {
146 + lowTemperature = result[i].temperature;
147 + }
148 + if (result[i].temperature > highTemperature) {
149 + highTemperature = result[i].temperature;
150 + }
151 + setTommorrowHighTemperature(highTemperature);
152 + setTommorrowLowTemperature(lowTemperature);
153 + setTommorrowWeatherSymbol(symbol);
154 + }
155 + })
156 + }, [tommorrowWeatherResult])
157 +
158 + // 유저 성별 정보
159 + useEffect(() => {
160 +
161 + user.then((result) => {
162 +
163 + if (result.logData.gender === '0') {
164 + userGender = 0;
165 + }
166 + else {
167 + userGender = 1;
168 + }
169 +
170 + });
171 + }, [user])
172 +
173 + const onClickLogout = useCallback((event) => {
174 + dispatch(logout());
175 + navigate('/login');
176 + })
177 +
178 + const onClickRegister = useCallback((event) => {
179 + navigate('/');
180 + })
181 +
182 + const onClickTitle = useCallback((event) => {
183 + navigate('/main')
184 + })
185 +
186 + const onSubmitHandler = useCallback((event) => {
187 + event.preventDefault(); //체크박스 미리 클릭 방지
188 +
189 + let isRain = 0;
190 +
191 + if (todayWeatherSymbol === '🌧️') {
192 + isRain = 1;
193 + }
194 +
195 + const sendData = {
196 + gender : userGender,
197 + weather : todayWeatherLevel,
198 + rain : isRain
199 + }
200 +
201 + dispatch(recommend(sendData));
202 + navigate('/recommand');
203 +
204 + }, [todayWeatherResult, user]);
205 +
206 + return (
207 + <>
208 + <dir id = "header">
209 + <dir className="header_title" onClick = {onClickTitle}>
210 + <h1>Weather_Briefing</h1>
211 + </dir>
212 + <dir className="header_choice_box">
213 + <button type="button" onClick = {onClickLogout}>Logout</button>
214 + <button type="button" onClick = {onClickRegister}>Register</button>
215 + </dir>
216 + </dir>
217 + <div id = "body">
218 + <div className="address">
219 + <p>{cityAdd} {guAdd} {dongAdd}</p>
220 + </div>
221 + <div className="today_weather">
222 + <div className="days">
223 + <h1>오늘의 날씨</h1>
224 + <h2 id="day">{today_year} {today_month + 1} {today_date}</h2>
225 + </div>
226 + <div className="today_now_weather_info">
227 + <h2>현재 온도</h2>
228 + <h1 id="present_do">{nowWeatherSymbol} {nowTemperature}</h1>
229 + </div>
230 + <div className="today_weather_info">
231 + <h2>전체 날씨</h2>
232 + <div className="today_whole_weather">
233 + <h1>{todayWeatherSymbol}</h1>
234 + <div className="today_whole_weather_temperature">
235 + <p>최고: {todayHighTemperature}</p>
236 + <p>최저: {todayLowTemperature}</p>
237 + </div>
238 + </div>
239 + </div>
240 + </div>
241 + <div className="tommorrow_weather">
242 + <div className="days">
243 + <h1>내일의 날씨</h1>
244 + <h2 id="day">{today_year} {today_month + 1} {today_date+1}</h2>
245 + </div>
246 + <div className="tommorrow_weather_info">
247 + <h2>날씨 정보</h2>
248 + <div className="tommorrow_whole_weather">
249 + <h1>{tommorrowWeatherSymbol}</h1>
250 + <div className="tommorrow_temperator">
251 + <p>최고: {tommorrowHighTemperature}</p>
252 + <p>최저: {tommorrowLowTemperature}</p>
253 + </div>
254 + </div>
255 +
256 + </div>
257 + </div>
258 + <div className="weather-btn-area" onClick={onSubmitHandler}>
259 + <button className="submit-btn">
260 + 추천
261 + </button>
262 + </div>
263 + </div>
264 + </>
265 + );
266 +}
267 +export default WeatherPage;
...\ No newline at end of file ...\ No newline at end of file
1 +* {
2 + margin: 0;
3 + padding: 0;
4 + box-sizing: border-box;
5 + background-color: rgb(245, 235, 223);
6 +}
7 +#body {
8 + display: flex;
9 + justify-content: center;
10 + align-items: center;
11 + border-top: 2px solid;
12 + border-bottom: 2px solid;
13 +
14 + .login-box {
15 + border: 2px solid;
16 + width: 35%;
17 + height: 60%;
18 + margin-top: 30px;
19 + margin-bottom: 30px;
20 + h2 {
21 + text-align: center;
22 + margin-top: 20px;
23 + margin-bottom: 20px;
24 + }
25 + }
26 + .input-area {
27 + display: flex;
28 + justify-content: center;
29 + align-items: center;
30 + margin-bottom: 5px;
31 +
32 + input {
33 + padding: 10px 0.1rem;
34 + background-color: rgb(255, 255, 255);
35 + }
36 + }
37 + .check-variable {
38 + display: flex;
39 + justify-content: center;
40 + align-items: center;
41 + margin-bottom: 5px;
42 + }
43 + .btn-area {
44 + display: flex;
45 + justify-content: center;
46 + margin-top: 30px;
47 + margin-bottom: 35px;
48 +
49 + button {
50 + width: 120px;
51 + height: 40px;
52 + font-size: large;
53 + font-weight: bold;
54 + background-color:rgb(255, 253, 238);
55 + cursor: pointer;
56 + }
57 + }
58 +}
1 +* {
2 + margin: 0;
3 + padding: 0;
4 + box-sizing: border-box;
5 + background-color: rgb(245, 235, 223);
6 + }
7 + #header {
8 + display: flex;
9 + position: fixed;
10 + justify-content: center;
11 + align-items: center;
12 + height: 15%;
13 + width: 100%;
14 + border-top: 2px solid;
15 + border-bottom: 2px solid;
16 +
17 + .header_clock {
18 + justify-content: left;
19 + align-items: left;
20 + width: 10%;
21 + height: 10%;
22 + h1{
23 + color:rgb(0, 0, 0);
24 + font-size: 15px;
25 + }
26 + }
27 +
28 + .header_title {
29 + display: flex;
30 + justify-content: center;
31 + align-items: center;
32 + margin-left: 300px;
33 + margin-right: 300px;
34 + h1 {
35 + font-size: 50px;
36 + font-family: 'Times New Roman', Times, serif;
37 + color: rgb(0, 0, 0);
38 + }
39 + }
40 + .header_choice_box {
41 + display: flex;
42 + justify-content: right;
43 + align-items: right;
44 + margin-right: 10x;
45 + }
46 + button {
47 + width: 70px;
48 + height: 25px;
49 + font-size: 15px;
50 + font-weight: bold;
51 + cursor: pointer;
52 + }
53 + }
54 +
55 + #body {
56 + display: flex;
57 + justify-content: center;
58 + align-items: center;
59 + border-top: 2px solid;
60 + border-bottom: 2px solid;
61 +
62 + .info-box {
63 + border: 2px solid;
64 + width: 55%;
65 + height: 100%;
66 + margin-top: 150px;
67 + margin-bottom: 30px;
68 + }
69 + hr {
70 + display: flex;
71 + justify-content: center;
72 + align-items: center;
73 + margin-top: 20px;
74 + margin-left: 100px;
75 + margin-right: 100px;
76 + }
77 + .info{
78 + display:flex;
79 + font-size: 30px;
80 + border: 2px solid rgb(225, 208, 134);
81 + background-color: rgb(242, 235, 130);
82 + color:rgb(255, 255, 255);
83 + justify-content: center;
84 + align-items: center;
85 + margin-top: 50px;
86 + margin-bottom: 50px;
87 + margin-left: 30%;
88 + margin-right: 30%;
89 + }
90 + .main-input-area {
91 + display: flex;
92 + justify-content: left;
93 + align-items: left;
94 + margin-top: 20px;
95 + margin-bottom: 15px;
96 + li{
97 + display:flex;
98 + font-size: 15px;
99 + color: gray;
100 + margin-left: 15%;
101 + margin-right: 10%;
102 + }
103 + p{
104 + display:flex;
105 + font-size: 15px;
106 + border: 2px dotted gray;
107 + margin-bottom: 5px;
108 + margin-left: 5%;
109 + }
110 + input {
111 + padding: 10px 2rem;
112 + background-color: rgb(255, 255, 255);
113 + margin-left: 50px;
114 + }
115 +
116 + select {
117 + padding: 10px 1.5rem;
118 + margin-right: 5px;
119 + background-color: rgb(255, 255, 255);
120 + }
121 + }
122 + .main-check-variable {
123 + display: flex;
124 + justify-content: center;
125 + align-items: center;
126 + margin-bottom: 5px;
127 + }
128 + .main-btn-area {
129 + display: flex;
130 + justify-content: center;
131 + margin-top: 60px;
132 + margin-bottom: 10px;
133 +
134 + button {
135 + width: 150px;
136 + height: 60px;
137 + font-size: 15px;
138 + font-weight: bold;
139 + background-color:rgb(255, 253, 238);
140 + cursor: pointer;
141 + }
142 + }
143 + }
144 +
...\ No newline at end of file ...\ No newline at end of file
1 +* {
2 + margin: 0;
3 + padding: 0;
4 + box-sizing: border-box;
5 + background-color: rgb(245, 235, 223);
6 + }
7 + img {
8 + width: 300px;
9 + height: 300px;
10 + object-fit: cover;
11 + }
12 +
13 + #header {
14 + display: flex;
15 + position: fixed;
16 + justify-content: center;
17 + align-items: center;
18 + height: 15%;
19 + width: 100%;
20 + border-top: 2px solid;
21 + border-bottom: 2px solid;
22 +
23 + .header_title {
24 + display: flex;
25 + justify-content: center;
26 + align-items: center;
27 + margin-left: 300px;
28 + margin-right: 300px;
29 + h1 {
30 + font-size: 50px;
31 + font-family: 'Times New Roman', Times, serif;
32 + color: rgb(0, 0, 0);
33 + }
34 + }
35 + .header_choice_box {
36 + display: flex;
37 + justify-content: right;
38 + align-items: right;
39 + margin-right: 10x;
40 + }
41 + button {
42 + width: 70px;
43 + height: 25px;
44 + font-size: 15px;
45 + font-weight: bold;
46 + cursor: pointer;
47 + }
48 + }
49 +
50 + #recommand_body {
51 + display: flex;
52 + justify-content: center;
53 + align-items: center;
54 + border-top: 2px solid;
55 + border-bottom: 2px solid;
56 +
57 + .fashion_recommand {
58 + border: 2px solid;
59 + width: 50%;
60 + height: 200%;
61 + margin-top: 150px;
62 + margin-bottom: 30px;
63 +
64 + .rainOrnot {
65 + display: flex;
66 + justify-content: center;
67 + align-items: center;
68 + margin-top: 20px;
69 + margin-left: 100px;
70 + margin-right: 100px;
71 + font-size :30px;
72 + font-weight: bold;
73 + }
74 + .clothes{
75 + display:flex;
76 + justify-content: space-between;
77 + align-items: space-between;
78 +
79 + .Top{
80 + display:flex;
81 + flex-wrap: wrap;
82 + flex-direction: column;
83 + font-size: 30px;
84 + border: 2px solid;
85 + color:rgb(0, 0, 0);
86 + justify-content: center;
87 + align-items: center;
88 + margin-top: 50px;
89 + margin-bottom: 50px;
90 + margin-left: 5%;
91 + margin-right: 1%;
92 + }
93 +
94 + .Bottom{
95 + display:flex;
96 + flex-wrap: wrap;
97 + flex-direction: column;
98 + font-size: 30px;
99 + border: 2px solid;
100 + color:rgb(0, 0, 0);
101 + justify-content: center;
102 + align-items: center;
103 + margin-top: 50px;
104 + margin-bottom: 50px;
105 + margin-left: 1%;
106 + margin-right: 5%;
107 + }
108 +
109 + .Head{
110 + display:flex;
111 + flex-wrap: wrap;
112 + flex-direction: column;
113 + font-size: 30px;
114 + border: 2px solid;
115 + color:rgb(0, 0, 0);
116 + justify-content: center;
117 + align-items: center;
118 + margin-top: 50px;
119 + margin-bottom: 50px;
120 + margin-left: 1%;
121 + margin-right: 1%;
122 + }
123 + .Shoes{
124 + display:flex;
125 + flex-wrap: wrap;
126 + flex-direction: column;
127 + font-size: 30px;
128 + border: 2px solid;
129 + color:rgb(0, 0, 0);
130 + justify-content: center;
131 + align-items: center;
132 + margin-top: 50px;
133 + margin-bottom: 50px;
134 + margin-left: 1%;
135 + margin-right: 1%;
136 + }
137 + }
138 + }
139 +}
140 +
...\ No newline at end of file ...\ No newline at end of file
1 +* {
2 + margin: 0;
3 + padding: 0;
4 + box-sizing: border-box;
5 + background-color: rgb(245, 235, 223);
6 +}
7 +
8 +#body {
9 + display: flex;
10 + justify-content: center;
11 + align-items: center;
12 + border-top: 2px solid;
13 + border-bottom: 2px solid;
14 +
15 + .register-box {
16 + border: 2px solid;
17 + width: 35%;
18 + height: 60%;
19 + margin-top: 30px;
20 + margin-bottom: 30px;
21 + h2 {
22 + text-align: center;
23 + margin-top: 20px;
24 + margin-bottom: 20px;
25 + }
26 + }
27 + .input-area {
28 + display: flex;
29 + justify-content: center;
30 + align-items: center;
31 + margin-bottom: 5px;
32 +
33 + input {
34 + padding: 10px 0.1rem;
35 + background-color: rgb(255, 255, 255);
36 + }
37 + }
38 + .check-variable {
39 + display: flex;
40 + justify-content: center;
41 + align-items: center;
42 + margin-bottom: 5px;
43 + }
44 + .btn-area {
45 + display: flex;
46 + justify-content: center;
47 + margin-top: 30px;
48 + margin-bottom: 35px;
49 +
50 + button {
51 + width: 120px;
52 + height: 40px;
53 + font-size: large;
54 + font-weight: bold;
55 + background-color:rgb(255, 253, 238);
56 + cursor: pointer;
57 + }
58 + }
59 +}
1 +#header {
2 + display: flex;
3 + position: fixed;
4 + justify-content: center;
5 + align-items: center;
6 + height: 15%;
7 + width: 100%;
8 + border-top: 2px solid;
9 + border-bottom: 2px solid;
10 +
11 + .header_title {
12 + display: flex;
13 + justify-content: center;
14 + align-items: center;
15 + margin-left: 300px;
16 + margin-right: 300px;
17 + cursor:pointer;
18 + h1 {
19 + font-size: 50px;
20 + font-family: 'Times New Roman', Times, serif;
21 + color: rgb(0, 0, 0);
22 + }
23 + }
24 + .header_choice_box {
25 + display: flex;
26 + justify-content: right;
27 + align-items: right;
28 + margin-right: 10x;
29 + }
30 + button {
31 + width: 70px;
32 + height: 25px;
33 + font-size: 15px;
34 + font-weight: bold;
35 + cursor: pointer;
36 + }
37 + }
38 +#body {
39 + display: flex;
40 + justify-content: center;
41 + align-items: center;
42 + flex-wrap: wrap;
43 + flex-direction: column;
44 + border-top: 2px solid;
45 + border-bottom: 2px solid;
46 + .address {
47 + display: flex;
48 + justify-content: center;
49 + align-items: center;
50 + margin-top: 150px;
51 + font-size: 30px;
52 + }
53 + .today_weather {
54 + display: flex;
55 + justify-content: center;
56 + align-items: center;
57 + border: 2px solid;
58 + width: 50%;
59 + margin-top: 30px;
60 + margin-bottom: 10px;
61 + .today_now_weather_info {
62 + display: flex;
63 + justify-content: center;
64 + align-items: center;
65 + flex-direction: column;
66 + margin-right: 30px;
67 + }
68 + .days {
69 + display: flex;
70 + justify-content: center;
71 + align-items: center;
72 + flex-wrap: wrap;
73 + flex-direction: column;
74 + margin-top: 25px;
75 + margin-bottom: 25px;
76 + margin-left: 15px;
77 + margin-right: 200px;
78 + }
79 + .today_whole_weather{
80 + display: flex;
81 + flex-direction: row;
82 + }
83 + }
84 +
85 + .tommorrow_weather {
86 + display: flex;
87 + justify-content: center;
88 + align-items: center;
89 + border: 2px solid;
90 + width: 50%;
91 + margin-top: 20px;
92 + margin-bottom: 20px;
93 + .days {
94 + display: flex;
95 + justify-content: center;
96 + align-items: center;
97 + flex-wrap: wrap;
98 + flex-direction: column;
99 + margin-top: 25px;
100 + margin-bottom: 25px;
101 + margin-left: 15px;
102 + margin-right: 200px;
103 + }
104 + .tommorrow_weather_info {
105 + display: flex;
106 + justify-content: center;
107 + align-items: center;
108 + flex-direction: column;
109 + margin-right: 30px;
110 + .tommorrow_whole_weather{
111 + display: flex;
112 + flex-direction: row;
113 + }
114 + }
115 + }
116 + .weather-btn-area {
117 + display: flex;
118 + justify-content: center;
119 + margin-top: 20px;
120 + margin-bottom: 10px;
121 +
122 + button {
123 + width: 150px;
124 + height: 60px;
125 + font-size: 15px;
126 + font-weight: bold;
127 + background-color:rgb(255, 253, 238);
128 + cursor: pointer;
129 + }
130 + }
131 + }
...\ No newline at end of file ...\ No newline at end of file
...@@ -2,13 +2,23 @@ import React from 'react'; ...@@ -2,13 +2,23 @@ import React from 'react';
2 import ReactDOM from 'react-dom/client'; 2 import ReactDOM from 'react-dom/client';
3 import './index.css'; 3 import './index.css';
4 import App from './App'; 4 import App from './App';
5 +import { Provider } from 'react-redux';
6 +import { applyMiddleware, createStore } from 'redux';
5 import reportWebVitals from './reportWebVitals'; 7 import reportWebVitals from './reportWebVitals';
8 +import { BrowserRouter } from 'react-router-dom';
9 +import rootReducer from './modules/Index';
10 +import { composeWithDevTools } from 'redux-devtools-extension';
11 +import ReduxThunk from 'redux-thunk';
12 +
13 +const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(ReduxThunk)));
6 14
7 const root = ReactDOM.createRoot(document.getElementById('root')); 15 const root = ReactDOM.createRoot(document.getElementById('root'));
8 root.render( 16 root.render(
9 - <React.StrictMode> 17 + <Provider store={store}>
10 - <App /> 18 + <BrowserRouter>
11 - </React.StrictMode> 19 + <App />
20 + </BrowserRouter>
21 + </Provider>,
12 ); 22 );
13 23
14 // If you want to start measuring performance in your app, pass a function 24 // If you want to start measuring performance in your app, pass a function
......
1 +import { combineReducers } from "redux";
2 +import user from "./user.js";
3 +import weather from "./weather";
4 +import clothes from "./clothes";
5 +
6 +const rootReducer = combineReducers({
7 + user,
8 + weather,
9 + clothes,
10 +})
11 +
12 +export default rootReducer;
...\ No newline at end of file ...\ No newline at end of file
1 +import axios from "axios";
2 +
3 +const CLOTHES_RECOMMEND = 'weather/RECOMMEND';
4 +
5 +export function recommend(dataToSubmit) {
6 + const req = axios.post('http://localhost:4000/api/clothes', dataToSubmit)
7 + .then(res => res.data);
8 +
9 + return {
10 + type: CLOTHES_RECOMMEND,
11 + payload: req,
12 + }
13 +}
14 +
15 +export default function (state = {}, action) {
16 + switch (action.type) {
17 + case CLOTHES_RECOMMEND:
18 + return { ...state, clothesRecommend: action.payload };
19 + break;
20 + default:
21 + return state;
22 + }
23 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import axios from 'axios';
2 +
3 +const USER_REGISTER = 'user/REGISTER';
4 +const USER_LOGIN = 'user/LOGIN';
5 +const USER_LOGOUT = 'user/LOGOUT';
6 +
7 +export function register (dataToSubmit) {
8 +
9 + const req = axios.post('http://localhost:4000/api/register', dataToSubmit)
10 + .then(res => res.data);
11 +
12 + return {
13 + type: USER_REGISTER,
14 + payload: req,
15 + }
16 +};
17 +
18 +export function login(dataToSubmit) {
19 + const req = axios.post('http://localhost:4000/api/login', dataToSubmit)
20 + .then(res => res.data);
21 +
22 + return {
23 + type: USER_LOGIN,
24 + payload: req,
25 + }
26 +};
27 +
28 +export function logout(dataToSubmit) {
29 + const req = axios.post('http://localhost:4000/api/logout', dataToSubmit)
30 + .then(res => res.data);
31 +
32 + return {
33 + type: USER_LOGOUT,
34 + }
35 + }
36 +
37 +export default function (state = {}, action) {
38 + switch (action.type) {
39 + case USER_REGISTER:
40 + return { ...state, registerSuccess: action.payload };
41 + break;
42 + case USER_LOGIN:
43 + return { ...state, loginData: action.payload };
44 + break;
45 + case USER_LOGOUT:
46 + return { ...state, loginData: action.payload };
47 + break;
48 + default:
49 + return state;
50 + }
51 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import axios from "axios";
2 +
3 +const WEATHER_ADDRESS = 'weather/ADDRESS';
4 +const WEATHER_COORDINATE = 'weather/COORDINATE';
5 +const WEATHER_TODAY_INFORMATION = 'weather/TODAY_INFORMATION';
6 +const WEATHER_TOMMORROW_INFORMATION = 'weather/TOMMORROW_INFORMATION';
7 +
8 +export function address() {
9 + const req = axios.post('http://localhost:4000/api/address')
10 + .then(res => res.data);
11 +
12 + return {
13 + type: WEATHER_ADDRESS,
14 + payload: req,
15 + }
16 +}
17 +
18 +export function coordinate(dataToSubmit) {
19 +
20 + const req = axios.post('http://localhost:4000/api/cordinate', dataToSubmit)
21 + .then(res => res.data);
22 +
23 + return {
24 + type: WEATHER_COORDINATE,
25 + payload: req,
26 + }
27 +};
28 +
29 +export function todayInformation(dataToSubmit) {
30 + const req = axios.post('http://localhost:4000/api/weather', dataToSubmit)
31 + .then(res => res.data);
32 + return {
33 + type: WEATHER_TODAY_INFORMATION,
34 + payload: req,
35 + }
36 +};
37 +
38 +export function tommorrowInformation(dataToSubmit) {
39 + const req = axios.post('http://localhost:4000/api/tommorrow', dataToSubmit)
40 + .then(res => res.data);
41 + return {
42 + type: WEATHER_TOMMORROW_INFORMATION,
43 + payload: req,
44 + }
45 +};
46 +
47 +export default function (state = {}, action) {
48 + switch (action.type) {
49 + case WEATHER_ADDRESS:
50 + return { ...state, address: action.payload };
51 + break;
52 + case WEATHER_COORDINATE:
53 + return { ...state, dot: action.payload };
54 + break;
55 + case WEATHER_TODAY_INFORMATION:
56 + return { ...state, todayInformation: action.payload };
57 + break;
58 + case WEATHER_TOMMORROW_INFORMATION:
59 + return { ...state, tommorrowInformation: action.payload };
60 + break;
61 + default:
62 + return state;
63 + }
64 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const { createProxyMiddleware } = require("http-proxy-middleware");
2 +
3 +module.exports = function(app) {
4 + app.use(
5 + createProxyMiddleware('/api', {
6 + target: 'http://localhost:4000', //접속하려는 서버의 루트 URL
7 + changeOrigin: true
8 + })
9 + );
10 +};
...\ No newline at end of file ...\ No newline at end of file