묻고 답해요
143만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
local storage
저장할 때밑에 코드를 쓰면 에러가 뜹니다ㅠㅠ어떻게 해결해야하나요?const initalTodoData = localStorage.getItem("todoData") ? JSON.parse(localStorage.getItem("todoData")) : []; function App() { const [todoData, setTodoData] = useState([initalTodoData]);
-
미해결Next + React Query로 SNS 서비스 만들기
api호출 3초뒤로 하고, loading.tsx 파일이 있을 때 없을때 /home/page.tsx
안녕하세요 제로초님제목처럼api호출 3초뒤로 하고, loading.tsx 파일이 있을 때 없을때 /home/page.tsx이런식으로 테스트를 해봤습니다. /home/page.tsx에서는 react-query로 ssr을 적용했고import style from './home.module.scss' import Tab from "@/app/(afterLogin)/home/_component/Tab"; import TabProvider from "@/app/(afterLogin)/home/_component/TabProvider"; import PostForm from "@/app/(afterLogin)/home/_component/PostForm"; import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query'; import TabDecider from "@/app/(afterLogin)/home/_component/TabDecider"; import { getPostRecommends } from '@/app/(afterLogin)/_lib/getPostRecommends'; export default async function Home() { const queryClient = new QueryClient(); await queryClient.prefetchInfiniteQuery({ queryKey: ['posts', 'recommends'], queryFn: getPostRecommends, initialPageParam: 0, }); const dehydratedState = dehydrate(queryClient); return ( <main className={style.main}> <HydrationBoundary state={dehydratedState}> <TabProvider> <Tab /> <PostForm /> <TabDecider /> </TabProvider> </HydrationBoundary> </main> ); } /mocks/handlers.tsx에서는 postRecommends를 가져오되 3초 delay를 줬습니다.http.get('/api/postRecommends', async ({ request }) => { console.log('----------------------------------handlers /api/postRecommends'); await delay(3000); const url = new URL(request.url); const cursor = parseInt(url.searchParams.get('cursor') as string) || 0; return HttpResponse.json( [ { postId: cursor + 1, User: User[0], content: `${cursor + 1} ${faker.lorem.paragraph()}`, Images: [{ imageId: 1, link: faker.image.urlLoremFlickr({ category: 'animals' }) }], createdAt: generateDate(), }, { postId: cursor + 2, User: User[0], content: `${cursor + 2} ${faker.lorem.paragraph()}`, Images: [ { imageId: 1, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 2, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, ], createdAt: generateDate(), }, { postId: cursor + 3, User: User[0], content: `${cursor + 3} ${faker.lorem.paragraph()}`, Images: [], createdAt: generateDate(), }, { postId: cursor + 4, User: User[0], content: `${cursor + 4} ${faker.lorem.paragraph()}`, Images: [ { imageId: 1, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 2, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 3, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 4, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, ], createdAt: generateDate(), }, { postId: cursor + 5, User: User[0], content: `${cursor + 5} ${faker.lorem.paragraph()}`, Images: [ { imageId: 1, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 2, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, { imageId: 3, link: faker.image.urlLoremFlickr({ category: 'animals' }) }, ], createdAt: generateDate(), }, ] ); }), 이 후에 ssr화면 테스트를 위해서브라우저의 자바스크립트 사용중지 했습니다.서버도 각각 재시작하고 테스트했습니다.그리고 나서 테스트한 화면입니다.아래 화면은 loading.tsx가 업을때,아래 화면은 loading.tsx가 있을때 입니다.선생님 강의에서ssr을 적용했을때 loading화면을 포기해야한다고 이해했었습니다.prefetchInfiniteQuqery를 쓰고 loading을 포기하거나, prefetchInfiniteQuqery을 포기하고 loading을 사용하거나.그런데 제가 테스트했을때는 ssr이라도 로딩 화면이 보이는걸로 봐서 제가 테스트를 잘못한건지,loading.tsx가 있는것 자체가 suspense를 사용한것과 동일한 효과를 주어서 보여지게 되는건지여쭙고 싶습니다. 제가 잘못테스트했다면 어떻게 테스트를 해야 좀 더 정확히 구분지을 수있을지도 궁금합니다.
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
list컴포넌트 생성하기
List 컴포넌트 생성하기에서 props로 key={data.id}를 넘겨주는데 저기서는 사용하지 않는데 넘겨주어야 하나요? 빼도 상관 없나요??
-
미해결Next + React Query로 SNS 서비스 만들기
The Middleware "/src/middleware" must export a middleware or a default function
미들웨어 matcher에 추가한 페이지 home으로 replace 시에 이렇게 에러가 뜨는데 이유를 모르겠습니다 ㅠ⨯ Error [ERR_HTTP_HEADERS_SENT]: Cannot append headers after they are sent to the clientat ServerResponse.appendHeader (node:_http_outgoing:689:11)at AsyncLocalStorage.run (node:async_hooks:346:14) import { auth as middleware } from "./auth"; export const config = { matcher: ["/compose/tweet", "/home", "/explore"], };
-
미해결Next + React Query로 SNS 서비스 만들기
msw 모바일 환경으로 local 접속 에러
localhost 환경에서 모바일로 접속 하였을때 IP 주소 확인하여 넣어줬는데도 api error가 발생하는데 이유를 잘 모르겠습니다.app.use( cors({ origin: 'http://내 IP 주소:3000', optionsSuccessStatus: 200, credentials: true }) );
-
미해결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를 사용하는 이유가 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
eslint 설치후 eslint.config.mjs 파일 생성이되고 .eslintrc.js 없습니다.
eslint 설치후 eslint.config.mjs 파일 생성이되고 .eslintrc.js 는 없습니다. 그리곳 eslint 설치시 첫 옵션 3가시 선택(강제로 고처주는옵션) 3번째 옵션도 없습니다. 2가지만 나오네요
-
미해결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 를 빠르게 사용할수있다고 생각되어서요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
localhost:3000/qqq 안됩니다..
사이트에 연결할 수 없다라고 나옵니다..혹시 몰라 5000/qqq도 해봤는데.. 액세스 거부로 나오구요.방화벽도 꺼져있습니다..ㅠ
-
미해결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를 사용하는 이유가 새로고침했을때를 위함이라고 이해하면 될까요? 혹은 다른 추가적인 이유가 더 있는지 궁금합니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out
nginx 부분을 따라하다가실수로 서버에서 나갔다가 다시 들어가려니까ssh: connect to host zzimzzim-front port 22: Connection timed out위와같은 에러가나면서 갑자기 들어가지지 않습니다.검색했는데도 해결되지 않아서 답답해다가이럴땐 어떤걸 참고하면 좋을지 조언 주시면 감사하겠습니다. ㅠ
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
sudo certbot --nginx 에러
ubuntu@ip-172-31-37-191:~/react-nodebird/prepare/front$ sudo certbot --nginx Saving debug log to /var/log/letsencrypt/letsencrypt.log Which names would you like to activate HTTPS for? We recommend selecting either all domains, or all domains in a VirtualHost/server block. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: zzimzzim.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel): Requesting a certificate for zzimzzim.com Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: zzimzzim.com Type: connection Detail: 3.37.101.58: Fetching http://zzimzzim.com/.well-known/acme-challenge/SNUl0WTK7OKWJ-oYyHTrIFuh67ww_P11CUJXw2zWRZk: Timeout during connect (likely firewall problem) Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains point to this nginx server and that it is accessible from the internet. Some challenges have failed. Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.위와같은 에러가 발생합니다.다른 질문들을 보고 제로초님https://www.zerocho.com/category/NodeJS/post/5ef450a5701d8a001f84baeb 따라해보았는데 해결되지 않아서 여쭤봅니다.
-
해결됨Next + React Query로 SNS 서비스 만들기
vercel 배포했는데 잘 되다가 안들어가집니다.
안녕하세요 제로초님 강의 잘 듣고있었는데, 강의 질문이 아니라 다른 질문이라 죄송합니다. 당장 다음주 월요일부터 심사가 시작되는 공모전에 나갈 프로젝트인데 잘 되던게 갑자기 안들어가져서요 ㅠㅠㅠㅠ 약 한달전에 배포 cicd 설정 다 해놓고 어제밤까지만해도 문제없이 잘 들어가졌는데 오늘 갑자기 안들어가지네요.. dns_probe_finished_nxdomain 이라고 뜬대요 근데 제 pc로는 캐시를 다 삭제하고 들어가도 잘 되거든요 근데 모바일에선 안되고 다른 사람은 안들어가진다고 하는데 원인이 뭘까요..? 제 pc에서도 크롬에서만 들어가지고 다른 브라우저는 안됩니다. 메일로 vercel에서 뭐 온것도 없습니다.. vercel 설정에서 domains에 문제도 없구요.메일로 vercel에서 뭐 온것도 없습니다.빌드 에러도 없구요무료플랜이었어서 유료플랜으로 업그레이드 했는데도 똑같습니다.. ㅠㅠ 도메인이름은 공개적으로 못 적지만 개인적으로 알려드리겠습니다. ci/cd 되어있어서 main에 계속 push해봐도 빌드 잘 되는데 들어가지지를 않아요...
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
Minified React error 콘솔에러 (hydrate)
안녕하세요 선생님카카오톡 공유하기 까지 듣고 화면을 테스트해보고 있는데Minified React error 이 에러가 떠서 질문드립니다. 메인화면, 상세보기에서 게시글이 있을때 나옵니다.Uncaught Error: Minified React error #418; visit https://reactjs.org/docs/error-decoder.html?invariant=418 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at lg (framework-ecc4130bc7a58a64.js:9:46457) at i (framework-ecc4130bc7a58a64.js:9:121052) at oO (framework-ecc4130bc7a58a64.js:9:99019) at framework-ecc4130bc7a58a64.js:9:98886 at oF (framework-ecc4130bc7a58a64.js:9:98893) at oS (framework-ecc4130bc7a58a64.js:9:93932) at x (framework-ecc4130bc7a58a64.js:33:1364) at MessagePort.T (framework-ecc4130bc7a58a64.js:33:1894)검색해봤더니 hydrate 쪽 이슈더라구요.혼자서 풀어보다가 잘 풀리지 않아서 여쭤봅니다.혹시 제로초님은 저런 에러가 나지 않으시는지난다면 어느 로직을 확인해야할지 조언 부탁드립니다./pages/post/[id].jsimport axios from 'axios'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { useSelector } from 'react-redux'; import AppLayout from '../../components/AppLayout'; import PostCard from '../../components/PostCard'; import { loadPost } from '../../reducers/post'; import { loadMyInfo } from '../../reducers/user'; import wrapper from '../../store/configurStore'; const Post = () => { const router = useRouter(); const { id } = router.query; const { singlePost } = useSelector((state) => state.post); return ( <AppLayout> {singlePost ? ( <> <Head> <title> {singlePost?.User.nickname} 님의 글 </title> <meta name="description" content={singlePost.content} /> <meta property="og:title" content={`${singlePost.User.nickname}님의 게시글`} /> <meta property="og:description" content={singlePost.content} /> <meta property="og:image" content={ singlePost.Images[0] ? singlePost.Images[0].src : 'https://nodebird.com/favicon.ico' } /> <meta property="og:url" content={`https://nodebird.com/post/${id}`} /> </Head> <PostCard post={singlePost}>{id}번 게시글</PostCard> </> ) : ( <div>존재하지 않는 게시물입니다.</div> )} </AppLayout> ); }; export const getServerSideProps = wrapper.getServerSideProps( (store) => async ({ req, params }) => { const cookie = req ? req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; // 쿠키가 브라우저에 있는경우만 넣어서 실행 // (주의, 아래 조건이 없다면 다른 사람으로 로그인 될 수도 있음) if (req && cookie) { axios.defaults.headers.Cookie = cookie; } await store.dispatch(loadMyInfo()); await store.dispatch(loadPost(params.id)); }, ); export default Post; /components/PostCard.jsimport { RetweetOutlined, HeartOutlined, HeartTwoTone, MessageOutlined, EllipsisOutlined, } from '@ant-design/icons'; import { Card, Popover, Button, Avatar, List } from 'antd'; import dayjs from 'dayjs'; import Link from 'next/link'; import PropTypes from 'prop-types'; import { useState, useCallback, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import CommentForm from './CommentForm'; import Followbutton from './FollowButton'; import PostCardContent from './PostCardContent'; import PostImages from './PostImages'; import { removePostRequestAction, likePostRequestAction, unLikePostRequestAction, retweetRequestAction, } from '../reducers/post'; dayjs.locale('ko'); const PostCard = ({ post }) => { // const { me: {id} } = useSelector((state) => state.user); // const id = me && me.id; // const id = me?.id; // 옵셔널 체이닝 연산자 const { removePostLoading } = useSelector((state) => state.post); const dispatch = useDispatch(); const [commentFormOpend, setCommentFormOpend] = useState(false); const id = useSelector((state) => state.user.me?.id); const liked = post.Likers.find((d) => d.id === id); const onLike = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(likePostRequestAction(post.id)); }, [id]); const onUnLike = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(unLikePostRequestAction(post.id)); }, [id]); const onToggleComment = useCallback(() => { setCommentFormOpend((prev) => !prev); }, []); const onRemovePost = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(removePostRequestAction({ id: post.id })); }, [id]); const onRetweet = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(retweetRequestAction(post.id)); }, [id]); return ( <div style={{ marginTop: 10 }}> <Card cover={post.Images[0] && <PostImages images={post.Images} />} actions={[ // 배열안에 들어가는 것들은 다 key를 넣어줘야 한다. <RetweetOutlined key="retweet" onClick={onRetweet} />, liked ? ( <HeartTwoTone key="heart" twoToneColor="#eb2f96" onClick={onUnLike} /> ) : ( <HeartOutlined key="heart" onClick={onLike} /> ), <MessageOutlined key="comment" onClick={onToggleComment} />, <Popover key="more" content={ <Button.Group> {id && post.User?.id === id ? ( <> <Button type="primary" key="modify"> 수정 </Button> <Button type="danger" key="delete" onClick={onRemovePost} loading={removePostLoading} > 삭제 </Button> </> ) : ( <Button type="dashed" key="report"> 신고 </Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>, ]} extra={id && <Followbutton post={post} />} title={ post.RetweetId ? `${post.User.nickname}님이 리트윗하셨습니다.` : null } > {post.RetweetId && post.Retweet ? ( <Card cover={ post.Retweet.Images[0] && ( <PostImages images={post.Retweet.Images} /> ) } > <div style={{ float: 'right' }}> {dayjs(post.createdAt).format('YYYY.MM.DD')} </div> <Card.Meta avatar={ <Link href={`/user/${post.Retweet.User.id}`}> <Avatar>{post.Retweet.User?.nickname[0]}</Avatar> </Link> } title={post.Retweet.User?.nickname} description={<PostCardContent postData={post.Retweet.content} />} /> </Card> ) : ( <> <div style={{ float: 'right' }}> {dayjs(post.createdAt).format('YYYY.MM.DD')} </div> <Card.Meta avatar={ <Link href={`/user/${post.User.id}`}> <Avatar>{post.User?.nickname[0]}</Avatar> </Link> } title={post.User?.nickname} description={<PostCardContent postData={post.content} />} /> </> )} </Card> {commentFormOpend && ( <div> {/* 게시글의 아이디 위해서 post 넘겨줌 */} <CommentForm post={post} /> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} renderItem={(item) => ( <List.Item key={item.id}> <List.Item.Meta title={item.User.nickname} avatar={ <Link href={`/user/${item.User.id}`}> <Avatar>{item.User.nickname[0]}</Avatar> </Link> } description={item.content} /> </List.Item> )} /> </div> )} </div> ); }; PostCard.propTypes = { post: PropTypes.shape({ id: PropTypes.number, User: PropTypes.object, content: PropTypes.string, createdAt: PropTypes.string, Comments: PropTypes.arrayOf(PropTypes.object), Images: PropTypes.arrayOf(PropTypes.object), Likers: PropTypes.arrayOf(PropTypes.object), RetweetId: PropTypes.number, Retweet: PropTypes.objectOf(PropTypes.any), }).isRequired, }; export default PostCard; 여유되실때 한번 봐주시면 감사하겠습니다