Next.js: ๋งˆ์ง€๋ง‰์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ์‚ฌ์šฉ์ž ์ •์˜ ๊ฒฝ๋กœ `post/`id`๊ฐ€ 404๋กœ ๊นœ๋ฐ•์ž…๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2017๋…„ 08์›” 22์ผ  ยท  47์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: vercel/next.js

ํ˜„์žฌ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— 404 ํŽ˜์ด์ง€๋ฅผ ๊นœ๋ฐ•์ด๋Š” post/:id ๊ฒฝ๋กœ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” post/id ํŽ˜์ด์ง€์—์„œ ์•ฑ ์ƒํƒœ๋ฅผ filter ๋กœ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์–•์€ ๋ผ์šฐํŒ…์„ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œ์‹œ๋˜์–ด์•ผ ํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์ด redux ๊ด€๋ จ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

slack ์ฑ„๋„์—์„œ ์ด์— ๋Œ€ํ•ด ๋…ผ์˜ํ•œ ํ›„ ๊ฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ API ํ˜ธ์ถœ์„ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Œ์„ ๋น ๋ฅด๊ฒŒ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด ์ ์–ด๋„ ํ•œ ๋ช…์˜ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๋ˆ„๊ตฌ๋“ ์ง€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ๋‚˜๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์— ๋งŒ๋“ค์—ˆ๋˜ ์ด ๊ฐœ์ธ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜์—์„œ ์ •ํ™•ํžˆ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠน์ • ๊ฒŒ์‹œ๋ฌผ์ด ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ๊นœ๋ฐ•์ด๋Š” 404๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋“  ๊ฒƒ์ด A1์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

next.js blog post err

๋‚ด๊ฐ€ ์–ป์€ ์ด์ ์€ ์—„์ฒญ๋‚˜์ง€๋งŒ ์ด์™€ ๊ฐ™์€ ์‚ฌ์†Œํ•œ ๋ฌธ์ œ๋กœ ์ธํ•ด ์•ฝ๊ฐ„ ์˜์•„ํ•ดํ–ˆ์Šต๋‹ˆ๋‹ค ๐Ÿค” . ์–ด์จŒ๋“  ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ˜„

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋”ฐ๋ผ์„œ ์ด ์งˆ๋ฌธ์— ํ•œ ๋ฒˆ๋งŒ ๋‹ตํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

href => pages ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€์˜ ๊ฒฝ๋กœ + ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด
as => ๋ธŒ๋ผ์šฐ์ € URL ํ‘œ์‹œ์ค„์— ๋ Œ๋”๋ง๋จ

์˜ˆ์‹œ:

/products/:id ๋ผ๋Š” URL์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. pages/product.js ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. Express ๋˜๋Š” ๋‹ค๋ฅธ ์„œ๋ฒ„์— ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” SSR ์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค . ์ด๊ฒƒ์ด ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค ๊ฒƒ์€ URL ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค /products/:id ์— pages/product.js ๋ฐ ์ œ๊ณต id ์˜ ์ผํ™˜์œผ๋กœ query getInitialProps์—์„œ :
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. ํด๋ผ์ด์–ธํŠธ ์ธก ๋ผ์šฐํŒ…์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด next/link ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
<Link href="/product?slug=something" as="/products/something"> 

href ๋ฐ as ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” Next.js๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ธก์˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ธก์— ๋ณด๋‚ด์ง€ ์•Š์œผ๋ฉฐ ํ›„์† ๊ฒฝ๋กœ๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ง€์—ฐ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒฝ๋กœ์˜ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ „์†กํ•˜์—ฌ href ๋ฐ as ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒํ™”ํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ํŒจํ‚ค์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

https://github.com/fridays/next-routes

๋˜ํ•œ ๋ Œ๋”๋ง 404๋Š” ๋” ์ด์ƒ Next.js์˜ ๊ธฐ๋ณธ ๋™์ž‘์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋Œ€์‹  ์˜์—ญ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  47 ๋Œ“๊ธ€

๊ฐ™์€ ๋ฌธ์ œ +1

๋งํฌ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ•ด๋‹น URL์— ์–ด๋–ป๊ฒŒ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๊นŒ? <Link href="/post/1"> ๋˜๋Š” <Link href="/post?id=1" as="/post/1"> ์ž…๋‹ˆ๊นŒ?

ํ˜„์žฌ <Link prefetch href={ /blog/${x.id} } /> ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ณผ๊ฑฐ์— <Link prefetch href={ /blog/${x.id} } as={ /blog/${x.id} } /> ๋ฅผ ์‹œ๋„ํ•˜๊ณ  ๋ฐฉ๊ธˆ ๋‹ค์‹œ ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

href ๋ฐ as ์†Œํ’ˆ์€ ๋™์ผํ•˜์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. href ๋Š” ์‹ค์ œ URL์ž…๋‹ˆ๋‹ค. pages/blog.js ์žˆ๊ณ  id๋ฅผ ์ฟผ๋ฆฌ๋กœ ์ˆ˜์‹ ํ•˜๋ฉด href ๋Š” /blog?id=${x.id} href ์ด์–ด์•ผ ํ•˜๊ณ  as ๋Š” ์ด ๊ฒฝ์šฐ /blog/${x.id} ๊ท€ํ•˜์˜ ์˜ˆ์œ ์‚ฌ์šฉ์ž ์ •์˜ URL์ž…๋‹ˆ๋‹ค.

@sergiodxa ์ด ์ฝ”๋“œ๋ฅผ ์ œ์ž๋ฆฌ์— ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.

<Link href={`/blog?id=${x.id}`} as={`blog/${x.id}`}>
    <a>Read Post</a>
 </Link>

์ด์ œ URL์— id ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค /blog ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

url error gif

@tgrecojs ์ด ์ฝ”๋“œ ์กฐ๊ฐ์ด ๋„์›€์ด

    const href = `/journal?home=${this.state.article.path[0]}&articlePath=${this.state.article.path[1]}&file=${this.state.article.path[2]}`
    const as = `/journal/${this.state.article.path[0]}/${this.state.article.path[1]}/${this.state.article.path[2]}`

https://www.someURL.com/journal/articlePath/morePath/evenMorePath ๋กœ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

๋‚ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹Œ @ugiacoman ๊ฒฝ๋กœ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์ดˆ๊ธฐ ๋ณด๊ณ ์„œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ์ด๋™ํ•˜๊ณ  ์žˆ์ง€๋งŒ ๋ฌธ์ œ๋Š” ์ด์ƒํ•œ 404 ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.

@tgrecojs ์˜ˆ์ œ https://github.com/sergiodxa/next-custom-query (๋ฐฐํฌ: https://next-custom-query.now.sh/blog)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์—์„œ ์„ค๋ช…ํ•œ ๋Œ€๋กœ next/link ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์‹œ ๋กœ๋“œํ•˜์ง€ ์•Š๊ณ  ์‚ฌ์šฉ์ž ์ •์˜ URL์„ 404๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

@sergiodxa ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๋ฐฉ๊ธˆ ํ™•์ธํ•˜๊ณ  ํ˜„์žฌ ping ์ค‘์ธ Google ๋ธ”๋กœ๊ฑฐ API์— ๋Œ€ํ•œ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ๊ฒฝ๋กœ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ์žˆ์ง€๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋กœ๋“œํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ๊ฑฐ๋œ GIF

์ด์ œ ์ด ๋ฌธ์ œ๋Š” ๋‚ด ์ฒซ ๋ฒˆ์งธ ์˜๊ฒฌ์— ํ‘œ์‹œ๋œ ๊ฒƒ๊ณผ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ์•ฝ๊ฐ„ ๋‹ค๋ฅด๊ฒŒ ์žฌํ˜„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฒ˜์Œ ์ด ๋ฌธ์ œ์— ๋ถ€๋”ช์ณค์„ ๋•Œ ๋‚˜๋Š” redux๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ๊ฐ€ post/id ํŽ˜์ด์ง€๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๊ท€ํ•˜๊ฐ€ ๋งŒ๋“  ์•ฑ์— ํ†ตํ•ฉํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ ๊ฒŒ์‹œ๋ฌผ์— ๋Œ€ํ•ด ์ƒˆ๋กœ์šด API ํ˜ธ์ถœ์„ ํ†ตํ•ฉํ•˜๊ฒŒ ๋˜์—ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Œ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋ถ„๊ธฐํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜ฌ ์—ฌ๊ธฐ์—์„œ ๋‚ด ์ฝ”๋“œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -> https://github.com/tgrecojs/next-custom-query-async . ๋‹น์‹ ์˜ ์ƒ๊ฐ์„ ์•Œ๋ ค์ฃผ์„ธ์š”! ๐Ÿ˜„

ํ”„๋กœ์ ํŠธ๋ฅผ ํฌํฌํ•  ๋•Œ usageLimits ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ๊ทธ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ™•์ธ. ๋‚ด์ผ ์กฐ๊ธˆ ๋” ์กฐ์‚ฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค๋งŒ, ์ œ๊ฐ€ ์ด์Šˆ๋ฅผ ์—ด์—ˆ์„ ๋•Œ ์˜ฌ๋ฆฐ ์˜์ƒ์ด /blog/:id ๋ผ์šฐํŠธ์—์„œ API ์š”์ฒญ์„ ํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋Š” ์ ์„ ์ง€์ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  /blog ๊ฒฝ๋กœ ์ „ํ™˜์—์„œ ์ˆ˜์ง‘๋œ ๋‚ด redux ์ €์žฅ์†Œ์˜ ๊ฒŒ์‹œ๋ฌผ์„ ํ•„ํ„ฐ๋งํ•˜๋ฏ€๋กœ ์ด ๋ฌธ์ œ์— ๋„๋‹ฌํ•œ ์ด์œ ๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด redux-store์™€ ๊ด€๋ จ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๋ฒˆ์งธ API ํ˜ธ์ถœ( blog/:id ๋‚ด๋ถ€์—์„œ ์ˆ˜ํ–‰๋จ)๋งŒ ๊ตฌํ˜„ํ–ˆ์ง€๋งŒ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์ง€๋งŒ ๋‚ด ์ฝ”๋“œ์˜ ํ˜„์žฌ ๊ตฌํ˜„์€ ์•„๋‹™๋‹ˆ๋‹ค.

๋™์ผํ•œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ž๋งˆ์ž API ํ˜ธ์ถœ์„ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉ(Inside /blog )ํ•œ ๋‹ค์Œ id๋กœ ํ•„์š”ํ•œ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ํ•„ํ„ฐ๋งํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋‹ค์‹œ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

@sergiodxa ์‚ฌ์šฉ๋Ÿ‰ ์ œํ•œ์ด ์•„๋‹˜์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. id ์ด ํ•ญ์ƒ /blog/:id ๊ฒฝ๋กœ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ „๋‹ฌ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹˜์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค... ์ˆ˜์‹  ์ค‘์ธ 400 ์˜ค๋ฅ˜๋ฅผ ์ฒจ๋ถ€ํ–ˆ์Šต๋‹ˆ๋‹ค. id ์ด ์ •์˜๋˜์ง€ ์•Š์•˜์œผ๋ฉฐ (์ด ๊ฒฝ์šฐ) ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์— ๋‹ค๋ฅธ ๋™์˜์ƒ์„ ํฌํ•จ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ์™œ ์ด๋Ÿฐ ์‹์œผ๋กœ ํ–‰๋™ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์ƒ๊ฐ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ด ์ €์žฅ์†Œ ๋‚ด์—์„œ <Link /> ํƒœ๊ทธ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜ฌ .

next-custom-query err

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ์œ„์˜ ๋น„๋””์˜ค๋Š” https://github.com/sergiodxa/next-custom-query์—์„œ ์ƒ์„ฑํ•œ ์˜ˆ์ œ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋‚ด์—์„œ ์˜ค๋ฅ˜๋ฅผ ์žฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž ์ง€์ • ๊ฒฝ๋กœ ์—†์ด๋„ 404๊ฐ€ ๊นœ๋ฐ•์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ pages/ orders.js ๋ผ๋Š” ํŒŒ์ผ๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

import { Component } from 'react';

class Orders extends Component {
  render() {
    return (
      <div>
        <h2>My Orders</h2>
      </div>
    )
  }
}

export default Orders;

๊ฝค ์ค‘์š”ํ•œ ๋ฌธ์ œ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ๐Ÿ˜‚

๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๋ณต์ œํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ๊ณ  URL ๋์— ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— /orders ๋กœ ์ด๋™ํ•˜๋ฉด 404๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š์ง€๋งŒ /orders/ ๋กœ ์ด๋™ํ•˜๋ฉด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. 404๊ฐ€ ์ฝ˜ํ…์ธ  ์ „์— ๊นœ๋ฐ•์ด๋ฉฐ ์ด๋Š” ๊ฐœ๋ฐœ ๋ฐ ํ”„๋กœ๋•์…˜ ๋ชจ๋“œ์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์•„ ์ด๊ฒƒ์€ ์•Œ๋ ค์ง„ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. https://github.com/zeit/next.js/issues/1189

๋‚ด ๋ฌธ์ œ๋Š” ํ›„ํ–‰ ์Šฌ๋ž˜์‹œ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ๋‹ค๋ฅธ ๊ฒƒ์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งž๋‚˜์š”?

๋‚ด ์ฝ”๋“œ๋ฅผ ์•ฝ๊ฐ„ ๋ณ€๊ฒฝํ–ˆ์œผ๋ฉฐ prefetch ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„์ „๊ณผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฒ„์ „์˜ 2๊ฐ€์ง€ ๋ฒ„์ „์„ ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‘ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋ชจ๋‘ ์ด์ œ post/:id ๊ฒฝ๋กœ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

ํ”„๋ฆฌํŽ˜์น˜ ์‚ฌ์šฉ - https://tgrecojs-hltqztsjpx.now.sh/

๋งํฌ์—์„œ prefetch ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด์ œ ๊ฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์— ๋Œ€ํ•œ ๋งํฌ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ Page Does Not Exist Error ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜์—์„œ ๋‚ด ์ฝ˜์†”์˜ ์Šคํฌ๋ฆฐ์ƒท์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

screen shot 2017-08-26 at 1 06 25 pm

ํ”„๋ฆฌํŽ˜์น˜ ์—†์ด - https://tgrecojs-qzpspdjrin.now.sh/

ํ”„๋ฆฌํŽ˜์น˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์œ„์— ํ‘œ์‹œ๋œ ์˜ค๋ฅ˜๋Š” ๋” ์ด์ƒ ์กด์žฌํ•˜์ง€ ์•Š์ง€๋งŒ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ์ฒ˜์Œ ๊ฒŒ์‹œํ–ˆ์„ ๋•Œ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ฐ post/:id ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ๋ธ”๋กœ๊ทธ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ดํ›„๋กœ ๊ฐœ๋ณ„ ๊ฒŒ์‹œ๋ฌผ์ด ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ์„ธ์š”. ๐Ÿค”

@tgrecojs ๊ฑฐ์˜ ๋˜‘๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. prefetch ๋Š” ๋‚ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์–ป๋Š” ํ–‰๋™์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์‚ฌ์šฉ์ž๊ฐ€ ๋งํฌ๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  • 404 ์˜ค๋ฅ˜๊ฐ€ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค
  • ํŽ˜์ด์ง€๊ฐ€ ์ƒˆ๋กœ๊ณ ์นจ(??)
  • ๋‹ค์‹œ ๋กœ๋“œํ•˜๋ฉด ์˜ฌ๋ฐ”๋ฅธ ํŽ˜์ด์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค!

์ถ”๊ฐ€ ์ •๋ณด: ๋…ธ๋“œ v6.10.3 , Chrome Version 60.0.3112.90

ํŽธ์ง‘: https://datahub.now.sh/ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ฑ์„ ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋™์ž‘์ด ์žˆ๋Š” ๋งํฌ๋Š” /@[username]์ž…๋‹ˆ๋‹ค.

@Theo- ๊ทธ ๋‹ค์Œ/๋งํฌ์˜ href๋Š” user?id=1 /user?id=1 ๋Œ€์‹  /current/path/user?id=1 ๋Œ€์‹  /user?id=1 ,ํ•˜์ง€๋งŒ ํŽ˜์ด์ง€๊ฐ€ ๋‹ค์‹œ๋กœ๋“œ ํ•  ๋•Œ ์„œ๋ฒ„์˜ ๋ Œ๋”๋ง ๋•Œ๋ฌธ์— ๊ทธ๊ฒƒ์„ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@sergiodxa ํ•ด๋ƒˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@tgrecojs ๋™์ผํ•œ ๊นœ๋ฐ•์ด๋Š” 404 ๋™์ž‘์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ œ ๊ฒฝ์šฐ์—๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์žˆ๋Š” ๋งํฌ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ 404์ด์ง€๋งŒ ์„œ๋ฒ„์—์„œ๋Š” ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. https://medium.com/the-tech-bench/next-js-environment-variables-d2f6ea1a1dca๋กœ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค

@andreaskeller ํ , ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!!! ์ €๋Š” ํ˜„์žฌ ์•ฑ์ด ์„œ๋ฒ„ ๋ Œ๋”์ผ ๋•Œ๋งŒ .env ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. dotenv๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CSR ๋˜๋Š” SSR์ธ์ง€ ํ™•์ธํ•˜๋Š” ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. SSR์ธ ๊ฒฝ์šฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์„ ๊ธฐ๋กํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ, ์ด๋ฏธ ๋ณ€์ˆ˜๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด BLOGGER_API_KEY ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” HOC๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด /blog ๊ฒฝ๋กœ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ํ•ด๋‹น ์˜ˆ์ œ์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.... ๋‚ด /post/:id s ํ•˜์œ„ ํŽ˜์ด์ง€๋Š” ์•„๋ฌด ๊ฒƒ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ ๋ณ€์ˆ˜.

์กฐ๊ธˆ ๋” ํŒŒํ—ค์น  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๋ฐ”๋ผ๊ฑด๋Œ€ ๋‚˜๋Š” ๊ทธ๊ฒƒ์˜ ๋ฐ”๋‹ฅ์— ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์—ฌ๊ธฐ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด์„œ๋„ ์ž‘์—…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค https://github.com/zeit/next.js/issues/1189

์ง€๊ธˆ ๋‹ซ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•ต์‹ฌ์ ์œผ๋กœ ํ•ด์•ผ ํ•  ์ผ์ด ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

@sergiodxa ์–ด์ ฏ๋ฐค ๋Œ€ํ™”์— ๋”ฐ๋ผ ๋‚ด ๋ฌธ์ œ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” lil barebones ์•ฑ์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”! ๋‚˜๋Š” ๊ทธ๊ฒƒ๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“  ๊ฒƒ์— ๋Œ€ํ•ด ๋‹น์‹ ์„ ๊ธฐ์˜๊ฒŒ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค! ๐Ÿ˜„

๊ณ ์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž ์ •์˜ ๊ฒฝ๋กœ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. /pages/pipeline.tsx๊ฐ€ ์žˆ๊ณ  /pipeline url์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š” ๋™์•ˆ /pipeline/์ด 404๋กœ ๊นœ๋ฐ•์ด๊ณ  "getInitialProps"๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๋‚ด ํŽ˜์ด์ง€๋ฅผ ๋กœ๋“œํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค(์„œ๋ฒ„์—์„œ 404์˜€๊ธฐ ๋•Œ๋ฌธ์—!).

@ex3ndr ์ด๊ฒƒ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ• ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด์„œ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
https://github.com/zeit/next.js#disabling -file-system-routing

๋‹ค์Œ ๊ธฐํšŒ๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๊ฒฝ๋กœ๊ฐ€ ์ ์ค‘๋˜๊ธฐ ์ „์— ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ํ•ด์ œํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@moaxaca ๋‚ด ์ž์‹ ์˜ ์‚ฌ์šฉ์ž ์ง€์ • ๋ผ์šฐํŒ…์ด ํ•„์š”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ๋‹ค์Œ ํ•ญ๋ชฉ์„ ์™„์ „ํžˆ ๋น„ํ™œ์„ฑํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €๋„ ์ด ๋ฌธ์ œ๋ฅผ ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. @moaxaca ์˜ ์†”๋ฃจ์…˜์ด ์ €์—๊ฒŒ ํšจ๊ณผ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. Link ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ๋Œ€์‹  ๋‹ค์Œ ๊ฒฝ๋กœ์˜ pushRoute ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์˜ˆ:

import { connect } from 'react-redux'
import { Router } from '../routes'

// ----

export default class Link extends React.Component {

  handleClick(evt, url) {
    evt.preventDefault()    
    Router.pushRoute(url)
  }

  render() {
    const { url, title } = this.props

    return (
      <a href={ url} onClick={ (evt) => this.handleClick(evt, url) }>{title}</a>
    )
  }

}

์ด๊ฒƒ์€ ์•Œ์•„๋‚ด๋Š” ๊ฒƒ ๊ฐ™๊ณ  ์˜ค๋ฅ˜ ๊นœ๋ฐ•์ž„์ด ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๋น ๋ฅธ ๋Š๋‚Œ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ ์ €์žฅ

๋‚˜๋Š” ์ „์— ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„ ์„ค์ •์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ํŽ˜์ด์ง€๋กœ ๋กœ๋“œํ•˜๊ธฐ ์ „์— 404 ํŽ˜์ด์ง€๊ฐ€ ๊นœ๋ฐ•์ด๋Š” ์ด์œ ๋Š” ๋‚ด ํŽ˜์ด์ง€ ์Šฌ๋Ÿฌ๊ทธ๊ฐ€ ๋‚ด ๊ตฌ์„ฑ ์š”์†Œ ์ด๋ฆ„๊ณผ ์ผ์น˜ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋ฉด 404๊ฐ€ ๊นœ๋ฐ•์ž…๋‹ˆ๋‹ค.

server.get('/search', (req, res) => {
    const actualPage = '/search_results'
    const queryParams = { filters: req.query }
    app.render(req, res, actualPage, queryParams)
  })

/search ์™€ ๋‚ด actualPage ๊ฐ’์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ ๋ฐ actualPage ๊ฐ’์˜ ์ด๋ฆ„์„ search ๋‚ด ํŽ˜์ด์ง€ ์Šฌ๋Ÿฌ๊ทธ์™€ ๋™์ผํ•˜๊ฒŒ ๋ฐ”๊พธ๋ฉด 404๋ฒˆ์˜ ๊นœ๋ฐ•์ž„์ด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์•„๋ž˜ ์ž‘์—… ์ฝ”๋“œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

server.get('/search', (req, res) => {
    const actualPage = '/search'
    const queryParams = { filters: req.query }
    app.render(req, res, actualPage, queryParams)
  })

์ด์ œ ๋‘˜ ๋‹ค /search ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ์˜์–ด ์นœ๊ตฌ๋“ค์—๊ฒŒ ๋ฏธ์•ˆํ•ฉ๋‹ˆ๋‹ค.

next.config.js useFileSystemPublicRoutes ์†์„ฑ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. false์ด๋ฉด server.js์˜ ๋ชจ๋“  ๊ฒฝ๋กœ ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ง์ ‘ ์ •์˜ํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ๊นœ๋ฐ•์ด๋Š” 404๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

๋งํฌ

<Link href="/somepage?id=value" as="/somepage/value">

์„œ๋ฒ„ ์ธก

server.get("/somepage/:id", (req, res) => {
  return app.render(req, res, "/maps", { id: req.params.id })
})

ํŽ˜์ด์ง€์˜ ์•ก์„ธ์Šค ๋งค๊ฐœ๋ณ€์ˆ˜

const { id } = this.props.url.query

๋”ฐ๋ผ์„œ ์ด ์งˆ๋ฌธ์— ํ•œ ๋ฒˆ๋งŒ ๋‹ตํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

href => pages ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€์˜ ๊ฒฝ๋กœ + ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด
as => ๋ธŒ๋ผ์šฐ์ € URL ํ‘œ์‹œ์ค„์— ๋ Œ๋”๋ง๋จ

์˜ˆ์‹œ:

/products/:id ๋ผ๋Š” URL์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. pages/product.js ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. Express ๋˜๋Š” ๋‹ค๋ฅธ ์„œ๋ฒ„์— ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” SSR ์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค . ์ด๊ฒƒ์ด ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค ๊ฒƒ์€ URL ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค /products/:id ์— pages/product.js ๋ฐ ์ œ๊ณต id ์˜ ์ผํ™˜์œผ๋กœ query getInitialProps์—์„œ :
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. ํด๋ผ์ด์–ธํŠธ ์ธก ๋ผ์šฐํŒ…์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด next/link ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
<Link href="/product?slug=something" as="/products/something"> 

href ๋ฐ as ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” Next.js๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ธก์˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ธก์— ๋ณด๋‚ด์ง€ ์•Š์œผ๋ฉฐ ํ›„์† ๊ฒฝ๋กœ๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ง€์—ฐ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒฝ๋กœ์˜ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ „์†กํ•˜์—ฌ href ๋ฐ as ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒํ™”ํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ํŒจํ‚ค์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

https://github.com/fridays/next-routes

๋˜ํ•œ ๋ Œ๋”๋ง 404๋Š” ๋” ์ด์ƒ Next.js์˜ ๊ธฐ๋ณธ ๋™์ž‘์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋Œ€์‹  ์˜์—ญ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

Tim, ์™„์ „ํžˆ ๋ช…ํ™•ํ•œ ์„ค๋ช… ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

@timneutkens
๋งํฌ์˜ href ๋ฐ as. ๋‚˜๋Š” ๋‹ค๋ฅธ ๊ฐ’์„ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. href ์˜ ๊ฐ’๊ณผ as ์˜ ๊ฐ’์„ ์–ด๋–ป๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
๋‚˜๋Š” ๋ฐ๋ชจ๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค. req.params.slug ๊ฐ€ as ์˜ ๊ฐ’์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด href ์˜ ๊ฐ’์„ ์–ด๋–ป๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@timneutkens ๋‹น์‹ ์€ ์ €๋ฅผ ๊ตฌํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค ๋‚ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ๊ณ ํ†ต์Šค๋Ÿฝ๊ฒŒ ๊ฒ€์ƒ‰ํ–ˆ๋Š”์ง€ ๋ชจ๋ฆ…๋‹ˆ๋‹ค :D

๋งํฌ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ•ด๋‹น URL์— ์–ด๋–ป๊ฒŒ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๊นŒ? <Link href="/post/1"> ๋˜๋Š” <Link href="/post?id=1" as="/post/1"> ์ž…๋‹ˆ๊นŒ?

์ด๊ฒƒ์„ ๋ฌธ์„œ์— ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. :๊ธฐ์จ:

@sergiodxa

๋”ฐ๋ผ์„œ ์ด ์งˆ๋ฌธ์— ํ•œ ๋ฒˆ๋งŒ ๋‹ตํ•˜์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

href => pages ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€์˜ ๊ฒฝ๋กœ + ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด
as => ๋ธŒ๋ผ์šฐ์ € URL ํ‘œ์‹œ์ค„์— ๋ Œ๋”๋ง๋จ

์˜ˆ์‹œ:

/products/:id ๋ผ๋Š” URL์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. pages/product.js ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
export default class extends React.Component {
  static async getInitialProps({query}) {
    console.log('SLUG', query.slug)
    return {}
  }
  render() {
    return <h1>The product page</h1>
  }
}
  1. Express ๋˜๋Š” ๋‹ค๋ฅธ ์„œ๋ฒ„์— ๊ฒฝ๋กœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ SSR์˜ ๊ฒฝ์šฐ _only_์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค ๊ฒƒ์€ URL ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค /products/:id ์— pages/product.js ๋ฐ ์ œ๊ณต id ์˜ ์ผํ™˜์œผ๋กœ query getInitialProps์—์„œ :
server.get("/products/:slug", (req, res) => {
  return app.render(req, res, "/product", { slug: req.params.slug })
})
  1. ํด๋ผ์ด์–ธํŠธ ์ธก ๋ผ์šฐํŒ…์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด next/link ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
<Link href="/product?slug=something" as="/products/something"> 

href ๋ฐ as ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” Next.js๊ฐ€ ํด๋ผ์ด์–ธํŠธ ์ธก์˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ธก์— ๋ณด๋‚ด์ง€ ์•Š์œผ๋ฉฐ ํ›„์† ๊ฒฝ๋กœ๋Š” ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ง€์—ฐ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒฝ๋กœ์˜ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋ฅผ ์ „์†กํ•˜์—ฌ href ๋ฐ as ๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ถ”์ƒํ™”ํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ ํŒจํ‚ค์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

https://github.com/fridays/next-routes

๋˜ํ•œ ๋ Œ๋”๋ง 404๋Š” ๋” ์ด์ƒ Next.js์˜ ๊ธฐ๋ณธ ๋™์ž‘์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋Œ€์‹  ์˜์—ญ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ชฉ๋ก ํ—ค๋” ๋ฐฐ์—ด๋งŒ ์žˆ์œผ๋ฉด ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
[ home: '/home', about:'/about', post: 'post/:post_id?' ] .
๋‹ค์Œ์—๋Š” as's ๊ฐ’๋งŒ ์–ป์„ ์ˆ˜ ์žˆ์ง€๋งŒ href ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ณ ๋งˆ์›Œ

๋‹น์‹ ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ router.push() ๋Œ€์‹  <Link> ์–ด๋–ป๊ฒŒ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค?

@justinmchase ๋ฐฉ๊ธˆ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์ง ํ™•์ธํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋‹ค์Œ ๋ผ์šฐํ„ฐ ์„ค๋ช…์„œ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.

Router.push(url, as, options) ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Router.push('/post/[pid]', '/post/abc')

@timneutkens ZEIT.now์—์„œ next.js ์•ฑ์„ ํ˜ธ์ŠคํŒ…ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์ด ์—†์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋‚ด ์˜ˆ์œ URL์„ ์žƒ์–ด๋ฒ„๋ฆฌ๊ณ  param=value&param=value ๋งŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? Sergio๊ฐ€ ๊ฒŒ์‹œํ•œ ์˜ˆ ๋Š” ์œ ๋งํ•ด ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์  ๊ฒฝ๋กœ๋ฅผ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์ด์ œ ๋‹จ์ผ ํŽ˜์ด์ง€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์†์ƒ๋˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋งํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

<Link href={`/integration?slug=${slug}`} as={`/integration/${slug}`}>

์ด์ œ http://localhost :8080/_next/static/development/pages/integration.js๊ฐ€ 404 not found๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

@timneutkens ZEIT.now์—์„œ next.js ์•ฑ์„ ํ˜ธ์ŠคํŒ…ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์ด ์—†์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋‚ด ์˜ˆ์œ URL์„ ์žƒ์–ด๋ฒ„๋ฆฌ๊ณ  param=value&param=value ๋งŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ ์•„๋‹ˆ๋ฉด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? Sergio๊ฐ€ ๊ฒŒ์‹œํ•œ ์˜ˆ ๋Š” ์œ ๋งํ•ด ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋™์  ๊ฒฝ๋กœ์—๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค( https://nextjs.org/docs/routing/dynamic-routes ์ฐธ์กฐ).

@timneutkens ๋น ๋ฅธ ํšŒ์‹ ์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋™์  ๊ฒฝ๋กœ๋ฅผ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋‹จ์ผ ํŽ˜์ด์ง€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค(์—…๋ฐ์ดํŠธ ์ฐธ์กฐ). ๊ฐœ๋ฐœ ๋ชจ๋“œ์˜ ๋‚ด package.json ์Šคํฌ๋ฆฝํŠธ์—์„œ next ํ•˜์—ฌ ๋กœ์ปฌ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ๊ฐ€ ์žˆ๋Š” pages/integration/index.js ๋ฐ ๋‹ค์Œ๊ณผ ํ•จ๊ป˜ pages/integration/[slug].js ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

import ProductDetailPage from './index'
export default ProductDetailPage

๊ท€ํ•˜๊ฐ€ ์„ค๋ช…ํ•œ ๊ฒƒ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€ pages/integration/[slug].js

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํฌํ•ฉ๋‹ˆ๋‹ค.

<Link href={`/integration/[slug]`} as={`/integration/${slug}`}>

์•ˆ๋…•ํ•˜์„ธ์š” ์ œ๊ฐ€ ์ด๊ฑธ ์™œ ๋ถ€๋ฅผ์ง€ ์—ฌ์ญค๋ด๋„ ๋ ๊นŒ์š”?

Router.push( '/about?id=1234','/about')

id๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์—†๊ณ  ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํŽ˜์ด์ง€๋Š” [...index].js์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋‹ค์ค‘ ๋™์  catch all ํŽ˜์ด์ง€์ด์ง€๋งŒ ๋™์  ํŽ˜์ด์ง€๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด car.js ์˜ˆ์ œ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Vehicle.js๋กœ ํ‘ธ์‹œ..
๋‚ด ๋™์  catch all ํŽ˜์ด์ง€๋กœ ํ‘ธ์‹œํ•œ ๋‹ค์Œ ID๋ฅผ ๋งˆ์Šคํฌํ•˜๊ณ  ์—ฌ์ „ํžˆ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰