iOS 디바이스에서 input 요소에 포커스가 되면 가상키보드가 나타나면서 다음과 같은 문제가 발생합니다: 문제 상황 : 가상키보드가 올라오면서 웹뷰의 전체 document가 위로 이동 position: fixed 로 설정한 헤더, 바텀 버튼 등의 엘리먼트들이 함께 올라가면서 고정 위치를 유지하지 못함 시도해본 해결방법 : 가상 인풋(virtual input) 방식 적용 → 효과 없음 Visual Viewport API 활용 → 해결되지 않음 질문 : 이 문제를 해결할 수 있는 효과적인 방법이 있을까요? 특히 iOS Safari 웹뷰 환경에서 position: fixed 엘리먼트가 가상키보드의 영향을 받지 않고 제자리를 유지할 수 있는 방법을 찾고 있습니다.
import BookItem from "@/app/components/book-item"; import BookListSkeleton from "@/app/components/skeleton/book-list-skeleton"; import { fetchSearchBooks } from "@/app/lib/fetch-search-books"; import { Suspense } from "react"; // export const dynamic = "force-dynamic"; // 특정 페이지 유형을 강제로 설정하는 dynamic 세그먼트 옵션 // 1. auto - 아무것도 강제하지 않는 옵션, 기본형 // 2. force-dynamic - dynamic 페이지를 강제하는 옵션 // 3. force-static - static 페이지를 강제하는 옵션 // 4. error - static 페이지를 강제하지만 이로 인해 오류가 발생할 것으로 예상되면 빌드과정에서 오류를 발생하는 옵션 async function SearchResult({ q }: { q: string }) { const searchBooks = await fetchSearchBooks(q); return ( <> {searchBooks.map((book) => { return <BookItem key={book.id} {...book} />; })} </> ); } export default async function Page({ searchParams, }: { searchParams: { q?: string }; }) { return ( <Suspense fallback={<BookListSkeleton count={3} />}> <SearchResult q={searchParams.q || ""} /> </Suspense> ); } 스켈레톤 UI를 적용하고 있는데 속도를 3g로 하고 새로고침을 해도 Suspence가 작동하지 않고 페이지가 랜더링 됩니다. 그리고 터미널에서 이런 에러가 발생하고 있습니다. Error: Route "/search" used `searchParams.q`. `searchParams` should be awaited before using its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis at Page (src/app/(with-searchbar)/search/page.tsx:31:37) 29 | return ( 30 | <Suspense fallback={<BookListSkeleton count={3} />}> > 31 | <SearchResult q={searchParams.q || ""} /> | ^ 32 | </Suspense> 33 | ); 34 | } GET /search?q=%ED%95%9C 200 in 47ms SearchResult가 서버 컴포넌트로 만들어져 있어서 서버에서 이미 만들어 가져오기 때문에 fallback이 적용되지 않는건가요? SearchResult를 클라이언트 컴포넌트로 바꿔야지 해결되는 문제인가요?
궁금한 내용 "NextJS에서 클라이언트 컴포넌트도 SSR을 수행하는데, 상호작용이 불가능한 정도의 SSR을 수행하여 리액트 트리를 구성하여 HTML을 만들고, HTML과 함께 클라이언트에 자바스크립트 번들 형태(하이드레이션 용도)로 전달된다." 라고 알고 있는데 맞는 말일까요? 그렇다면, '첫 페이지 로드 시'에만 1번과 같이 동작하고 첫 페이지가 아닌 '다른 페이지'에 들어갈 땐 CSR처럼(JS번들을 클라이언트에게 전달하여 브라우저 단에서 리액트 생명주기 시작) 동작한다면, 강의 중 나온 SearchBar 컴포넌트는 빌드 단계에 렌더링되는 컴포넌트 안에 있다하더라도 CSR될 것인데 Suspense로 래핑하여 클라이언트 단에서 렌더링이 되게끔 강제할 필요가 없지 않나요? 클라이언트 컴포넌트로 선언된 페이지 컴포넌트(create 페이지)가 next/link를 통해 프리패칭된 것을 네트워크 탭을 통해 확인했습니다. 다만, 클라이언트 페이지 컴포넌트 임에도 RSC Playload로 가져온 것을 확인했는데, 이것은 왜 그런 걸까요? 첫 페이지 로드 이후 페이지 이동 시에 CSR로 동작한다하더라도 진입하는 페이지의 RSC Payload와 JS번들은 주기적으로 서버로부터 가져와야하지 않나요? 아래의 그림에는 JS 실행 전에 서버로부터 가져오는 단계가 보이지 않는데 프리 패칭 과정이 생략된 걸까요?
안녕하세요, 강사님. 좋은 강의 감사히 듣고 있습니다! 현재 실제 앱 출시를 목표로 개발을 시작하려고 하는데, Expo와 CLI 중 어떤 방식으로 진행할지 고민 중입니다. 공식 문서에서도 Expo를 권장하고 있고, Prebuild 기능 덕분에 대부분의 네이티브 라이브러리도 사용할 수 있고, 성능 최적화도 많이 이루어진 것으로 알고 있습니다. CLI와 Expo 사이의 런타임 성능이나 최종 번들 용량 차이에 대한 정확한 비교 자료가 부족해 선택이 쉽지 않은 상황입니다. 강사님께서 CLI와 Expo를 모두 사용해보셨을 때, 두 방식 간에 실제 체감되는 성능이나 앱 용량 차이가 유의미했는지 궁금합니다. 만약 차이가 크다면 CLI를 선택하는 쪽이 맞을 것 같기도 한데, 강사님의 의견이 듣고 싶습니다. 감사합니다!
npm ls react "react": "19.0.0", npm ls react-native "react-native": "0.79.2", npm ls expo = "expo": "~53.0.9", 문제: expo go 앱에서 제가 빌드한 앱을 실행하면 로딩이 계속되다가 위와 같은 화면이 뜹니다. 터미널에서도 에러를 찾을 수 없어서 이렇게 질문 드립니다.
안녕하세요 선생님 금일 수강신청 및 프로젝트해보면서 오류가 발생하여 질문드립니다. 우선 프로젝트를 clone하고 pnpm install로 의존성을 설치했습니다. 그리고 docker compose -f docker-compose/redis/redis-compose.yml up -d 를 실행하였는데 아래와 같은 결과가 나왔습니다. network ludgi-network declared as external, but could not be found 어떻게 조치하면 될까요~?
안녕하세요! 서버 액션에 관해 궁금한 점이 생겨 질문드립니다. 일단 제가 강의를 보고 실습하면서 느꼈던 서버 액션의 큰 장점은 다음과 같습니다. 브라우저에서 서버 측에서 사용되는 함수를 실행할 수 있다. => 별도의 백엔드 API를 구현하지 않고도 DB에 직접적으로 접근하는 등의 작업이 가능 브라우저와의 상호작용을 하면서도 컴포넌트를 서버 컴포넌트로 유지할 수 있다. 하지만 1번 장점의 경우 혼자 풀스택으로 개발하지 않고 백엔드 개발자와 함께 개발하는 경우에는, 주로 이번 강의와 같이 서버 액션 함수 내에서 별도의 API를 호출하게 될 것이라 생각됩니다. 이런 경우에도 기존의 방식대로 onSubmit 등을 사용해서 API를 호출하는 것보다, 2번 장점을 위해 서버 액션을 사용하는 것이 바람직 할지 궁금해 질문드립니다! state를 이용해 실시간 validation이 이루어져야 하는 곳이 아니라면 유용할 것 같다고 생각이 들지만, 구글링을 해봐도 최신 기술이라 그런지 실제 적용 사례를 잘 찾아볼 수 없어 질문 드립니다!
안녕하세요 1.3) 실습용 백엔드 서버 세팅 이후 terminal 에서 npm run start 로 Swagger UI 까지 작동이 되었는데요. 2.1) Page Outer를 소개합니다. 에서 npx create-next-app@14 section02 를 진행하고 나면, npm run start 시 에러가 나고 있습니다. SSR 강의를 진행하면서 계속해서 백엔드 서버에 접속을 못하는데 왜 이러는 걸까요? error 사진파일 첨부하였습니다.
안녕하세요, 정환님 궁금한게 있습니다. 동적 경로를 갖는 page.tsx 파일 안에 generateStaticParams 라는 함수를 설정하게되면, 경로에 해당하는 모든 페이지는 모두 Static Page로 만들어지는 것으로 알고 있습니다. return하지 않은 URL Parameter를 갖는 페이지에 대해서도 동일하다고 봤었는데요 그렇다면 , 도서 id999번까지 있다고 가정해볼게요 return [{id:"1"}] 이렇게만 작성해도 999번까지 static page가 되는게 맞을까요 ?
선생님~ 커서에서 코드 수정하고 연동된 깃허브에 커밋도 시켜주었는데, 버셀에 최종 마스터가 뜨지 않는데 왜 이런걸까요? ㅠㅠ 어제 저녁 이것땀시 너무 #&!@ 해서 ㅠㅠ 커서에 '최종1'이 마스터로 된 화면이고요.. 깃허브에 최종1이 master 로 되어 있는것 같구요? 근데 버셀에서는 그전단계로만 되어 있어서요 ㅠㅠ (근데 사실 이것도 어제 저녁에 계속 마스터로 안되 있어서 버셀에 있는 프로젝트 계속 지우면서 최신 깃허브 가져온거거든요?) 깃허브에 버셀에 제대로 업뎃이 안되는데, 어디부터 봐야할지 좀 도움 좀 주세요~
안녕하세요! 이정환님 강의 정말 재미있게 잘 보고 있습니다. 😊 New 페이지 구현하기 2.기능 강의 중에 const getStringedDate = (targetDate) => { let year = targetDate.getFullYear(); let month = targetDate.getMonth() + 1; let date = targetDate.getDate(); if(month < 10) { month = `0${month}`; } if(date < 10) { date = `0${date}`; } return `${year}-${month}-${date}`; } 로 날짜 변환 함수로 강의에서 언급 해주셨는데요! 혹시 이건 어떨까요? const getStringedDate = (targetDate) => { return `${targetDate.getFullYear()}-${String(targetDate.getMonth() +1).padStart(2,0)}-${String(targetDate.getDate()).padStart(2,0)}`; } 결과는 동일하게 나올 수 있을 것 같아 상관은 없지만 보통 저런 경우에는 조건문을 애용하시는 편이실까요? 어떤 방식으로 짜는 것을 더 선호 하시는지 정말 호기심에 질문을 드립니다! 😆