๐ Next.js 9์ฅ: Parallel & Intercepting Routes โ ๋ชจ๋ฌ๊ณผ ๋ผ์ฐํ ์ ๊ทนํ
๐ ๊ฐ์
Parallel Routes์ Intercepting Routes๋ก URL ๊ธฐ๋ฐ ๋ชจ๋ฌ์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ Parallel Routes (
@folder): ํ ์ง๋ถ ๋ ๊ฐ์กฑ ๐ข - ๐ฑ Intercepting Routes (
(..)): ๋ด๊ฐ ์ฑ๊ฐ๋ ๋ง์ ๐ก - ๐ก๏ธ ์ธ์คํ๊ทธ๋จ ๋ชจ๋ฌ ์ค๊ณ: ๋ ๊ธฐ์ ์ ์๋ฒฝํ ์์๋ธ ๐ด
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ(์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
๋
๋ฆฝ๋ ์์ญ ๋ณ๋ ฌ ๋ ๋๊ธฐ(@) โ ํ๋ฆ ๊ฐ๋ก์ฑ๊ธฐ((..)) โ ์ด ๋์ ์กฐํฉํ ๊ถ๊ทน์ "๊ณต์ URL ๋ชจ๋ฌ" ํจํด ๋ง์คํฐ
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ์ธ์คํ๊ทธ๋จ, ํํฐ๋ ์คํธ์ฒ๋ผ (1) ํผ๋ ์์์๋ ๋ชจ๋ฌ๋ก ๋จ๊ณ , (2) ๋จ์๊ฒ ์นดํก์ผ๋ก ๊ณต์ ํ URL์ ๋๋ฅด๋ฉด ์ ์ฒดํ๋ฉด ์ ์ฉ ํ์ด์ง๋ก ์ ์๋๋ ์ด์ค ๋ผ์ฐํ ์ํคํ ์ฒ๋ฅผ ๊ตฌํํ ์ ์๋ค.
- ํ๋์
layout.tsx์์์ A, B, C ๊ตฌ์ญ์ด ๊ฐ๊ฐ ๋จ์ ๋์น ์ ๋ณด๊ณ ๋ ๋ฆฝ์ ์ธ ๋ก๋ฉ/์๋ฌ ํ๋ฉด์ ๋ฟ์ด๋ด๋ "๋์๋ณด๋ ๋ณ๋ ฌ ๋ ๋๋ง" ์ ์ค๊ณํ ์ ์๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์ฒ (์๋ก ์จ ์ฃผ๋์ด): "๋ฆฌ๋ ๋... ํผ๋์์ ์ฌ์ง์ ํด๋ฆญํ๋ฉด ๋ชจ๋ฌ๋ก ๋์์คฌ๊ฑฐ๋ ์. ๊ทธ๋ฐ๋ฐ ์ฌ์ฉ์๊ฐ ํ์ ์ ๋ซ์ผ๋ ค๊ณ '๋ค๋ก ๊ฐ๊ธฐ'๋ฅผ ๋๋ ๋๋ ์ฌ์ดํธ ๋ฐ์ผ๋ก ํ๊ฒจ ๋๊ฐ๋์! URL์ด ์ ๋ณํด์ ๊ทธ๋ฐ ๊ฒ ๊ฐ์๋ฐ, ๊ทธ๋ ๋ค๊ณ ์ ํ์ด์ง๋ก ์ด๋์ํค์๋ ๋ฐฐ๊ฒฝ ํผ๋๊ฐ ๋ค ์ฌ๋ผ์ ธ์. ๐ญ"
- ์ํธ(FE ๋ฆฌ๋): "์์ฒ ๋... ๋ชจ๋ฌ์ ์ต๊ณ ๋์ ์ธ 'URL ๋๊ธฐํ ๋ ์ด์ด' ๋ฅผ ๋ง์ฃผํ์
จ๊ตฐ์! Next.js์
@folder(๋ณ๋ ฌ ๋ผ์ฐํธ) ์(..)(๊ฐ๋ก์ฑ๊ธฐ ๋ผ์ฐํธ) ๋ฅผ ์์ด ์ฐ๋ฉด, URL์ ์ ํํ ๋ฐ๋๋ฉด์๋ ๋ฐฐ๊ฒฝ ํ๋ฉด์ ๊ทธ๋๋ก ์ ์ง๋๋ ์ธ์คํ๊ทธ๋จ ๋บจ์น๋ ํ์ ์ ๋ง๋ค ์ ์๋ค๊ณ ์!"
๐ค ์ ์์์ผ ํ๋๊ฐ
๊ณผ๊ฑฐ ๋ฆฌ์กํธ ์์ ์ ํ์
/๋ชจ๋ฌ ๋์ฐ๊ธฐ๋ ์ฌ์ ์ด. const [isOpen, setIsOpen] = useState(false) ํ๋ ์ ์ธํ๊ณ ์กฐ๊ฑด๋ถ ๋ ๋๋ง({isOpen && <Modal/>}) ๋๋ฆฌ๋ฉด ๋์ด์์ง.
ํ์ง๋ง ์ด ๋ฐฉ์์ ์น๋ช
์ ์ธ ๊ฒฐํจ์ด ์์๋จ๋ค.
- ๋ค๋ก ๊ฐ๊ธฐ ๋ถ๊ฐ๋ฅ: ๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ(URL)์ ๊ธฐ๋ก์ด ์ ๋จ์์, ๋ค๋ก ๊ฐ๊ธฐ๋ฅผ ๋๋ฅด๋ฉด ๋ชจ๋ฌ๋ง ๊บผ์ง๋ ๊ฒ ์๋๋ผ ์ ํ์ด์ง๋ก ์ด๋ํด๋ฒ๋ฆผ.
- ๊ณต์ ๋ถ๊ฐ๋ฅ: ์ด "์ฌ์ง ๋ชจ๋ฌ์ด ๋ ์๋ ์ํ" ์์ฒด๋ฅผ ์น๊ตฌํํ ๋งํฌ๋ก ๋ณต์ฌํด์ ์ค ์๊ฐ ์์.
- SEO ๋ถ๊ดด: ํฌ๋กค๋ฌ ์
์ฅ์์๋ ์จ๊ฒจ์ง
div๋ฉ์ด๋ฆฌ์ผ ๋ฟ, ๋ ๋ฆฝ๋ ๋ฌธ์(์ฌ์ง ์์ธ ์ ๋ณด)๋ก ์ธ์ํ์ง ๋ชปํจ.
์ด๊ฑธ ๊ณ ์น๋ ค๋ฉด ๋ชจ๋ฌ์ ๋์ธ ๋ ๊ฐ์ ๋ก URL์ ๋ฐ๊ฟ์ผ ํ๋๋ฐ(์: /feed -> /photo/123), ๊ณผ๊ฑฐ์ URL์ด ๋ฐ๋๋ฉด ๋ฐฐ๊ฒฝ์ ๊น๋ ค์๋ ํผ๋ ๋ชฉ๋ก์ด ์น ๋ ์๊ฐ๊ณ ์๋ก์ด ๋ฐฑ์ง ๋ ๋ ์์ญ(๋ชจ๋ฌ๋ง ๋ฉ๊ทธ๋ฌ๋ ์๋ ํ์ด์ง) ์ด ๋ํ๋๋ ํ๊ณ๊ฐ ์์์ด.
์ด ๋ชจ๋ฌ ๋๋ ๋ง๋ฅผ "์๋ฒ ์ปดํฌ๋ํธ ํ๋ ์์ํฌ" ์ฐจ์์์ ๊ฐ์ฅ ์ฐ์ํ๊ฒ, ์๋ฒฝํ๊ฒ ๊นจ๋ฒ๋ฆฐ Next.js์ ์๋์ค๋ฌ์ด ๋ ๊ฐ์ง ๊ณ ๊ธ ํ์ผ ์์คํ ๋ฃฐ์ ๋ฐฐ์๋ณด์.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด? (์์๋ค ๊ตญ๋ฐฅ์ง์ ๋ ๋ฆฝ ํ ์ด๋ธ๊ณผ ์์ ๊ฐ๋ก์ฑ๊ธฐ)
Parallel Routes (
@) : 1์ธ์ฉ ์นธ๋ง์ด ํ ์ด๋ธ
์๋น ํ๊ฐ์ด๋ฐ์ ๋ ๋ฆฝ๋ ๋ฐฅ์ 3๊ฐ(@table1,@table2,@table3) ๊ฐ ๋์ฌ ์๋ ๊ฑฐ์ผ.
1๋ฒ ์๋์ด ๊น๋๊ธฐ๋ฅผ ๋ ๋ฌ๋ผ๊ณ ํด๋(@analytics) , 2๋ฒ ์๋์ ์๊ธฐ ๊ตญ๋ฐฅ์ ์กฐ์ฉํ ๋จน๊ณ ์์ด.
์ ํ ์ด๋ธ์์ ๊ทธ๋ฆ์ ๊นจ๋จ๋ ค๋(@error) , ๋ด ํ ์ด๋ธ ์๋์ ์๋ฌด ์ผ ์๋ค๋ ๋ฏ ๋ฐฅ์ ๋จน์ ์ ์๋ ์๋ฒฝํ ๋ ๋ฆฝ ์์ฌ ๊ณต๊ฐ ์ด์ผ.Intercepting Routes (
(..)): ์ฃผ๋ฐฉ ์ ๊ฐ๋ก์ฑ๊ธฐ ์์ํ
์๋ "ํน๋ ์์ก" ์ฃผ๋ฌธ(photo/1) ์ "์์ชฝ ๋จ์ฒด ๋ฐฉ" ์ผ๋ก ๋ฐฐ๋ฌ๋์ด์ผ ํด.
๊ทธ๋ฐ๋ฐ ์๋์ด ๋ณต๋๋ฅผ ์ง๋๊ฐ๋ค๊ฐ ์ฃผ๋ฐฉ ์ ๊ตฌ์์ "์ ๊น! ๊ทธ ์์ก ๋ ํ ์ ๋ง ๋ง๋ณด๊ฒ ํด์ค!" ํ๊ณ ๋ชฉ์ ์ง๋ก ๊ฐ๋ ์์ก์ ์ค๊ฐ์ ๋์์ฑ์(Intercept) ํ์ฌ ์๋ ๋ณต๋(ํผ๋ ํ๋ฉด) ์์ ํ ์ ๋จน์ด๋ณด๋ ๋ง์ ์ด์ผ.
๋ค ๋จน๊ณ ์ ์๋ฅผ ๋๋ ค์ฃผ๋ฉด(๋ค๋ก ๊ฐ๊ธฐ) , ๋ค์ ์๋ ์๋ฆฌ๋ก ๋์๊ฐ๊ณ , ์์ ์ฒ์๋ถํฐ "๋จ์ฒด ๋ฐฉ" ์ผ๋ก ๋ค์ด์จ ์๋(์ง์ URL ์ ์) ์ ์ ๋๋ก ๋ ์์ก ํ ์์ ๋ฐ๊ฒ ๋ผ.
๐งฉ Parallel Routes (@folder): ํ ์ง๋ถ ๋ ๊ฐ์กฑ ๐ข
๋จผ์ ๋ณ๋ ฌ ๋ผ์ฐํธ์ผ. ํด๋ ์ด๋ฆ ์์ ๊ณจ๋ฑ
์ด(@)๋ฅผ ๋ถ์ด๋ฉด, Next.js๋ ์ด๊ฑธ ๋จ์ํ URL ๊ฒฝ๋ก ์กฐ๊ฐ์ผ๋ก ํด์ํ์ง ์๊ณ "๋ถ๋ชจ Layout์ ๊ฝ์ ๋ฃ์ ๋
๋ฆฝ๋ ์ฌ๋กฏ(Slot)" ์ผ๋ก ์ทจ๊ธํด.
์ํฉ: ๋ณต์กํ ๊ด๋ฆฌ์ ๋์๋ณด๋
๋์๋ณด๋(Root) ์์ 1) ์ฌ์ฉ์ ํต๊ณํ์ 2) ์ต์ ์๋ฆผ์ฐฝ ๋ ๊ฐ์ ๊ฑฐ๋ํ ๊ตฌ์ญ์ด ์์ด.
app/
โโ layout.tsx (๋ผ๋ ๋ ์ด์์)
โโ page.tsx (๊ฐ์ด๋ฐ ๊ธฐ๋ณธ ๊ตฌ์ญ)
โโ @analytics/ (๊ณจ๋ฑ
์ด! ์ฌ๋กฏ A)
โ โโ page.tsx
โโ @notifications/ (๊ณจ๋ฑ
์ด! ์ฌ๋กฏ B)
โโ page.tsx์๋น ๋ ์ด์์(layout.tsx)์ ๋ง๋ฒ ์ฌ๋กฏ ์กฐ๋ฆฝ
@ํ์ผ๋ช
ํด๋๋ฅผ ๋ง๋ค๋ฉด, Next.js ํ๋ ์์ํฌ ์ฝ์ด๊ฐ ์๋์ผ๋ก layout.tsx ์ปดํฌ๋ํธ์ ํ๋ผ๋ฏธํฐ(Props)์ ๊ทธ ์ด๋ฆ๊ณผ ๋๊ฐ์ children์ ๋ชฐ๋ ์ฃผ์
ํด์ค! ์์ฒญ๋ ํ๋ง๋ฒ์ด์ง.
// app/layout.tsx
export default function DashboardLayout({
children, // ์๋ ๋ค์ด์ค๋ ๊ธฐ๋ณธ page.tsx ๊ตฌ์ญ
analytics, // @analytics ํด๋ ๋ด๋ถ์ ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฌผ์ด ํต์งธ๋ก ๋ค์ด์ด! (๋ง์ )
notifications, // @notifications ๋ด๋ถ ๊ฒฐ๊ณผ๋ฌผ
}: {
children: React.ReactNode
analytics: React.ReactNode
notifications: React.ReactNode
}) {
return (
<body>
<main className="grid">
<div className="center"> {children} </div>
{/* ํ ์ง๋ถ(layout) ์๋์ 3๊ฐ์กฑ์ด ๋
๋ฆฝ๋ ์คํฌ๋ฆฐ์ผ๋ก ๊ณต์กดํ๋ค! */}
<aside className="left"> {analytics} </aside>
<aside className="right"> {notifications} </aside>
</main>
</body>
)
}์ด ์ง์ ์ ํ๋์? (์ต๋ ์ฅ์ )
์ 3๊ฐ์ ๊ตฌ์ญ(children, analytics, notifications)์ ์์ ํ ๋จ๋จ์ฒ๋ผ ํ๋ํด.
- ๋ง์ฝ ํต๊ณ(
analytics) ๋ฐ์ดํฐ ์กฐํ๊ฐ 5์ด๋ ๊ฑธ๋ ค์ ๋ก๋ฉ ์ค(์์ฒดloading.tsx๋ณด์ )์ด๊ฑฐ๋ ์ค๋ฅ๊ฐ ๋๋(์์ฒดerror.tsx๋ณด์ ), ์ค๋ฅธ์ชฝ์ ์๋ฆผ์ฐฝ(notifications)์ ๋์น ์ ๋ณด๊ณ 0.1์ด ๋ง์ ๋ ๋๋ง์ ๋๋ด๊ณ ํผ์ ๊ทธ๋ ค์ ธ. ์๋ฒฝํ ๊ฒฉ๋ฆฌ ๋ณด๋ฃจ๊ฐ ๋๋ ๊ฑฐ์ผ!
๐ฑ Intercepting Routes ((..)): ๋ด๊ฐ ์ฑ๊ฐ๋ ๋ง์ ๐ก
์ด๊ฒ ๋ฐ๋ก ์ธ์คํ๊ทธ๋จ์ด ํผ๋ ๋๋ฅผ ๋ ์ ์ฐฝ์ผ๋ก ์ ๊ฐ๊ณ ์ฌ์ง๋ง ๋์ฐ๋ ํต์ฌ ๊ธฐ์ ์ด์ผ. ํด๋ ์ด๋ฆ์ ์๋๊ฒฝ๋ก ์ฉ์ฉ๊ดํธ ๊ธฐํธ (..)๋ฅผ ๋ถ์ฌ์ ๋ง๋ค์ด.
(.) : ๋์ ๊ฐ์ ํด๋ ๋ ๋ฒจ ๊ฒฝ๋ก ๊ฐ๋ก์ฑ๊ธฐ
(..) : ๋๋ณด๋ค ํ ๋จ๊ณ ์๋ฐฉ(๋ถ๋ชจ) ๊ฒฝ๋ก ๊ฐ๋ก์ฑ๊ธฐ
(..)(..) : ๋ ๋จ๊ณ ์๋ฐฉ ๊ฐ๋ก์ฑ๊ธฐ
(...) : ์์ ์ต์๋จ app/ ๊ธฐ์ค ๋ฃจํธ(Root) ๊ฐ๋ก์ฑ๊ธฐ๐ฏ ์์ฒ ์ด์ ์ธ์คํ๊ทธ๋จ ๋ง๋ค๊ธฐ ๋ฏธ์
์ฐ๋ฆฌ๋ ๊ธฐ๋ณธ์ ์ผ๋ก /photo/123 ์ด๋ผ๋ ๊ณ ์ ์ฃผ์๋ฅผ ๊ฐ์ง ์ ์งํ๊ณ ์ปค๋ค๋ "์ฌ์ง ์ ์ฉ ์ ํ์ด์ง"๋ฅผ ๊ฐ์ง๊ณ ์์ด. (๋จํํ
์นดํก์ผ๋ก ๊ณต์ ํ์ ๋ ๋จ์ด์ง๋ ๋์ฐฉ์ง)
app/
โโ feed/
โ โโ page.tsx (๊ฒ์๋ฌผ ๋ชฉ๋ก)
โโ photo/
โโ [id]/
โโ page.tsx (๊ทธ๋ฅ ์ ์ํ๋ฉด ๋จ๋ ์ปค๋ค๋ ์ ์ฒด์ฌ์ง ํ์ด์ง)์ด ์ํ์์ feed/page.tsx ์์ ์ฌ์ง ๋งํฌ <Link href="/photo/123"> ๋ฅผ ํด๋ฆญํ๋ฉด? ์ํ๊น๊ฒ๋ ํ๋ฉด ๋ฐฐ๊ฒฝ์ด ํ์๊ฒ ์น ๋ค ์ง์์ง๋ฉฐ ๋ป๊ฑด ์ ์ฒดํ๋ฉด ์ฌ์ง ํ์ด์ง /photo/123 ์ผ๋ก ๋์ด๊ฐ๋ฒ๋ ค.
์ด๊ฑธ ๋ง์ผ๋ ค๋ฉด? ํผ๋(Feed) ๊ตฌ์ญ ๋ด๋ถ์์, ์๋ /photo/ ๋ฐฉ์ผ๋ก ํ๋ฌ ๋ค์ด๊ฐ์ผ ํ ๋ผ์ฐํ
ํ๋ฆ์ ๊ฐ๋ก์ฑ๋ฉด ๋ผ!
๐ก๏ธ ์ธ์คํ๊ทธ๋จ ๋ชจ๋ฌ ์ค๊ณ: ๋ ๊ธฐ์ ์ ์๋ฒฝํ ์์๋ธ ๐ด
์ด์ ์์์ ๋ฐฐ์ด ๋ ๊ฐ์ ๋ฌด๊ธฐ(๋ณ๋ ฌ @, ๊ฐ๋ก์ฑ๊ธฐ (..))๋ฅผ ํฉ์ฒดํด ์ ์ค์ ๋ฌด๊ธฐ(Soft Navigation Modal)๋ฅผ ๋ฒผ๋ ค๋ด์.
์๋ฒฝํ ๋ชจ๋ฌ ํด๋ ๊ตฌ์กฐ ์ค๊ณ๋
app/
โโ layout.tsx
โโ feed/
โ โโ layout.tsx โ
๋ชจ๋ฌ ์ฌ๋กฏ์ ๊ตฌ๋นํด๋์
โ โโ page.tsx โ
<Link href="/photo/1">๊ฐ ์๋ ๋ชฉ๋ก
โ โโ @modal/ โ
[Parallel] ๋ชจ๋ฌ์ ๋์ธ ์ ์ฉ ๋
๋ฆฝ ๋ณ๋ ฌ ์ฌ๋กฏ
โ โโ default.tsx (ํ์ ๋ก๋ฉ ์ ์ฉ ํฌ๋ช
์ธ๊ฐ ๋ทฐ)
โ โโ (..)photo/ โ
[Intercept] ํ๋ฆ ๊ฐ๋ก์ฑ๊ธฐ ํด๋! (ํผ๋์ ํ์ ์ธ photo๋ฅผ ๋์์ฑ)
โ โโ [id]/
โ โโ page.tsx โ
์ฌ๊ธฐ๊ฐ ๋ฐ๋ก ํผ๋ ๋ฐฐ๊ฒฝ์ ๊น๊ณ ๋จ๋ "๋ชจ๋ฌ ๋ฐ์ค" ์ปดํฌ๋ํธ!
โ
โโ photo/ โ
[Original] ์ฌ๊ธฐ๋ ๋ณํจ ์์ด ์กด์ฌํ๋ "๊ทผ๋ณธ ์ ์ฒดํ๋ฉด" ๋ชฉ์ ์ง.
โโ [id]/
โโ page.tsx โ
์นดํก/์ธ์คํ๋ก ๋ณต์ฌ๋ ๋งํฌ ์ง๊ฒฐ ์ ์ ์ ๋จ๋ ๊ฑฐ๋ํ ํ๋ฉด![๋ง๋ฒ์ด ํผ์ณ์ง๋ ์๊ฐ]
-
์ํฉ 1: ์นดํก์ผ๋ก ๋งํฌ ๊ณต์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ์ ์ฉ์ผ๋ก ๋ณต๋ถ ์ ์ํ์ ๋ (Hard Navigation)
๋๊ฐ/photo/1์ณค๋ค. Next.js ์์ง์ "๊ฐ๋ก์ฑ ์ถ๋ฐ์ง๊ฐ ์๋ค?" ํ๊ณ ์ ์งํ๊ฒ ๊ทผ๋ณธ ๊ฒฝ๋ก์ธ ๋งจ ๋ฐ๋ฐ๋ฅapp/photo/[id]/page.tsx์ฐํ์ด์ง๋ฅผ ์ด์ด์ ๋ณด์ฌ์ค๋ค. -
์ํฉ 2: ์ฌ์ฉ์๊ฐ ์ฌ์ดํธ ๋ด์์ ํผ๋(feed) ์ธ๋ค์ผ์ ๋ง์ฐ์ค๋ก ๋ถ๋๋ฝ๊ฒ ํด๋ฆญํ ๋ (Soft Navigation)
ํผ๋(feed/page.tsx) ์์์<Link href="/photo/1">ํด๋ฆญ!
๊ฒฝ๋ก๊ฐ ์ด๋ํ๋ ค๋๋ฐ, ์!feedํด๋ ๋ด๋ถ์ ์ ๋ณต ๊ฒฝ์ฐฐ(..)photo๊ฐ "์ก์๋ค ์์ฒญ!" ํ๊ณ ๋์์ฑ๋ค.
๋์์ฑ ์ด ๊ฐ์ง ๋ชจ๋ฌ ๊ป๋ฐ๊ธฐ๋ ์๊น ๊ณจ๋ฑ ์ด๋ก ๋ซ์ด๋@modal๋ณ๋ ฌ ์ฌ๋กฏ ์์น์(์๋น ๋ ์ด์์ ์์๋ฆฌ์ ๋ผ์ด์) ๋ฟ ํ๊ณ ํ์ด๋์จ๋ค!
๊ฒฐ๊ณผ? URL ์ฐฝ์ /photo/1 ์ด๋ผ๊ณ ์์๊ฒ ์ฐํ์์ง๋ง, ํ๋ฉด์ ์๋ ์๋ feed ๋ชฉ๋ก ์์ ๋ชจ๋ฌ(@modal)์ด ๋ ์๋ ๊ถ๊ทน์ ์ํคํ
์ฒ๊ฐ ์์ฑ๋๋ค. ์ฌ์ฉ์๊ฐ ํฐ ๋ค๋ก ๊ฐ๊ธฐ๋ฅผ ๋๋ฅด๋ฉด ๋ชจ๋ฌ๋ง ๊บผ์ง๊ณ ๋ค์ ๊ทผ์์ง(ํผ๋ URL)๋ก ์ ํ๊ทํ๋ค.
๐ layout.tsx ํฉ์ฒด ์ฝ๋
// app/feed/layout.tsx
export default function FeedLayout({
children,
modal // @modal ํด๋ ์์ฒด๊ฐ Props๋ก ๊ฝํ!
}: { children: React.ReactNode, modal: React.ReactNode }) {
return (
<>
{children} {/* ํผ๋ ๋ชฉ๋ก (๋ฐฐ๊ฒฝ์ ์์ํ ๊น๋ ค์์) */}
{modal} {/* ๊ฐ๋ก์ฑ๊ธฐ ๋ ์ด๋. ํ์์ ๋น์ด์๋ค๊ฐ ๋๋ฅด๋ฉด ์ด ์๋ฆฌ์ ํ์
์ด ๋! */}
</>
)
}๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
์๋ฌ ๋ฉ์์ง๊ฐ ๋จ๋ฉด Ctrl+F๋ก ๊ฒ์ํด๋ด.
โ ๋ชจ๋ฌ ํด๋ ์ธํ ์ ๋ค ํ๋๋ฐ๋ ๋น๋๋ ํ์ด์ง ์ด๋ ์ ํฐ์ง๋๋ค (404 Error)
์์ธ: Parallel Route (@ํด๋)๋ฅผ ์ฌ์ด๋๊ณ , ํ์์ ๋ ๋๋ง์ ํ์ํ default.tsx ๋ฅผ ๋ฏธ๋ฐฐ์นํจ.
ํด๊ฒฐ์ฑ
: Next.js ๋ณ๋ ฌ ๋ผ์ฐํธ๋ "ํ๋ฉด์ ๋์์ค URL ๋์์ด ๋ช
ํํ์ง ์์ ๋" ํ์ํ ๋น ๊นกํต ์คํฌ๋ฆฐ ํ์ผ์ ๊ฐ์ํ๋ค. ์ปดํฌ๋ํธ๋ช
default.tsx ๋ฅผ ๋ง๋ค๊ณ ์ฟจํ๊ฒ return null; ํ๋๋ง ์ ์ด์ ๋ฐํ์ํค๋ฉด ๋ง๋ฒ์ฒ๋ผ ์ถฉ๋์ด ์ฌ๋ผ์ง๋ค.
โ (..) ๊ฒฝ๋ก๋ฅผ ํ ๋ฒ ์ผ๋๋ฐ ๊ฐ๋ก์ฑ๊ธฐ๊ฐ ์๋์ ์ ํ๊ณ ๋ณธ ํ์ด์ง๋ก ์๊พธ ํ๊น๋๋ค.
์์ธ: (..) ์งฌ์งฌ ํด๋์ ๊น์ด ๋งค์นญ ์ค๋ฅ.
์๋ฅผ ๋ค์ด ๊ฐ๋ก์ฑ๋ ค๋ ๋์์ด app/movies/[id] ์ธ๋ฐ, ์ ๋ณต ๊ฒฝ์ฐฐ ์์น๊ฐ app/feed/@modal/(..)movies ๋ผ ์น์.
๊ฒฝ์ฐฐ์ "๋ด ๋ถ๋ชจ(feed)๋ ๊ฐ์ ๊ธ์ ๋ฐฉ(movies)" ๋ง ๋์์ฑ ์ ์๋ค! ๋ง์ฝ ๊น์ด๊ฐ ํ๋๋ผ๋ ์ด๊ธ๋๋ค๋ฉด ๊ฐ๋ก์ฑ๊ธฐ๋ ๋ฐ๋ ์กฐ๊ฑด์ ์ถฉ์กฑํ์ง ๋ชปํด ์ ์งํ ๋งํฌ๋ก ํ๊ฒจ๋ฒ๋ฆฐ๋ค. (๊น์ด๊ฐ ํท๊ฐ๋ฆฌ๋ฉด ํด๋ ํธ๋ฆฌ๋ฅผ ์ข
์ด์ ๊ทธ๋ ค๋ณด๋ ๊ฑธ ์์น์ผ๋ก ํ๋ผ!)
๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ๊ธฐ๋ฅ ๊ตฌ๋ถ | ์ฃผ์ ์ญํ ์ค๋ช | ์ค๋ฌด ์ฌ์ฉ ๋น๋ & ์์ |
|---|---|---|
@folder (Parallel) | ํ ํญ ๋ฌธ์ ํ๋ฉด์ ์ฌ๋ฌ ๊ฐ์ "๋ ๋ฆฝ ์์กด ๊ตฌ์ญ"์ผ๋ก ์นผ๋ก ์ชผ๊ฐฌ | ๋์๋ณด๋ 3๋ถํ ๋ทฐ, ๋ก๊ทธ์ธ ๋ค์ด์ผ๋ก๊ทธ ๊ตฌ์ญ ํ๋ณด (๋งค์ฐ ๋์) |
(..) (Intercept) | ์ด๋ํ๋ ค๋ ๋ชฉ์ ์ง์ URL๊ณผ ๋ด์ฉ๋ฌผ์ ๋์์ฑ "ํ์ฌ ์๋ ๋ทฐํฌํธ์ ๋ฎ์ด์" | ์ธ์คํ ์ฌ์ง ํผ๋ ํ๋, ํํฐ๋ ์คํธ ์นด๋ ํด๋ฆญ ์ (๋์) |
| ์ ๋ ๊ฐ ์กฐํฉ ๋ง์ | URL ์ผ์น, ๋ธ๋ผ์ฐ์ ๋ค๋ก ๊ฐ๊ธฐ ๋์, ์ง์ ์ ์(๊ณต์ ) ์ฒ๋ฆฌ๊น์ง ์๋ฒฝ ์ปค๋ฒํ๋ ๋ผ์ฐํ ๋ ๋ฒจ์ ๋ชจ๋ฌ ํ๋ช | ํ๋ก๋ํธ ์ต๊ณ ๋๋ UX ํ์ (Next.js๋ฅผ ๋์ ํ๋ ์ด์ ๊ทธ ์์ฒด!) |
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
ํ์ ๋ชจ๋ฌ์ ๋ง๋ค ๋useState(false)Boolean ์ค์์น์๋ง ์์กดํ๋ ๋จ์ํ ์์ ์ ๋๋ฌ๋ค! ๊ฒฝ๋ก(URL)๋ฅผ ๊ฐ๋ก์ฑ๊ณ ((..)), ๊ทธ ๊ฐ๋ก์ฑ ํ๋ฌผ์ ๋ด ์์ ๋ณ๋ ฌ ๋ถํ(@)๋ก ๋ฐฐ์นํ๋ฉด ์ง์ง ์ฑ ๊ฐ์ ๋ค์ดํฐ๋ธ ํ์ ๋ผ์ฐํ ๋ชจ๋ฌ์ด ํ์ํ๋ค!
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. Parallel Route ์ฌ๋กฏ์ default.tsx๊ฐ ํ์ํ ์ด์ ๋?
โ ์ ๋ต: ํ์ฌ URL์ ์ฌ๋กฏ์ด ๋งค์นญ๋์ง ์์ ๋ ๋ณด์ฌ์ค ๊ธฐ๋ณธ ์ํ๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๐ก ์์ธ ํด์ค: ์๋ก๊ณ ์นจ์ด๋ ์ง์ ์ง์ ์์๋ ๋ชจ๋ฌ ์ฌ๋กฏ์ ๋์ํ๋ URL ์ํ๊ฐ ์์ ์ ์๋ค. default.tsx๊ฐ ์์ผ๋ฉด ์๋์น ์์ 404๋ ๊นจ์ง ๋ ์ด์์์ด ์๊ธด๋ค.
Q2. Intercepting Route์ (.)์ (..)๋ ๋ฌด์์ ๊ธฐ์ค์ผ๋ก ํด์๋ ๊น?
โ ์ ๋ต: ํ์ผ ์์คํ ๊น์ด๊ฐ ์๋๋ผ URL ์ธ๊ทธ๋จผํธ ๋ ๋ฒจ์ ๊ธฐ์ค์ผ๋ก ํด์๋๋ค.
๐ก ์์ธ ํด์ค: ํด๋๊ฐ ๊น์ด ๋ณด์ฌ๋ route group์ด๋ slot์ URL์ ๋ํ๋์ง ์์ ์ ์๋ค. ๊ทธ๋์ ์ค์ ์ฃผ์ ๊ตฌ์กฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ธํฐ์ ํธ ์์น๋ฅผ ํ๋จํด์ผ ํ๋ค.
Q3. ์์ฒ ์ด์ ํ ์คํธ ํ์: ํผ๋์์ ์ฌ์ง์ ๋๋ฅด๋ฉด ๋ชจ๋ฌ, ์ ํญ์ผ๋ก ์ด๋ฉด ์ ์ฒด ํ์ด์ง๊ฐ ๋ ์ผ ํ๋ค. ํ์ํ ๊ตฌ์กฐ๋?
โ ์ ๋ต: ์๋ณธ ์์ธ page์ ํผ๋ ์ปจํ ์คํธ์์ ๊ฐ๋ก์ฑ๋ intercepting modal route๋ฅผ ํจ๊ป ๋๋ค.
๐ก ์์ธ ํด์ค: ๋ชจ๋ฌ์ ์ปจํ ์คํธ๊ฐ ์์ ๋๋ง ๋ชจ๋ฌ์ด์ด์ผ ํ๋ค. ์ง์ ์ ๊ทผ๊น์ง ๊ณ ๋ คํด์ผ ๊ณต์ ๋งํฌ์ ์๋ก๊ณ ์นจ์ด ๊นจ์ง์ง ์๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ๋ชจ๋ฌ๋ URL๊ณผ ํ์คํ ๋ฆฌ๋ฅผ ๊ฐ์ง ํ๋ฉด์ด ๋ ์ ์๋ค๋ ๊ฑธ ๋ฐฐ์ ๋ค.
๐ก "๊ณต์ ๊ฐ๋ฅํ UI๋ ์ํ ๋ณ์๋ณด๋ค ๋ผ์ฐํ ์ผ๋ก ์ค๊ณํด์ผ ์๋ก๊ณ ์นจ๊ณผ ๋ค๋ก๊ฐ๊ธฐ๊ฐ ์์ฐ์ค๋ฝ๋ค."
๋ค์ ๋ชจ๋ฌ ์๊ตฌ์ฌํญ์๋ ๋จผ์ ์ ํญ, ๋ค๋ก๊ฐ๊ธฐ, ์๋ก๊ณ ์นจ ์๋๋ฆฌ์ค๋ฅผ ์ ์ด ๋ณด๊ฒ ๋ค.