묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Next + React Query로 SNS 서비스 만들기
provider 가 안되요...
안녕하세요 provider 따라해보는중 다른데는 잘되는데 안되는곳이 있어 문의드립니다. "use client" import {createContext, ReactNode, useState} from "react"; const currentYear = new Date().getFullYear(); const currentMonth = new Date().getMonth() + 1; export const FilterContext = createContext({ year: currentYear, setYear: (value: number) => {}, month: currentMonth.toString().padStart(2, "0"), setMonth: (value: string) => {}, }); type Props = { children: ReactNode }; export default function FilterProvider({ children }: Props) { const [ year, setYear ] = useState(currentYear); const [ month, setMonth ] = useState(currentMonth.toString().padStart(2, "0")); return ( <FilterContext.Provider value={{ year, setYear, month, setMonth }}> {children} </FilterContext.Provider> ); } const { year, setYear, month, setMonth } = useContext(FilterContext); const [selectBox2, setSelectBox2] = useState(false); const [selectBox3, setSelectBox3] = useState(false); {Array.from({ length: year - 2019 }, (_, index) => year - index).map((item) => ( <li key={item} onClick={() => { setSelectBox2(false); setYear(item); console.log(item); }}>{item}</li> ))} {Array.from({ length: 12 }, (_, index) => (index + 1).toString().padStart(2, "0")).map((item) => ( <li key={item} onClick={() => { setSelectBox3(false); console.log("month",item); setMonth(item); }}>{item}</li> ))}yser, month 가 업데이트가 될질 않아서요. 콘솔에는 찍히는데 값이 변경이 안되는 이유를 모르겠습니다.
-
미해결Next + React Query로 SNS 서비스 만들기
useMutation의 onError에 관하여 질문있습니다.
안녕하세요. 제로초님 항상 좋은 강의 감사드립니다. 해당 강의에서 팔로우 api에 의도적으로 에러를 일으켜 보았는데, onError에서 캐치하지 못하는 것을 확인했습니다. const follow = useMutation({ mutationFn: (userId: string) => { // console.log('follow', userId); return fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/users/${userId}/follow`, { credentials: 'include', method: 'post', } ); }, ... onError(error, userId: string) { ... } 그래서 밑에와 같이 바꾸어보니 onError에서 캐치하여 롤백이 정상적으로 되었습니다. mutationFn: async (userId: string) => { const response = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/users/${userId}/follow`, { credentials: 'include', method: 'post', } ); if (!response.ok) { throw new Error('팔로우 실패'); } return response; },혹시 본 강의 코드대로 하면 onError에서 원래 캐치해야하는데 안 되는 것인지 아니면 수강생들에게 숙제로 낸 것인지 궁금합니다.
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
marker.d.ts 에러 문제
import {LatLng, MapMarkerProps} from 'react-native-maps'; // marker props의 타입을 변경 declare module 'react-native-maps' { export interface MyMapMarkerProps extends MapMarkerProps { coordinate?: LatLng; } } Interface 'MyMapMarkerProps' incorrectly extends interface 'MapMarkerProps'.Type 'MyMapMarkerProps' is not assignable to type '{ anchor?: Point | undefined; calloutAnchor?: Point | undefined; calloutOffset?: Point | undefined; centerOffset?: Point | undefined; coordinate: LatLng; ... 22 more ...; zIndex?: number | undefined; }'.Types of property 'coordinate' are incompatible.Type 'LatLng | undefined' is not assignable to type 'LatLng'.Type 'undefined' is not assignable to type 'LatLng'.ts(2430)이런 에러가 발생합니다. 강의와 동일하게 했는데 그렇습니다! 아래 질문주신분 답변에 응답이 없어서 다시 질문드리는 점 양해부탁드립니다!
-
해결됨Next + React Query로 SNS 서비스 만들기
무한 스크롤 prefetch SSR 로그인 쿠키 관련 질문
안녕하세요 제로초님,api/users/{id} 에서 Followers 데이터는제가 상대방을 팔로잉 했고 로그인 정보 쿠키가 잘 전달되면상대방의 팔로워 수와 관계없이 제가 팔로우 했다면 저의 userId만 배열에 담겨서 돌아오기 때문에 api/posts/followings 혹은 api/posts/recommends의Comments 데이터도 이와 동일할 것이라고 생각했습니다. 추천 포스트는 prefetch를 진행하고(Suspense를 적용하지 않음)팔로잉 포스트는 SSR을 할 때 서버에서 로그인 여부를 판단하는 쿠키를 함께 보내야 하기 때문에prefetch에서는 next의 cookies를 사용해서 쿠키를 전달했습니다.하지만 Comments 배열에 로그인 유저가 아닌 다른 유저의 id도 함께 날라옵니다. 추천 포스트가 아닌 팔로잉 포스트는 useSuspenseInfiniteQuery를 사용했는데로그인 유저의 id만 잘 전달받았습니다. 이게 Suspense 내부에 있어서 그런가 싶어서Suspense를 걷어내고 prefetch만 사용했을 때도동일하게 로그인 유저가 아닌 다른 유저의 id도 확인할 수 있었습니다.왜 로그인 쿠키를 보내는데도 다른 유저의 정보도 날라오는지 궁금합니다.어디가 잘못된 것일까요?useSuspenseInfiniteQuery를 사용한 팔로잉 포스트는 추천 포스트와 어떤점이 달라서 정상 작동한 것일까요?추가) 이 글 작성하고 추천 포스트에도 useSuspenseInfiniteQuery를 적용했는데 팔로잉 포스트처럼 안 나오고 이전과 동일하게 다른 유저의 id도 함께 날라오는데 왜 그럴까요? 추천 포스트 결과현재 로그인 한 유저의 id는 'jihwan3'Comments에 'jihwan2'도 함께 날라오는중 팔로잉 포스트 결과현재 로그인 한 유저의 id는 'jihwan3'Comments에 'jihwan3'만 날라오는 중Hearts도 jihwan3가 좋아요를 안 눌러서 비어있는 모습page.tsximport { Suspense } from "react"; import { auth } from "@/auth"; import { Container } from "./_component/styled"; import HomeTab from "./_component/HomeTab"; import HomeTabProvider from "./_component/HomeTabProvider"; import PostForm from "./_component/PostForm"; import SuspenseDecider from "./_component/SuspenseDecider"; import Loading from "../_component/LoadingUI"; export default async function Home() { const session = await auth(); return ( <Container> <HomeTabProvider> <HomeTab></HomeTab> <PostForm me={session} /> <Suspense fallback={<Loading />}> {/* @ts-expect-error Server Component */} <SuspenseDecider /> </Suspense> </HomeTabProvider> </Container> ); } SuspenseDecider.tsximport { QueryClient, HydrationBoundary, dehydrate } from "@tanstack/react-query"; import getRecommendPosts from "../_lib/getRecommendPosts"; import getRecommendPostsServer from "../_lib/getRecommendPostsServer"; import PostDisplay from "./PostDisplay"; export default async function SuspenseDecider() { const queryClient = new QueryClient(); await queryClient.prefetchInfiniteQuery({ queryKey: ["posts", "recommends"], queryFn: getRecommendPostsServer, initialPageParam: 0, getNextPageParam: (lastPage, pages) => lastPage.at(-1)?.postId, pages: 1, }); return ( <HydrationBoundary state={dehydrate(queryClient)}> <PostDisplay /> </HydrationBoundary> ); }getRecommendPostsServer.tsimport { QueryFunction } from "@tanstack/query-core"; import { Post as IPost } from "@/model/Post"; import { cookies } from "next/headers"; type Prop = { pageParam: number; }; const getRecommendPostsServer: QueryFunction<IPost[], [_1: string, _2: string], number> = async ({ pageParam }: Prop) => { const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts/recommends?cursor=${pageParam}`, { method: "get", headers: { Cookie: cookies().toString(), }, }); if (response.ok) return response.json(); else throw new Error(); }; export default getRecommendPostsServer;postDisplay.tsx"use client"; import { use } from "react"; import { TabContext } from "./HomeTabProvider"; import RecommendPosts from "./RecommendPosts"; import FollowingPosts from "@/app/(afterLogin)/home/_component/FollowingPosts"; export default function PostDisplay() { const { selectedMenu } = use(TabContext); if (selectedMenu === "recommend") { return <RecommendPosts />; } return <FollowingPosts />; } RecommendPosts.tsx"use client"; import { useEffect, Fragment } from "react"; import { InfiniteData, useInfiniteQuery } from "@tanstack/react-query"; import { useInView } from "react-intersection-observer"; import getRecommendPosts from "../_lib/getRecommendPosts"; import Post from "../../_component/Post"; import { Post as IPost } from "@/model/Post"; export default function RecommendPosts() { const { isFetching, fetchNextPage, hasNextPage, data } = useInfiniteQuery<IPost[], Object, InfiniteData<IPost[]>, [_1: string, _2: string], number>({ queryKey: ["posts", "recommends"], queryFn: getRecommendPosts, initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.at(-1)?.postId, }); const { ref, inView } = useInView({ /* Optional options */ threshold: 0, delay: 100, }); useEffect(() => { if (inView) !isFetching && hasNextPage && fetchNextPage(); }, [inView, isFetching, hasNextPage, fetchNextPage]); if (!data) return null; // 이거 에러는...? type error인데.. -> InfiniteData로 해결 return ( <> {data.pages.map((ele: IPost[], idx: number) => ( <Fragment key={idx}> {ele.map((post: IPost) => ( <Post key={post.postId} post={post}></Post> ))} </Fragment> ))} <div ref={ref} style={{ height: "50px" }}></div> </> ); } getRecommendPost.tsimport { QueryFunction } from "@tanstack/query-core"; import { Post as IPost } from "@/model/Post"; type Prop = { pageParam: number; }; const getRecommendPosts: QueryFunction<IPost[], [_1: string, _2: string], number> = async ({ pageParam }: Prop) => { const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/posts/recommends?cursor=${pageParam}`, { method: "get", credentials: "include", }); if (response.ok) return response.json(); else throw new Error(); }; export default getRecommendPosts; FollowingPosts.tsx"use client"; import { Fragment, useEffect } from "react"; import { useSuspenseInfiniteQuery, InfiniteData } from "@tanstack/react-query"; import { useInView } from "react-intersection-observer"; import getFollowingPosts from "../_lib/getFollowingPosts"; import Post from "../../_component/Post"; import { Post as IPost } from "@/model/Post"; export default function FollowingPosts() { const { isFetching, fetchNextPage, hasNextPage, data } = useSuspenseInfiniteQuery<IPost[], Object, InfiniteData<IPost[]>, [_1: string, _2: string], number>({ queryKey: ["posts", "followings"], queryFn: getFollowingPosts, initialPageParam: 0, getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => lastPage.at(-1)?.postId, }); const { ref, inView } = useInView({ threshold: 0, delay: 100, }); useEffect(() => { if (inView) !isFetching && hasNextPage && fetchNextPage(); }, [inView, fetchNextPage, hasNextPage, isFetching]); if (!data) return null; return ( <> {data.pages.map((ele: IPost[], idx: number) => ( <Fragment key={idx}> {ele.map((ele) => ( <Post key={ele.postId} post={ele}></Post> ))} </Fragment> ))} <div ref={ref} style={{ height: "100px" }}></div> </> ); }
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
react-native-vector-icons 에러 관련 질문
import React, {useRef, useState} from 'react'; import {Alert, Pressable, StyleSheet, View} from 'react-native'; import MapView, { Callout, LatLng, LongPressEvent, Marker, PROVIDER_GOOGLE, } from 'react-native-maps'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import Ionicons from 'react-native-vector-icons/Ionicons'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import {StackNavigationProp} from '@react-navigation/stack'; import {alerts, colors, mapNavigations} from '@/constants'; import {CompositeNavigationProp, useNavigation} from '@react-navigation/native'; import {DrawerNavigationProp} from '@react-navigation/drawer'; import {MainDrawerParamList} from '@/navigations/drawer/MainDrawerNavigator'; import {MapStackParamList} from '@/navigations/stack/MapStackNavigator'; import useUserLocation from '@/hooks/useUserLocation'; import usePermission from '@/hooks/usePermission'; import mapStyle from '@/style/mapStyle'; import CustomMarker from '@/components/CustomMarker'; type Navigation = CompositeNavigationProp< StackNavigationProp<MapStackParamList>, DrawerNavigationProp<MainDrawerParamList> >; const MapHomeScreen = () => { const inset = useSafeAreaInsets(); const navigation = useNavigation<Navigation>(); const mapRef = useRef<MapView | null>(null); const {userLocation, isUserLocationError} = useUserLocation(); const [selectLocation, setSelectLocation] = useState<LatLng | null>(); usePermission('LOCATION'); const handleLongPressMapView = ({nativeEvent}: LongPressEvent) => { setSelectLocation(nativeEvent.coordinate); }; const handlePressAddPost = () => { // if (!selectLocation) { return Alert.alert( alerts.NOT_SELECTED_LOCATION.TITLE, alerts.NOT_SELECTED_LOCATION.DESCRIPTION, ); } navigation.navigate(mapNavigations.ADD_POST, { location: selectLocation, }); // 다시 뒤로 돌아왔을때는 위치를 초기화 setSelectLocation(null); }; const handlePressUserLocation = () => { if (isUserLocationError) { // 에러 메시지 표시 return; } mapRef.current?.animateToRegion({ latitude: userLocation.latitude, longitude: userLocation.longitude, latitudeDelta: 0.0922, longitudeDelta: 0.0421, }); }; // 1. 나의 위치 구하고. (geolocation) // 2. 지도를 그곳으로 이동. return ( <> <MapView ref={mapRef} style={styles.container} provider={PROVIDER_GOOGLE} showsUserLocation followsUserLocation showsMyLocationButton={false} customMapStyle={mapStyle} onLongPress={handleLongPressMapView}> <CustomMarker color="RED" score={3} coordinate={{ latitude: 37.52016541, longitude: 127.127520372, }} /> <CustomMarker color="BLUE" coordinate={{ latitude: 37.550165411, longitude: 127.127520372, }} /> {selectLocation && ( <Callout> <Marker coordinate={selectLocation} /> </Callout> )} </MapView> <Pressable style={[styles.drawerButton, {top: inset.top || 20}]} onPress={() => navigation.openDrawer()}> <Ionicons name="menu" color={colors.WHITE} size={25} /> </Pressable> <View style={styles.buttonList}> <Pressable style={styles.mapButton} onPress={handlePressAddPost}> <MaterialIcons name="add" color={colors.WHITE} size={25} /> </Pressable> <Pressable style={styles.mapButton} onPress={handlePressUserLocation}> <MaterialIcons name="my-location" color={colors.WHITE} size={25} /> </Pressable> </View> </> ); }; const styles = StyleSheet.create({ container: { flex: 1, }, drawerButton: { position: 'absolute', left: 0, top: 20, paddingVertical: 10, paddingHorizontal: 12, backgroundColor: colors.PINK_700, borderTopRightRadius: 50, borderBottomRightRadius: 40, shadowColor: colors.BLACK, shadowOffset: {width: 1, height: 1}, shadowOpacity: 0.5, // 안드는 elevation elevation: 4, }, buttonList: { position: 'absolute', bottom: 30, right: 15, }, mapButton: { backgroundColor: colors.PINK_700, marginVertical: 5, height: 48, width: 48, alignItems: 'center', justifyContent: 'center', borderRadius: 30, shadowColor: colors.BLACK, shadowOffset: {width: 1, height: 2}, shadowOpacity: 0.5, elevation: 2, }, }); export default MapHomeScreen; 제대로 import도 한 것 같고, 실제로 안드로이드에서는 아이콘 이모지가 매우 정상적으로 보이는데 ios에서만 아이콘이 다르게 보이거나, 아예 안뜹니다. drawer 이모지도 동일하게 동작합니다. 혹시 어떻게 해결하는지에 대해 알고싶습니다! cache clean도 진행해보았습니다!!!
-
해결됨Next + React Query로 SNS 서비스 만들기
로그인을 서버액션으로 구현해봤는데 궁금한게 있습니다.
로그인을 클라이언트 컴포넌트에서 서버액션을 통해 구현해봤는데, 궁금한게 생겨서 질문 드립니다.로그인 모달의 코드입니다. "use client"; import style from "@/app/(beforelogin)/_component/login.module.css"; import { ChangeEventHandler, FormEventHandler, useState } from "react"; import { redirect, useRouter } from "next/navigation"; import { signIn } from "next-auth/react"; import { useFormState, useFormStatus } from "react-dom"; import onSubmit from "../_lib/signin"; import BackButton from "./BackButton"; function showMessage(messasge: string | null | undefined) { if (messasge === "no_id") { return "아이디를 입력하세요."; } if (messasge === "no_password") { return "비밀번호를 입력하세요."; } return ""; } export default function LoginModal() { const [state, formAction] = useFormState(onSubmit, { message: null }); const { pending } = useFormStatus(); return ( <div className={style.modalBackground}> <div className={style.modal}> <div className={style.modalHeader}> <BackButton /> <div>로그인하세요.</div> </div> <form action={formAction}> <div className={style.modalBody}> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="id"> 아이디 </label> <input id="id" name="id" className={style.input} type="text" placeholder="" /> </div> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="password"> 비밀번호 </label> <input id="password" name="password" className={style.input} type="password" placeholder="" /> </div> </div> <div className={style.message}>{showMessage(state?.message)}</div> <div className={style.modalFooter}> <button className={style.actionButton} disabled={pending}> 로그인하기 </button> </div> </form> </div> </div> ); } 아래는 signin.ts 의 코드입니다."use server"; import { redirect } from "next/navigation"; import { signIn } from "@/auth"; const onSubmit = async (prevState: any, formData: FormData) => { if (!formData.get("id") || !(formData.get("id") as string)?.trim()) { return { message: "no_id" }; } if ( !formData.get("password") || !(formData.get("password") as string)?.trim() ) { return { message: "no_password" }; } let shouldRedirect = false; try { const response = await signIn("credentials", { username: formData.get("id"), password: formData.get("password"), redirect: false, }); console.log(response.status, "1"); console.log(response, "2"); shouldRedirect = true; } catch (err) { console.error(err); return { message: null }; } if (shouldRedirect) { redirect("/home"); // try/catch문 안에서 X } }; export default onSubmit; 이렇게 했을 때에, 콘솔이 이렇게 찍힙니다. 1. response가 http://localhost:3000/i/flow/login이렇게 날라오고, status는 그에 따라 undefined 입니다.응답이 이렇게 오면 response에 따른 status를 모르는데 에러처리를 어떻게 해야하나요 ?? 아래는 회원가입을 똑같이 서버액션으로 구현했을때에, signup.ts의 코드입니다."use server"; import { redirect } from "next/navigation"; import { signIn } from "@/auth"; const onSubmit = async (prevState: any, formData: FormData) => { if (!formData.get("id") || !(formData.get("id") as string)?.trim()) { return { message: "no_id" }; } if (!formData.get("name") || !(formData.get("name") as string)?.trim()) { return { message: "no_name" }; } if ( !formData.get("password") || !(formData.get("password") as string)?.trim() ) { return { message: "no_password" }; } if (!formData.get("image")) { return { message: "no_image" }; } let shouldRedirect = false; try { const response = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/users`, { method: "post", body: formData, credentials: "include", } ); console.log(response.status, "1"); if (response.status === 403) { return { message: "user_exists" }; } console.log(await response.json(), "2"); console.log(response, "3"); shouldRedirect = true; /*await signIn("credentials", { username: formData.get("id"), password: formData.get("password"), redirect: false, });*/ } catch (err) { console.error(err); return { message: null }; } if (shouldRedirect) { redirect("/home"); // try/catch문 안에서 X } }; export default onSubmit; 이 때에, console.log 의 결과입니다.response 응답 객체에서 await response.json()을 취했는데, OK로 나오는 이유가 궁금합니다. 로그인 구현에서 redirect를 false로 하면 client측에서 라우팅하는 것이고, true로 하면 서버쪽에서 리다이렉트 하는 것이라고 알고 있습니다.그런데 서버 액션으로 api 호출을 하는 것인데,redirect를 false로 하고 아래 redirect를 해주어도 redirect가 되는 것인지 궁금합니다. 그리고 redirect를 true로 하면이렇게 에러가 떠버립니다.서버액션으로 리다이렉트 시켜주는 것이라 생각해서 true옵션을 주었는데 에러가 왜 뜨는지 궁금합니다.바쁘실텐데 많은 질문 죄송합니다 ㅜㅜ
-
해결됨Next + React Query로 SNS 서비스 만들기
서버 컴포넌트에서 server action 사용 질문이 있습니다.
서버 컴포넌트에서 server actions 사용 중 네트워크 탭에서 응답이 안담기는 문제가 있어서 질문 드립니다.강의에 나와있는대로 코드를 따라했는데 뭐가 문제인지 잘 모르겠습니다.아래는 signupModal의 코드입니다.import style from "./signup.module.css"; import onSubmit from "../_lib/signup"; import BackButton from "@/app/(beforelogin)/_component/BackButton"; import { useFormState, useFormStatus } from "react-dom"; import { redirect } from "next/navigation"; function showMessage(messasge: string | null | undefined) { if (messasge === "no_id") { return "아이디를 입력하세요."; } if (messasge === "no_name") { return "닉네임을 입력하세요."; } if (messasge === "no_password") { return "비밀번호를 입력하세요."; } if (messasge === "no_image") { return "이미지를 업로드하세요."; } if (messasge === "user_exists") { return "이미 사용 중인 아이디입니다."; } return ""; } export default function SignupModal() { //const [state, formAction] = useFormState(onSubmit, { message: null }); //const { pending } = useFormStatus(); const formAction = async (formData: any) => { "use server"; let shouldRedirect = false; try { const response = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/users`, { method: "post", body: formData, credentials: "include", } ); console.log(response.status); console.log(await response.json(), "abc"); if (response.status === 403) { console.log("???"); return { message: "user_exists" }; } shouldRedirect = true; } catch (err) { console.log(err); } if (shouldRedirect) { redirect("/home"); // try/catch문 안에서 X } }; return ( <> <div className={style.modalBackground}> <div className={style.modal}> <div className={style.modalHeader}> <BackButton /> <div>계정을 생성하세요.</div> </div> <form action={formAction}> <div className={style.modalBody}> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="id"> 아이디 </label> <input id="id" name="id" className={style.input} type="text" placeholder="" required /> </div> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="name"> 닉네임 </label> <input id="name" name="name" className={style.input} type="text" placeholder="" required /> </div> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="password"> 비밀번호 </label> <input id="password" name="password" className={style.input} type="password" placeholder="" required /> </div> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor="image"> 프로필 </label> <input id="image" name="image" required className={style.input} type="file" accept="image/*" /> </div> </div> <div className={style.modalFooter}> <button type="submit" className={style.actionButton}> 가입하기 </button> </div> </form> </div> </div> </> ); } 아래는 핸들러 세팅입니다.http.post("/api/users", async ({ request }) => { console.log("회원가입"); return HttpResponse.text(JSON.stringify("user_exists"), { status: 403, }); // return HttpResponse.text(JSON.stringify("ok"), { // headers: { // "Set-Cookie": "connect.sid=msw-cookie;HttpOnly;Path=/;Max-Age=0", // }, // }); }),여기서 회원가입을 누를시에 console.log() 처리한 부분은 잘 찍히고 서버쪽에서 찍은 회원가입 콘솔도 잘 찍히는 모습입니다.네트워크탭에서 페이로드는 잘 담겼는데, 응답이 없습니다.회원가입 누를 때 콘솔이 찍히는 것으로 보아서 포트도 9090으로 제대로 열려있고, 403응답이 오는 것으로 보아 핸들러 쪽은 제대로 작동하는 것 같습니다.그리고 서버액션 쪽 콘솔 "???"가 찍히는 것으로 보아서 status도 403으로 잘 오는 것 같은데 리턴 메시지가 제대로 안되는 것인지 응답이 왜 없는 것인지 궁금합니다.
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
AOS 플랫폼 문제 [해결]
안드로이드 실행시마다, GooglePlay Services keeps stopping이라는 현상이 매초 간격으로 발생하고, 제대로 지도 또한 실행하지 않는 것 같습니다. 혹시 이런 문제를 해결할려면, 어떻게 해야할까요?ios는 정상적으로 실행됩니다!AOS 같은 경우 Matzip isn't responding 하면서, 로그인 회원가입도 잘 동작하지 않습니다. 아래 방법으로 진행해봐도, 제대로 동작하지 않습니다!https://stackoverflow.com/questions/50313967/google-play-services-are-updating-error-on-release-not-emulator-google-play/50327544#50327544몇시간 찾아봤는데...특정 기기에서는 동작하지 않는 것 같습니다. 기기를 바꾸니 동작하네요!! 정확한 이유가 궁금하긴 합니다...ㅠㅠ
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
문법 문의드립니다!
안녕하세요 강사님! 알찬 수업 잘듣고 있습니다~다름이아닌 아직 typescript문법이 약해서 공부를 동행하며 수업을 진행중인데요 막히는부분이 하나씩 발생하여 문의드려요! 예를들어 하기 문법이 이해가안가면 어느 문서를 참고하는게 좋을까요? ㅠㅠ{...login.getTextInputProps('email')}
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
로그인 통신 질문
import axios from 'axios'; const axiosInstance = axios.create({ // 안드는 localhost:3000이 안먹힐 수 있기에 10.0.2.2로 테스트 baseURL: 'http://10.0.2.2:3030', withCredentials: true, }); export default axiosInstance; 이럴때 안드로이드는 제대로 동작하지만, ios는 다시import axios from 'axios'; const axiosInstance = axios.create({ // 안드는 localhost:3000이 안먹힐 수 있기에 10.0.2.2로 테스트 baseURL: 'http://localhost:3030', withCredentials: true, }); export default axiosInstance; 이렇게 바꿔주어야 동작합니다. 이렇게 매번 바꿔서 체크하는게 맞을까요?
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
EncryptedStorage import 오류가 발생합니다.
환경:Macbook Air M2 SonomaVS-CodeRN v0.74.1Metro v0.80.8 문제:아래 명령어로 라이브러리 설치 및 pod install 과정은 모두 완료한 상태입니다. $ yarn add react-native-encrypted-storage$ npx pod-install ios/frontend/utils/encryptStorage.ts에서 EncryptStorage 라이브러리를 불러올 때 다음과 같은 경로로 불러와집니다. 자동완성 기준으로 불러왔으며, 라이브러리 삭제 후 재설치해도 현상은 같습니다. import EncryptedStorage from 'react-native-encrypted-storage/lib/typescript/EncryptedStorage'; 이 경로로 importing된 라이브러리를 파일 내에서 사용하면 정상적으로 메서드가 자동완성 됩니다. (따라서 코딩할 때에는 문제가 되는지 몰랐습니다)이 코드를 바탕으로 앱 실행 시 react-native-encrypted-storage모듈을 불러올 수 없다는 오류가 발생합니다.error: Error: Unable to resolve module react-native-encrypted-storage/lib/typescript/EncryptedStorage from /Users/popo/Desktop/ClipProjects/frontend/src/utils/encryptStorage.ts: react-native-encrypted-storage/lib/typescript/EncryptedStorage could not be found within the project or in these directories: node_modules 1 | import EncryptedStorage from 'react-native-encrypted-storage/lib/typescript/EncryptedStorage'; | ^ 2 | 3 | const setEncryptedStorage = async <T>(key: string, value: T) => { 4 | await EncryptedStorage.setItem(key, JSON.stringify(value)); 해당 모듈 외 문제는 없는 상황 같습니다. 서버 연결 및 응답(로그인, 회원가입에 대해)은 잘 됩니다. 시도해본 것:import EncryptedStorage from 'react-native-encrypted-storage/lib/typescript/EncryptedStorage';를import EncryptedStorage from 'react-native-encrypted-storage'; 로 바꿔 실행해봤는데, 컴파일 오류는 발생하지 않고 로그인 및 회원가입 시점에 eject가 발생합니다. 질문: 왜 이런 문제가 발생하는 것이며, 어떻게 해결할 수 있을까요?
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
forwardRef를 사용하는 정확한 이유에 대해서 알고싶습니다!
forwardRef를 사용하는 정확한 이유에 대해서 알고싶습니다! 부모 컴포넌트에서 자식 컴포넌트로 ref를 전달하고 싶을때 사용하는게 맞을까요?ref는 자식으로 전달이 불가능한 prop이고 forwardRef를 사용해서, 두번쨰 인자로 ref를 전달하는것으로 이해했는데 혹시 맞을까요!!조금 자세한 설명을 듣고 싶습니다!
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
createDrawerNavigator를 위로 빼주는 이유에 대해서 알고싶습니다.
createDrawerNavigator를 위로 빼주는 이유에 대해서 알고싶습니다. 왜 function 위로 올리는지 이유에 대해서 궁금합니다!! 공식문서에서 그렇게 나와있기 떄문인가요!?
-
해결됨Next + React Query로 SNS 서비스 만들기
리액트쿼리 어떤부분이 잘못되었을까요?
안녕하세요.인피니티스크롤 을 따라하는데 잘안되서요.const queryClient = new QueryClient(); await queryClient.prefetchInfiniteQuery({ queryKey: ["member", "sales", sawonCode], queryFn: getSales, initialPageParam: 0, }) const dehydratedState = dehydrate(queryClient); return <> <Suspense fallback={<Loading />}> <HydrationBoundary state={dehydratedState}> <SalesList sawonCode={sawonCode} /> </HydrationBoundary> </Suspense> </>const { data, fetchNextPage, hasNextPage, isFetching, } = useInfiniteQuery<Item[], Object, InfiniteData<Item[]>, [_1: string, _2: string, _3: number], number>({ queryKey: ["member", "sales", sawonCode], queryFn: getSales, initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.at(-1)?.page, staleTime: 60 * 1000, // fresh -> stale, 5분이라는 기준 gcTime: 300 * 1000, }); const { ref, inView } = useInView({ threshold: 0, delay: 0, }); useEffect(() => { if(inView) { console.log(data); !isFetching && hasNextPage && fetchNextPage(); } }, [inView, isFetching, hasNextPage, fetchNextPage]);useEffect에 콘솔에찍은 data는 undefined 이 찍히고 그후에 getSales에 찍은 api 로 가져온 데이터가 찍힙니다.어떤게 잘못되서 data에 값이 안들어가는지 몇시간을 봐도 잘모르겠네요..ㅜㅜ
-
미해결Next + React Query로 SNS 서비스 만들기
NextAuth Credentials authorize의 결과 타입
return { name: user.nickname, email: user.email, image: user.profileImage, ...user, };강의에서 user를 그대로 리턴해서 사용하지 않고 user를 커스텀해준 것을 보았습니다. 현재 return하는 값은 Typescript에서 지정한 User 타입을 그대로 사용하면서 수정해서 값을 할당해줬다고 이해했는데 추가로 들어가는 ...user는 어디에서 사용할 수 있는지 궁금합니다. 추가로 제 코드에는 반환하는 정보가 더 많은데 어떤 식으로 값을 할당해서 사용할 수 있는지 궁금합니다.타입을 하나 만들어서 확장해서 사용하고 싶은데 따로 파일을 만들어줘야 할까요?
-
미해결Next + React Query로 SNS 서비스 만들기
Nextjs fetch, react-query 캐시 개념
Nextjs fetch도 캐시가 지원되고, react-query도 캐시가 지원되는걸로 이해를 했는데요 문득 궁금한 점이 생겨서 질문 드립니다. Nextjs fetch와 react-query에서의 캐시는 같은 개념인가요? 아니면 서로 다른 개념인가요?왜 Nextjs fetch를 안 쓰고 react-query를 쓰는 걸까요?Nextjs fetch는 어떨 때 쓰고 react-query는 어떨 때 쓰는 건가요?감사합니다.
-
해결됨Next + React Query로 SNS 서비스 만들기
Suspense 컴포넌트의 fallback 요소로 클라이언트 컴포넌트 전달?
안녕하세요.Suspense 컴포넌트의 fallback 요소로 서버 컴포넌트 전달은 문제가 없는데, 내부적으로 useEffect와 타이머를 사용하는 클라이언트 컴포넌트를 전달했더니 해당 훅과 관련된 내용은 모두 스킵되고 그냥 초기 렌더링 내용만 나오는 것 같은데, 애초에 클라이언트 컴포넌트는 전달이 불가능한걸까요?클라이언트 컴포넌트 사용 의도는 1초마다 로딩바 게이지가 증가하는 모습을 보여주고 싶어서 사용해보려고 했습니다.공식 문서를 봐도 해당 내용에 대해서는 언급이 없는 것 같습니다.감사합니다.
-
해결됨[리뉴얼] 맛집 지도앱 만들기 (React Native & NestJS)
pressed? 적용 후 버튼 클릭해도 색상이 변하지 않는 경우 [해결 방법]
프로젝트 재실행하면 되네요!! 참고하시길...npx react-native start --reset-cache
-
미해결Next + React Query로 SNS 서비스 만들기
useQuery 오류가 발생합니다
"use client"; import { useQuery } from "@tanstack/react-query"; import { getPostRecommends } from "@/app/(afterLogin)/home/_lib/getPostRecommends"; import Post from "@/app/(afterLogin)/_component/Post"; import { Post as IPost } from "@/model/Post"; export default function PostRecommends() { const { data, error, isLoading } = useQuery<IPost[]>({ queryKey: ["posts", "recommends"], queryFn: getPostRecommends, // gcTime은 staleTime보다 길어야한다 staleTime: 5 * 1000, // 새로 가져온 데이터를 몇 초 후에 fresh에서 stale로 바꿀 것인지 gcTime: 300 * 1000, }); if (isLoading) { return <div>Loading...</div>; } if (error) { return <div>Failed to load posts</div>; } return data?.map((post) => <Post key={post.postId} post={post} />); } react query에서 에러가 발생합니다 이유는 모르겠지만 useQuery부분에서 에러가 발생하는 것 같습니다getPostRecommend.ts는 이렇게 작성한 상태입니다export async function getPostRecommends() { const res = await fetch(`http://localhost:9090/api/postRecommends`, { next: { tags: ["posts", "recommends"], }, //캐시를 저장하라고 지정하는 태그 // 너무 강력하게 캐싱을 하면 새로운 데이터가 안불러와 질 수 있다 // 이런 일을 방지하기위해 새로고침을 해야하는 이때 tags를 사용한다 }); // The return value is *not* serialized // You can return Date, Map, Set, etc. if (!res.ok) { // This will activate the closest `error.js` Error Boundary throw new Error("Failed to fetch data"); } // 이렇게 하면 recommends를 키로 가지고 있는 서버에 있는 캐시가 날아감 // revalidateTag("recommends") // home으로 온 요청이 왔을때 페이지 전체의 캐시를 새로고침한다 // revalidatePath('/home') return res.json(); }근데 getPostRecoomed.ts가 잘못된거 같지는 않은것이처음 작동하는 queryClient.prefetchQuery는 잘 작동합니다
-
미해결Next + React Query로 SNS 서비스 만들기
하이드레이션 에러
Unhandled Runtime ErrorError: Hydration failed because the initial UI does not match what was rendered on the server. Warning: Did not expect server HTML to contain a <a> in <div>. See more info here: https://nextjs.org/docs/messages/react-hydration-error이런 에러가 발생하는데초반 랜더링과 뭐가 다르거나 랜덤 데이터를 사용하면 오류가 나온다는 것 같아서 혹시 faker를 사용해서 생긴느 에러인지 궁금합니다"use client"; import { useQuery } from "@tanstack/react-query"; import { getPostRecommends } from "@/app/(afterLogin)/home/_lib/getPostRecommends"; import Post from "@/app/(afterLogin)/_component/Post"; import { Post as IPost } from "@/model/Post"; export default function PostRecommends() { const { data } = useQuery<IPost[]>({ queryKey: ["posts", "recommends"], queryFn: getPostRecommends, // gcTime은 staleTime보다 길어야한다 staleTime: 60 * 1000, // 새로 가져온 데이터를 몇 초 후에 fresh에서 stale로 바꿀 것인지 }); return data?.map((post) => <Post key={post.postId} post={post} />); }여기에서 queryFn: getPostRecommends이 부분을 지워도 정상 작동하는데 queryFn: getPostRecommends 이것이 하는 기능이 무엇인가요?또 여기에서 Retech와 invalidate를 눌러도 트윗이 변하지 않습니다 혹시 위 에러와 관련이 있나요?