接続ブログAPIクライアント-2-
Photo by Kostiantyn Li on Unsplash
何の情報もないまま会員加入要求を送信した場合に応答する
----->クライアントエラーハンドル(プロンプト入力情報)
すべての情報を入力する場合
同じ情報で再度会員に加入した場合.
----->ルータエラー処理(ユーザー名、電子メールを繰り返すときに条件を追加し、コンテキスト関連errを提供する)
エラーハンドルtry.ほそくろ
会員加入ボタンを押すとhandleSubmit->e.preventDefault()を実行し、リフレッシュを防止->成功登録ページ 失敗時catch文の使用->設定エラー(true)->エラーステータスがtrueの場合に発生するエラーメッセージ出力
contextのフォルダを作成する
React - useRef
React-refとDOM
今回は
コンテキストのインポート
リクエストは一度に1つしかできませんが、2回以上入るとエラーになるそうです.
でもPostmanではよくできていました...
これはルータドームの問題を反映しているようで、アプリのルータ構造を変えても解決できません.
最終的には、ルータ内の条件文を
バックエンドで作成されたエラーメッセージにresでアクセスしようとした結果、未定義が表示されました.
catchが受信したerrオブジェクトは
errオブジェクト出力
ログイン成功時応答
会員登録ページ
import "./register.scss"
import { Link } from "react-router-dom"
import { useState } from "react"
import axios from "axios";
export default function Register() {
const [username,setUsername] = useState("");
const [email,setEmail] = useState("");
const [password,setPassword] = useState("");
const [error,setError] =useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setError(false);
try {
const res = await axios.post("http://localhost:5000/api/auth/register", {
username,
email,
password,
});
console.log(res);
res.data && window.location.replace("/login");
} catch (err) {
console.log(err);
setError(true);
}
}
return (
<div className="register">
<span className="register-title">회원가입</span>
<form className="register-form" onSubmit={handleSubmit}>
<label>User name</label>
<input
type="text"
placeholder="이름을 입력해주세요 ..."
className="register-input"
onChange={e => setUsername(e.target.value)}
/>
<label>Email</label>
<input
type="text"
placeholder="이메일을 입력해주세요 ..."
className="register-input"
onChange={e => setEmail(e.target.value)}
/>
<label>Password</label>
<input
type="password"
placeholder="비밀번호를 입력해주세요 ..."
className="register-input"
onChange={e => setPassword(e.target.value)}
/>
<button type="submit" className="register-button">회원가입</button>
</form>
<span className="register-login">
이미 회원이신가요? <Link to="/login" className="link">로그인</Link> 하러가기
</span>
{error && <span style={{color:"red"}}>이미 가입된 회원입니다.</span>}
</div>
)
}
何の情報もないまま会員加入要求を送信した場合に応答する
----->クライアントエラーハンドル(プロンプト入力情報)
すべての情報を入力する場合
同じ情報で再度会員に加入した場合.
----->ルータエラー処理(ユーザー名、電子メールを繰り返すときに条件を追加し、コンテキスト関連errを提供する)
エラーハンドルtry.ほそくろ
console.log(err)
const [error,setError] = useState(false);
エラーステータスfalse会員加入ボタンを押すとhandleSubmit->e.preventDefault()を実行し、リフレッシュを防止->
setError(false)
エラー初期化->axiosリクエストの実行(input valueをbodyに渡す)->context APIの使用
contextのフォルダを作成する
//context.js
import { createContext, useReducer } from "react";
import Reducer from "./Reducer";
// 1.모든 렌더링 작업이 성공적으로 끝났을때 이 값을 업로드
const INITIAL_STATE = {
user: null,
inFetching: false,
error: false,
}
// 2.이 값(INITIAL_STATE)을 가지고 콘텍스트 프로바이더 내보내기
export const Context = createContext(INITIAL_STATE);
// 3.콘텍스트에 접근하는 방법
export const ContextProvider = ({children}) => {
//13. state dispatch하기, state,dispatch의 값은 Redcucer에서 가지고온다.
//14. -> useReducer(Redcucer) -> Reducer.js에서 Reducer가져오기
//15. 여기서 reducer가 사용하는 state는 INITIAL_STATE다. -> ,INITIAL_STATE)
// 이제 Context를 이 Provider로 줄 수 있음
const [state, dispatch] = useReducer(Reducer, INITIAL_STATE);
//16. context.Provider의 props로 user,isFetching,error를 state에서 가져오고, 마지막으로dispatch한다.
// ex 로그인 버튼을 누르면 이 정보들을 dispatch -> 서버에 응답에 따라 dispatch SUCCESFUL 이나 FAILURE를 가져옴
//17. 이제 ContextProvider를 App.js나 index.js에서 사용가능
// index.js에서 <App/>을 감싸게 되면 모든 곳에서 Context 사용가능 -> 18.index.js로
return(
<Context.Provider value={{
user:state.user,
isFetching:state.isFetching,
error:state.error,
dispatch,
}}>{children}</Context.Provider>
)
}
// 4.로그인 버튼을 눌렀을때 유저네임과 패스워드 두가지를 컨트롤해야한다 credential이 끝난 후
// 추가로 해야하는 일이 있다. 성공이냐 실패냐 일때
// 성공시 INITIAL_STATE 의 프로퍼티들을 업데이트 user: abcd, email:asdfas...
// 실패시 (인가실패 몽고디비에러 등등) -> INITAL STATE의 error 값 바뀜
// 5. -> 이걸 Actions에서 컨트롤
//Actions.js
// 6.타입 -> 액션이름이;라 생각하면 됨
export const LoginStart = (userCredentials) => ({
type: "LOGIN_START"
})
//7. payload -> user를 업데이트 하기 위한 프로퍼티
export const LoginSuccess = (user) => ({
type: "LOGIN_SUCCESS",
payload: user,
})
// 8.에러발생시 - 아무 페이로드도 인수로 집어넣지 않았다.
export const LoginFailure = () => ({
type: "LOGIN_FAILURE"
})
// 9.이제 이 액션들을 가지고 state를 다룰 수 있는게 reducer임
const Reducer = (state, action) => {
//10.switch로 제어
switch(action.type){
case "LOGIN_START":
return {
user:null,
insFetching:true,
error:false,
};
//10.payload에 있는 user를 user값으로 바꿔준다.
//11.fetch는 끝났으니 false로 (작업을 여기서 끝내야되니까)
case "LOGIN_SUCCESS":
return {
user:action.payload,
isFetching: false,
error:false,
}
// 11. error 발생상황이니 error 는 true로
// 작업은 끝난상태니 isFetching은 여전히 false
case "LOGIN_FAILURE":
return {
user:null,
isFetching: false,
error:true,
}
default:
return state;
}
}
//12. 이제 Action과 Reducer를 사용하려면 이것들을 dispatch하면 된다. ->Context로
export default Reducer;
//index.js
import React from 'react';
// import ReactDOM from 'react-dom';
import * as ReactDOMClient from 'react-dom/client'
import App from './App';
import { ContextProvider } from './context/Context';
// ReactDOM.render(
// <React.StrictMode>
// <App />
// </React.StrictMode>,
// document.getElementById('root')
// );
const rootElement = document.getElementById('root');
// 18. ContextProvider로 context를 줄 컴포넌트 감싸기 -> App을 감싸게 되면 모든곳에서 사용가능
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<React.StrictMode>
<ContextProvider>
<App callback={() => console.log("rendered")}/>
</ContextProvider>
</React.StrictMode>
);
//login.jsx
import "./login.scss"
import { Link } from "react-router-dom"
import axios from "axios";
import { useContext, useRef } from "react";
import { Context } from "../../context/Context";
export default function Login() {
const userRef = useRef();
const passwordRef = useRef();
const { user, dispatch, isFecthing } = useContext(Context);
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({type:"LOGIN_START"});
try {
const res = await axios.post("http://localhost:5000/api/auth/login", {
username: userRef.current.value,
password: passwordRef.current.value,
})
dispatch({ type:"LOGIN_SUCCESS", payload: res.data });
} catch (err) {
dispatch({ type:"LOGIN_FAILURE" })
}
}
console.log(user);
return (
<div className="login">
<span className="login-title">환영합니다.</span>
<form className="login-form" onSubmit={handleSubmit}>
<label>username</label>
<input ref={userRef} type="text" placeholder="이름을 입력해주세요 ..." className="login-input" />
<label>Password</label>
<input ref={passwordRef} type="password" placeholder="비밀번호를 입력해주세요 ..." className="login-input"/>
<button type="submit" className="login-button">로그인</button>
</form>
<button className="login-register-button">
<Link to="/register" className="link">회원가입</Link>
</button>
</div>
)
}
React - useRef
React-refとDOM
今回は
useRef()
でお持ちしました.userRef.current.value
userefの現在の値を使用できます.input ref={userRef}
const userRef = useRef();
useRef()
を対象として、ref
を使用してDOM ELMENTをマウントできます.コンテキストのインポート
const {불러올콘텍스트state1,state2... , dispatch} = useContext(불러올콘텍스트);
dispatch({type:"LOGIN_START"});
ログインを開始するには、コンテキスト値ログインを開始するアクション名「LOGIN START」がdispatchにロードされます. dispatch({ type:"LOGIN_SUCCESS", payload: res.data });
ログインに成功すると、「LOGIN SUCCESS」アクションがロードされ、res.data
がpayload
に割り当てられます.dispatch({ type:"LOGIN_FAILURE" })
エラーが発生した場合は、値を「LOGIN FAILURE」に変更します.Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
というエラーが発生しました.リクエストは一度に1つしかできませんが、2回以上入るとエラーになるそうです.
でもPostmanではよくできていました...
これはルータドームの問題を反映しているようで、アプリのルータ構造を変えても解決できません.
最終的には、ルータ内の条件文を
if
およびelse if
の条件文に変換して解決する.//LOGIN
router.post("/login", async (req, res) => {
try {
console.log(req,"req")
//바꾸기 전 코드
//const user = await User.findOne({ username: req.body.username });
//!user && res.status(400).json("아이디 혹은 비밀번호가 틀렸습니다.(사실 아이디가 틀림)");
//const validated = await bcrypt.compare(req.body.password, user.password);
//!validated && res.status(400).json("아이디 혹은 비밀번호가 틀렸습니다.(사실 비밀번호가 틀림)");
//const { password, ...나머지정보들 } = user._doc;
//res.status(200).json(나머지정보들);
//console.log("요청성공")
// 에러 해결
const user = await User.findOne({ username: req.body.username })
const validated = await bcrypt.compare(req.body.password, user.password);
if (!user) {
console.log("아이디문제")
res.status(400).json("아이디 혹은 비밀번호가 틀렸습니다.(사실 아이디가 틀림)");
} else if(!validated) {
console.log("비번문제");
return res.status(400).json("아이디 혹은 비밀번호가 틀렸습니다.(사실 비밀번호가 틀림)");
} else {
const { password, ...나머지정보들 } = user._doc;
res.status(200).json(나머지정보들);
console.log("요청성공")
}
} catch (err) {
console.log("에러발생")
res.status(500).json(err);
}
})
サーバが作成したエラーメッセージを出力
バックエンドで作成されたエラーメッセージにresでアクセスしようとした結果、未定義が表示されました.
catchが受信したerrオブジェクトは
console.dir(err)
であるため、apiの作成時に作成されたメッセージはdata
に含まれる.errオブジェクト出力
err.response.data
にアクセスすることで、サーバ(auth.js)が指定したメッセージを出力することができる.Reference
この問題について(接続ブログAPIクライアント-2-), 我々は、より多くの情報をここで見つけました https://velog.io/@mhnormal/블로그-API-클라이언트와-연결하기-2-テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol