묻고 답해요
140만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Next + React Query로 SNS 서비스 만들기
msw text/event-stream 질문
안녕하세요. 강의를 다 듣고 혼자 프로젝트를 진행해보고 있는 중인데 궁금한 것이 생겨 질문 드립니다!msw로 모킹하여 스트림데이터를 넘겨주고, 브라우저에 데이터가 나타날 때 gpt 답변처럼 한글자씩 보여지게 처리하고 싶은데 공식문서를 봐도 잘 안되네요..(한글자씩 안 나오고 한번에 나타남) 감사합니다.버전 : msw 2.0참고 문서 : https://mswjs.io/docs/recipes/streaming/
-
해결됨Next + React Query로 SNS 서비스 만들기
Suspense와 prefetch 관련 질문이 있습니다.
안녕하세요 제로초님! 강의를 듣던 중 궁금한게 생겨서 질문 드립니다. TabDeciderSuspense 컴포넌트를 사용하는 것을 보았습니다.원래는 postRecommends의 데이터를 프리페칭해서 서버에서 데이터를 페칭하고 클라이언트에서 하이드레이션 하고있었는데, suspense를 사용해버리면 서버에서 프리페칭이 되는건지 궁금합니다.suspense를 적용하고 나서 페이지 새로고침 후에 페이지 document를 보면이렇게 prefetch한 데이터가 있는 것이 아니라 loading이 넘어와 있습니다.원래는 서버에서 prefetch한 데이터가 document로 넘어왔는데, suspense 적용 후 로딩스피너가 넘어 온 것으로 보아 suspense를 적용하면 prefetch가 되지 않는 것 인가요 ?? 그것이 아니라면 suspense를 적용하고 prefetch를 사용하는 이유가 궁금합니다.
-
미해결Next + React Query로 SNS 서비스 만들기
next-auth 질문 + 새소식 올려주신거 참고해서 await signIn('credentials', { ...data, redirect: true });수정한후 로그인 후 홈으로 이동안됩니다.
안녕하세요 선생님새소식 올려주신거 참고해서 await signIn('credentials', { ...data, redirect: true });수정한후 로그인 후 홈으로 이동안됩니다.home 갔다가 로그인화면으로 이동해버리는데 어디서 이동시키는건지 뒤저봐도 못 찾겠더라구요. 로그인화면으로 새로고침되는듯 합니다...true만 하고 이전 코드와 다 똑같은데 조언 부탁드립니다 ㅠ/src/app/(beforeLogin)/_component/Loginmodal.tsxconst onSubmit: SubmitHandler<formProps> = async (data: formProps) => { console.log(data); try { await signIn('credentials', { ...data, redirect: true }); router.replace('/home'); console.log('---------------------------------------after LoginModal login') } catch(error) { console.error(error); console.log('아이디와 비밀번호가 일치히자 않습니다.'); } }; src/middleware.tsexport async function middleware() { const session = await auth(); console.log(session, '------------------------------middleware session') if (!session) { return NextResponse.redirect('http://localhost:3001/i/flow/login'); } } // See 'Matching Paths' below to lean more // 미들웨어를 적용할 라우트로 로그인을 해야하는 페이지 // 페이지 접근관리 하기 쉬워짐 export const config = { matcher: ['/compose/tweet', '/home', '/explore', '/messages', '/search'] } /src/auth.tsimport NextAuth, {CredentialsSignin} from "next-auth" // import CredentialsProvider from "next-auth/providers/credentials" import Credentials from "next-auth/providers/credentials" import { NextResponse } from 'next/server'; export const { // api 라우트 handlers: { GET, POST }, // auth 함수 실행하면 로그인 유무알 수 있다. auth, // 로그인 하는 함수 signIn } = NextAuth({ pages: { signIn: "/i/flow/login", newUser: '/i/flow/signup', }, providers: [ Credentials({ // You can specify which fields should be submitted, by adding keys to the `credentials` object. // e.g. domain, username, password, 2FA token, etc. credentials: { id: {}, password: {}, }, authorize: async (credentials) => { console.log('-------------------------------------------auth.ts'); const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials) }) // 여기 주목!!! 서버에서 에러가 발생할 때 그 에러 내용이 서버에 담겨 있을 겁니다. console.log(authResponse.ok, authResponse.status, authResponse.statusText) if (!authResponse.ok) { const credentialsSignin = new CredentialsSignin(); if (authResponse.status === 404) { credentialsSignin.code = 'no_user'; } else if (authResponse.status === 401) { credentialsSignin.code = 'wrong_password'; } throw credentialsSignin; } // 로그인 성공 const user = await authResponse.json(); console.log('user', user); // return user object with the their profile data return { ...user, name: user.nickname, email: user.id, } }, }), ] }) next-auth 소식이나 말쓰하시는거 들어봐도 아직 안정화가 되지 않은것같은데 선생님은 next-auth를 실무에 도입해도 된다고보시나요? 아니면 nodebird 처럼 express로 하는걸 더 추천하실지 궁금합니다. 넥스트오쓰에서 가입한 사용자를 데이터베이스로 볼수있는지도 궁금하구 어차피 배워야할것이니 이런저런 부족한게 좀 있더라도 밀고나가야하는지.. 여유되실때 확인해주시면 감사하겠습니다.
-
미해결Next + React Query로 SNS 서비스 만들기
로그인후 바로 뒤로가기, 회원가입 후 홈으로 이동하고 session에 정보 안쌓임
안녕하세요 선생님.로그인 후에 새로고침이나 url을 치고 /(메인)으로 가면 홈으로 잘 리다이렉트 되는데 로그인 후 바로 뒤로가기를 누르면 리다이렉트되지 않고 / 페이지로 이동합니다. 이 부분 어떻게 하면 좋을지 문의 드립니다. 회원가입 후303뜨면서 홈으로 이동하는데, 이 303이 괜찮은건지와이동 후에 로그아웃버튼에서 session 정보를 가져오지 못하고 있습니다. 새로고침하면 잘 나옵니다. me정보를 가져올때 useEffect로 바꿔야할지 문의 드립니다.로그를 보면 회원가입 후, 로그인도 잘 되는것 같은데 어떤부분을 확인해야할지 알려주시면 감사하겠습니다. @/app/(beforelogin)/_lib/signup.tsx'use server'; import { signIn } from '@/auth'; import { redirect } from 'next/navigation'; 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 { console.log('-------------------------signup start'); const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/users`, { method: 'post', body: formData, credentials: 'include', // cookie 전달 위해서 }); // console.log(response); console.log(response.status); if (response.status === 403) { return { message: 'user_exists' }; } const user = await response.json(); console.log(user, '-------------------------signup'); shouldRedirect = true; // 회원가입 성공하고 로그인 시도 await signIn("credentials", { username: formData.get('id'), password: formData.get('password'), redirect: false, }) } catch (error) { console.error(error); return { message: null }; } if (shouldRedirect) { redirect('/home'); // redirect는 try/catch문에서 쓰면 안된다. } } export default onSubmit; @/auth.tsimport NextAuth from "next-auth" // import CredentialsProvider from "next-auth/providers/credentials" import Credentials from "next-auth/providers/credentials" export const { // api 라우트 handlers: { GET, POST }, // auth 함수 실행하면 로그인 유무알 수 있다. auth, // 로그인 하는 함수 signIn } = NextAuth({ pages: { signIn: "/i/flow/login", newUser: '/i/flow/signup', }, providers: [ Credentials({ // You can specify which fields should be submitted, by adding keys to the `credentials` object. // e.g. domain, username, password, 2FA token, etc. credentials: { id: {}, password: {}, }, authorize: async (credentials) => { console.log('-------------------------------------------auth.ts'); const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(credentials) }) // console.log('authResponse-----------------------------------', authResponse); // 로그인 실패 if (!authResponse.ok) { return null } // 로그인 성공 const user = await authResponse.json(); console.log('user', user); // return user object with the their profile data return { ...user, name: user.nickname } }, }), ] }) @/app/(beforelogin)/_component/loginmodal.tsx'use client'; import style from '@/app/(beforeLogin)/_component/login.module.scss'; import { useRouter } from 'next/navigation'; import { SubmitHandler, useForm } from 'react-hook-form'; // import { signIn } from '@/auth'; // 서버환경일 때 import { signIn } from 'next-auth/react'; // 클라이언트일 때 type formProps = { id: string, password: string, } export default function LoginModal() { const { register, handleSubmit, formState: { errors } } = useForm<formProps>(); const router = useRouter(); const onClickClose = () => { router.back(); // TODO: 뒤로가기가 /home이 아니면 /home으로 보내기 }; const onSubmit: SubmitHandler<formProps> = async (data: formProps) => { console.log(data); try { await signIn('credentials', { ...data, redirect: false }); router.replace('/home'); } catch(error) { console.error(error); console.log('아이디와 비밀번호가 일치히자 않습니다.'); } }; return ( <div className={style.modalBackground}> <div className={style.modal}> <div className={style.modalHeader}> <button className={style.closeButton} onClick={onClickClose}> <svg width={24} viewBox='0 0 24 24' aria-hidden='true' className='r-18jsvk2 r-4qtqp9 r-yyyyoo r-z80fyv r-dnmrzs r-bnwqim r-1plcrui r-lrvibr r-19wmn03'> <g> <path d='M10.59 12L4.54 5.96l1.42-1.42L12 10.59l6.04-6.05 1.42 1.42L13.41 12l6.05 6.04-1.42 1.42L12 13.41l-6.04 6.05-1.42-1.42L10.59 12z'></path> </g> </svg> </button> <div>로그인하세요.</div> </div> <form onSubmit={handleSubmit(onSubmit)}> <div className={style.modalBody}> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor='id'> 아이디 </label> <input id='id' className={style.input} type='text' placeholder='' {...register('id', { required: '아이디를 입력해주세요.' })} /> {errors.id?.message && typeof errors.id.message === 'string' && <p>{errors.id.message}</p>} </div> <div className={style.inputDiv}> <label className={style.inputLabel} htmlFor='password'> 비밀번호 </label> <input id='password' className={style.input} type='password' placeholder='' {...register('password', { required: '비밀번호를 입력해주세요.' })} /> {errors.password?.message && typeof errors.password.message === 'string' && <p>{errors.password.message}</p>} </div> </div> <div className={style.modalFooter}> <button className={style.actionButton}>로그인하기</button> </div> </form> </div> </div> ); } @/app/(beforelogin)/page.tsximport Main from '@/app/(beforeLogin)/_component/Main'; import { auth } from '@/auth'; import { redirect } from 'next/navigation'; export default async function Home() { console.log('--------------before login home'); const session = await auth(); if (session?.user) { redirect('/home'); return null; } return ( <> <Main /> </> ); }
-
미해결Next + React Query로 SNS 서비스 만들기
tailwind 질문입니다.
테일윈드랑 비교하셨는데욥!tailwind는 결국 프레임워크이고다른 css 는 직접 작성을 해야하니 목적이 다르다고 생각했는데요.css를 직접 작성하신 이유가있을까요??테일윈드를 쓰면 호불호가 있긴 하겠지만 강의특성상 css 를 빠르게 사용할수있다고 생각되어서요
-
미해결Next + React Query로 SNS 서비스 만들기
앱라우터를 쓰면서 컴포넌트 분류가 궁금합니다...
제로초님 강의를 보면서 새롭게 페이지 작업을 하고 있는데 최다 클라이언트 컴포넌트로 작업이 되고 잇어서 고민이 됩니다.예를들어 쇼핑몰의 상세페이지를 만드는데 좋아요나 판매하기 기타등등 여기에 써야하는 click이벤트 들때문에 서버컴포넌트를 쓰기는 애매해서 클라이언트 컴포넌트로 작업 중인데... 모든 컴포넌트들이 이런 상황입니다ㅠㅠ 혹시 클라이언트 컴포넌트와 서버컴포넌트를 나누는 기준 같은거 알수 잇을까요??
-
미해결Next + React Query로 SNS 서비스 만들기
next.config.js
next.config.js 파일이 아닌 next.config.mjs 파일이 생기는데 import 문을 사용하면 해결이 되는 것 같습니다. 그런데 기본적으로 js -> mjs파일이 셋팅되는 이유와 mjs파일을 그대로 쓰는게 나을지 js 파일로 사용하면 될지 궁금합니다.
-
미해결Next + React Query로 SNS 서비스 만들기
msw 실행 방식에 관해서
안녕하세요 선생님msw 방식에 대해서 강의해주신것과 다른 질문들을 보다가 궁금한게 생겨서 문의드립니다.강의에서는 클라리언트 환경에서는 mockServiceWorker.js가 api(요청)를 가로채서, http.ts(서버)로 보낸다.그리고 handlers.ts가 실행된다. 이렇게 말씀해주셨는데, 다른질문에서msw express 서버는 next server에서 요청보내는 걸 모킹하는 것이고요. MSWComponent는 브라우저에서 요청을 보내는 걸 모킹하는 것 이런 답변을 보았습니다. 그렇다면 제가 이해하기로는기본적으로는 아래와 같이 실행이 되는데,클라리언트 환경에서는 mockServiceWorker.js가 api(요청)를 가로채서, http.ts(서버)로 보낸다.그리고 handlers.ts가 실행 next server에서 요청받은 것은(ssr) createMiddleware에서 받아서 handlers로 보내고클라이언트에서 요청받은 것은 MSWComponent에서 받아서, brwoser로 보낸다음에 handlers로 보낸다로 이해하면 될까요?
-
미해결Next + React Query로 SNS 서비스 만들기
modal에 intercept route를 사용하는 이유가 새로고침했을때를 위함인가요?
안녕하세요 선생님.수업을 듣다 궁금한게 생겨서 문의 드립니다.모달창만 열려면 intercept를 쓰지 않고 패러렐 라우트에 일반라우트 폴더를 넣어도 작동하더라구요. (beforelogin)/@modal2/i/flow/login하지만 새로고침때는 그 페이지를 찾을 수 없기에 not-found.tsx를 호출하는걸 확인했습니다.modal에 intercept route를 사용하는 이유가 새로고침했을때를 위함이라고 이해하면 될까요? 혹은 다른 추가적인 이유가 더 있는지 궁금합니다.
-
미해결Next + React Query로 SNS 서비스 만들기
searchParams props
안녕하세요search 페이지 props에서 searchParams props가 기본적으로 제공된다고 알고있는데 저는 콘솔로 찍어보니 나오지 않습니다..! 혹시 제가 잘못적용한건지 아니면 다시한번 확인해야 될곳이 있을까요?/import SelectBox from "@/components/common/SelectBox"; import SelectStack from "@/components/common/SelectStack"; import Peoples from "@/components/people/Peoples"; import SearchForm from "@/components/common/SearchForm"; type Props = { searchParams: { page: string; size: string; sort?: string; keyword?: string; position?: string; teckStack?: string; }; }; export default function Page({ searchParams }: Props) { console.log("??", searchParams); return ( <div> <div className="relative flex items-center gap-4"> <h1 className="text-[36px] font-bold">People</h1> </div> <div className="flex justify-between"> <div className="flex gap-3"> <SelectBox options={option} title="포지션" /> <SelectStack /> </div> <SearchForm keyword={searchParams.keyword as string} /> </div> <Peoples searchParams={searchParams} /> </div> ); }
-
미해결Next + React Query로 SNS 서비스 만들기
하트 클릭 시 Hearts 배열 업데이트 관련 질문
하트 버튼을 눌러서 Heart 배열에 userId를 넣어줄 때Hearts: [...value.pages[pageIndex][index].Hearts, { userId: session?.user?.email as string }],위와 같이 기존 Hearts 배열에 본인 userId 값을 추가하는 방식이 아니라Hearts: [{ userId: session?.user?.email as string }],이렇게 Hearts 배열에 본인 userId 값만 넣어주는 이유는 무엇인가요? 어차피 보여지는 결과는 동일하기 때문에 굳이 배열을 복사하는 과정을 거치지 않아 성능을 높이기 위함이라고 보면 될까요?
-
미해결Next + React Query로 SNS 서비스 만들기
프리즈마로 api만드시는 강의는 언제쯤 나올지 알 수 있을까요?
안녕하세요 선생님프리즈마로 api만드시는 강의는 언제쯤 나올지 알 수 있을까요?직접만든 api랑 이번수업에서 배울next로 플젝을 만들고 싶은데 궁금합니다.
-
해결됨Next + React Query로 SNS 서비스 만들기
next-auth로 다시 한번 질문을 올립니다...
공지의 올려주신대로 auth.ts 를 수정하였으며 loginModal 의 redirect:trueexport const { handlers: { GET, POST }, auth, signIn, } = NextAuth({ pages: { signIn: "/i/flow/login", newUser: "/i/flow/signup", }, callbacks: { // async signIn() // async authorized({ auth }) { // if (!auth) { // // 쿠키가 없으면 로그인 페이지로 돌리기 // return NextResponse.redirect("http://localhost:3000/i/flow/login"); // } // return true; // }, }, providers: [ CredentialsProvider({ async authorize(credentials) { const authResponse = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, // NEXT_PUBLIC_BASE_URL=http://localhost:9090 { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ id: credentials.username, password: credentials.password, }), } ); // 여기 주목!!! 서버에서 에러가 발생할 때 그 에러 내용이 서버에 담겨 있을 겁니다. console.log(authResponse.status, authResponse.statusText); if (!authResponse.ok) { const credentialsSignin = new CredentialsSignin(); if (authResponse.status === 404) { credentialsSignin.code = "no_user"; } else if (authResponse.status === 401) { credentialsSignin.code = "wrong_password"; } throw credentialsSignin; } const user = await authResponse.json(); console.log("user", user); // id, name, image, email만 허용 return { id: user.id, name: user.nickname, image: user.image, }; }, }), ], }); 아래와 같은 오류가 계속 해서 발생하고 있습니다. TypeError: next_dist_server_web_exports_next_request__WEBPACK_IMPORTED_MODULE_0__ is not a constructor pakage.json 의 버젼은 아래와 같이 사용하고 있고요 "@auth/core": "^0.27.0", "next-auth": "^5.0.0-beta.16",혹시 이 부분의 같은 에러가 나오신 분들 중 해결 하신 분들이 있을까요?
-
미해결Next + React Query로 SNS 서비스 만들기
제로초님 에러를 해결 하다가 전달 드립니다 ㅠㅠ
react-query를 사용 하고 있는 그중 useQuery를 사용 하고 있습니다 네트워크 탭상에서는 한번만 호출 햇는데 console에서는 회색으로 아래와 같이 두번 호출 한것 처럼 나오는데 이건 어떻게 못막을까요?!조건을 걸어서 확인을 해봤지만 계속 해서 나오더라구여 ㅠㅠ두번의 호출인지 소켓을 요청 했을때 두번을 요청 하더라구여 ㅜㅜ
-
미해결Next + React Query로 SNS 서비스 만들기
router.replace() 질문
팔로우 버튼을 누르면 로그인 화면으로 가도록 기능을 넣어봤습니다.FollowRecommend 컴포넌트에서 router.replace('/i/flow/login'); 를 하면 notfound 페이지가 뜹니다. (당연히 router.replace('/home')을 해도 마찬가지 입니다.<- /i/flow/login으로 가기때문)그러나 이 상태에서 새로고침하면 로그인 화면 잘 뜹니다.이렇게 root 경로로는 잘 넘어가서 일단은 메인화면으로 해놨는데, 왜 이런 현상이 발생하는 걸까요..? 감사합니다.
-
미해결Next + React Query로 SNS 서비스 만들기
is not valid JSON 에러 ..!
import { http, HttpResponse } from "msw"; const testData = [ { result: "success", userId: 2, nickname: "닉네임", favoriteCount: 223, viewCount: 336, position: "Backend", userFileUrl: "/Users/user/Desktop/pictures/userPicture.jpg", year: "경력없음", techStack: "react, java, ---", softSkill: "소통, 적극성, ---", links: "http://블로그주소", alarmStatus: true, content: "안녕하세요 구인 중입니다", }, ]; export const handlers = [ http.post("/api/post", () => { return HttpResponse.text(JSON.stringify("ok")); }), http.get("/api/get", ({ request }) => { console.log("request", request); return HttpResponse.json(testData); }), ]; export default handlers;"use client"; import { redirect } from "next/navigation"; export default function Home() { const handleClick = () => { fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/get`, { method: "get", }) .then((res) => console.log("res", res.json())) // JSON 형식으로 변환된 데이터를 가져옴 .then((data) => console.log("res?", data)); // JSON 데이터를 로그에 출력 }; return ( <> <div> <button onClick={handleClick}>test btn</button> <p>계정을 생성하세요</p> <form> <div> <> <label htmlFor="id">아이디</label> <input type="text" id="id" name="id" required /> </> <> <label htmlFor="name">이름</label> <input type="text" id="name" name="name" required /> </> <button type="submit">가입하기</button> </div> </form> </div> </> ); } browser.ts, handlers.ts, http.ts, MSWComponent.tsx, server.ts 는 제로초님과 다 동일하게 설정하였고 get부분에서 위와같이 하였는데 Unexpected token '<', "<!DOCTYPE "... is not valid JSON 라는 에러가 계속 발생합니다 ㅠㅠ 어떤부분이 문제일까요.. !console.log("res", res.json())) 이부분에서 res만 콘솔로 확인하면 이렇게 나옵니다..!
-
미해결Next + React Query로 SNS 서비스 만들기
cookies().toString() 질문
getUserServer headers 내부 Cookie에 cookies().toString()을 통해 넣어주셨는데 쿠키 안에 있는 모든 값들을 통으로 toString 변환하여 넣어준 이유가 어떤 이유일까요?
-
미해결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에서 원래 캐치해야하는데 안 되는 것인지 아니면 수강생들에게 숙제로 낸 것인지 궁금합니다.
-
해결됨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옵션을 주었는데 에러가 왜 뜨는지 궁금합니다.바쁘실텐데 많은 질문 죄송합니다 ㅜㅜ