inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

172만명의 커뮤니티!! 함께 토론해봐요.

혹시 Next.js의 핸드북 사이트는 없을까요??

해결됨

한 입 크기로 잘라먹는 Next.js

안녕하세요! 지금까지 강의 너무 잘 들었습니다. React와 TypeScript 강의를 듣고 복습겸으로 제공해주신 핸드북 사이트까지 참고하면서 공부하고 있습니다! 혹시나 Next.js의 핸드북 사이트를 제가 못 찾은건지 아니면 없다면 만드실 계획은 없으신지 궁금해서 질문 올려봅니다. 좋은 강의 만들어주셔서 감사합니다.

  • react
  • typescript
  • next.js
Aurora 댓글 2 좋아요 1 조회수 280

modal에 intercept routes를 사용하는 이유?

해결됨

Next + React Query로 SNS 서비스 만들기

안녕하세요 제로초님 modal을 구현하고있는데 궁금증이 생겨 질문드립니다. modal을 구현할 때 intercepting routes를 사용하는 이유가 궁금합니다. 단순히 parallel로만 띄우면 안되는건가요? 아래는 제가 구현한 코드예시입니다. https://github.dev/datoybi/next-playground simple-modal1은 parallel routes만 사용하였고, simple-modal2는 parallel routes + intercepting routes를 사용하였습니다. 두개 다 구현을 해보았는데 새로고침을 해도 둘의 결과값이 같습니다. 무엇때문에 intercepting routes를 사용하는 건가요? 미리 감사합니다!

  • react
  • next.js
foo 댓글 2 좋아요 0 조회수 203

동일한 fetch 경로에 대해 cache 옵션만 변경된다면 어떻게 적용되나요?

해결됨

한 입 크기로 잘라먹는 Next.js

all books 를 가져오는 fetch 에서 한부분은 no-store 로 적용되고 한 부분은 force-cache 로 적용된다고 했을 때 한 부분이 no-store 이기 때문에 force-cache 한 부분도 계속 갱신되어질까요?

  • react
  • typescript
  • next.js
kimgni.dev 댓글 2 좋아요 0 조회수 206

searchParams 및 params 의 사용법이 변경된 것 같습니다.

해결됨

한 입 크기로 잘라먹는 Next.js

기존처럼 사용하면 빌드시 타입 오류가 나와서 따로 type을 Promise 로 만들어 사용해줘야 하는 듯합니다. nextjs 링크도 첨부해두었습니다. https://nextjs.org/docs/app/building-your-application/upgrading/version-15#params--searchparams 참고로 search/page.tsx 에서 searchParams 를 다음과 같이 타입 적용해주었더니 정상 빌드되었습니다. type SearchParams = Promise<{ q?: string }>;

  • react
  • typescript
  • next.js
kimgni.dev 댓글 2 좋아요 0 조회수 811

searchParams, params 를 사용하는 부분이 15에서 변경되었습니다.

해결됨

한 입 크기로 잘라먹는 Next.js

https://nextjs.org/docs/messages/sync-dynamic-apis 에 내용이 변경되었다고 하네요. 혹 어떻게 수정하면 좋을지에 대한 공지가 따로 있을까요?

  • react
  • typescript
  • next.js
kimgni.dev 댓글 2 좋아요 2 조회수 1871

댓글 내용 수정 중 문제 / inputs 리팩토링 중 질문

해결됨

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

안녕하세요~ 포트폴리오 과제하다가 질문 생겨 남겨요 첫번째는 댓글 내용 수정할 때 댓글 내용을 지우다가 다 지워지면 수정 전 원래 댓글 내용으로 도로 바뀌는 문젠데요 댓글부분 리팩토링 끝마치고나서 발견해서 처음엔 뭘 잘못했나? 했는데 계속 보니 그냥 value에서 defaultValue를 대체하여 작성해주던 아래 부분 때문인 것 같더라고요 value={ props.inputs.contents !== "" ? props.inputs.contents : props.el?.contents ?? "" } 수정을 시작할 땐 props.inputs.contents === ""이니까 댓글 내용인 props.el?.contents를 불러와서 띄워주고, 지우는 중엔 state가 빈 문자열이 아니니까 props.inputs.contents대로 표시되다가 댓글 내용을 전부 지우면 props.inputs.contents가 다시 빈 문자열이 되어서 바로 기존 댓글 내용을 띄워버려요 아예 댓글 내용을 지우고 싶어도 지울수가 없게 되어버리는데 어떻게 바꾸면 될까요? 좀 생각해봤는데 감이 안와서요 혹시 나중에 고치는 내용이 나오나요 두번째는 이번 포트폴리오 과제의 댓글부분 리팩토링 진행하다가 궁금했던 건데요 // BoardCommentWrite.container.tsx // ... const onClickWrite = async (): Promise<void> => { // ... // setWriter(""); // setPassword(""); // setContents(""); setInputs( (prev) => Object.fromEntries( Object.keys(prev).map((key) => [key, ""]), ) as typeof prev, ); // ... } 댓글 작성 끝날때 writer, password, contents를 비워줄 수 있도록 빈 문자열로 초기화시켜주던 걸 이런식으로 바꾸었는데 리팩토링 마치고 나서 잘 돌아가긴 했는데요 위 부분을 너무 어거지로 바꾼 것 같아서 좀 그래요... 사실 setInputs({ writer: "", password: "", contents: "" }) 그냥 이렇게 하는게 여기선 가장 좋은 방법인 것 같긴 한데, 더 많은 state들을 묶는 경우도 있다고 생각하면 것도 좀... 그래서요 이번 댓글 부분 코드에서는 setInputs({ writer: "", password: "", contents: "" }) 이렇게 하는게 가장 적당할까요? 그리고 더 많은 걸 초기화해야한다고 치면 어떻게 하는게 좋을까요?

  • react
  • node.js
  • seo
  • graphql
  • next.js
희주 댓글 1 좋아요 0 조회수 156

git stash 관련

미해결

쉬운 용어로 배우는 Git & Github 첫걸음 - 협업까지 마스터하기

git stash -m "임시저장 포인트" 라고 명령어를 실행했는데 영상 강의 내용대로라면 그 코드 작성부분이 사라져야되는데 안사라지더라구요 다른 브랜치로 갔다가 와야되나요? 강의내용

  • git
  • github
ahha 댓글 1 좋아요 0 조회수 120

두개의 차이점

해결됨

Next + React Query로 SNS 서비스 만들기

안녕하세요, 아래 ISR에 대해서 질문드렸었는데, nextjs 에서 2가지 구현 방법이있다고 해서 두가지가 어떤 차이점이 있고 어떻게 활용하는게 좋을지 궁금해서 질문드립니다! 1번 방법 = fetch API 의 revlidate를 활용하여 ISR 구현 import ProductList from "@/component/ProductList"; import { getQueryClient } from "@/component/TanstackQueryOption"; import { getProducts } from "@/fetch/getProducts"; import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query"; import Image from "next/image"; export default async function ProductPage () { const queryClient = getQueryClient(); await queryClient.prefetchQuery({ queryKey:['products'], queryFn: getProducts, }) return ( <> <section className='visual-sec'> <Image src="/visual.png" alt="visual" width={1920} height={300}/> </section> <section className="product-sec"> <h2>상품 리스트</h2> <HydrationBoundary state={dehydrate(queryClient)}> <ProductList /> </HydrationBoundary> </section> </> ) } import { isServer, QueryClient, defaultShouldDehydrateQuery, } from '@tanstack/react-query' function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { staleTime: 10 * 1000, }, dehydrate: { shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === 'pending', }, }, }) } let browserQueryClient: QueryClient | undefined = undefined export function getQueryClient() { if (isServer) { return makeQueryClient() } else { if (!browserQueryClient) browserQueryClient = makeQueryClient() return browserQueryClient } } 'use client' import { getQueryClient } from '@/component/TanstackQueryOption'; import { QueryClientProvider, } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactNode } from 'react' export default function TanstackQueryProvider({ children }: { children: ReactNode }) { const queryClient = getQueryClient() return ( <QueryClientProvider client={queryClient}> {children} <ReactQueryDevtools initialIsOpen={true} /> </QueryClientProvider> ) } export const getProducts = async () => { const res = await fetch(`http://localhost:9090/api/products`, { method: "GET", headers: { "Content-Type": "application/json", }, next: { revalidate: 10, } }); const data = await res.json(); const currentTime = new Date().toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3, hour12: false, }); if (typeof window === "undefined") { console.log('fetch products', 'server', currentTime); console.table(data); } else { console.log('fetch products', 'client', currentTime); console.table(data); } if(!res.ok) { throw new Error("Failed to fetch products"); } return data; } 2번 방법 = export const revalidate 로 시간 설정 후 fetch로 받은 data 값을 바로 렌더링 시키기 import Product from "@/component/Product"; import styles from '@/component/ProductList.module.css'; import Image from "next/image"; export const revalidate = 10; export default async function Product2Page() { const data = await fetch('/api/products'); const products = await data.json(); console.log(revalidate, 'Product2Page'); return ( <> <section className='visual-sec'> <Image src="/visual.png" alt="visual" width={1920} height={300}/> </section> <section className="product-sec"> <h2>상품 리스트</h2> <div className={styles.productList}> {products.map((product: any) => ( <Product key={product.item_no} product={product} /> ))} </div> </section> </> ) } 2개다 ISR로 구현되며, 1번 방법은 Data Cache 캐싱 매커니즘을 활용하고, 2번 방법은 Full Router Cache 캐싱 매커니즘을 활용한다의 차이점으로 생각이 드는데, 이 외에 다른 차이점과 실제 개발을 하면서 선호되는 방식이 따로 있을까요? 해당 페이지만 보았을때는 2번 방법으로 해도 어차피 주기적으로 다시 생성해서 최신 데이터를 반영하면 복잡하게 react-query를 사용하고, Hydration을 하면서 데이터를 동기화할 필요가 있나 싶어서 어떻게 사용해야할지 감이 안잡히네여

  • react
  • next.js
  • react-query
  • next-auth
  • msw
밍끼 댓글 1 좋아요 0 조회수 185

git 파일에 있는 git 프로젝트는 뭔가요?

해결됨

코딩은 실전이다! - Git알못을 위한 깃린이코스(Git, Github 실습위주)

git파일에 git프로젝트를 넣어놓는다고 하셨는데 git프로젝트가 그냥 평소에 진행하는 프로젝트 말씀하시는거 맞나요?

  • git
  • github
  • 버전관리시스템
힘을내자 댓글 2 좋아요 0 조회수 250

prisma db push 소요 시간

해결됨

한 입 크기로 잘라먹는 Next.js

안녕하세요! 이번에 새롭게 강의를 듣기 시작하게 된 sunny라고 합니다! 학습용 DB를 설정하고 있는 상황인데됴! 말씀주신대로 code를 다운로드 받고, 사용 폴더로 이동시킨 후 npm i , Supabase 설정하여 .env 파일에 DATABASE_URL 변수까지 잘 설정해주었습니다. 이후 과정인 npx prisma db push 과정에서 소요시간이 너무 오래 걸려서.. 혹시 제가 확인해볼 수 있는 방법 혹은 예측되는 상황이 있으실까 하여 질문드리게 되었습니다!! ㅠㅠ 혹시 몰라 node_modules 폴더 삭제 후 재실행 했는데도 동일한 현상이 발생하고 있는데.. 제가 확인해볼 수 있는 부분들이 있을까요?? 감사합니다!

  • react
  • typescript
  • next.js
Sunny Sideup 댓글 2 좋아요 0 조회수 161

ISR 테스트 중 궁금점

해결됨

Next + React Query로 SNS 서비스 만들기

// src/components/TanstackQueryOption.ts import { isServer, QueryClient, defaultShouldDehydrateQuery, } from '@tanstack/react-query' function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { staleTime: 15 * 1000, }, dehydrate: { shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === 'pending', }, }, }) } let browserQueryClient: QueryClient | undefined = undefined export function getQueryClient() { if (isServer) { return makeQueryClient() } else { if (!browserQueryClient) browserQueryClient = makeQueryClient() return browserQueryClient } } // src/components/TanstackQueryProvider.tsx 'use client' import { getQueryClient } from '@/component/TanstackQueryOption'; import { QueryClientProvider, } from '@tanstack/react-query' import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { ReactNode } from 'react' export default function TanstackQueryProvider({ children }: { children: ReactNode }) { const queryClient = getQueryClient() return ( <QueryClientProvider client={queryClient}> {children} <ReactQueryDevtools initialIsOpen={process.env.NEXT_PUBLIC_MODE === 'local'} /> </QueryClientProvider> ) } // src/app/layout.tsx import Banner from "@/component/Banner"; import Footer from "@/component/Footer"; import Header from "@/component/Header"; import TanstackQueryProvider from "@/component/TanstackQueryProvider"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "@/app/global.css"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body> <TanstackQueryProvider> <div className='container'> <Banner/> <Header/> <main>{children}</main> <Footer/> </div> </TanstackQueryProvider> </body> </html> ); } // src/app/page.tsx import ProductList from "@/component/ProductList"; import { getQueryClient } from "@/component/TanstackQueryOption"; import { getProducts } from "@/fetch/getProducts"; import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query"; import Image from "next/image"; export default function Page () { const newQueryClient = getQueryClient(); newQueryClient.prefetchQuery({ queryKey:['products'], queryFn: getProducts, }) return ( <> <section className='visual-sec'> <Image src="/visual.png" alt="visual" width={1920} height={300}/> </section> <section className="product-sec"> <h2>상품 리스트</h2> <HydrationBoundary state={dehydrate(newQueryClient)}> <ProductList /> </HydrationBoundary> </section> </> ) }; 'use client' // src/components/ProductList.tsx import Product from "@/component/Product"; import { getProducts } from "@/fetch/getProducts"; import { useQuery, useSuspenseQuery } from "@tanstack/react-query"; import styles from "@/component/ProductList.module.css"; export const ProductList = () => { const {data, isLoading, isFetching} = useSuspenseQuery({queryKey: ['products'], queryFn: getProducts}); console.log(`isLoading: ${isLoading}, isFetching: ${isFetching}`) return ( <div className={styles.productList}> {data?.map((product: any) => ( <Product key={product.item_no} product={product} /> ))} </div> ) }; export default ProductList; // src/components/Product.tsx import Link from "next/link"; import Image from "next/image"; export const Product = ({product} : any) => { return ( <Link href={`/product/${product.item_no}`} prefetch> <Image src={product.detail_image_url} alt={product.item_name} width={500} height={300} /> <h3>{product.item_name}</h3> <span>{product.price}</span> </Link> ) } export default Product; // src/app/product/[id]/page.tsx export default function ProductDetailPage() { return ( <> 상품 상세페에지 </> ) } // src/fetch/getProducts.ts export const getProducts = async () => { const res = await fetch(`http://localhost:9090/api/products`, { method: "GET", headers: { "Content-Type": "application/json", }, next: { revalidate: 10, } }); const currentTime = new Date().toLocaleTimeString(); const data = await res.json(); if (typeof window === "undefined") { console.log('fetch products', 'server', currentTime); console.table(data); } else { console.log('fetch products', 'client', currentTime); console.table(data); } if(!res.ok) { throw new Error("Failed to fetch products"); } return data; } // src/server/server.js import express from "express"; import cors from "cors"; const app = express(); const port = 9090; app.use(cors()); app.use(express.json()); app.get("/api/products", (req, res) => { const currentTime = new Date().toLocaleTimeString(); console.log(`Received request at ${currentTime}`); const products = [ { item_no: 122997, item_name: '상품 1', detail_image_url: 'https://picsum.photos/id/237/500/500', price: 75000, }, { item_no: 768848, item_name: '상품 2', detail_image_url: 'https://picsum.photos/id/238/500/500', price: 42000, }, { item_no: 552913, item_name: '상품 3', detail_image_url: 'https://picsum.photos/id/239/500/500', price: 240000, }, // { // item_no: 1045738, // item_name: '상품 4', // detail_image_url: // 'https://picsum.photos/id/240/500/500', // price: 65000, // }, ]; res.json(products); }); app.listen(port, () => console.log('Server is running')); 안녕하세요, fetch와 tanstackQuery를 사용해서 ISR 동작을 테스트하고있었습니다. 테스트 마다 .next 파일은 지우고 새로 build 하여 run start를 통하여 확인하였습니다. staleTime과 revalidate 의 시간이 서로 상이한데, 동일하게 설정했을때, 시간의 간격을 두었을때의 차이점을 직접 확인하려고 하였는데 어떤점에서 차이가 나는지 보고도 이해가 안가서 질문드립니다. 궁금점 1. staleTime과 revalidate 는 gcTime 처럼 staleTime이 revalidate보다 적은 시간으로 설정을 해야하는지? 그렇다면 그 이유는 gcTime보다 작게 설정하는 이유와 같은지? 가 궁금합니다. 2. server.js에 주석처리해놓은 item을 다시 주석을 해지하면 처음 revaildate의 10초 설정으로 인해 새로고침을해도 아이템은 계속 페이지에서 3개만 노출되고있고, 상품을 클릭해서 이동을 하면서 staleTime의 설정인 15초가 되었을때는 client 요청이 발생하여 아이템이 4개로 잘 노출되고있습니다. 하지만 이 때 새로고침을 하게되면 처음 fetch revalidate로 cache되어있던 데이터인 아이템 3개까지만 노출이 되고 새로고침을 한번 더 진행해야 그때서야 4개로 노출이되는데 클라이언트와 서버 쪽이 서로 싱크가 안맞는거같은데 이러한 문제점이 왜 일어나는지 이해가 잘안됩니다! 3. 확장된 fetch와 tanstackQuery를 어떻게 분리해서 사용해야할까도 많이 고민이 되는데.. queryFn 에 이미 fetch로 만들어둔 함수를 가져와 사용하니 분리라는 개념을 생각하면 안되는걸까요? fetch를 독립적으로 사용하는 경우도있다고하는데 이 경우는 왜 독립적으로 사용하는지 잘모르겠습니다.

  • react
  • next.js
  • react-query
  • next-auth
  • msw
밍끼 댓글 1 좋아요 0 조회수 264

7.2 강 구글 로그인 1 강좌에서 redirectTo 로 설정해도 이동이 안되요.

미해결

Supabase, Next 풀 스택 시작하기 (feat. 슈파베이스 OAuth, nextjs 14)

강좌 잘 보고 있습니다. package.json 버전은 모두 같습니다. 강좌에 있는데로 모두 supabase.com 에서 셋팅을 했습니다. 구글 로그인 코드도 다 정상 작동이 되는데 http://localhost:3000 으로 이동을 하네요. Redirect URLs 에는 http://localhost:3000/auth 로 작성해 둔 상태입니다. ㅠㅠ; "use client"; import useHydrate from "@/hooks/useHydrate"; import { createSupabaseBrowserClient } from "@/lib/client/supabase"; import { Auth } from "@supabase/auth-ui-react"; import { ThemeSupa } from "@supabase/auth-ui-shared"; import { useEffect, useState } from "react"; export default function AuthUI() { const [user, setUser] = useState(); const supabase = createSupabaseBrowserClient(); const isMount = useHydrate(); const getUserInfo = async () => { const result = await supabase.auth.getUser(); console.log(result); }; useEffect(() => { getUserInfo(); }, []); if (!isMount) return null; return ( <section className="w-full"> <div className="mx-auto max-width-[500px]"> <Auth // redirectTo={process.env.NEXT_BUBLIC_AUTH_REDIRECT_TO} redirectTo="http://localhost:3000/auth" supabaseClient={supabase} appearance={{ theme: ThemeSupa, }} onlyThirdPartyProviders providers={["google", "github"]} /> </div> </section> ); }

  • react
  • 클론코딩
  • next.js
  • supabase
댓글 3 좋아요 0 조회수 443

메인페이지 새로고침할때 hydration 오류

미해결

Next + React Query로 SNS 서비스 만들기

로그인하고 홈 메인페이지에서 새로고침하면 오류 뜹니다 PostForm.tsx쪽 TextAreaAutosize 라이브러리 이 부분 주석하면 에러 안뜨는데 라이브러리 문제인지 제가 잘못한건지 모르겠습니다 key값 없다고 하는데 TrendSection.tsx에 {data?.map((trend) => ( <Trend trend={trend} key={trend.tagId} /> ))} 여기 trend.tagId에서 tagId를 못찾는거 같은데 백엔드 문제인가요??? 타입스크립트 Hashtag에 tagId: number 있어요

  • react
  • next.js
  • react-query
  • next-auth
  • msw
br 댓글 2 좋아요 0 조회수 164

서버 성능 문의

해결됨

한 입 크기로 잘라먹는 Next.js

안녕하세요. 강사님 React 로 CSR 개발/배포된 시스템을 운영해본 적이 있는데요. nginx 가 단순히 정적 파일들만 클라이언트로 전달하니 서버 성능을 생각할 필요가 전혀 없었습니다. 그런데 Next.js 강의를 들으면서 SSR/SSG 에 대한 설명을 듣고나니 이 경우에는 서버 성능이 중요할 것 같다는 생각이 드네요. SSR/SSG 는 사용자 요청량이 증가하는만큼 서버 스펙도 확장될 수 있는 고민들이 필요한가요?

  • react
  • typescript
  • next.js
아리마 댓글 2 좋아요 0 조회수 230

smtp 사용

해결됨

[풀스택 완성] Supabase로 웹사이트 3개 클론하기 (Next.js 14)

안녕하세요! 강의 잘 듣고있습니다. 강의를 듣던 중 cannot be used as it is not authorized 라는 오류가 떠서 찾아봤는데 supabase에서 SMTP설정이 필수적으로 정책이 변경된 것 같아요.

  • firebase
  • next.js
  • tailwind-css
  • react-query
  • supabase
단추 댓글 2 좋아요 0 조회수 313

error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403

미해결

쉬운 용어로 배우는 Git & Github 첫걸음 - 협업까지 마스터하기

안녕하세요 말씀하신대로 세 줄을 복사해서 적어넣으면 git push에서 아래와 같은 에러가 납니다. 어떻게 해결하면 좋을까요? C:\Users\Documents\git_practice>git push -u origin main Enumerating objects: 6, done. Counting objects: 100% (6/6), done. Delta compression using up to 20 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (6/6), 541 bytes | 135.00 KiB/s, done. Total 6 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0) error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403 send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpectedly Everything up-to-date 그리고 강사님과 다르게 저는 복사 붙여넣기를 하면 세줄을 한번에 칠 수 없고, 첫줄 + enter + 둘째줄 + enter + 셋째줄 이렇게 쳐지더라구요 어떻게 설정하나요?

  • git
  • github
tjrwns3731 댓글 1 좋아요 0 조회수 1590

tanstack-query prefetchQuery 질문

미해결

Next + React Query로 SNS 서비스 만들기

안녕하세요 강의를 듣는중 추가적으로 tanstack-query를 공부하다가 혼자서 도저히 이해를 할수 없는 부분이 있어서 이 부분에 대해 혹시 조언을 받을수 있을까 싶어 문의드립니다. prefetchQuery가 개인적으로 잘 이해가 안되어서 별도로 프로젝트를 생성하여 기본적인 것부터 다시 공부하고 있었습니다만, tanstack-query 공식사이트에서 권장하던 방법대로 임의적으로 코드를 생성하였더니 router.push()로 다른 페이지에 갔다가(->홈으로[/]) 다시 돌아오는것(->Post페이지(/post))을 반복하다보면 가끔 서버 컴포넌트에 있는 prefetchQuery안의 fetch와 클라이언트 컴포넌트에 있는 useQuery의 fetch가 동시에 실행이 되는 일이 가끔 발생을 해서요. fetch가 이중으로 실행이 되고 있는것 같은데 아무리 코드를 살펴봐도 제가 잘못한 부분을 찾을수가 없어서 조언을 구합니다ㅠ page.tsx import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query'; import Post from './_component/Post'; import getPostRecommends from './_hook/fetch'; export default async function tanstackQuery() { const queryClient = new QueryClient(); await queryClient.prefetchQuery({ queryKey: ['movies'], queryFn: getPostRecommends, }); const dehydratedState = dehydrate(queryClient); return ( <HydrationBoundary state={dehydratedState}> <Post /> </HydrationBoundary> ); } post.tsx 'use client'; import { useQuery } from '@tanstack/react-query'; import getPostRecommends from '../_hook/fetch'; import { useRouter } from 'next/navigation'; export default function TanstackQuery() { const { data } = useQuery({ queryKey: ['movies'], queryFn: getPostRecommends, }); const router = useRouter(); type PostItem = { id: number; title: string; }; return ( <div> <button onClick={() => router.push('/')}>홈으로</button> {data?.map((item: PostItem) => { return ( <div key={item.id}> <h2>{item.title}</h2> </div> ); })} {data?.message} </div> ); } getPostRecommends export default async function getPostRecommends() { if (typeof window === 'undefined') { console.log('서버에서 fetch 실행' + new Date()); } else { console.log('클라이언트에서 fetch 실행' + new Date()); } const response = await fetch('https://jsonplaceholder.typicode.com/posts?_page=1&_limit=10', { cache: 'no-store', }); if (!response.ok) { throw new Error('Failed to fetch data'); } const res = await response.json(); return res; } 기본 provider 설정 'use client'; import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ReactNode } from 'react'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; function makeQueryClient() { return new QueryClient({ defaultOptions: { queries: { staleTime: 6 * 1000, }, }, }); } type Props = { children: ReactNode }; let browserQueryClient: QueryClient | undefined = undefined; function getQueryClient() { if (isServer) { return makeQueryClient(); } else { if (!browserQueryClient) browserQueryClient = makeQueryClient(); return browserQueryClient; } } export default function Providers({ children }: Props) { const queryClient = getQueryClient(); return ( <QueryClientProvider client={queryClient}> {children} <ReactQueryDevtools /> </QueryClientProvider> ); } 서버 콘솔 브라우저 콘솔 위에 캡쳐화면 같이 fetch가 거의 동시간에 발생을 하고 있는 모습입니다. next.js router cache 때문에 30초마다 서버 컴포넌트쪽이 리랜더링 되어서 페이지를 새로고침을 하지 않고 router.push로 다시 페이지에 들어가도 서버 컴포넌트쪽이 다시 실행된다는건 이해를 했는데, 그렇다면 초기 랜더링할때와 똑같이 데이터가 prefetch되어서 클라이언트쪽 useQuery가 실행이 되지 않아야하지 않나요? 왜 두번이나 fetch가 도는건지 아무리 자료를 찾아봐도 잘 모르겠어서 결국 문의드리게 되었습니다ㅠ

  • react
  • next.js
  • react-query
  • next-auth
  • msw
uo f 댓글 2 좋아요 0 조회수 345

상호작용이 있어도 동적인 페이지라면 RSC만 보내나요?

해결됨

한 입 크기로 잘라먹는 Next.js

3.6 네비게이팅 18분 10초 관련해서 질문있습니다. 이전 서버 컴포넌트 강의에서는 상호작용이 없는 페이지는 JS가 없기 때문에 RSC로 HTML을 만들어 보내고, JS 번들은 보내지 않는다고 이해했습니다. 그래서 프로젝트상 /book1/1 페이지는 상호작용이 없는 페이지라 프리페칭 받아도 RSC Payload만 받는게 맞지 않나?라 생각했습니다. 그런데 /book/1 페이지가 동적인 페이지라서 JS 번들을 안 받는다고 말씀하셔서 좀 헷갈립니다. 즉, 말씀하시는 부분이 상호작용이 있는 페이지라도 동적인 페이지라면 RSC만 보내주신다는 말씀인가요??

  • react
  • typescript
  • next.js
mins 댓글 2 좋아요 0 조회수 324

학습 자료 rest-api도 사용 가능한가요?

해결됨

[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스

Swagger 사용 가능한 건지 문의드립니다.

  • react
  • node.js
  • seo
  • graphql
  • next.js
thekrjm 댓글 2 좋아요 0 조회수 150

도커 볼륨이 작동이 안되는것 같아서 질문드립니다!

미해결

따라하며 배우는 도커와 CI환경 [2023.11 업데이트]

안녕하세요 덕분에 도커를 이해하기 시작한 수많은 수강생 중 한명입니다! 다름이 아니라 도커볼륨을 이용한 소스 코드 변경 강의에서 운영체제가 window라서 docker run -it -p 3000:3000 -v /usr/src/app/node_modules -v %cd%:/usr/src/app {이미지 이름} 를 해도 볼륨이 작동하지 않고 $(pwd) 로 바꾸어서 실행을 시켜봐도 실시간으로 수정이 되지 않아서 질문 드립니다!

  • aws
  • docker
  • github
  • ci/cd
  • travis-ci
  • 데이터-엔지니어링
김민규 댓글 2 좋아요 0 조회수 249

인기 태그

인프런 TOP Writers

주간 인기글