티스토리 뷰

 

웹스톰 import 이후의 과정이다.

 

 

index.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
 
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
 
 
cs

App.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from 'react';
import { BrowserRouter } from "react-router-dom";
import { Page } from './pages';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { combineReducers, createStore, applyMiddleware } from 'redux';
import * as serviceWorker from './serviceWorker';
import { joinReducer, loginReducer } from './boxes/account/account_board';
import {userListReducer} from './boxes/admin/admin_board/UsersList';
 
const rootReducer = combineReducers({
  joinReducer,
  loginReducer,
  userListReducer
});
 
const store = createStore(rootReducer, applyMiddleware(thunk));
 
function App() {
  return (
    <BrowserRouter>
      <Provider store={store}>
        <Page />
      </Provider>
    </BrowserRouter>
  );
}
 
export default App;
 
serviceWorker.unregister();
 
cs

 

src /boxes /index.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { MainNav, MainMap, MainSearchBar, MainLogo } from "./main";
import { CommonHeader, CommonMenuBar, CommonContainer } from "./common";
import { AccountHeader, AccountContainer } from "./account";
import { AdminHeader, AdminMenuBar, AdminContainer } from "./admin";
 
export {
  MainLogo,
  MainNav,
  MainMap,
  MainSearchBar,
  CommonHeader,
  CommonMenuBar,
  CommonContainer,
  AccountHeader,
  AccountContainer,
  AdminHeader,
  AdminMenuBar,
  AdminContainer,
};
 
cs

 

src /boxes /account /index.ts

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { MainNav, MainMap, MainSearchBar, MainLogo } from "./main";
import { CommonHeader, CommonMenuBar, CommonContainer } from "./common";
import { AccountHeader, AccountContainer } from "./account";
import { AdminHeader, AdminMenuBar, AdminContainer } from "./admin";
 
export {
  MainLogo,
  MainNav,
  MainMap,
  MainSearchBar,
  CommonHeader,
  CommonMenuBar,
  CommonContainer,
  AccountHeader,
  AccountContainer,
  AdminHeader,
  AdminMenuBar,
  AdminContainer,
};
 
cs

 

src /boxes /account /account_board /index.ts

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import Login, { loginReducer } from './Login';
import TermAndCondition from './TermAndCondition';
import Join, { joinReducer } from './Join';
import AccountDetail from './AccountDetail';
 
export {
  Login,
  TermAndCondition,
  Join,
  AccountDetail,
  joinReducer,
  loginReducer
}
cs

 

이상은 해당 문제를 해결하기 위한 설정파트입니다.

이제 본격적인 코드리뷰를 합니다.

 

src /boxes /account /account_board /Login.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { useDispatch } from 'react-redux';
import axios from 'axios';
 
const POST_LOGIN_REQUEST = 'POST_LOGIN_REQUEST';
 
export const loginRequestAction = data => ({type: POST_LOGIN_REQUEST, payload: data});
 
export const loginReducer = (state = {}, action) => {
  switch (action.type) {
    case 'POST_LOGIN_REQUEST'return action.payload;
    defaultreturn state;
  }
}
const Login = () => {
  const [userId, setUserId] = useState("");
  const [password, setPassword] = useState("");
  const history = useHistory();
  const dispatch = useDispatch();
 
  const handleLoginButton = e => {
    e.preventDefault();
    axios.post(`http://localhost:8080/users/login`,
      {userId: userId, password: password},
      {
          'Content-type' : 'application/json',
          // 'Authorization' : 'JWT fefege...'
          'Access-Control-Allow-Origin''*'
      })
      .then(response => {
          sessionStorage.setItem("sessionUser",JSON.stringify(response.data))
          dispatch(loginRequestAction(response.data))
          history.push("/mypage");
      })
      .catch(error => { throw(error) });
  }
 
  return (
    <div className="container account_login">
      <form >
        <div className="form-group">
          <input 
            type="text" 
            className="form-control" 
            id="userId" 
            placeholder="아이디"
            value={userId}
            onChange={e => setUserId("bnRFZA97")}
          />
          <input 
            type="password" 
            className="form-control" 
            id="password" 
            placeholder="비밀번호" 
            value={password}
            onChange={e => setPassword("i*fNTYQ%tYvH93")}
          />
          <div>
            <label><input type="checkbox" /> 자동로그인</label>
            <span id="login-link"><Link to="/">아이디/비밀번호 찾기</Link></span>
          </div>
        </div>
      </form>
      <button 
        type="submit" 
        className="btn btn-success" 
        id="login-button"
        onClick={handleLoginButton}
      >
        로그인
      </button>
      <div>
        <p>아직 회원이 아니신가요?</p>
        <Link to="/account/term-n-condition" className="btn btn-primary" id="login-button">회원가입</Link>
      </div>
    </div>
  );
};
 
export default Login;
cs

22 ~ 37: 이 코드의 핵심입니다. 버튼이벤트 발생시 axios 로 비동기처리를 해야 합니다. 이 부분을 JSX 바깥으로 내보내면, cors 에러가 발생합니다. 아래 상황에서 F12 를 클릭하면 console 에 cors 에러 발생 메시지가 있습니다.

https://parksrazor.tistory.com/313

 

hawaii-mobeom-local-currency axios problem

문제 : axios 로 프론트단과 서버단 연결 되어서 회원정보 데이터가 넘어오나, 화면에 뿌려지지 않음 ( 아래 사진은 회원 정보를 서버단에서 들고온 내용 console.log 찍은거 ) 해당 부분 front 코드 사�

parksrazor.tistory.com

27 ~ 29 : 후에 JWT 를 설정하기 위한 JSON 설정입니다. 현재는 기능이 없습니다.

 

32 : 현재 기능이 로그인기능입니다. 따라서 로그인 성공시 sessionStorage 에 로그인한 정보를 저장합니다.

이때 sessionStorage 는 string 값만 저장이 되므로, JSON 객체를 string타입으로 캐스팅해서 저장합니다.

33 : dispatch({type: ''}) 은 anonymous Reducer 가 됨을 꼭 기억하세요. 지금 이 코드는 react-redux 의 store 에 저장하고 있는 상황입니다.

 

현재는 로그인 성공 후 바로 마이페이지로 넘어오는 시나리오입니다.

넘어오면서 바로 두가지를 하고자 합니다.

헤더의 변경과 기존 정보값의 자동입력입니다.

 

 

 

 

 

src /boxes /account /account_board /AccountDetail.js

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { PostcodeButton } from '../../../items';
import { Modal } from 'react-bootstrap';
import './AccountDetail.css';
import axios from 'axios';
 
const GET_ACCOUNT_INFO = 'GET_ACCOUNT_INFO';
const PATCH_UPDATE_PASSWORD = 'PATCH_UPDATE_PASSWORD';
const PATCH_UPDATE_USER = 'PATCH_UPDATE_USER';
const DELETE_USER = 'DELETE_USER';
 
export const accountInfoAction = data => ({type: GET_ACCOUNT_INFO, payload: data});
export const patchPasswordAction = data => ({type: PATCH_UPDATE_PASSWORD, payload: data});
export const patchUserAction = data => ({type: PATCH_UPDATE_USER, payload: data});
export const deleteUserAction = data => ({type: DELETE_USER, payload: data});
 
export const getAccountInfo = userId => dispatch => {
    axios.get(`http://localhost:8080/users/account-info/${userId}`)  // 나중에 id로 바꿔야함. 로그인할 때 sessionStore에 id 저장하기...
        .then(response => {
            dispatch(accountInfoAction(response.data));
        }).catch(
        error => { throw(error) }
    );
};
 
export const patchUpdatePassword = (id, data) => dispatch => {
    axios.patch(`http://localhost:8080/users/${id}`, data)
        .then(response => {
            dispatch(patchPasswordAction(response.data));
        }).catch(
        error => { throw (error) }
    );
};
 
export const patchUpdateUser = (id, data) => dispatch => {
    axios.patch(`http://localhost:8080/users/${id}`, data)
        .then(response => {
            dispatch(patchUserAction(response.data));
        }).catch(
        error => { throw (error)}
    );
};
 
export const deleteUser = id => dispatch => {
    axios.delete(`http://localhost:8080/users/${id}`)
        .then(response => {
            dispatch(deleteUserAction(response.data));
        }).catch(
        error => { throw (error)}
    );
};
 
export const accountDetailReducer = (state = {}, action) => {
    console.log("reducer running");
    console.log(action);
    switch(action.type) {
        case 'GET_ACCOUNT_INFO'return Object.assign({}, state, {
            accountInfo: action.payload
        });
        case 'PATCH_UPDATE_PASSWORD'return action.payload;
        case 'PATCH_UPDATE_USER'return action.payload;
        case 'DELETE_USER'return action.payload;
        defaultreturn state;
    }
}
 
 
const AccountDetail = () => {
 
    const [id, setId] = useState( "");
    const [userId, setUserId] = useState("");
    const [password, setPassword] = useState("");
    const [newPassword, setNewPassword] = useState("");
    const [confirmNewPassword, setConfirmNewPassword] = useState("");
    const [name, setName] = useState("");
    const [birthDate, setBirthDate] = useState("");
    const [gender, setGender] = useState("");
    const [defaultAddress, setDefaultAddress] = useState("");
    const [optionalAddress, setOptionalAddress] = useState("");
    const [email, setEmail] = useState("");
    const [show, setShow] = useState(false);
    const [showOptionalAddress, setShowOptionalAddress] = useState(false);
    const [isReadOnly, setIsReadOnly] = useState(true);
 
    const history = useHistory();
    const dispatch = useDispatch();
    const accountDetail = useSelector((state) => state.accountInfo);
const sessionUser = JSON.parse(sessionStorage.getItem("sessionUser"))
    useEffect(() => {
      
        if(sessionUser) {
          
            setId(sessionUser.id);
            setUserId(sessionUser.userId);
            setName(sessionUser.name);
            setBirthDate(sessionUser.birthDate);
            setGender(sessionUser.gender);
        } else {
           setId('');
           setUserId('');
           setName('');
           setBirthDate('');
           setGender('');
          
        }
   },[sessionUser])
 
 
    const handleClose = () => setShow(false);
 
    const handleUpdate = e => {
        e.preventDefault();
        //dispatch(patchUpdateUser(id, {defaultAddress: defaultAddress, optionalAddress: optionalAddress ,email: email}));
        alert("회원정보 수정이 완료되었습니다.");
        history.push("/");
    };
 
    const handleDelete = e => {
        e.preventDefault();
        //dispatch(deleteUser(id));
        alert("회원탈퇴 완료");
        history.push("/");
    };
 
    const handleAddAddress = e => {
        e.preventDefault();
        setShowOptionalAddress(true);
    }
 
    const handleChangePassword = e => {
        e.preventDefault();
        setShow(true);
    }
 
    const handleUpdatePassword = e => {
        e.preventDefault();
        if(newPassword === confirmNewPassword) {
            //dispatch(patchUpdatePassword(id, {password: newPassword}));
            alert("비밀번호 변경이 완료되었습니다. 다시 로그인 하세요");
            history.push("/account/login");
        } else {
            alert("새로운 비밀번호를 다시 확인하세요.");
            setConfirmNewPassword("");
        }
    }
 
    const handleUpdateEmail = e => {
        e.preventDefault();
        setIsReadOnly(!isReadOnly);
    }
 
 
    return <>
        <div className="container" id="account-detail">
            <div id="account-detail-title">
                <h3>나의 정보</h3>
            </div>
 
            <form>
                <div className="form-group">
                    <p>아이디</p>
                    <div className="input-group">
                        <input
                            type="text"
                            className="form-control"
                            readOnly
                            value={userId}
                        />
                    </div>
                    <p>비밀번호</p>
                    <button
                        className="btn btn-primary btn-block mb-2"
                        onClick={handleChangePassword}
                    >
                        비밀번호 변경
                    </button>
 
                    <Modal show={show} onHide={handleClose}>
                        <Modal.Header closeButton>
                            <Modal.Title>비밀번호 변경</Modal.Title>
                        </Modal.Header>
                        <Modal.Body>
                            <div>
                                <div>
                                    <p className="change-password-modal-p">현재 비밀번호</p>
                                    <input
                                        type="password"
                                        value={password}
                                        onChange={e => setPassword(e.target.value)}
                                    />
                                </div>
                                <div>
                                    <p className="change-password-modal-p">새 비밀번호</p>
                                    <input
                                        type="password"
                                        value={newPassword}
                                        onChange={e => setNewPassword(e.target.value)}
                                    />
                                </div>
                                <div>
                                    <p className="change-password-modal-p">새 비밀번호 확인</p>
                                    <input
                                        type="password"
                                        value={confirmNewPassword}
                                        onChange={e => setConfirmNewPassword(e.target.value)}
                                    />
                                </div>
                                <button
                                    className="btn btn-primary btn-block mb-2 mt-2"
                                    onClick={handleUpdatePassword}
                                >
                                    비밀번호 변경하기
                                </button>
 
                            </div>
                        </Modal.Body>
                    </Modal>
 
                    <p>이름</p>
                    <input
                        type="text"
                        className="form-control"
                        readOnly
                        value={name}
                    />
                    <p>생년월일</p>
                    <input
                        type="date"
                        className="form-control"
                        readOnly
                        value={birthDate}
                    />
                    <p>성별</p>
                    <input
                        type="text"
                        className="form-control"
                        readOnly
                        value={gender}
                    />
                    <p>거주지 주소</p>
                    <div className="input-group">
                        <input
                            type="text"
                            className="form-control"
                            value={defaultAddress}
                            onChange={e => setDefaultAddress(e.target.value)}
                        />
                        <div className="input-group-append">
                            <PostcodeButton onPostcodeSelected={setDefaultAddress}/>
                        </div>
                    </div>
 
                    {optionalAddress &&
                    <>
                        <p>추가 주소</p>
                        <div className="input-group">
                            <input
                                type="text"
                                className="form-control"
                                value={optionalAddress}
                                onChange={e => setOptionalAddress(e.target.value)}
                            />
                            <div className="input-group-append">
                                <PostcodeButton onPostcodeSelected={setOptionalAddress}/>
                            </div>
                        </div>
                    </>
                    }
 
                    {!optionalAddress &&
                    <>
                        {showOptionalAddress &&
                        <>
                            <p>추가 주소</p>
                            <div className="input-group">
                                <input
                                    type="text"
                                    className="form-control"
                                    value={optionalAddress}
                                    onChange={e => setOptionalAddress(e.target.value)}
                                />
                                <div className="input-group-append">
                                    <PostcodeButton onPostcodeSelected={setOptionalAddress}/>
                                </div>
                            </div>
                        </>
                        }
                        <button
                            className="btn btn-primary btn-block mb-2"
                            id="account-detail-add-address-btn"
                            onClick={handleAddAddress}
                        >
                            주소 추가 하기
                        </button>
 
                    </>
                    }
 
                    <p>이메일</p>
                    <div className="input-group">
                        <input
                            type="email"
                            className="form-control"
                            readOnly={isReadOnly}
                            value={email}
                            onChange={e => setEmail(e.target.value)}
                        />
                        <button
                            className="btn btn-warning btn-block mb-2"
                            id="account-detail-update-email-btn"
                            onClick={handleUpdateEmail}
                        >
                            이메일 수정
                        </button>
                    </div>
                </div>
            </form>
            <button
                type="submit"
                className="btn btn-success btn-block mb-2"
                onClick={handleUpdate}
            >
                수정
            </button>
            <button
                type="submit"
                className="btn btn-danger btn-block mb-2"
                onClick={handleDelete}
            >
                탈퇴
            </button>
        </div>
    </>
}
 
 
export default AccountDetail;
cs

 

91 ~ 108 : 이 문제를 해결하는 핵심입니다. hooks 중에서 useEffect(()=>{}, []) 을 사용해야 합니다.

const sessionUser = JSON.parse(sessionStorage.getItem("sessionUser")) 

를 통해 가져오는데, string 타입을 다시 JSON 으로 변경하는 과정에서 JSON.parse() 가 필요합니다.

useEffect 의 Props 중에서 [] 에 해당하는 것을 최우선으로 실행하라는 의미를 이해하고 있다면, 위의 예처럼

[sessionUser] 에 해당하는 90번을 먼저 실행하는 것도 알 수 있습니다.

당연히 이 페이지가 로딩되기 전에는 먼저 sessionUser 을 처리합니다.

그래서 sessionUser 의 존재를 TF 로 판단하여 T 이면 값을 JSX 에 입력합니다. 100 ~ 106 번은 굳이 하지 않아도(로그인성공이기때문에) 되지만, 학습을 위한 부분으로 남겨두었습니다.

 

src /boxes /account /account_board /CommandHeader.js

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import React, {useEffect, useState} from 'react';
import { Link, useHistory } from 'react-router-dom';
import { Logo, SearchBar } from '../../items';
import './CommonPage.css'
 
 
const CommonHeader = () => {
  const [loginedAccount, setLoginedAccount] = useState(false);
 
  const history = useHistory();
  const handleSearch = (searchWord) => {
    alert(searchWord);
    history.push('/merchant-list');
  }
  const sessionUser = JSON.parse(sessionStorage.getItem("sessionUser"))
  useEffect(() => {if(sessionUser) setLoginedAccount(true)},[sessionUser])
  return (
    <div className="container">
      <div className="row">
 
        <div className="col-lg-2">
          <div id="common-header-logo">
            <Logo />
          </div>
        </div>
 
        <div className="col-lg-8" id="common-header-search-bar">
          < SearchBar onSearch={handleSearch} />
        </div>
 
        <div className="col-lg-2">
 
          {!loginedAccount &&
            <div id="common-header-links">
              <Link to="/account/login">
                <span className="btn-link btn-sm">로그인</span>
              </Link>
              <Link to="/account/term-n-condition">
                <span className="btn-link btn-sm">회원가입</span>
              </Link>
            </div>
          }
          {loginedAccount &&
            <div id="common-header-links">
 
              <Link to="/account/login">
                <span className="btn-link btn-sm">로그아웃</span>
              </Link>
              <Link to="/mypage">
                <span className="btn-link btn-sm">마이페이지</span>
              </Link>
            </div>
          }
 
        </div>
 
      </div>
    </div >
  );
}
 
export default CommonHeader;
cs

 

헤더부분 처리입니다.

원리는 같습니다.

15: useEffect() 훅 사용과 session 처리 내용 33 ~ 53 은 리액트 공식 튜토리얼에 있는 조건부 렌더링 문법입니다. 

기초학습에 해당하므로 이해가 안되면 반드시 숙지하길 바랍니다.

 

https://ko.reactjs.org/docs/conditional-rendering.html

 

조건부 렌더링 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

이 페이지에서 해당내용을 발췌한 부분입니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함