동기와 비동기에 대한 설명으로 틀린 것 > 비동기가 항상 동기 방식보다 효율적이다
비동기 처리를 위한 promise객체가 가질 수 있는 상태로 틀린 것 > pending다른 작업 수행 막으며 비동기 실행 종료 기다림
mph에 대한 설명 중 틀린 것 > promise.race - 프로미스 중 하나라도 rejected되길 기다림
비동기 통신과 promise
js는 single threaded language로 서버 요청을 기다려야한다면 멈춰있는 브라우저 봄 > 비동기 처리 필요
비동기 요청 후 main thread는 유저 입력을받거나 페이지 그림 (작업 처리)
비동기 응답 받으면 응답 처리하는 callback함수를 task queue에 넣음
event queue는 main thread에 여유가 있을 때 task queue에서 함수 꺼내 실행
동기(synchronous) - 해당 코드 블록 실행 시 thread의 제어권을 넘기지 않고 순서대로 실행함
비동기(asynchronous) - 코드 순서와 다르게 실행됨
비동기 코드로 감싼 블록은 task queue에 넣어짐 > main thread가 동기 코드를 실행 후 event loop가 비동기 코드 실행
비동기 처리를 위한 내부 구조
브라우저에서 실행되는 js코드는 event driven 시스템으로 작동
웹앱 로드 시 브라우저는 html document읽어 문서에 있는 css, js 불러옴
main thread는 js코드에서 동기적으로 처리되는 코드 실행 외 웹 페이지 실시간으로 렌더링하고 유저 입력 감지 등 처리
비동기 작업 할당 시 처리 끝나고 브라우저는 task queue에 실행 코드 넣음
main thread는 event loop를 돌려 task queue에 작업이 있는지 체크 > 작업 유면 싫행
callback pattern - 비동기 처리 후 실행 될 코드를 callback function으로 보냄 / 콜백 지옥 등이 단점으로 부각
promise - 비동기 처리 순서 조작, 에러 핸들링, 여러 비동기 요청 처리 등 쉽게 처리 가능
비동기 실행 완료 후 .then, .catch, .finally 등의 핸들러를 붙여 각각 데이터 처리 / 에러 처리 / 클린업 로직 실행
then 체인을 붙여 비동기 실행을 동기 실행처럼 동작하도록 함
promise 상태
fufilled - 비동기 실행 성공 / rejected - 비동기 실행 실패 / pending - 비동기 실행 끝나기 기다림
multiple promise handling
promise.all() - 모든 프로미스가 fulfilled되길 기다림 > 하나라도 에러 발생 시 모든 프로미스 요청 중단
promise.allSettled() - 모든 프로미스가 settled되길 ㄱ다림
promise.race() - 넘겨진 프로미스들 중 하나라도 settled되길 기다림
promise.any() - 넘겨진 프로미스 중 하나라도 fulfilled되길 기다림
promise.all()
promise.allSettled
promise.race()
promise.any()
+프로미스 객체는 settled 되더라도 계속 핸들러 추가 가능
핸들러 붙인 순서대로 호출됨
catch 뒤에 핸들러가 붙어있다면 에러 처리 후 계속 진행 됨 > catch 리턴값이 then으로 전달됨
async / await
프로미스 구축 안해도 프로미스를 직관적으로 사용할 수 있는 문법
try catch문으로 에러 처리
async function 만들고 promise기다리는 코드 앞에 await 붙임
여러개의 await을 순서대로 나열 해 then chain 구현 가능
promise.all은 비동기 작업 빠름 > 느린 처리 기다려야 함
async/await으로 parallelism구현 가능 > 끝난 대로 먼저 처리
postman - api 테스트 위한 개발도구
서버와의 통신을 위해 api활용하는 경우 react엡으로만 요청하는 건 비효율적
auth, header, payload, query 등 api 요청에 필요한 데이터 쉽게 세팅함
api 정보 공유 가능
request 모아 collection으로 만들어 api종류별로 관리
환경변수 만들어 환경별로 테스트 가능
open api
restful api를 하나의 문서로 정의하기 위한 문서 표준
openapi specification(oas)로 정의됨
swagger 등의 툴로 open api 작성된 문서를 파싱해 테스팅 도구 생성 가능
api의 method, endpoint 정의
endpoint 마다 인증 방식, content type 등 정의
body data, query string, path variable 등 정의
요청 응답 데이터 형식과 타입 정의 > data model 활용
cors
cross origin resource sharing
브라우저는 모든 요칭 시 origin 헤더 포함
서버는 origin헤더를 보고 해당 요청이 원하는 도메인에서부터 출발한 것인지 판단
다른 origin에서 온 요청은 서버에서 거부함
서버의 endpoing와 홈페이지 도메인은 다른 경우 많음
>> 서버는 홈페이지 도메인 허용 / 다른 도메인이더라도 요청 수신 수락
서버는 access control allow origin 외 access control * 포함하는 헤더에 cors 관련 정보 클라로 보냄
웹사이트에 악성 script가 로드되어 이상한 요청 수신 방지
익명 유저로부터 디도스공격 방지
서버에 직접 cors 설정 불가 시 proxy서버 등 만들어 해결
유저 데이터 비동기로 요청해 렌더링하기 실습
<bitcoinapp.jsx>
import React, {useState, useEffect} from "react";
import styled from "styled-components";
import UserDetail from "./UserDetail";
import * as authAPI from '../service/auth';
const WrappedUserDetail = styled(UserDetail)`
& + & {
margin-top: 12px;
}
`;
// 유저 정보를 받아온 정보를 UserDetail에 넘겨 화면에 출력하세요.
// 데이터가 로딩 중인 경우 유저 정보를 불러오고 있다는 안내문을 띄웁니다.
export default function BitcoinApp() {
const [users, setUsers] = useState(undefined);
useEffect(()=> {
authAPI
.getUsers()
.then(setUsers)
},[])
return (
<div>
{!users ? (<div>유저 정보를 로딩중입니다.</div>)
: users.map(user => (<WrappedUserDetail
email={user.email}
bitcoinAddress={user.bitcoinAddress}
bitcoinBalance={user.bitcoinBalance}
/>))}
</div>
);
}
유저 등록하기 실습
import React, { useState, useEffect } from "react";
import * as authAPI from "../service/auth";
import styled from "styled-components";
import UserDetail from "./UserDetail";
// RegisterForm을 이용해 유저 정보를 가져와 화면을 업데이트하세요.
import RegisterForm from './RegisterForm';
const WrappedUserDetail = styled(UserDetail)`
& + & {
margin-top: 12px;
}
`;
export default function BitcoinApp() {
const [users, setUsers] = useState(undefined);
const handleSubmit = (formData) => {
authAPI
.register(formData) //실행 안되면 registerUser(fromData)
.then(authAPI.getUsers)
.then(setUsers)
.catch(console.error)
}
useEffect(() => {
authAPI.getUsers().then((data) => {
setUsers(data);
});
}, []);
if (!users) {
return <div>유저 정보를 불러오는 중입니다...</div>;
}
return (
<div>
<WrappedRegisterForm onSubmit={handleSubmit}/>
{users.map((user) => (
<WrappedUserDetail {...user} />
))}
</div>
);
}
const WrappedRegisterForm = styled(RegisterForm)`
margin-bottom: 12px;
`
등록 페이지, 유저 목록 페이지 추가하기
<registerpage.jsx>
import { Link, useHistory } from "react-router-dom";
import styled from "styled-components";
import { PageLayout } from "../PageLayout";
import RegisterForm from "../components/RegisterForm";
import *as authAPI from '../../service/auth';
export default function RegisterPage() {
const history = useHistory();
const handleSubmit = (formData) => {
// formData에는 email, password가 들어있습니다.
// 이 정보를 바탕으로 authAPI.registerUser 를 이용해 유저를 등록하세요.
// 성공적으로 유저를 등록했으면 유저 목록 페이지로 이동하세요.
authAPI
.registerUser(formData)
.then(()=> history.push('/users'))
.catch(console.error)
};
return (
<PageLayout>
<nav>
<Link to="/users">Users</Link>
</nav>
<WrappedRegisterForm onSubmit={handleSubmit} />
</PageLayout>
);
}
const WrappedRegisterForm = styled(RegisterForm)`
margin-bottom: 12px;
`;
<userpage.jsx>
import { useState,useEffect } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import { PageLayout } from "../PageLayout";
import UserDetail from "../components/UserDetail";
import * as authAPI form '../../service/auth';
function UsersPage() {
const [users, setUsers] = useState(undefined);
// authAPI.getUsers를 이용해 유저 목록을 불러오세요.
useEffect(()=> {
authAPI
.getUsers()
.then(setUsers)
.catch(console.error)
}, [])
return (
<PageLayout>
<nav>
<Link to="/register">Register</Link>
</nav>
{!users ? (
<div>유저 정보를 불러오는 중입니다...</div>
) : (
users.map((user) => <WrappedUserDetail {...user} />)
)}
</PageLayout>
);
}
export default UsersPage;
const WrappedUserDetail = styled(UserDetail)`
& + & {
margin-top: 12px;
}
`;
유저 상세페이지 추가하기 실습
<registerpage.jsx>
import { Link, useHistory } from "react-router-dom";
import styled from "styled-components";
import * as authAPI from "../../service/auth";
import { PageLayout } from "../components/PageLayout";
import RegisterForm from "../components/RegisterForm";
import Navigation from "../components/Navigation";
export default function RegisterPage() {
const history = useHistory()
const handleSubmit = (formData) => {
// formData에는 email, password가 들어있습니다.
// 이 정보를 바탕으로 authAPI.registerUser 를 이용해 유저를 등록하세요.
// 성공적으로 유저를 등록했으면 유저 목록 페이지로 이동하세요.
authAPI
.registerUser(formData)
.then(()=>history.push('/users'))
};
return (
<PageLayout>
<Navigation>
<Link to="/users">유저 목록</Link>
</Navigation>
<h2>유저 회원가입</h2>
<WrappedRegisterForm onSubmit={handleSubmit} />
</PageLayout>
);
}
const WrappedRegisterForm = styled(RegisterForm)`
margin-bottom: 12px;
`;
<userdetailpage.jsx>
import React, { useState, useEffect } from "react";
import { Link, useHistory, useParams } from "react-router-dom";
import * as authAPI from "../../service/auth";
import { PageLayout } from "../components/PageLayout";
import TransferForm from "../components/TransferForm";
import Navigation from "../components/Navigation";
import { colors } from "../../style/colors";
import styled from "styled-components";
import { Bitcoin } from "../elements/Bitcoin";
function UserDetailPage() {
const { email } = useParams();
const [addresses, setAddresses] = useState([]);
const history = useHistory();
const [user, setUser] = useState(null);
const [error, setError] = useState("");
// 유저의 상세 정보를 로드하여 user에 저장하고, 정보를 보여주세요.
useEffect(()=> {
const decodedEmail = decodeURIComponent(email);
authAPI
.getUser(decodedEmail)
.then(setUser)
.catch(e => setError(e.message))
authAPI
.getUser()
.then(users => users.map(user => user.bitcoinAddress))
.then(setAddresses)
.catch(e => setError(e.message))
},[]);
const handleSubmit = (formData) => {
const { address, amount } = formData;
// address는 다른 유저의 주소입니다.
// 비트코인을 다른 유저에게 전송하는 기능을 구현하세요.
// 성공적으로 전송했으면 유저 목록 페이지로 이동하세요.
authAPI
.tranferBitcoin(user.bitcoinAddress, to, amount)
.then(() => history.push('/users'))
.catch(e => setError(e.message))
};
return (
<PageLayout>
<Navigation>
<Link to="/users">유저 목록</Link>
</Navigation>
<h2>유저 상세정보</h2>
{!user ? (
<div>Loading...</div>
) : (
<>
<Bitcoin>
<div>
<strong className="title">Bitcoin Address</strong>
<span className="content">{user.bitcoinAddress}</span>
</div>
<div>
<strong className="title">Bitcoin Balance</strong>
<span className="content">{user.bitcoinBalance} BTC</span>
</div>
</Bitcoin>
<h3>비트코인 전송</h3>
<TransferForm addresses={addresses} onSubmit={handleSubmit} />
{error && <div>{error}</div>}
</>
)}
</PageLayout>
);
}
export default UserDetailPage;
<try catch문으로 에러처리하기>
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function Users() {
const [users, setUsers] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUser() {
// try ~ catch를 이용해 예외 처리를 하세요.
try{
const users = await axios.get('/users')
return users.data;
}catch(e){
console.log("에러발생!");
setError(e);
}
const response = await axios.get(
`https://${window.location.hostname}:8190/data`
);
setUsers(response.data);
};
fetchUser();
}, []);
const userName = users.map(
(user) => (<li key={user.id}> {user.name} </li>)
);
if (error) return <h4>에러 발생!</h4>;
return (
<>
<h4>사용자 리스트</h4>
<div> {userName} </div>
</>
);
}
export default Users;
#엘리스트랙 #엘리스트랙후기 #리액트네이티브강좌 #온라인코딩부트캠프 #온라인코딩학원 #프론트엔드학원 #개발자국비지원 #개발자부트캠프 #국비지원부트캠프 #프론트엔드국비지원 #React #Styledcomponent #React Router Dom #Redux #Typescript #Javascript