묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
async 제거(영상내용) 후 searchParams 오류
안녕하세요 강사님.해당 강의 8:31 초 내용을 저장했을때 아래와 같은 오류가 나왔습니다. 언뜻 봤을 때는 await를 적용하지 않아서 라는 것으로 해석이 되는데, 영상 초반에 비동기 컴포넌트를 따로 빼내어 사용하기로 하여 async를 제거하였는데 아래와 같은 오류가 나오지만 화면은 의도된 바와 같이 동작을 합니다. 강의 영상 및 자료와 동일하게 진행하였습니다 강사님의 강의자료에서 코드를 복사하여 BookListSkeleton만 제거 수정을 하여 테스트 시 위와 같은 오류가 동일하게 발생합니다.https://github.com/winterlood/onebite-next/blob/main/section06/chapter05/src/app/(with-searchbar)/search/page.tsx 영상에서는 저와 같은 오류가 발생하지 않는 것 같은데, 이런 오류가 발생하면 빌드가 안되는데... 확인 부탁드립니다.
-
미해결한 입 크기로 잘라먹는 Next.js(v15)
revalidatePath 오류
강의 처럼 revalidatePath(`book/${bookId}`)를 해서 리뷰를 작성하고 작성하기 버튼을 눌러서 페이지를 재검증하는 상황revalidatePath는 퍼지를 통해서 캐시가 모두 삭제되는 것이 이해함빌드 후, 프로젝트 모드로 실행리뷰를 작성하고 리뷰를 누르는 상황작성한게 보이긴 하지만 두번째꺼가 ㅋㅋㅇㄹㅋ 였는데 아래와 같이 바뀝니다.또한 새로고침을 누르면 다시 원상태로 돌아갑니다.아래와 같이 새로고침을 누르면 퍼지 후에 book/1 페이지는 다이나믹으로 작동해야되는데 하지 않는 것으로 파악됩니다. prisma studio를 확인해보면 데이터는 들어갔습니다.그런데 npm run dev로 하면 잘 됩니다...왜 그런 걸까요??
-
미해결Next + React Query로 SNS 서비스 만들기
트렌드섹션 질문
회원가입 후 바로 로그인 된 상태에서 home으로 가면 트렌드를 가져올 수 없다고 뜨는데 혹시 로그인 후 로그아웃버튼이 뜨지 않았던 상황과 비슷한 상황인가요 ? 로그아웃 버튼처럼 prop으로 session정보를 넘겨줘야 하는건지 궁금합니다
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
에러 핸들링 오류
인덱스 페이지를 오류를 발생시키는 상황입니다.과정에서 error.tsx 파일을 (with-searchbar) 폴더안에 만들었습니다.데이터를 불러오는 서버를 끄로 인덱스 페이지를 새로고침하면 Footer에서 오류가 발생합니다.글로벌 레이아웃에 Footer 컴포넌트를 불러와서 이런 오류가 발생하는 걸까요?
-
해결됨[코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스
'섹션 06-01 동기/비동기' 강의
[중급] 웹 프론트엔드 부트캠프 강의중에서'섹션 06-01 동기/비동기' 강의는 강의영상이 없다고 뜨는데 강의영상이 없는게 맞는건가요??
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
esbuild 설치 오류
안녕하세요 😀 강의 중 프로젝트 1 에서는 npm install 이 정상적으로 진행되었으나, 이번 useEffect 실습을 위해 강의내용처럼 새폴더 생성 후 npm i를 하니 아래와 같은 오류가 발생합니다.구글링해서 여러 방법을 시도했으나 해결되지 않아 어떻게 해야할 지 질문드립니다 😥
-
미해결따라하며 배우는 리액트 네이티브 기초
React-Native-Cli 컴파일 오류
이런 오류가 계속뜨네요...캐시도 지웠다 다시 깔아봤고 종속성들도 다시 설치했는데 계속 오류가 뜹니다. 혹시 해결방법이 어떻게 될까요?!
-
미해결한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
리스트의 검색 기능에 대한 질문입니다!
안녕하십니까! 해당 강의를 열심히 들으며 리엑트를 공부하고 있는 대학생입니다. 듣다가 막히는 부분이 생겨 질문을 하게 되었습니다.List.jsx 에서 검색 기능을 구현하는 부분에서 궁금한 것이 생겼습니다. 강의에서는 아래 코드처럼 getFilteredData 함수를 작성하셨습니다.const getFilteredData = () => { if(search === ""){ return todos; } return todos.filter((todo) => { todo.content.toLowerCase().includes(search.toLowerCase()) }); };하지만 저는 아무리 오타를 점검하고 수정해도 기능이 구현이 되지 않아서 답답한 마음에 GPT를 통해 질문을 했는데 위 함수에서 return이 빠져있다고 수정해주었습니다. 아래는 GPT가 수정해준 코드입니다.const getFilteredData = () => { if(search === ""){ return todos; } return todos.filter((todo) => { return todo.content.toLowerCase().includes(search.toLowerCase()) }); };위 코드를 보시면 filter() 안에 화살표 함수 내부에 return이 추가된 것을 볼 수 있습니다. 그리고 실제로 이렇게 수정을 하고서야 제 기능이 정상적으로 작동이 되었습니다. 왜 이런 결과가 생겼는지 궁금합니다. 제가 뭘 잘못했길래 강의와는 다르게 return을 추가해야만 기능이 제대로 구현이 되는지 여쭤보고 싶습니다. 혹시나 제가 찾지 못하는 오타가 있을까 해서 강의와 똑같이 따라한 전체 List.jsx 코드도 함께 첨부하겠습니다. 감사합니다!import './List.css'; import TodoItem from "./TodoItem"; import {useState} from "react"; const List = ({todos}) => { const [search, setSearch] = useState(""); const onChangeSearch = (e) => { setSearch(e.target.value); }; const getFilteredData = () => { if(search === ""){ return todos; } return todos.filter((todo) => { todo.content.toLowerCase().includes(search.toLowerCase()) }); }; const filteredTodos = getFilteredData(); return <div className="List"> <h4>Todo List 🌱</h4> <input value={search} onChange={onChangeSearch} placeholder="검색어를 입력하세요" /> <div className = "todos_wrapper"> {filteredTodos.map((todo) => { return <TodoItem key={todo.id} {...todo}/>; })} </div> </div> } export default List;
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
SearchResult 인수 타입 적용
search 페이지에서 아래 처럼 SearchResult 함수로 빼준 경우, searchParams의 타입을 어떻게 해야 하나요?async function SearchResult({ searchParams }: { searchParams: Promise<T> }) { await delay(1500); const { q } = await searchParams; const response = await fetch( `${process.env.NEXT_PUBLIC_API_SERVER_URL}/book/search?q=${q}`, { cache: "force-cache" } ); if (!response.ok) { return <div>오류가 발생했습니다.</div>; } const books: BookData[] = await response.json(); return ( <div> {books.map((book) => ( <BookItem key={book.id} {...book} /> ))} </div> ); }
-
미해결손에 익는 Next.js - 블로그 만들기
초반부 pnpm dev 후 발생하는 에러
초기에 pnpm으로 example 다운받고pnpm dev로 실행 후기능을 사용해보면, 블로그의 글을 클릭하면 아래처럼 에러가 나오는데이유와 해결방법이 어떻게 되나요? 아래는 명령창에 나오는 에러입니다.Error: Route "/blog/[slug]" used `params.slug`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis at eval (app\blog\[slug]\page.tsx:55:70) at Array.find (<anonymous>) at Blog (app\blog\[slug]\page.tsx:55:34) 53 | 54 | export default async function Blog({ params }) { > 55 | let post = await getBlogPosts().find((post) => post.slug === params.slug); | ^ 56 | if (!post) { 57 | notFound(); // ?룷?뒪?듃媛� ?뾾?쑝硫? 404 ?럹?씠吏�瑜? 諛섑솚 58 | } 殊? [Error: A React Element from an older version of React was rendered. This is not supported. It can happen if: - Multiple copies of the "react" package is used. - A library pre-bundled an old copy of "react" or "react/jsx-runtime". - A compiler tries to "inline" JSX instead of using the runtime.] { digest: '685037146' } Error: Route "/blog/[slug]" used `params.slug`. `params` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis at eval (app\blog\[slug]\page.tsx:15:64) at Array.find (<anonymous>) at Module.generateMetadata (app\blog\[slug]\page.tsx:15:28) 13 | 14 | export function generateMetadata({ params }) { > 15 | let post = getBlogPosts().find((post) => post.slug === params.slug) | ^ 16 | if (!post) { 17 | return 18 | }
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
클라이언트 컴포넌트에서의 서버컴포넌트 재호출에 대한 질문
선생님이 이번 강의에서 서버액션을 이용한 서버컴포넌트 재검증에 대한 설명을 해주셨는데요. 방금 강의를 듣고나니 불현듯, 어? 그럼 클라이언트 컴포넌트에서, post요청을 날리면 서버컴포넌트 업데이트를 어떻게 치지? 방금 설명해주신것처럼 모든 post 요청을 서버액션으로 만든 후에 revalidatePath나 revalidateTag를 사용해야하는건가? 라는 의문점이 들었습니다. 보통 실무에서는 프론트가 다이렉트로 db에 접근하는 일이 거의 없기 때문에, 백엔드가 항상 붙어있는데그럼 결국 클라이언트 컴포넌트에서 서버액션을 통해 post요청을 보내고, 웹서버측에서는 백엔드에 post요청을 보내는식의 2번작업이 일어나야만, post요청이 끝나고 서버컴포넌트의 업데이트가 가능한걸까요 ?
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
App 라우터 쿼리스트링
App 라우터 방식에서 쿼리 스트링을 불러오는 방식이 강의를 보니까 두 가지로 확인 됩니다. 두 가지의 차이점이 무엇 인지와 두 개를 병행하면서 사용해도 상관없는지 확인 부탁 드립니다. 1. useSearchParams( ) 사용export default function Searchbar() { const router = useRouter(); const searchParams = useSearchParams(); //searchParams 훅은 쿼리스트링을 가져오는 것인데 빌드타임에는 //쿼리스트링을 가져올 수 없으므로 빌드타임에서 오류가 발생한다. //그래서 해당 페이지는 사전렌더링에서 제외시켜야 한다. const [search, setSearch] = useState(""); const q = searchParams.get("q"); 2. searchParams를 함수에 인자로 넣은 경우export default async function Page({ searchParams, }: { searchParams: Promise<{ q?: string; }>; }) { const { q } = await searchParams;
-
해결됨[코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스
koreanjson 관련 사이트가 접속이 안되어 postman으로 실행이 안되네요
koreanjson 관련 사이트가 접속이 안되어 postman으로 실행이 안되네요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
안녕하세요,
4월에 구매하여 늦게서야 해당 강의를 수강 중인데 업데이트 된 강의 쿠폰 발급하는걸 몰랐네요ㅠㅠ 혹시 현강의랑 많이 다를까요? 어떤 차이점이 있는지 궁금해요.듣는다고 하면 중간부터 들어야하나요. 현재 eslint부분 듣고있는데 애매하네요. 그리고 듣는다는 가정 하에 쿠폰 재발급이 가능한지 궁금합니다.
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
2.16) 빌드 후 실행한 환경에서 네트워크 탭에서의 Json 요청
안녕하세요. 좋은 강의 잘 듣고 있습니다. "2.16) SSG 4. 폴백옵션 설정하기" 강의를 듣다가 궁금한 점이 생겨서 질문남깁니다!홈 경로는 getStaticProps 함수를 사용해 빌드 타임에 사전 렌더링이 이루어지고 있습니다. Next App을 빌드 후에 홈 / 경로에서 네트워크 탭을 살펴보다가 현재 화면 내에서 인덱스에 해당하는 Json 응답을 받은 것을 보았습니다.하나를 열어보니 아래와 같습니다.// 6.json?id=6 { "pageProps": { "book": { "id": 6, "title": "프론트엔드 성능 최적화 가이드", "subTitle": "웹 개발 스킬을 한 단계 높여 주는", "description": "...", "author": "유동균", "publisher": "인사이트", "coverImgUrl": "https://shopping-phinf.pstatic.net/main_3562758/35627588630.20230704084326.jpg" } }, "__N_SSG": true }제가 이해한 바로는 / 경로에 진입해서 초기 요청이 이루어지면 서버는 사전에 렌더링 된 HTML 페이지를 응답한 이후 현재 페이지에 필요한 모든 자바스크립트 파일을 JS Bundle로 전달합니다.네트워크 탭에서 localhost HTML 문서를 확인하고 이후에 http://localhost:3000/_next/static/chunks/pages/book/%5Bid%5D-4bfc2418ccf58b5f.js JS Bundle을 응답받은 것을 확인했습니다.그리고 현재 페이지에서 이동 가능성이 있는 페이지들에 한해 사전에 요청이 이루어집니다. js 파일이 전달되어야 하지만 북(/book/[id])페이지가 SSG 방식으로 사전 렌더링이 이루어지므로 데이터를 뿌려주기만 하면 되니까 json 형식으로 응답이 온 것이 아닐까 생각했습니다. (북페이지를 SSR 방식으로 바꾸니 북페이지의 js 파일이 전달되었습니다.)이후 Link 컴포넌트인 도서 목록 중 하나를 클릭하면 자바스크립트 코드를 실행시켜 컴포넌트를 교체하는 CSR 방식으로 페이지를 이동합니다.Q. SSG 방식에서는 사전 렌더링의 응답으로 Json 형식으로 전달받아 새로운 페이지를 보여주는 제가 이해하고 있는 방식이 맞는지 궁금합니다.다른 하나는, 현재 화면에서 네트워크 요청(예: 6.json?id=6)이 무수히 많이 이루어진 것을 보았습니다. 그래서 Link 컴포넌트의 prefetch 값을 false로 설정하여 각 컴포넌트에 마우스를 호버했을 때 프리페칭이 이루어지도록 했습니다. 그러자 마우스를 호버하면 프리페칭이 이루어졌지만 마우스를 호버할 때마다 요청이 이루어져 의문이 생겼습니다.Q. 잦은 요청이 서버에 부하를 주는 것은 아닌지, 이를 개선할 수 있는 방법이 있는지 궁금합니다. 긴 글 읽어주셔서 감사합니다.아래 코드 첨부하였습니다!📄/pages/index.tsximport SearchableLayout from "@/components/Searchable-layout"; import BookItem from "@/components/Book-item"; import { InferGetStaticPropsType } from "next"; import fetchBooks from "@/lib/fetch-books"; import style from "./index.module.css"; import fetchRandomBooks from "@/lib/fetch-random-books"; export const getStaticProps = async () => { const [allBooks, recoBooks] = await Promise.all([ fetchBooks(), fetchRandomBooks(), ]); return { props: { allBooks, recoBooks, }, }; }; export default function Home({ allBooks, recoBooks, }: InferGetStaticPropsType<typeof getStaticProps>) { return ( <div className={style.container}> <section> <h3>지금 추천하는 도서</h3> {recoBooks.map((book) => ( <BookItem key={book.id} {...book} /> ))} </section> <section> <h3>등록된 모든 도서</h3> {allBooks.map((book) => ( <BookItem key={book.id} {...book} /> ))} </section> </div> ); } Home.getLayout = (page: NextPageWithLayout) => { return <SearchableLayout>{page}</SearchableLayout>; }; 📄/pages/book/[id].tsx/* eslint-disable jsx-a11y/alt-text */ /* eslint-disable @next/next/no-img-element */ import fetchBook from "@/lib/fetch-one-book"; import style from "./[id].module.css"; import { GetStaticPropsContext, InferGetStaticPropsType } from "next"; export const getStaticPaths = () => { return { paths: [ { params: { id: "1" } }, { params: { id: "2" } }, { params: { id: "3" } }, ], fallback: "blocking", }; }; export const getStaticProps = async (context: GetStaticPropsContext) => { const { id } = context.params!; const book = await fetchBook(Number(id)); return { props: { book, }, }; }; export default function Page({ book, }: InferGetStaticPropsType<typeof getStaticProps>) { if (!book) return "문제가 발생했습니다. 다시 시도해주세요."; const { title, subTitle, description, author, publisher, coverImgUrl } = book; return ( <div className={style.container}> <div className={style.cover_img_container} style={{ backgroundImage: `url('${coverImgUrl}')` }} > <img src={coverImgUrl} /> </div> <div className={style.title}>{title}</div> <div className={style.subTitle}>{subTitle}</div> <div className={style.author}> {author} | {publisher} </div> <div className={style.description}>{description}</div> </div> ); }
-
미해결React 기반 Gatsby로 기술 블로그 개발하기 v2
GaphQL reference 질문
강의에서는 references 필드를 사용해 Rich Text 필드에서 이미지와 링크 데이터를 가져오라고 했지만, 현재 Contentful의 GraphQL 탐색기에서는 content 필드에 raw만 있고 references 필드가 보이지 않습니다. 혹시 Contentful의 GraphQL API가 업데이트된 것인가요? 그렇다면, 현재 버전에서는 Rich Text 필드에 포함된 이미지나 링크 데이터를 어떻게 가져오는 것이 좋을까요?
-
미해결Next + React Query로 SNS 서비스 만들기
middleware 관련 질문입니다
해당 코드를 수정했던 강의가 어디였는지 기억이 안나 여기에 글 남깁니다.아래는 auth.ts의 코드이고 callbacks 부분을 작성하셨다가 나중에 없애는 걸로 수정하셨는데 이 코드의 역할을 middleware에서 대신한다고 생각하면 될까요 ? export async function middleware() { const session = await auth(); if (!session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } } // callbacks: { // async authorized({ request, auth }) { // if (!auth) { // return NextResponse.redirect('http://localhost:3000/i/flow/login'); // } // return true; // }, // },
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
마지막 강의 배포 후 최적화에서 오류
안녕하세요 next 강의를 거의 마치고 vercel에 배포까지 완료한 후 마지막 강의인 배포 후 최적화를 강사님과 동일하게 작성 후 vercel --prod 명령어를 통해 프로젝트를 다시 배포하였는데 해당 오류가 나타납니다.. ㅠㅠ book 폴더에서 오류가 나는것 같은데 혼자 해결해보려고 했으나, 원인도 잘 모르겠고 검색해도 잘 안나와서 여쭤봅니다.. -오류- -/book/[id]/page.tsx 코드-import { notFound } from "next/navigation"; import style from "./page.module.css"; import { BookData, ReviewData } from '@/types'; import ReviewItem from "@/components/review-item"; import ReviewEditor from "@/components/review-editor"; import Image from "next/image"; export async function generateStaticParams() { const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/book`); if(!response.ok) { throw new Error(response.statusText); } const books:BookData[] = await response.json(); return books.map((book) => ({ id: book.id.toString(), })) } async function BookDetail({bookId} :{bookId:string}) { const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/book/${bookId}`); if(!response.ok) { if(response.status === 404) { notFound(); } return <div>오류가 발생했습니다...</div> } const book = await response.json(); const { id, title, subTitle, description, author, publisher, coverImgUrl } = book; return ( <section> <div className={style.cover_img_container} style={{ backgroundImage: `url('${coverImgUrl}')` }} > <Image src={coverImgUrl} width={240} height={300} alt={`도서 ${title}의 표지 이미지`} /> </div> <div className={style.title}>{title}</div> <div className={style.subTitle}>{subTitle}</div> <div className={style.author}> {author} | {publisher} </div> <div className={style.description}>{description}</div> </section> ); } async function ReviewList({bookId}:{bookId : string}) { const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/review/book/${bookId}`, {next:{tags:[`review-${bookId}`]}}); if(!response.ok) { throw new Error(`Review fetch failed : ${response.statusText}`); } const reviews:ReviewData[] = await response.json(); return ( <section> {reviews.map((review) => ( <ReviewItem key={`review-item-${review.id}`} {...review} /> ))} </section> ) } export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) { const {id} = await params; const response = await fetch(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/book/${id}`); if(!response.ok) { throw new Error(response.statusText); } const book:BookData = await response.json(); return { title:`${book.title} - 한입북스`, description:`${book.title}`, openGraph:{ title:`${book.title} - 한입북스`, description:`${book.title}`, images:[book.coverImgUrl], } } } export default async function Page({ params }: { params: Promise<{ id: string }> }) { const resolvedParams = await params; return ( <div className={style.container}> <BookDetail bookId={resolvedParams.id} /> <ReviewEditor bookId={resolvedParams.id} /> <ReviewList bookId={resolvedParams.id} /> </div> ) }
-
해결됨[코드캠프] 부트캠프에서 만든 '완벽한' 프론트엔드 코스
[06-01] 동기/비동기는 강의 영상이 없나요?
강의 시간이 1분이고 영상 자체가 없어서요.그냥 학습자료만 보고 넘어가는 부분인가요?그리고 전체 강의 코드는 어디서 다운로드 받을 수 있나요?
-
해결됨한 입 크기로 잘라먹는 Next.js(v15)
오류2
Next 강의에서 App Router 실습을 위해서 강사님께서 올려주신 자료를 다운받으면위 사진과 같이 강의와는 다르게 Promise 타입이 존재하여 params.id에 오류가 발생하게 됩니다.