(Server Side Rendering, 서버사이드렌더링)
브라우저에서 /mypage
페이지 요청
서버에서 페이지에 필요한 데이터를 불러와 완성된 mypage.html
생성
서버에서 클라이언트로 mypage.html
반환
클라이언트는 ㉠렌더링이 완료된 페이지를 화면에 ㉡렌더링 시킴
㉠렌더링 : 리액트 컴포넌트와 같이 자바스크립트로 작성되어 있는 페이지를 실제 HTML코드로 변환하는 과정을 말함
㉡렌더링 : HTML을 화면(브라우저)에 실제로 그려내는 것을 말함
4번 이후 화면 상태는 모든 컨텐츠가 다 그려진 상태이지만 인터렉션은 불가능함 (아직 JS파일을 못 받았음)
서버가 JavaScript Bundle을 보내줌
클라이언트가 JavaScript코드와 HTML요소들을 서로 연결함 (Hydration, 수화)
완성된 웹페이지가 화면에 렌더링
props: {}
를 포함해야 함window.location
과 같이 window객체에 접근 불가 (존재하지 않는 값)export default function Home({ name }) {
return (
<div>{ name }</div>
);
}
export const getServerSideProps = async () => { // 👈🏻SSR 함수
return {
props: {
name: "KOREA",
},
};
};
export default function Page() {
console.log("2");
useEffect(() => { // 브라우저에서만 출력 (컴포넌트 마운트는 서버측에서 발생하지 않음 )
console.log("3");
}, []);
return <div>...</div>;
}
export const getServerSideProps = async () => {
console.log("1"); // 서버에서만 출력 (새로고침할때마다)
return {
props: {},
};
};
2번의 경우 : 서버에서 렌더링하기 위해 한번 실행되고, 그 후 브라우저에서 Hydration과정 끝난 후 한번더 마운트 되어 실행되기 때문에 양쪽에서 출력 된다. (서버 → 브라우저)
~/search?q={검색어}
export default function Search() {
return <div>...</div>;
}
export const getServerSideProps = async (context) => {
const { q } = context.query; // 👈🏻Query String으로 전달된 값 q에 접근
return {
props: {},
};
};
import axios from "axios"; // ※ axios 라이브러리 설치하기
// 전체 국가 데이터
export async function fetchCountries() {
try {
const response = await axios.get("https://naras-api.vercel.app/all");
return response.data;
} catch (e) {
return [];
}
}
// 검색 결과 데이터
export async function fetchSearchResults(q) {
try {
const response = await axios.get(`
https://naras-api.vercel.app/search?q=${q}
`);
return response.data;
} catch (e) {
return [];
}
}
// 특정 국가 데이터
export async function fetchCountry(code) {
try {
const response = await axios.get(
`https://naras-api.vercel.app/code/${code}`
);
return response.data;
} catch (e) {
return null;
}
}
import { fetchCountries } from "@/api";
export default function Home({ countries }) {
return (
<div>
{countries.map((country) => {
return <div key={country.code}>{country.commonName}</div>;
})}
</div>
);
}
export const getServerSideProps = async () => {
// API호출 코드가 필요함
const countries = await fetchCountries();
return {
props: {
countries,
},
};
};
import { fetchSearchResults } from "@/api";
import SubLayout from "@/components/SubLayout";
export default function Search({ countries }) {
return (
<div>
{countries.map((country) => {
return <div key={country.code}>{country.commonName}</div>;
})}
</div>
);
}
Search.Layout = SubLayout;
export const getServerSideProps = async (context) => { // 👈🏻 매개변수 이용
//1.검색결과 api호출
//2.props리턴
const { q } = context.query; // 쿼리스트링 꺼내오기
let countries = [];
if (q) {
countries = await fetchSearchResults(q);
}
return {
props: { countries },
};
};
import { fetchCountry } from "@/api";
import SubLayout from "@/components/SubLayout";
import { useRouter } from "next/router";
export default function Country({ country }) {
// 클라이언트에서 사용
const router = useRouter();
const { code } = router.query;
return (
<div>
{country.commonName}
{country.officialName}
</div>
);
}
Country.Layout = SubLayout;
export const getServerSideProps = async (context) => {
// 서버측에서 불러옴
const { code } = context.params; //url parameter
let country = null;
if (code) {
country = await fetchCountry(code);
}
return {
props: { country },
};
};
URL parameter 불러오는 방식 차이 이해하기
- 클라이언트측에서 이용하는 경우 : 컴포넌트 내부에서 불러옴
(브라우저에서 hydration과정이 일어난 뒤에 동작)const router = useRouter(); // 리액트훅을 사용 const { code } = router.query;
- 서버측에서 이용하는 경우 : context객체에서 불러옴
const { code } = context.params;