묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Next + React Query로 SNS 서비스 만들기
제가 이해한 dehydrate의 동작방식이 맞는지 여쭤보고 싶어서 질문드립니다!
서버 컴포넌트 강의의 경우, Home 페이지 컴포넌트에서의 prefetchQuery를 통해서 pre-rendering할 데이터를 미리 가져와서 JS-disabled되었을 때에도 사용자에게 보여줄 페이지를 만들고, PostRecommends의 useQuery를 통해서 실제 서버에서 데이터를 가져오는 것이 맞는 건가요?? 그게 아니라면 강의 마지막에 선생님께서 보여주신 pre-rendering 페이지에서 텍스트는 잘 받아오는데 이미지가 나오지 않는 이유가 궁금합니다!
-
미해결Next + React Query로 SNS 서비스 만들기
모달창 띄울 때 화면 회색빛 뜨는 것 질문
모달창을 띄울 때 배경이 회색빛으로 바뀌는 것은 뭐로인해 적용되는 것인가요? 그냥 하나하나 css를 적용시킨 것인가요?
-
미해결Next + React Query로 SNS 서비스 만들기
port를 80으로 다시 바꿔주셨는데, 궁금합니다!
보너스: 배포 맛보기(과금주의)port를 80으로 다시 바꿔주셨는데, 궁금합니다!최근에 AWS에 관심이 상당히 많이 생겨서 리눅스도 공부하고, 가상머신에 우분투 설치하여 돌려봤습니다. 많이 부족함을 느끼고 있는데, 혹시 aws, docker등 설명해주시는 부분이 있으실까요? node bird에서는 aws section이 있는 것을 봤는데 살짝 아쉽습니다 ㅜㅜ
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
리트윗 라우터에서 설정한 리트윗 검사가 되지 않습니다!(중복 리트윗 체크 및 본인 게시글 리트윗이 막아지지 않는 문제)
안녕하세요! React로 NodeBird SNS 만들기섹션4 리트윗하기 강의 시청 중 발생한 에러에 대해 질문 드립니다!항상 강의 잘 보고 있습니다! 제로초님 감사합니다! [리트윗 라우터에서 설정한 리트윗 검사]1. 자기 게시글을 리트윗한 경우2. 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗한 경우3. 이미 리트윗한 게시글을 또 리트윗 하는 경우(중복 리트윗) 여러 개의 게시글을 가져오는 라우터에서 리트윗한 게시글, 작성자, 이미지 모델을 넣었으며,게시글 Reducer에서 리트윗 실패 시 실패 확인을 'draft.retweetError = action.error;'로 확인하였습니다.게시글 Saga에서도 리트윗 요청 실패 시 실패 결과를 'error: err.response.data'로 설정했습니다.★ 프론트, 백엔드 쪽 터미널과 콘솔, 리덕스, 네티워크 쪽에 오류가 없음을 확인하였습니다.코드 오타가 원인이라고 파악해 제로초님의 깃허브와 제 코드를 비교하며 확인했으나원인을 찾을 수 없어 질문 올립니다. 아래는 가장 의심되는 코드 입니다.중요하지 않은 코드는 '. . .' 으로 생략하였습니다.front/pages/index.js // React 라이브러리 훅 불러오기 import React, { useEffect } from 'react'; . . . // 홈 컴포넌트(사용자 정의 태그) const Home = () => { const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post); . . . // 리트윗 실패 시 리트윗 에러 alert 창 띄우기 useEffect(() => { if (retweetError) { alert(retweetError); } }, [retweetError]);front/components/PostCard.js따로 게시글을 리트윗 실패(RETWEET_FAILURE) 액션을 디스패치 해야 하는지 의심이 들었습니다!// 게시글 카드 컴포넌트(사용자 정의 태그) const PostCard = ({ post }) => { . . . // 리트윗 버튼 콜백 함수 const onRetweet = useCallback(() => { // 로그인을 안했을 때 '로그인이 필요합니다.' alert 창 띄우기 if (!id) { return alert('로그인이 필요합니다.'); } /* 리트윗 요청 액션 객체 디스패치 */ return dispatch({ type: RETWEET_REQUEST, // 리트윗 요청 액션 data: post.id, // 게시글 아이디 }); }, [id]); . . . return ( <div style={{ marginBottom: '20px' }}> <Card /* ---------- 이미지 : 이미지는 1개 이상 ---------- */ cover={post.Images[0] && <PostImages images={post.Images} />} /* ---------- 액션 버튼 ---------- */ actions={[ /* ---------- 리트윗 버튼 ---------- */ <RetweetOutlined key="retweet" onClick={onRetweet} />, . . . ]} /* 카드 제목 */ title={post.RetweetId // 리트윗 게시글이면 게시글 사용자 닉네임님이 리트윗 하셨습니다. 제목 써주기 ? `${post.User.nickname}님이 리트윗 하셨습니다.` // 일반 게시글이면 제목 안 써주기 : null } . . . > {/* Card 닫기 */} {/* ---------- 리트윗 게시글 ---------- */} {post.RetweetId && post.Retweet ? ( <Card /* ---------- 이미지 : 이미지는 1개 이상 ---------- */ cover={post.Retweet.Images[0] && <PostImages images={post.Retweet.Images} />} > <Card.Meta // 메인 게시글 리트윗한 사용자 닉네임의 첫 번째 글자를 // 아바타 아이콘으로 표시 avatar={<Avatar>{post.Retweet.User.nickname[0]}</Avatar>} // 메인 게시글 리트윗한 게시글 작성자 이름 title={post.Retweet.User.nickname} // 메인 게시글 게시글 콘텐츠 description={<PostCardContent postData={post.Retweet.content} />} /> </Card> ) : ( /* ---------- (리트윗을 하지않은) 일반 게시글 ---------- */ <Card.Meta // 메인 게시글 사용자 닉네임의 첫 번째 글자를 아바타 아이콘으로 표시 avatar={<Avatar>{post.User.nickname[0]}</Avatar>} // 메인 게시글 작성자 이름 title={post.User.nickname} // 메인 게시글 콘텐츠 description={<PostCardContent postData={post.content} />} /> )} </Card> back/routes/post.js// 리트윗 라우터 router.post('/:postId/retweet', isLoggedIn, async (req, res, next) => { // POST /post/동적 히든/retweet try { /* 존재하지 않는 게시글이 있는지 검사하는 함수 */ const post = await Post.findOne({ where: { id: req.params.postId }, // 모델 가져오기 include: [{ model: Post, as: 'Retweet', // as: 'Retweet'으로 include를 해주면 post.retweet이 생긴다. }], }); /* ---------- 만약 존재하지 않는 게시글이 있다면 400번대 에러 출력 ---------- */ if (!post) { return res.status(403).send('존재하지 않는 게시글입니다.'); } /* 자기 게시글을 리트윗하기 or 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗하기 막기 */ if (req.user.id === post.UserId || (post.Retweet && post.Retweet.UserId === req.user.id)) { return res.status(403).send('자신의 글은 리트윗할 수 없습니다.'); } // 리트윗할 Id : 리트윗한 게시글이면 리트윗 아이디 사용 or 아니면 게시글 아이디 사용 const retweetTargetId = post.RetweetId || post.id; /* 이미 리트윗한 게시글을 또 리트윗하는지 검사하는 함수(두 번 리트윗 막기) */ const exPost = await Post.findOne({ where: { UserId: req.user.id, RetweetId: retweetTargetId, }, }); /* ----- 만약 이미 리트윗한 게시글을 또 리트윗한다면 400번대 에러 출력 ----- */ if (exPost) { return res.status(403).send('이미 리트윗했습니다.'); } /* await : 실제로 데이터가 들어감, create : 테이블 안에 데이터를 넣음 */ const retweet = await Post.create({ UserId: req.user.id, RetweetId: retweetTargetId, content: 'retweet', // 게시글 모델에서 allowNull을 false로 설정했기 때문에 게시글 콘텐츠가 필수다. }); /* ---------- 내가 어떤 게시글을 리트윗했는지 찾는 함수 ---------- */ const retweetWithPrevPost = await Post.findOne({ where: { id: retweet.id }, // 모델 가져오기 include: [{ /* ---------- 리트윗한 게시글 ---------- */ model: Post, as: 'Retweet', // 리트윗한 게시글이 post.Retweet으로 담긴다. // 모델 가져오기 include: [{ /* ---------- 리트윗한 게시글의 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }, { /* ---------- 리트윗한 게시글의 이미지 ---------- */ model: Image, }] }, { /* ---------- 게시글 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }, { /* ---------- 게시글 좋아요 누른 사람들 ---------- */ model: User, as: 'Likers', attributes: ['id'], // id 데이터만 가져오기 }, { /* ---------- 게시글 이미지 ---------- */ model: Image, }, { /* ---------- 게시글 답글 ---------- */ model: Comment, // 모델 가져오기 include: [{ /* ---------- 게시글 답글의 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }], }], }); /* 게시글 작성 성공 시 어떤 게시글을 리트윗 했는지에 대한 정보를 프론트로 돌려주기 */ res.status(201).json(retweetWithPrevPost); /* ---------- 에러 캐치 ---------- */ } catch (error) { console.error(error); next(error); } }); +++ 줄바꿈이 되지 않은 문제를 수정하였습니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
인피니트 스크롤링 적용시 LOAD_POST_REQUEST 두번 찍히는 문제
안녕하세요 선생님 상황)인피니트 스크롤링 적용시 LOAD_POST_REQUEST 두번 찍히는 상황인데 이거의 원인과 해결방법을 어떻게 찾을 수 있을까요? loadPostsLoading과 throttle을 적용했는데도 2번씩 실행되는 상황입니다.redux) 작성한 코드) 10,000 자이하만 적을 수 있어서 LOAD_POSTS_REQUEST 관련 코드만 올립니다..!!pages/index.jsimport React, { useEffect } from 'react'; import {useDispatch, useSelector} from 'react-redux'; import AppLayout from '../components/AppLayout'; import PostCard from '../components/PostCard'; import PostForm from '../components/PostForm'; import {LOAD_POSTS_REQUEST} from '../reducers/post'; const Home = () => { const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post); useEffect(() => { dispatch({ type: LOAD_POSTS_REQUEST, }); }, []); useEffect(() => { function onScroll() { if(window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight-300) { if(hasMorePosts && !loadPostsLoading) { dispatch({ type: LOAD_POSTS_REQUEST, }); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); }; }, [hasMorePosts, loadPostsLoading]); return ( <AppLayout> {me && <PostForm />} {mainPosts.map((post) => <PostCard key={post.id} post={post} />)} </AppLayout> ); }; export default Home;reducers/post.jsimport shortId from 'shortid'; import {produce} from 'immer'; import faker from 'faker'; export const initialState = { mainPosts:[], imagePaths: [], //게시물 저장 경로 hasMorePosts: true, loadPostsLoading: false, //게시글 로드 완료시 true loadPostsDone: false, loadPostsError: null, } export const generateDummyPost = (number) => Array(number).fill().map(() => ({ id: shortId.generate(), User: { id: shortId.generate(), nickname: faker.name.findName() }, content: faker.lorem.paragraph(), Images: [{ src: 'https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_1280.jpg' //faker.image.imageUrl(640, 480, true), lorempixel.com 고장나서 임시로 }], Comments: [{ User: { id:shortId.generate(), nickname:faker.name.findName(), }, content:faker.lorem.sentence(), }], })); export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST'; export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS'; export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE'; const dummyPost = (data) => ({ id: data.id, content: data.content, User: { id:1, nickname:'해지니', }, Images: [], Comments: [], }); const dummyComment = (data) => ({ id: shortId.generate(), content: data, User: { id: 1, nickname: '제로초' }, }); const reducer = (state = initialState, action) => produce(state, (draft) => { switch(action.type){ case LOAD_POSTS_REQUEST: draft.loadPostsLoading = true; draft.loadPostsDone = false; draft.loadPostsError = null; break; case LOAD_POSTS_SUCCESS: draft.loadPostsLoading = false; draft.loadPostsDone = true; draft.mainPosts = action.data.concat(draft.mainPosts); draft.hasMorePosts = draft.mainPosts.length < 50; break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; default: break; } }); export default reducer; sagas/post.jsimport { all, fork, takeLatest, put, delay, throttle } from 'redux-saga/effects'; import axios from 'axios'; import shortId from 'shortid'; import { LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, generateDummyPost, } from '../reducers/post'; function loadPostsAPI(data){ return axios.get('/api/post', data); } function* loadPosts(action) { try{ // const result = yield call(loadPostsAPI, action.data); yield delay(1000); yield put({ type: LOAD_POSTS_SUCCESS, data:generateDummyPost(10) }); } catch(err) { yield put({ type: LOAD_POSTS_FAILURE, data: err.response.data }); } } function* watchLoadPosts(){ yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts); } export default function* postSaga() { yield all([ fork(watchLoadPosts) ]); }사용중인 OS) macOS (Apple M1 Pro)
-
해결됨Next + React Query로 SNS 서비스 만들기
fetch 사용이유
안녕하세요 강의중에는 fetch를 사용하는데기존에 프로젝트를 진행할때,baseUrl 설정이나 기본 헤더 설정, 인터셉터 및 json 직렬화 등 의 불편함으로 axios를 사용하였었는데next에서 캐싱관련해서 fetch를 확장해서 제공해주기때문에 axios를 사용하지않고 fetch로 사용하는걸까요?
-
해결됨React + GPT API로 AI회고록 서비스 개발 (원데이 클래스)
프롬프트 명령어 - 입력값과 동일한 언어로 받으려면 어떻게 작성할까요?
좋은 내용 감사합니다.프롬프트 내용중에 "Translate Into Korean~" 이라는 내용으로 답변을 한글로 받게 됩니다.혹시 [events] 밑에 오는 사용자 입력값과 동일한 언어로 결과를 받고 싶다면 어떻게 작성하면 될까요?강의 내용을 기준으로 다국어 서비스를 만들려고 하는데, 영어가 짧아서 질문 드려요
-
해결됨Next + React Query로 SNS 서비스 만들기
인터셉트 라우팅 적용 후 page.tsx 파일명을 변경 했을 때, 404 에러 발생
안녕하세요. 제로초 강사님. 수업 그대로 따라가고 있었는데 궁금한 점이 생겨서 질문 드립니다.영상과 동일하게 따라하고 있었고, 질문이 있는 파일 경로는 다음과 같습니다.src\app\(beforeLogin)\@modal\(.)i\flow\login\page.tsx이 때,파일명을 page.tsx --> qwer.tsx로 변경하면 404 에러를 띄웁니다. 혹시나 해서,src\app\(beforeLogin)\i\flow\login\page.tsx의 파일 또한 qwer.tsx로 바뀐 뒤 해당 경로로 들어가려고 하면 똑같이 404 에러를 띄웁니다. src\app\(beforeLogin)\page.tsx 파일의 import Image from "next/image"; import styles from '@/app/page.module.css' import Link from "next/link"; import zlogo from '../../../public/zlogo.png' export default function Home() { return ( <> <div className={styles.left}> <Image src={zlogo} alt="logo"/> </div> <div className={styles.right}> <h1> 지금 일어나고 있는 일 </h1> <h2> 지금 가입하세요 </h2> <Link href="/i/flow/signup" className={styles.signup}> 계정 만들기</Link> <h3> 이미 트위터에 가입하셨나요? </h3> <Link href="/i/flow/login" className={styles.login}> 로그인 </Link> </div> </> ); }에서<Link href="/i/flow/login" className={styles.login}> 로그인 </Link>컴포넌트를 클릭시 제가 원하는 해당 경로의 특정파일명과 매핑할 수 있는 방법이 있나요?? 아니면 하나의 폴더에는 하나의 page.tsx만 존재해야하고, 그 파일 명은 page.tsx가 되야하는 규칙인건가요??
-
해결됨Next + React Query로 SNS 서비스 만들기
useSelectedLayoutSegment 의 사용법
useSelectedLayoutSegment 를 DetailPage 안의 infoData 라는 컴포넌트에서 사용하려하였고,클라이언트 컴포넌트입니다.useSelectedLayoutSegment은 layout.tsx 에서 infoData 를 부를때만 사용할수있는건가요?layout.tsx 에서 infoData 를 불러오면 segment를 잘 가져오고 DetailPage 라는 곳에서 infoData를 사용하면 segment 가 null로만 반환되고있습니다.Layout의 children으로 DetailPage가 들어가고 DetailPage의 children으로 infoData가 들어가는데layout.tsx 에서 직접 컴포넌트를 불러올때만 값을 받아오고 layout의 자식의 자식으로 사용될때에는 왜 값을 가져오지 못하는건지 이해가 안되서 질문드립니다.
-
해결됨실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트
act 함수와 renderHook 함수 내 rerender 차
안녕하세요 궁금한게 있습니다. act 함수에 대해 이해했는데 act 함수없이 아래와 같이 해도 테스트가 통과되더라구요. 혹시 차이가 있을까요?it('훅의 toggleIsModalOpened()를 호출하면 isModalOpened 상태가 toggle된다.', () => { const { result, rerender } = renderHook(useConfirmModal); result.current.toggleIsModalOpened(); rerender(); expect(result.current.isModalOpened).toBe(true); });
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
react에서 코드 작성하실때 태그 작성하시는것 질문 있습니다.
리액트 강의에서 코드 작성하실 때input이나 button 같은 태그를 작성하실 때한 태그만 작성하실 때에도 굳이 div태그를 만들어서 그 안에 작성하시던데 그 이유가 있을까요??JSX문법에서 전체 태그를 하나의 부모 태그로 감싸야 하는 것은 알고 있지만 왜 태그 하나 하나를 다 div태그로 감싸는지 궁금합니다
-
미해결
파일 절대경로 설정
pnpm, vite를 사용하여 리액트 프로젝트 생성한 다음 tailwind 랑 typescript, shadcn/ui 를 설치하였는데요. shadcn/ui를 쓰려고 버튼 컴포넌트를 임포트 하는데 파일경로가 계속 잘못되었다고 합니다. 다 확인했는데 도저히 어디가 문제인지 몰라서 여쭤 봅니다.이런 오류가 뜨고요tsconfig.json{ "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "baseUrl": ".", "paths": { "@/*": ["./*"] }, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", /* Linting */ "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] }vite.config.tsimport path from "path" import { defineConfig } from "vite" import react from "@vitejs/plugin-react" // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, }) App.tsximport './App.css' import { Button } from "@/components/ui/button" function App() { return ( <> <div className="ml-4 mt-8 text-3xl font-bold underline"> Hello world! </div> <div> <Button>Click me</Button> </div> </> ) } export default App 입니다. 현재 @이게 적용이 안되고 있는 것 같습니다 근데 파일 누르면 올바르게 해당하는 파일로 이동이 잘됩니다. 어디가 문제일까요 ?
-
미해결MERN STACK 커뮤니티 : 시작부터 배포까지 알려주는 React
axios 문제 404에러
선생님 서버부분 /list axios 부분 /setupProxy부분 까지 서버도 틀어놨구요 정말 똑같이 따라했는데 404에러가 떠요 ㅜㅜ 선생님 강의 정말뒤로가서 다시 쳐보고 똑같이 헀거든요. 왜이럴까요ㅜㅜ
-
미해결Next + React Query로 SNS 서비스 만들기
얇은 복사 value.pages 질문
강의 17분 50초 정도에 말씀하신 얇은 복사중에서 왜 이런식으로 복사해야하는지 이해가 잘 되지 않습니다. const shallow = { ...value }; value.pages = { ...value.pages }; value.pages[pageIndex] = [...value.pages[pageIndex]];제가 강의를 보기전 테스트할때는 해당 코드로 작성하였는데 해당 코드는 정상적으로 작동하지 않았습니다 const shallow = { ...value }; shallow.pages = { ...value.pages }; shallow.pages[pageIndex] = [...value.pages[pageIndex]];혹시 왜 첫번째 코드블록처럼 코드를 작성해야하는지 간단하게 설명해주시면 감사하겠습니다
-
미해결Next + React Query로 SNS 서비스 만들기
useSWR, react-query 중 어떤것을 사용해야하는지 선택하는 기준점에 대해서 알고싶습니다.
예를들어, 이런 소셜 네트워크 서비스같은 경우에는 useSWR보다, react-query가 다양한 기능을 제공해주어, 적합하다고 생각이 되어집니다. 1 . 단순 데이터만 갖고오는 사이트가 아니라면, 대부분 react-query가 더 좋다고 생각하는데, useSWR은 어떤 서비스에서 주로 채택하면 좋은지에 대해 알고싶습니다. react-query와 useSWR을 혼용해서 사용했을때 더 좋아지는 경우가 있을까요? 어떠한 서비스를 만들때, useSWR, react-query를 선택해야한다면, 어떤 기준으로 판별하는 것이 좋은지에 대해서도 알고 싶습니다.
-
미해결Next + React Query로 SNS 서비스 만들기
CredentialsSignin 에러
CredentialsSignin 에러가 발생합니다.아래의 CredentialsSignin 질문과 같이 redis 재시작,백엔드 서버 재시작, next, node_modules 삭제 후 다시 npm install 후 확인해도 계속 같은 에러가 나옵니다.next[auth][error] CredentialsSignin: Read more at https://errors.authjs.dev#credentialssignin at Module.callback (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/actions/callback/index.js:256:30) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async AuthInternal (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/index.js:65:24) at async Auth (webpack-internal:///(rsc)/./node_modules/@auth/core/index.js:123:29) at async /Users/mac/Documents/next14/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:63251백엔드[Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [WebSocketsController] EventsGateway subscribed to the "test" message +22ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [WebSocketsController] EventsGateway subscribed to the "sendMessage" message +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [WebSocketsController] EventsGateway subscribed to the "login" message +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] AppController {/}: +2ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] ApiController {/api}: +1ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/login, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/logIn, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/logout, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/logOut, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/user, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/decode, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] HashtagsController {/api/hashtags}: +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags, GET} route +1ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags/trends, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags/:id, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags/:id, PATCH} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/hashtags/:id, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] UsersController {/api/users}: +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/followRecommends, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id/posts, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id, PATCH} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id, DELETE} route +1ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id/follow, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id/follow, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id/rooms, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/users/:id/rooms/:roomId, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] PostsController {/api/posts}: +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/recommends, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/followings, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/heart, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/heart, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/reposts, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/reposts, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/comments, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/comments, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/posts/:id/photos/:imageId, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RoutesResolver] MessagesController {/api/messages}: +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/messages, POST} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/messages, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/messages/:id, GET} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/messages/:id, PATCH} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [RouterExplorer] Mapped {/api/messages/:id, DELETE} route +0ms [Nest] 6106 - 01/01/2024, 3:07:18 PM LOG [NestApplication] Nest application successfully started +2ms redis connected test 12345 test 123125 test sadfasf test zcxv
-
미해결Next + React Query로 SNS 서비스 만들기
[질문아님] VScode에도 내가만든 파일명 찾기 기능이 있습니다.
command + p (or ctrl+p) 누르면 나오는 서치창에내가 만든 파일명을 입력하면 해당 파일을 찾을 수 있습니다.어느 소통창구로 알려드려야 할지 몰라서 여기다 적어요
-
미해결Next + React Query로 SNS 서비스 만들기
children 오류??
(afterLogin)의 layout.tsx에서 35번째 주석부분 children이요여기 children에 각 폴더 page.tsx, layout.tsx이 들어가는건가요??여기 35번 줄 children 주석풀고 실행해보면이런 오류 뜨는데 왜 이런지 알 수 있을까요??
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
코드 질문드립니다.
import "./App.css"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import Board from "./common/Board"; import Login from "./component/Login"; import { useEffect } from "react"; function App() { useEffect(() => console.log("app is loading"), []); return ( <BrowserRouter> <Routes> <Route path="/login" element={<Login />}></Route> <Route path="/board/:boardId/page/:page" element={<Board />}></Route> <Route path="/board/:boardId/new" element={<Board />}></Route> <Route path="/board/:boardId/post/:postId" element={<Board />}></Route> <Route path="/board/:boardId/post/:postId/edit" element={<Board />} ></Route> </Routes> </BrowserRouter> ); } export default App; import React, { useEffect, useState, useContext } from "react"; import { useNavigate, useParams } from "react-router-dom"; // useParams를 import import BoardList from "../component/BoardList"; import BoardEdit from "../component/BoardEdit"; import BoardView from "../component/BoardView"; import Layout from "../layout/Layout"; import axios from "axios"; import { TokenProcess } from "../common/TokenProcess"; export const BbsSettingContext = React.createContext(); const Board = () => { const { boardId, postId, page } = useParams(); // const [currentUrl, setCurrentUrl] = useState(null); const [boardSettData, setBoardSettData] = useState(null); const navigate = useNavigate(); useEffect(() => { console.log("board:::::::::::::::::"); const fetchData = async () => { const access_token = localStorage.getItem("Authorization"); try { const response = await axios.get( `http://localhost:8080/api/board/${boardId}`, { headers: { Authorization: `Bearer ${access_token}`, }, } ); setBoardSettData(response.data.returnData.board); let mode = ""; if ( window.location.pathname.split("/")[3] == "page" && window.location.pathname.split("/")[5] == null ) { mode = "page"; } else if ( window.location.pathname.split("/")[3] == "post" && window.location.pathname.split("/")[5] == null ) { mode = "view"; } else if (window.location.pathname.split("/")[5] == "new") { mode = "new"; } else if (window.location.pathname.split("/")[5] == "edit") { mode = "edit"; } else { mode = ""; } setCurrentUrl(mode); console.log("모드" + mode); } catch (error) { console.log(error); if ( error.response.status == 401 && error.response.data.data == "EXPIRE_TOKEN" ) { (await TokenProcess()) ? fetchData() : navigate("/login"); } else { console.log(error); return false; } } }; fetchData(); }, [window.location.pathname]); return ( <BbsSettingContext.Provider value={boardSettData}> {currentUrl == "page" ? ( <Layout> <BoardList boardId={boardId} page={page} /> </Layout> ) : currentUrl == "new" ? ( <Layout> <BoardEdit /> </Layout> ) : currentUrl == "edit" ? ( <Layout> <BoardEdit boardId={boardId} postId={postId} /> </Layout> ) : currentUrl == "view" ? ( <Layout> <BoardView boardId={boardId} postId={postId} /> </Layout> ) : null} </BbsSettingContext.Provider> ); }; export default Board; 아래는 상세 보기입니다.import React, { useState, useEffect, useContext, useRef } from "react"; import { useNavigate } from "react-router-dom"; import axios from "axios"; import { BbsSettingContext } from "../common/Board"; import ToastEditor from "../component/ToastEditor"; import MyButton from "../common/ComButton"; import { TokenProcess } from "../common/TokenProcess"; const BoardView = ({ postId }) => { const titleInputRef = useRef(); const contentsAreaRef = useRef(); const toastEditorRef = useRef(); // ToastEditor의 ref const navigate = useNavigate(); const [post, setPost] = useState({ postId: "", postTitle: "", postContent: "", regDate: "", }); const [boardSetting, setBoardSetting] = useState(null); const boardSetData = useContext(BbsSettingContext); const Navigate = useNavigate(); const formatDate = (timestamp) => { const date = new Date(timestamp); const options = { year: "numeric", month: "long", day: "numeric" }; return date.toLocaleDateString("ko-KR", options); }; useEffect(() => { console.log("view>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); if (boardSetData) { setBoardSetting(boardSetData); const access_token = localStorage.getItem("Authorization"); const fetchData = async () => { try { const response = await axios.get( `http://localhost:8080/api/board/${boardSetData.boardId}/post/${postId}`, { headers: { Authorization: `Bearer ${access_token}`, }, } ); console.log(response.data.returnData); setPost({ postId: response.data.returnData.postId, postTitle: response.data.returnData.postTitle, postContent: response.data.returnData.postContent, regDate: response.data.returnData.regDate, }); } catch (error) { console.log(error); if ( error.response.status == 401 && error.response.data.data == "EXPIRE_TOKEN" ) { (await TokenProcess()) ? fetchData() : navigate("/login"); } else { console.log(error); return false; } } }; fetchData(); } }, [boardSetData, postId]); const editBoard = async () => { navigate(`/board/${boardSetting.boardId}/post/${postId}/edit`); }; return ( <div className="BoardView"> <div className="upInfoArea"> <div type="text" name="boardTitle" className="titleDiv"> {post.postTitle} </div> <div type="text" name="regId" className="regIdDIv"> {formatDate(post.regDate)} </div> </div> <div className="contentArea"> <div className="contentsText">{post.postContent}</div> </div> <section> <div className="btnArea"> <MyButton text={"리스트이동"} onClick={() => Navigate(-1)} /> <MyButton text={"수정하기"} type="positive" onClick={editBoard} /> </div> </section> </div> ); }; export default BoardView; 리스트이동이라는 버튼을 누르면 바로 이동을 하는데브라우저에서 <- 뒤로가기 버튼을 누르면 갑자기 BoardView의 useEffect를 타는데 이유를 모르겠습니다 .어떻게 해야될까요. ..
-
미해결Next + React Query로 SNS 서비스 만들기
dvw를 사용하는 경우와 vw를 사용하는 경우의 차이
안녕하세요 제로초님! 강의 즐겁게 듣고 있습니다. 깃허브의 코드를 보면 main.module.css에서 container를 잡을 때 width와 heigth로 dvw, dvh단위를 사용하시고다른 부분(login.module.css 와 signup.module.css)에서는 vw를 사용하시고 있어서요. dvw를 사용하시는 경우와 vw를 사용하시는 경우의 차이가 궁금합니다.