[ React Router Dom - 소개, hooks, children ]
▶ react-router-dom
페이지를 구현할 수 있게 해주는 패키지
react-router-dom을 이용하면 여러 페이지를 가진 웹을 만들 수 있음
▶ react-router-dom 설치하기
패키지 설치
터미널에 입력하여 패키지 설치
yarn add react-router-dom
▶ react-router-dom 사용하기
사용방법 순서
- 페이지 컴포넌트 생성
- Router.js 생성 및 router 설정 코드 작성
- App.js에 import 및 적용
- 페이지 이동 테스트
페이지 컴포넌트 생성
src 폴더에 pages라는 폴더를 만들고 그 안에 페이지를 만듬
Router.js 생성 및 route 설정 코드 작성
브라우저에 우리가 URL을 입력하고 이동했을 때 우리가 원하는 페이지 컴포넌트로 이동하게끔 만드는 부분
URL 1개당 페이지 컴포넌트를 매칭해주는 것. 이 한개의 URL을 Route라고 함
그리고 Route들을 설정하는 코드는 Router.js 라는 파일을 별도로 분리해서 많이 작성하곤 함
src안에 shared 라는 폴더를 생성해주고 그 안에 Router.js 파일을 생성해줌
Router.js 생성 및 route 설정 코드 작성
Router.js 생성
import React from "react";
// 1. react-router-dom을 사용하기 위해서 아래 API들을 import 함
import { BrowserRouter, Route, Routes } from "react-router-dom";
// 2. Router 라는 함수를 만들고 아래와 같이 작성
// BrowserRouter를 Router로 감싸는 이유는 SPA의 장점인 브라우저가 깜빡이지 않고
// 다른 페이지로 이동할 수 있게 만들어줌
const Router = () => {
return (
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
);
};
export default Router;
route 설정 코드 작성
React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";
const Router = () => {
return (
<BrowserRouter>
<Routes>
{/*
Routes안에 이렇게 작성
Route에는 react-router-dom에서 지원하는 props들이 있음
path는 우리가 흔히 말하는 사용하고싶은 "주소"를 넣어주면 됨
element는 해당 주소로 이동했을 때 보여주고자 하는 컴포넌트를 넣어줌
*/}
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
<Route path="works" element={<Works />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
App.js에 Router.js import 해주기
import React from "react";
import Router from "./shared/Router";
function App() {
return <Router />;
}
export default App;
Router.js를 App 컴포넌트에 넣어주는 이유는 프로젝트에서 가장 최상위에 존재하는 컴포넌트가 App.js이기 때문
즉 우리가 어떤 컴포넌트를 화면에 띄우던 항상 App.js를 거쳐야만 함. 그래서 path별로 분기가 되는 Router.js 를 App.js에 위치시키고 서비스를 이용하는 모든 사용자가 항상 App.js → Router.js 거치도록 코드를 구현해주는 것
페이지 이동 테스트
프로젝트를 실행시키고 영상처럼 브라우저에 paty를 입력하면 각 페이지로 이동됨
▶ react-router-dom Hooks
react-router-dom Hooks
react-router-dom에서도 유용하게 사용할 수 있는 hook을 제공함
useNavigate
다른 페이지로 보내고자 할 때 사용할 수 있음
위에서 테스트 했을 때 우리가 브라우저에 직접 path를 입력해서 페이지를 이동했지만 브라우저에 path를 직접 입력해서 페이지를 이동하게 만드는 서비스는 거의 없을 것. 보통 어떤 버튼을 누르면 페이지로 이동하거나 또는 어떤 컴포넌트를 눌렀을 때 페이지를 이동하게 만듬. 이럴때 사용하는 훅. navigate를 생성하고 navigate(’보내고자 하는 url’)을 통해 페이지를 이동 시킬 수 있음
// src/pages/home.js
import { useNavigate } from "react-router-dom";
const Home = () => {
const navigate = useNavigate();
return (
<button
onClick={() => {
navigate("/works");
}}
>
works로 이동
</button>
);
};
export default Home;
useLocation
react-router-dom을 사용하면 현재 위치하고 있는 페이지의 여러가지 정보를 추가적으로 얻을 수 있음
이 정보들을 이용해서 페이지 안에서 조건부 렌더링에 사용하는 등 여러가지 용도로 활용할 수 있음
// src/pages/works.js
import { useLocation } from "react-router-dom";
const Works = () => {
const location = useLocation();
console.log("location :>> ", location);
return (
<div>
<div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
</div>
);
};
export default Works;
Link
Link 는 훅이 아니지만 꼭 알아야 할 API. Link는 html 태그중에 a 태그의 기능을 대체하는 API
만약 JSX에서 a 태그를 사용해야 한다면 반드시 Link 를 사용해서 구현해야 함
왜냐하면 a 태그를 사용하면 페이지를 이동하면서 브라우저가 새로고침(refresh)되기 때문
브라우저가 새로고침 되면 모든 컴포넌트가 다시 렌더링되야 하고 또한 리덕스나 useState를 통해 메모리상에 구축해놓은 모든 상태값이 초기화됨. 이것은 곧 성능에 악역향을 줄 수 있고 불필요한 움직임입니다.
import { Link, useLocation } from 'react-router-dom';
const Works = () => {
const location = useLocation();
console.log('location :>> ', location);
return (
<div>
<div>{`현재 페이지 : ${location.pathname.slice(1)}`}</div>
<Link to="/contact">contact 페이지로 이동하기</Link>
</div>
);
};
export default Works;
페이지를 이동시키고자 할때 useNavigate 또는 Link를 사용할 수 있음
구현할때 더 적합한 방식으로 API를 활용하면 됨. 이 밖에도 URL의 query를 알 수 있는 useParams 등 많은 Hook이 있음
▶ children 용도
공식 문서에는 props.children에 대해 아래와 같이 설명하고 있음
어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올지 미리 예상할 수 없는 경우가 있습니다. 범용적인 ‘박스’ 역할을 하는 Sidebar 혹은 Dialog와 같은 컴포넌트에서 특히 자주 볼 수 있습니다
여기서 말하는 범용적인 “박스” 역할을 하는 컴포넌트란 크게 봤을 때 Layout 역할을 하는 컴포넌트
children props를 찾아보면 composition이라는 말을 많이 보게 되는데 composition은 합성이라는 의미가 있음
// src/shared/Layout.js
import React from 'react';
const HeaderStyles = {
width: '100%',
background: 'black',
height: '50px',
display: 'flex',
alignItems: 'center',
paddingLeft: '20px',
color: 'white',
fontWeight: '600',
};
const FooterStyles = {
width: '100%',
height: '50px',
display: 'flex',
background: 'black',
color: 'white',
alignItems: 'center',
justifyContent: 'center',
fontSize: '12px',
};
const layoutStyles = {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
minHeight: '90vh',
}
function Header() {
return (
<div style={{ ...HeaderStyles }}>
<span>Sparta Coding Club - Let's learn React</span>
</div>
);
}
function Footer() {
return (
<div style={{ ...FooterStyles }}>
<span>copyright @SCC</span>
</div>
);
}
function Layout({ children }) {
return (
<div>
<Header />
<div style={{...layoutStyles}}>
{children}
</div>
<Footer />
</div>
);
}
export default Layout;
//src/shared/Router.js
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from '../pages/Home';
import About from '../pages/About';
import Contact from '../pages/Contact';
import Works from '../pages/Works';
import Layout from './Layout';
const Router = () => {
return (
<BrowserRouter>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
<Route path="works" element={<Works />} />
</Routes>
</Layout>
</BrowserRouter>
);
};
export default Router;
▶ 정리
- react-router-dom을 이용하면 SPA 기반인 리액트 프로젝트 안에서 여러개의 페이지를 구현할 수 있음
- Router.js에 Route 설정에 관련된 코드를 작성하고 최상위 컴포넌트인 App.js에서 import 해서 사용
- react-router-dom는 여러가지 훅을 제공함
[ React Router Dom - Dynamic Route, useParam ]
▶ 상세 페이지 구현
Dynamic Route
Dynamic Route는 동적 라우팅이라고도 말하는데 path에 유동적인 값을 넣어서 특정 페이지로 이동하게끔 구현하는 방법
react-router-dom에서 지원하는 Dynamic Routes 기능을 이용해서 간결하게 동적으로 변하는 페이지를 처리할 수 있음
Dynamic Route 설정
Router.js로 이동해서 Dynamic Routes를 설정
// src/pages/Works.js
import React from "react";
const Works = () => {
return <div>Works</div>;
};
export default Work;
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../pages/Home";
import About from "../pages/About";
import Contact from "../pages/Contact";
import Works from "../pages/Works";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
<Route path="works" element={<Works />} />
<Route path="works/:id" element={<Works />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
path에 works/:id 라고 path가 들어감. :id 라는 것이 바로 동적인 값을 받겠다라는 의미
그래서 works/1, works/2 ... works/100 모두 <Work /> 로 이동하게 해줌. :id는 useParams 훅에서 조회할 수 있는 값이 됨
▶ Dynamic Routes와 useParam
query parameter 조회하기
Dynamic Routes를 사용하면 element에 설정된 같은 컴포넌트를 렌더링하게 됨
<Route path="works/:id" element={<Work />} />
위에 예제 코드에서도 Work 페이지 컴포넌트를 동일하게 렌더링됨
하지만 useParam을 이용하면 같은 컴포넌트를 렌더링하더라도 각각의 고유한 id 값을 조회할 수 있음
useParams는 path의 있는 id 값을 조회할 수 있게 해주는 훅
// src/pages/Works.js
import React from 'react';
import { Link, useParams } from 'react-router-dom';
const data = [
{ id: 1, todo: '리액트 배우기' },
{ id: 2, todo: '노드 배우기' },
{ id: 3, todo: '자바스크립트 배우기' },
{ id: 4, todo: '파이어 베이스 배우기' },
{ id: 5, todo: '넥스트 배우기' },
{ id: 6, todo: 'HTTP 프로토콜 배우기' },
];
function Works() {
return (
<div>
{data.map((work) => {
return (
<div key={work.id}>
<div>할일: {work.id}</div>
<Link to={`/works/${work.id}`}>
<span style={{ cursor: 'pointer' }}>➡️ Go to: {work.todo}</span>
</Link>
</div>
);
})}
</div>
);
}
export default Works;
// src/pages/Work.js
import React from 'react';
import { useParams } from 'react-router-dom';
const data = [
{ id: 1, todo: '리액트 배우기' },
{ id: 2, todo: '노드 배우기' },
{ id: 3, todo: '자바스크립트 배우기' },
{ id: 4, todo: '파이어 베이스 배우기' },
{ id: 5, todo: '넥스트 배우기' },
{ id: 6, todo: 'HTTP 프로토콜 배우기' },
];
function Work() {
const param = useParams();
const work = data.find((work) => work.id === parseInt(param.id));
return <div>{work.todo}</div>;
}
export default Work;
▶ 정리
- react-router-dom을 통해 Dynamic Route를 설정할 수 있음
- Dynamic Route를 설정할때는 :id로 설정하고 id 값은 useParams을 이용해서 각 컴포넌트에서 조회할 수 있음
[ 비동기 프로그래밍 입문 ]
▶ 비동기 프로그래밍 개요
개념
동기적(synchronous) 방식
동기적 프로그래밍은 코드는 모드 동기적으로 실행됨
즉 앞선 코드가 끝나야만 다음 코드가 수행됨. 일반적인 프로그래밍 세계에서는 코드는 모드 동기적으로 실행
// (1)
const a = 1;
// (2)
const b = 2;
// (3)
console.log(`a의 값은 ${a}입니다.`);
// (4)
alert(`hello ${b}!`);
// 항상 코드는 (1) -> (4)의 순서대로 실행이 됩니다.
비동기(asynchronous)적 방식
실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
- setTimeout, addEventListner 등
- 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드
- 대표적으로 서버 통신과 관련된 로직들 포함
콜백지옥
비동기적 프로그래밍을 하다 보면 예기치 못한 상황 즉, 콜백지옥 있음
//0.5초 주기마다 커피 목록을 수집하고 출력
//각 콜백은 커피 이름을 전달하고 목록에 이름을 추가
//문제점 : 들여쓰기, 값 전달의 순서가 아래 -> 위
setTimeout(function (name) {
var coffeeList = name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
setTimeout(function (name) {
coffeeList += ', ' + name;
console.log(coffeeList);
}, 500, '카페라떼');
}, '카페모카');
}, '아메리카노');
}, '에스프레소');
콜백지옥과 콜백지옥의 문제점
- 콜백 함수를 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 헬 수준인 경우
- 주로 이벤트 처리 및 서버 통신과 같은 비동기적 작업을 수행할 때 발생
- 가독성 낮고 수정이 어려움
▶ Promise 객체
개념
mds 공식문서에서는 이렇게 설명하고 있음
Promise 객체는 비동기 작업을 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다
이름이 약속인 이유
비동기 작업이라는 것은 수행의 제어권을 제 3자에게 넘겨준 후 작업이 완료되면 안내를 받아 제어권을 이양받는 식의 일 처리 방법
- localhost:3000 에서 잘 놀고 있다가 버튼을 클릭해서 네이버 서버에 날씨 정보를 달라고 API 문서에 적혀있는대로 요청함
- 그럼 그 API 문서에 적혀있는 약속대로 약 0.3sec가 지나고 나니 데이터가 뿅 하고 들어옴
위 과정에서 localhost에 있을 때의 모든 제어권은 나에게 있다가 네이버에 api 요청을 하는 순간 제어권은 네이버에게 넘어감. 그 이후 데이터를 전달받는 순간 다시 제어권은 나에게 넘어옴
비동기처리는 이처럼 약속에 의해 움직임. 그 약속에 관련된 사항들이 모두 Promise 객체에 담기는 것
promise 객체에 담기는 주요한 상태정보
- 대기 : pending, localhost에서 네이버에 요청한 직후. 아직 성공(resolve) 또는 실패(rejected)되지 않은 상태
- 이행 : fulfilled, 네이버가 정상적으로 데이터를 localhost한테 전달을 성공적으로 해준 상태
- 거부 : rejected, 어떠한 이유인지는 모르겠지만 네이버가 localhost에게 데이터를 전달을 못해준 경우
Promise 객체 핸들링 방법
promise 객체가 갖는 주요한 상태정보 3가지에 따라 적절한 처리를 해줘야 함
# then ~ catch
// http://api.naver.com/weather/today 로 요청을 한다고 가정
axios.get('http://api.naver.com/weather/today')
.then(response => {
console.log('정상처리 되었습니다 : ' + response);
})
.catch(error => {
console.log('오류가 발생하였습니다 : ' + error);
})
.finally(()=>{
console.log('항상 실행되는 부분입니다!');
});
# async / await
const getWeather = async () => {
try {
const response = axios.get('http://api.naver.com/weather/today');
console.log('정상처리 되었습니다 : ' + response);
} catch (error) {
console.log('오류가 발생하였습니다 : ' + error);
}
}
[ REST(Path Variable vs Query Parameter) ]
▶ REST API
개념
REpresentational State Transfer의 약자로서 어떤 자원에 대해 CRUD를 진행할 수 있게 HTTP Method(GET, POST, PUT, DELETE)를 사용하여 요청을 보내는 것. 이 때 요청을 위한 자원은 특정한 형태로 표현됨
“URI를 통해 정보의 자원을(only 자원만을) 표현하고 자원의 행위는 HTTP Method로 명시한다” 정도로 이해하면 됨
- 자원(Resource) : URI
- 행위(Verb) : HTTP Method
- 표현(Representations)
GET /users/3/profile
위에 요청을 보면 누구라도 user 중에서 3번 아이디를 갖고 있는 사람의 프로필을 줘라는 것을 알 수 있음
이것이 바로 REST API의 장점
예시
// bad
GET /members/delete/1
// good
DELETE /members/1
// bad
GET /members/show/1
// good
GET /members/1
// bad
GET /members/insert/2
// good
POST /members/2
//규칙
http://example.com/posts (O)
http://example.com/posts/ (X)
http://example.com/post (X)
http://example.com/get-posts (X)
--> URI는 명사를 사용하고 소문자로 작성
--> 명사는 복수형을 사용
--> URI의 마지막에는 /를 포함하지 않음
http://example.com/post-list (O)
http://example.com/post_list (X)
--> URI에는 언더바가 아닌 하이픈을 사용
http://example.com/post/assets/example (O)
http://example.com/post/assets/example.png (X)
--> URI에는 파일의 확장자를 표시하지 않음
RestFul하다는 것
REST API의 까다로운 조건을 만족시킨 통신 설계 상태를 말함
누가 보더라도 이해하기 쉽고 사용하기 쉬운 REST API라면 “RestFul하구나!” 라고 할 수 있음
RestFul하지 못하다는 것
- CRUD의 기능을 모두 POST method로만 이용하는 경우
- URI에 행위(method)에 대한 부분이 들어가는 경우(/classes/createPeople)
▶ Path Variable vs Query Parameter
개요
REST한 개발을 하다 보면 GET method를 이용하여 데이터를 가져올 때 등의 경우에서 Path Variable과 Query Parameter에 대한 이야기는 항상 같이 나옴
Path Variable
/users/10
특징
- 이름에서도 유추할 수 있듯 경로 자체에 변수(10)를 사용한 방법
- 전체 데이터 또는 특정 하나의 데이터를 다룰 때 처럼 리소스를 식별하기 위해 사용됨
Query Parameter
/users?user_id=10
특징
- 데이터를 정렬하거나 필터링 하는 경우 더 적합함
예시를 통해 비교해보기
/users # Fetch a list of users
/users?occupation=programer # Fetch a list of programer user
/users/123 # Fetch a user who has id 123
[ JSON ]
▶ 개념
JSON : JavaScript Object Notation
자바스크립트 객체 문법에 토대를 둔, 문자 기반의 데이터 교환 형식
▶ 구조
일반적인 JSON 구조는 자바스크립트 객체 리터럴 작성법을 따름
자바스크립트의 원시 자료형인 문자열, 숫자, 불리언을 가질 수 있고 중첩된 계층 구조 또한 가질 수 있음
{
"squadName": "Super hero squad",
"homeTown": "Metro City",
"formed": 2016,
"secretBase": "Super tower",
"active": true,
"members": [
{
"name": "Molecule Man",
"age": 29,
"secretIdentity": "Dan Jukes",
"powers": [
"Radiation resistance",
"Turning tiny",
"Radiation blast"
]
},
{
"name": "Madame Uppercut",
"age": 39,
"secretIdentity": "Jane Wilson",
"powers": [
"Million tonne punch",
"Damage resistance",
"Superhuman reflexes"
]
},
{
"name": "Eternal Flame",
"age": 1000000,
"secretIdentity": "Unknown",
"powers": [
"Immortality",
"Heat Immunity",
"Inferno",
"Teleportation",
"Interdimensional travel"
]
}
]
}
자바스크립트 객체 형태와 완전히 같지는 않고 비슷한 것 뿐. 작은 따옴포(’’)가 아닌 끝 따옴표(””)만이 허용됨
메서드
JSON → 문자열 형태 → 서버 - 클라이언트 간 데이터 전송 시 사용
하지만 다음 두 경우를 위해 파싱(parsing) 과정이 필요함
- JS 객체를 JSON 형태로 전송
- JSON 형태를 JS 객체 형태로 사용
# stringify()
: 자바스크립트 객체 → JSON 문자열 변환. 네트워크를 통해 객체를 JSON 문자열로 변환할 때 주로 사용함
console.log(JSON.stringify({ x: 5, y: 6 }));
// Expected output: "{"x":5,"y":6}"
console.log(JSON.stringify([new Number(3), new String('false'), new Boolean(false)]));
// Expected output: "[3,"false",false]"
console.log(JSON.stringify({ x: [10, undefined, function(){}, Symbol('')] }));
// Expected output: "{"x":[10,null,null,null]}"
console.log(JSON.stringify(new Date(2006, 0, 2, 15, 4, 5)));
// Expected output: ""2006-01-02T15:04:05.000Z""
# parse()
: JSON 문자열 → 자바스크립트 객체 변환. 네트워크 통신의 결과를 통해 받아온 JSON 문자열을 프로그램 내부에서 사용하기 위해 JS 객체로 변환할 때 사용
const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
console.log(obj.count);
// Expected output: 42
console.log(obj.result);
// Expected output: true
▶ JSONPlaceholder
가짜 서버(= fake server, mock API server)
API를 통해 JSON 기반 DB 통신을 일으키기
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((json) => console.log(json));
//App.jsx
import "./App.css";
import { useEffect, useState } from "react";
function App() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
console.log();
console.log("response.json()", response);
return response.json();
})
.then((json) => {
console.log("json", json);
setData([...json]);
});
}, []);
return (
<div>
{data.map((item) => {
return (
<div
style={{
border: "1px solid black",
margin: "3px",
}}
key={item.id}
>
<ul>
<li>userId : {item.userId}</li>
<li>id : {item.id}</li>
<li>title : {item.title}</li>
<li>body : {item.body}</li>
</ul>
</div>
);
})}
</div>
);
}
export default App;
다음과 같은 결과를 얻을 수 있음
'항해99' 카테고리의 다른 글
리액트 심화주차2 (0) | 2023.07.04 |
---|---|
리액트 심화주차1 (3) | 2023.07.04 |
리액트 숙련주차 3 (0) | 2023.06.27 |
리액트 숙련주차 2 (1) | 2023.06.27 |
리액트 숙련주차 1 (0) | 2023.06.27 |
댓글