gravatar 는 이메일 기반 글로벌 프로필 이미지 서비스이지만 간단한 해시 기반 아바타 생성기로도 사용이 가능합니다.
npm install gravatar
gravatar.url(email, options);
email 을 넣지 않더라도 아바타 생성이 가능합니다. option 에는 size, default, ratio 등을 구성하여 아바타 생성이 가능합니다.
중첩 라우팅은 부모 라우트 안에 자식 라우트를 정의해서 레이아웃을 공유하거나 페이지 일부만 바꿔치기할 수 있게 해주는 기능입니다.
// front/layouts/App
<Routes>
{/* 부모 라우트 */}
<Route path="/workspace" element={<Workspace />}>
{/* 자식 라우트 */}
<Route path="channel" element={<Channel />} />
<Route path="dm" element={<DirectMessage />} />
</Route>
</Routes>
// front/layouts/Workspace
const Workspace = () => {
return (
<div>
<Outlet /> // 자식 라우터 랜더링
</div>
)
}
react-router 의 Route 내부에 또 다른 Route 들을 구성하여 중첩 라우팅을 구성합니다. Outlet 을 사용하여 자식 라우터를 랜더링합니다.
React 에서 컴포넌트단위로 나눠 개발하면 다양한 이점이 있습니다.( 재사용성, 유지보수성등 )
// front/components/Menu
import React, { CSSProperties, useCallback } from 'react';
import { CloseModalButton, CreateMenu } from './styles';
interface Props {
children: React.ReactNode;
style: CSSProperties;
show: boolean;
onCloseModal: () => void;
closeButton?: boolean;
}
const Menu = ({ children, style, show, onCloseModal, closeButton = true }: Props) => {
const stopPropagation = useCallback((event: any) => {
event.stopPropagation(); // 이벤트 버블링 제어
}, []);
if (!show) {
return null;
}
return (
<CreateMenu onClick={onCloseModal}>
<div style={style} onClick={stopPropagation}>
{closeButton && <CloseModalButton onClick={onCloseModal}>×</CloseModalButton>}
{children}
</div>
</CreateMenu>
);
};
export default Menu;
// front/layouts/Workspace
<Menu style={{ right: 0, top: 38 }} show={showUserMenu} onCloseModal={onClickUserProfile}>
<ProfileModal>
<img src={gravatar.url(useData.nickname, { s: '28px', d: 'retro' })} alt={useData.nickname} />
<div>
<span id="profile-name">{useData.nickname}</span>
<span id="profile-active">Active</span>
</div>
</ProfileModal>
<LogOutButton onClick={onLogout}>로그아웃</LogOutButton>
</Menu>
한 컴포넌트는 한 가지 역할만 수행하는 단일 책임 원칙을 준수하는 편이 좋습니다.
event.stopPropagation() 는 정 요소에서 발생한 이벤트가 상위 DOM 요소로 계속해서 전파되는 버블링 현상을 막습니다
react-toastify는 React 에서 토스트 알림을 손쉽게 구현할 수 있게 해주는 라이브러리입니다.
npm install react-toastify
// front/latouts/App
const App = () => {
return (
<>
<ToastContainer />
</>
);
};
최상단에 한 번만 ToastContainer 넣어두면 앱 어디서든 toast 호출 가능합니다.
// 토스트 호출
toast.success("성공적으로 저장되었습니다!");
toast.error("에러가 발생했습니다.");
// 토스트 위치 변경
<ToastContainer position="bottom-left" />
toast.error(message, { position: 'bottom-center' });
// 비동기
toast.promise(
fetch("/api/save"),
{
pending: "저장 중...",
success: "저장 완료! 🎉",
error: "저장 실패 😢"
}
);
동적 라우팅은 URL의 일부를 변수처럼 받아서, 같은 컴포넌트로 여러 경로를 처리할 때 쓰입니다.
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
{/* 동적 라우트 */}
<Route path="/users/:userId" element={<UserProfile />} />
</Routes>
</BrowserRouter>
);
}
function UserProfile() {
// useParams로 URL 파라미터 가져오기
const { userId } = useParams();
return <h1>사용자 ID: {userId}</h1>;
}
useParams 를 사용하여 컴포넌트 내에서 URL 파라미터를 가져올 수 있습니다.
// /search?keyword=react&page=2
import { useSearchParams } from 'react-router';
function SearchPage() {
const [searchParams, setSearchParams] = useSearchParams();
const keyword = searchParams.get('keyword'); // "react"
const page = searchParams.get('page'); // "2"
const handleNextPage = () => {
searchParams.set('page', (Number(page) + 1).toString());
setSearchParams(searchParams); // URL 변경
};
return (
<div>
<h1>검색 결과</h1>
<p>검색어: {keyword}</p>
<p>페이지: {page}</p>
<button onClick={handleNextPage}>다음 페이지</button>
</div>
);
}
쿼리 파라미터는 useSearchParams 를 사용하여 편하게 읽고 쓸 수 있습니다. 기존에 사용되었던 useLocation + URLSearchParams 와 달리 useNavigate 를 사용하여 URL을 변경할 필요가 없습니다.
react-router의 버전이 달라짐에 따라 기존에 사용하던 방식이 많이 바뀌었습니다. 이렇게 라이브러리들의 사용법이 달라질 수 있으니 개발 및 이슈 발생 시에는 다른 개발자들의 포스팅 보다는 공식문서를 제일 먼저 확인해야할 것 같습니다.
실제 운영중인 서비스에서 라이브러리 버전 변경에 따른 큰 마이그레이션 작업이 발생할 때 효율적인 방법이 있을까?