SPA 장점
Optimizing performance
리액트에서 SPA(single page application)의 장점은 사용자 경험, 개발 효율성, 유지 보수성등이 있습니다. 이 SPA는 전체 페이지를 새로고침하지 않고도 빠르게 웹 어플리케이션을 개발할수있습니다. 이 SPA는 페이지간 이동이 없기때문에 애플리케이션의 복잡성이 낮아지고 변경사항을 쉽게 추적할수있습니다.
이러한 SPA는 초기 로드시간 검색엔진 최적화 메모리 사용등의 성능 문제가 있을수있습니다. 따라서 최적화기술들이 사용됩니다.
SPA 단점
초기로드시간: SPA는 초기 로드시간이 오래 걸릴 수 있습니다. 애플리케이션의 모든 자원을 다운로드하고 초기 렌더링을 위해 필요한 자바스크립트 파일을 다운로드하고 실행하여야하기 때문입니다.
검색엔진 최적화: SPA는 검색엔진 최적화가 어렵습니다. 검색엔진은 SPA의 동적인 콘텐츠를 인식하지 못하고, 페이지 url이 단일하므로 검색엔진에서 쉽게 인덱싱 되지 않을 수 있습니다.
보안: SPA는 XSS공격에 취약합니다 SPA는 동적으로 콘텐츠를 로드하기 때문에 제3자가 악성 스크립트를 삽입할수 있습니다.
브라우저 지원: SPA는 모든 브라우저에서 동작하지 않을 수 있습니다. 특히 오래된 브라우저나 모바일 기기의브라우저에서는 일부기능이 지원되지않을수있습니다.
메모리 사용량: SPA는 자바스크립트 코드가 클라이언트 측에서 실행되므로, 브라우저의 메모리 사용량이 높아질 수 있습니다. 이를 해결하기 위해 코드 스플리팅과 메모리 관리를 신경써야 합니다.
서버요청: SPA는 모든 자원을 최초에 한번 다운로드하고 이후에는 서버로 요청하지 않기 때문에 서버에서 변경된 데이터가 즉시 반영되지 않을 수 있습니다. 이를 해결하기 위해 서버와의 API통신을 통해 데이터를 주기적으로 가져와야 할 수 있습니다.
SSR
Next.js
SSR(server side rendering)은 서버 측에서 웹 페이지를 최초에 완전한 형태로 렌더링하여 클라이언트에게 보내주는 기술입니다. 일반적으로 SPA에서는 초기 로드시 모든 자원을 다운로드하고 자바스크립트를 실행한 뒤 동적으로 콘텐츠를 렌더링합니다 이는 초기 로드 시간이 오래 걸릴 수 있고, 검색 엔진 최적화가 어렵다는 단점이 있습니다. 하지만 SSR을 사용하면 서버에서 웹 페이지를 렌더링한 결과를 클라이언트에게 보내주기 때문에 초기 로드 시간이 줄어들고 검색 엔진 최적화도 개선됩니다. 또한 서버에서 데이터를 미리 가져와서 렌더링 할 수 있기 때문에 초기 데이터 로딩 시간도 단축됩니다.
NEXT.JS는 리액트 기반의 SSR웹 프레임 워크입니다. 이것을 사용하면 리액트 애플리케이션을 더 빠르고 개발자 친화적인 방식으로 개발할 수 있습니다.
Next.js의 주요 기능
서버 사이드 렌더링(SSR): Next.js는 서버 사이드 렌더링 기능을 제공하여 초기로딩 속도를 향상시키고 검색 엔진 최적화를 쉽게 할 수 있습니다.
코드 스플러팅: Next.js는 페이지별로 자동으로 코드를 분할하여 필요한 코드만 로드하여 초기 로딩 속도를 개선할 수 있습니다.
정적 사이트 생성: Next.js는 정적 사이트 생성 기능을 제공하여 서버리스 방식으로 정적 사이트를 생성할 수 있습니다.
hot reloading: 코드 수정 후 새로고침하지 않아도 브라우저에서 즉시 변경 사항이 적용됩니다.
기본적인 보안기능: next.js는 XSS및 CSRF(Cross site request forgery)공격에 대한 보안 기능을 내장하고 있습니다.
type script지원: next.js는 type script를 지원합니다.
쉬운 배포: next.js는 vercel이라는 클라우드 플렛폼에서 호스팅을 지원하여 쉽게 배포할 수 있습니다.
next.js는 리액트와 함께 사용할 수 있으며, 리액트 생태계와 호환성이 높아 리액트 개발자에게 더욱 익숙한 방식으로 개발할 수 있습니다.
CSR
CSR(client side rendering)은 클라이언트 측에서 자바스크립트를 이용해 동적으로 웹페이지를 렌더링하는 기술입니다. SPA에서 주로 사용됩니다.
CSR에서는 초기 로드시에 모든 자원을 다운로드하고 자바스크립트를 실행한 뒤, 동적으로 콘텐츠를 렌더링합니다. 이를 통해 사용자 경험을 향상 시킬수있습니다 예를들어 사용자가 특정 버튼을 클릭하면 해당 버튼과 관련된 콘텐츠만 다시 렌더링되어 화면 전체를 새로고침하지 않고도 빠르게 변경된 콘텐츠를 확인할 수 있습니다.
하지만 CSR은 초기 로딩 속도가 느리고 검색 엔진 최적화가 어려운 단점이 있습니다. 초기 로딩속도가 느린 이유는 초기에 모든 자원을 다운로드해야하고 자바스크립트를 실행해야 하기 때문입니다. 또한 검색엔진 최적화가 어려운 이유는 CSR에서는 초기 로드 시점에 웹 페이지가 비어있기 때문입니다. 이러한 문제를 해결하기 위해 SSR기술이 등장합니다.
React-Router-dom
react router dom 은 리액트 애플리케이션에서 라우팅을 다루기 위한 인기있는 라이브러리 입니다.현재 url경로를 기반으로 라우팅 및 네비게이션을 처리할 수 있도록, 라우트를 컴포넌트로 정의하고 렌더링하는 방식으로 선언적으로 처리할수 있게 해줍니다.
react router dom을 사용하려면 먼저 설치해줍니다.
npm install react-router-dom
설치를 완료했다면 해당 라이브러리에서 필요한 컴포넌트를 가져와서 라우트를 정의 할 수 있습니다.
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
function App() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
</Switch>
</div>
</Router>
);
}
browser 컴포넌트를 사용하여 라우팅을 설정하고, switch와 route컴포넌트를 사용하여 경로에 따라 적절한 컴포넌트를 렌더링하도록 설정합니다 이렇게 설정된 라우팅을 통해 사용자가 url경로를 변경하면 해당 경로에 맞는 컴포넌트가 렌더링되게 합니다.
Route연결
Switch
react router dom의 switch 컴포넌트는 하위 route컴포넌트중에서 가장 먼저 일치하는 하나의 라우트만 렌더링하도록하는 컴포넌트입니다. 즉 url경로에 따라 렌더링할 컴포넌트를 선택하는데 switch는 이를 처리하는 라우팅 컴포넌트중에서 하나만 선택하여 렌더링하도록 해줍니다.
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
</Switch>
이렇게 두개의 route컴포넌트가 있다고 가정해보면 사용자가 ‘/contact’경로로 이동하면 컴포넌트는 두번째 route컴포넌트에 일치하므로 contact컴포넌트를 렌더링합니다 반면 ‘/about’경로로 이동하면 첫 번째 route컴포넌트에 일치하므로 about컴포넌트를 렌더링 합니다.
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route>
<NotFound />
</Route>
</Switch>
switch컴포넌트는 일치하는 라우트가 없는 경우에는 아무것도 렌더링하지 않습니다 이를 이용하여 404페이지를 구현할수있습니다.
위 코드에서 처럼 route컴포넌트는 path prop이 없는 경우이므로 어떤 경로와도 일치하지 않습니다. 따라서 일치하는 라우트가 없는 경우에는 ‘not found’컴포넌트를 렌더링하게됩니다.
Link
react router dom의 link컴포넌트는 사용자가 클릭할 때 url경로를 변경하고 새로운 페이지를 로드하지 않고도 라우팅할 수 있도록 해주는 컴포넌트입니다. 즉 애플리케이션에서 다른페이지로 이동할때 일반적인 앵커 태그’a’ 대신에 link컴포넌트를 사용하여 이동할 수 있습니다.
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<Link to="/about">About Us</Link>
);
}
예를들어 다음과 같이 link컴포넌트를 사용하여 ‘/about’경로로 이동할 수 있습니다.
link컴포넌트는 일반적으로a태그같은 스타일을 가지며 마우스 포린터를 올리면 커서가 바뀌고 클릭하면 url로 경로가 변경됩니다. 그러나 페이지를 새로고침하지 않고도 url경로를 변경할 수 있기 때문에 페이지가 로드되지않고 애플리케이션이 더 빠르게 동작합니다.
Redirect
react router dom의 redirect컴포넌트는 사용자를 다른 경로로 리디렉션 시켜주는 컴포넌트입니다. 즉 url경로를 변경하고 새로운 페이지를 로드하여 리디렉션하는 기능을 제공합니다.\
redirect컴포넌트는 일반적으로 사용자가 인증되지않은 경우 로그인페이지로 리디렉션하는데 사용됩니다. 예를들어 다음과 같이 인증되지않은 사용자인경우 ‘/login’경로로 리디렉션 할 수 있습니다.
import { Redirect } from 'react-router-dom';
function MyComponent() {
const isAuthenticated = false;
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
// 인증된 사용자의 경우 처리할 코드
return <div>Welcome!</div>;
}
History API
history api는 웹 브라우저의 세션기록을 조작하는 자바스크립트 인터페이스입니다. 이 api를 사용하면 브라우저의 url을 동적으로 변경하고 뒤로가기와 앞으로가기 버튼을 클릭하면 브라우저의 상태를 이전 또는 이후로 변경할 수 있습니다.
history api는 pushState(), replaceState(), go(), forward(), back() 등의 메서드를 제공합니다.
window.history.pushState({ page: "home" }, "Home", "/");
위 코드에서 page:home는 state object로, 브라우저의 상태를 나타내는 객체입니다. home은 새로운 history 항목의 title이며, /는 새로운 URL 경로입니다.
Dynamic routing
다이나믹 라우팅은 react router dom에서 동적으로 url을 생성하는 것을 말합니다. 이를 통해 특정 데이터 또는 id값을 url에 전달하고 동적으로 렌더링 할 수있는 페이지를 만들 수 있습니다.
예를들어 다음과 같이 /users/:id경로로 라우팅하는 route를 설정할 수 있습니다.
import { Route } from 'react-router-dom';
import User from './User';
function App() {
return (
<div>
<Route path="/users/:id" component={User} />
</div>
);
}
위 코드에서 :id는 경로 매개변수이며 /users/1, /users/2, /users/3과 같은 경로를 동적으로 생성할수있습니다
user컴포넌트에서는 props.match.params.id와 같은 방식으로 url매개변수를 읽어와 처리할 수 있습니다
import { useParams } from 'react-router-dom';
function User() {
const { id } = useParams();
return (
<div>
User ID: {id}
</div>
);
}
위 코드에서 useParam() hook을 사용하여 url매개변수를 읽어올 수 있습니다. 이를 통해 /users/1, /users/2, /users/3과 같은 동적 경로에 대응하는 페이지를 동적으로 렌더링 할 수 있습니다.
Nested routes
nested routes는 react router dom에서 하위 라우팅을 설정하는 것을 말합니다 즉 한 route내에서 다른 route를 중첩하여 사용하는 것입니다 이를통해 복잡한 ui를 구성하거나 중첩된 컴포넌트를 처리할때 유용하게 사용할수 있습니다
Nested routes를 설정하려면, Route 컴포넌트 내부에서 또 다른 Route 컴포넌트를 사용하면 됩니다. 이때, 중첩된 Route 컴포넌트에는 path prop을 부여해야 합니다.
예를들어 다음과 같이 /users경로에서 userList컴포넌트를 /users/:id경로에서 userdetail컴포넌트를 렌더링하도록 중첩된 라우팅을 설정할 수 있습니다.
import { Route, Switch } from 'react-router-dom';
import UserList from './UserList';
import UserDetail from './UserDetail';
function Users() {
return (
<Switch>
<Route exact path="/users" component={UserList} />
<Route path="/users/:id" component={UserDetail} />
</Switch>
);
}
파라미터
react router dom에서 파라미터는 url경로의 일부로 전달되는 값들을 의미합니다 예를들어 /users/1경로에서 1은 파라미터입니다
파라미터를 사용하려면 url경로에 파라미터를 나타내는 placeholder를 추가해야합니다 이를위해 : 기호를 사용하여 url경로의 일부를 변수로 표시합니다 예를들어 /users/:id 경로에서 id는 변수입니다
파라미터를 사용하기 위해, Route 컴포넌트에서 path prop을 사용하여 URL 경로를 설정할 때, 변수로 사용할 이름을 : 기호를 사용하여 지정합니다. 이때, 변수 이름은 식별자 규칙에 따라 유효한 이름이어야 합니다. 예를 들어, /users/:userId 경로에서 userId는 유효한 변수 이름입니다.
Route 컴포넌트에서 파라미터 값을 사용하기 위해서는, 컴포넌트의 props에서 match 객체를 사용합니다. match 객체는 URL 경로와 일치하는 라우트 정보를 포함하고 있으며, params 속성을 통해 파라미터 값을 가져올 수 있습니다.
import { Route, Link } from 'react-router-dom';
import UserDetail from './UserDetail';
function UserList(props) {
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
return (
<div>
<ul>
{users.map(user => (
<li key={[user.id](http://user.id/)}>
<Link to={’/users/${user.id}’}>{[user.name](http://user.name/)}</Link>
</li>
))}
</ul>
<Route path="/users/:id" render={(props) => <UserDetail id={[props.match.params.id](http://props.match.params.id/)} />} />
</div>
);
}
쿼리
쿼리(Query)는 React Router DOM에서 URL 경로의 끝에 ? 기호를 사용하여 전달되는 문자열 파라미터 값입니다. 쿼리 문자열은 key=value 형태로 작성하며, 여러 개의 쿼리 파라미터는 & 기호로 구분합니다. 예를 들어, /search?q=react&sort=popular경로에서 q와 sort는 쿼리 파라미터입니다.
Route 컴포넌트에서 쿼리 파라미터 값을 사용하기 위해서는, 컴포넌트의 props에서 location 객체를 사용합니다. location 객체는 현재 URL 정보를 포함하고 있으며, search 속성을 통해 쿼리 파라미터 값을 가져올 수 있습니다. 이때, search 속성의 값은 ? 기호를 포함한 전체 문자열입니다.
예를 들어, 다음과 같이 /search?q=react&sort=popular 경로에서 쿼리 파라미터 값을 가져와서 SearchResults 컴포넌트에서 사용할 수 있습니다.
import { Route } from 'react-router-dom';
import SearchResults from './SearchResults';
function SearchPage(props) {
return (
<div>
<form onSubmit={(e) => {
e.preventDefault();
const searchValue = e.target.elements.search.value;
props.history.push(’/search?q=${searchValue}&sort=popular’);
}}>
<input type="text" name="search" />
<button type="submit">Search</button>
</form>
<Route path="/search" render={(props) => <SearchResults query={new URLSearchParams(props.location.search).get('q')} sort={new URLSearchParams(props.location.search).get('sort')} />} />
</div>
);
}