묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
yarn 설치가 되지않습니다.
node.js와 npm은 --version을 이용하니 버전이 나오는데 yarn은 버전이 나오지 않아 npm install -g yarn 이라는 명령어를 하니 이미지처럼 에러가 나옵니다.
-
해결됨[React / VanillaJS] UI 요소 직접 만들기 Part 1
이벤트 핸들러가 Root에 모이면
리액트에서는 onClick으로 이벤트를 등록하면 Root에 모이게 된다고 하셨는데제가 이 부분을 정확히 이해했는지 알고싶어서 질문 드립니다!보통은 자바스크립트에서는 각 요소마다 addEventListener를 여러 번 호출하면 메모리 측면에서 비효율적이고 성능이 떨어질 수 있다고 알고 있습니다. 그래서 이벤트 위임을 사용하는 것으로 이해했습니다. 현재 강의에서는 각 요소마다 onclick 이벤트를 등록하셨는데 리액트에서는 Root에서 중앙통제하기 때문에 상관없다는 것으로 인지했습니다.제가 알기로는 이벤트들을 모아다가 root에 addEventListener를 하는 것으로 알고 있는데 이게 맞을까요?그러면 리액트에서는 굳이 이벤트 위임을 사용할 필요가 없을까요? 예를 들어 onClick을 여러 요소에 등록하지 않고 상위 요소 하나에만 등록하는 것처럼 말이죠..!
-
미해결코드로 배우는 React with 스프링부트 API서버
아까랑 같은 질문입니다..
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));이걸 어디에 추가해야 하는지 알 수 있을까요??if(path.startsWith("/api/member/")) { return true; } if(path.startsWith("/api/todo/")) { return true; }이건 이렇게 입력했지만 저 부분이 어딘지 몰라서 작동이 안되네요 ㅠㅠ
-
미해결코드로 배우는 React with 스프링부트 API서버
세션 7 Axios인터셉터와 AccessToken
before request.............jwtUtil.js:34 -----------------------------eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb2NpYWwiOmZhbHNlLCJwdyI6IiQyYSQxMCRWQ1dGY0ZhWC9aTzF2TGhpU0VpY3NlY0hGWmdIV2dZV0RNa0JMRmlCZk5yYTQvVVgvMERORyIsIm5pY2tuYW1lIjoiVXNlcjUiLCJyb2xlTmFtZXMiOlsiVVNFUiIsIk1BTkFHRVIiXSwiZW1haWwiOiJ1c2VyNUBhYWEuY29tIiwiaWF0IjoxNzE2OTc2NTc5LCJleHAiOjE3MTY5NzcxNzl9.r_619xyQq9zGhf5YG4-Sprtiz4W9rNNhf5ml4XyAQ3M:3000/todo/list:1 Access to XMLHttpRequest at 'http://localhost:8080/api/todo/list?page=1&size=10' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.jwtUtil.js:56 response fail error.............:3000/todo/list:1 Uncaught (in promise)AxiosError {message: 'Network Error', name: 'AxiosError', code: 'ERR_NETWORK', config: {…}, request: XMLHttpRequest, …}code: "ERR_NETWORK"config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}message: "Network Error"name: "AxiosError"request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}stack: "AxiosError: Network Error\n at XMLHttpRequest.handleError (http://localhost:3000/static/js/bundle.js:54389:14)\n at Axios.request (http://localhost:3000/static/js/bundle.js:54841:41)\n at async getList (http://localhost:3000/static/js/bundle.js:184:15)"[[Prototype]]: ErrorPromise.then(비동기)(익명)@ListComponent.js:29프레임 11개 더 표시 jwtUtil.js를 강의에서 알려주신대로 틀린거 없이 따라하고 todoApi.js에 Axios에서 jwtAxios로 변경하고 로그인 하고 todo를 클릭했는데 위에 메세지 처럼 에러가 발생합니다 그래서 오류메세지를 검색해 보니 이 에러는 CORS(Cross-Origin Resource Sharing) 정책 위반 이 에러는 주로 프론트엔드 애플리케이션(React 등)이 백엔드 API에 요청을 보낼 때 발생한다고 하는데요 어떻게 해야할지 모르겠습니다 ㅠㅠ
-
미해결처음 만난 리액트(React)
이벤트 실행순서에 대해 질문드려요
import React from 'react'; import {useState} from 'react'; function ConfirmButton2 (){ const [isConfirmed,setIsconfirmed] = useState(false); const handleConfirm = ()=>{ setIsconfirmed((prevIsConfirmed) => !prevIsConfirmed); // setIsconfirmed(!isConfirmed); }; const [number, setNumber] = useState(0); const checkNumber =() =>{ alert("첫번째 "+number); // 0 setNumber((prevnumber)=>{ alert(prevnumber+"//"+number); // 0 0 return prevnumber+10; }); setNumber((prevnumber)=>{ alert(prevnumber+"//"+number); // 10 0 return prevnumber+10; }); alert("두번째"+number); // 0 }; return ( <> <button onClick = {handleConfirm} > {isConfirmed ? "확인됨2" : "확인하기2"} </button> <button onClick = {checkNumber}> {number} </button> </> ); }; export default ConfirmButton2; 이렇게 해서 실행을 하고 숫자 버튼을 클릭하면 첫번째 alert -> set함수 alert -> 두번째 alert -> 두번째 set함수 alert 이 순서로 나오는데, alert -> set 함수 alert -> 두번째 set함수 alert -> 두번째 alert 이 순서가 아닌 이유를 모르겠어요.
-
해결됨Next + React Query로 SNS 서비스 만들기
인터셉팅 라우터가 signup에는 적용이 안되는 문제
login은 인터셉팅 라우터가 잘 되는데,signup은 (.)i로 인터셉팅이 안되고 버튼 클릭을 하면 그냥 i/flow/signup/page.tsx로만 보여지는 문제가 있습니다. 왜 signup은 인터셉팅 라우터가 작동이 안되는 것일까요?? 경로는 이렇게 잘 설정되어있고 안에 파일 내용은 아래와 같습니다. 아래는 @modal/(.)i/flow/signup/page.tsx아래는 i/flow/signup/page.tsx홈 버튼 링크는 아래와 같이 되어있습니다.혹시 제가 빠뜨린 무엇이 있을까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
섹션11 eslint 설치
eslint 설치 시 3개 중에 마지막 3번째를 선택하라고 하는데 저는 선택지가 2개만 나옴니다. 2번째를 선택해서 설치해도 되나요?
-
미해결Next + React Query로 SNS 서비스 만들기
http://localhost:3000/api/auth/session 500에러
안녕하세요 선생님 로컬에서 locallhost:3000/api/auth를 호출하면 500에러가 발생합니다.* 참고: 1. useSession()은 모두 클라이언트 컴포넌트에만 적용했습니다. 2. 원인을 찾아보다가 예전 선생님 답변해주신 서버 컴포넌트에서 세션props로 받아스라고 하신거 참고해서 그렇게 수정해도 동일하게 발생합니다.3. 비슷한 질문에 쿠키랑 다 없앤뒤 잘됐다는것도 보고 다제거해봤지만 동일하게 발생했습니다.4. .next, node_module 다 제거후 새로 깔거나, 빌드를 새로 하거나 테스트도 해보았습니다.5. 브라우저 껐다키고, 컴퓨터 재부팅도 해보았습니다.6. 선생님의 깃코드를 가져다 쓰기도 해보았습니다.빌드 한후에 npm run start로 했을땐 정상적으로 나오는데 로컬에서만 발생합니다. /src/auth.tsimport NextAuth from "next-auth" import Credentials from "next-auth/providers/credentials" export const { handlers, signIn, signOut, auth } = NextAuth({ pages: { signIn: '/login', newUser: '/signup', }, providers: [ Credentials({ credentials: { id: {}, password: {}, }, authorize: async (credentials) => { try { 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.ok'); if (!authResponse.ok) { return null; } let user = await authResponse.json(); console.log(user, '--------------------------------'); return { ...user, email: user.id, name: user.nickname, image: user.image, } } catch (err) { console.error('로그인 에러', err); } }, }), ], }).envAUTH_SECRET=WKFOJhbw7gZOYXumT66CwwKtDZ9YsalV8qMRx134Uc8= AUTH_TRUST_HOST=http://localhost:3000.env.localNEXT_PUBLIC_API_MOCKING=enabled NEXT_PUBLIC_MODE=local NEXT_PUBLIC_BASE_URL=http://localhost:9090 NEXTAUTH_URL=http://localhost:3000 /src/app/layout.tsximport type { Metadata, Viewport } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import { MSWComponent } from './_component/MSWComponent'; import AuthSession from './_component/AuthSession'; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: { template: '%s | MBTI', default: 'MBTI가 어떻게 되세요?', }, description: "MBTI로 찾는 내 연인", }; export const viewport: Viewport = { width: 'device-width', initialScale: 1, maximumScale: 1, userScalable: false, // Also supported by less commonly used // interactiveWidget: 'resizes-visual', } export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="en"> <body className={`${inter.className} antialiased`}> <MSWComponent /> <AuthSession> {children} </AuthSession> </body> </html> ); } /src/app/_component'use client'; import { SessionProvider } from 'next-auth/react'; import { ReactNode } from 'react'; export default function AuthSession({children}: {children: ReactNode}) { return ( <SessionProvider>{children}</SessionProvider> ) } /src/app/api/auth/[...nextauth]/route.tsimport { handlers } from "@/auth" // Referring to the auth.ts we just created export const { GET, POST } = handlers; 폴더구조📦src ┣ 📂app ┃ ┣ 📂(afterLogin) ┃ ┃ ┣ 📂(home) ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┣ 📜mbtiCarousel.module.css ┃ ┃ ┃ ┃ ┣ 📜MbtiCarousel.tsx ┃ ┃ ┃ ┃ ┗ 📜UserCardList.tsx ┃ ┃ ┃ ┗ 📂_lib ┃ ┃ ┃ ┃ ┗ 📜getUserAll.ts ┃ ┃ ┣ 📂@modal ┃ ┃ ┃ ┣ 📂(.)promise ┃ ┃ ┃ ┃ ┗ 📂form ┃ ┃ ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┃ ┣ 📂[userId] ┃ ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┃ ┣ 📜UserDetailContent.tsx ┃ ┃ ┃ ┃ ┃ ┣ 📜UserDetailPromise.tsx ┃ ┃ ┃ ┃ ┃ ┣ 📜UserDetailTop.tsx ┃ ┃ ┃ ┃ ┃ ┣ 📜UserInfo.tsx ┃ ┃ ┃ ┃ ┃ ┗ 📜UsrCarousel.tsx ┃ ┃ ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┃ ┃ ┣ 📜getAUser.ts ┃ ┃ ┃ ┃ ┃ ┗ 📜getUserPromise.ts ┃ ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┃ ┗ 📜default.tsx ┃ ┃ ┣ 📂like ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┣ 📜LikeCard.tsx ┃ ┃ ┃ ┃ ┣ 📜LikeTabProvider.tsx ┃ ┃ ┃ ┃ ┣ 📜Tab.tsx ┃ ┃ ┃ ┃ ┣ 📜TabDecider.tsx ┃ ┃ ┃ ┃ ┣ 📜UserILike.tsx ┃ ┃ ┃ ┃ ┗ 📜UserLikeMe.tsx ┃ ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┃ ┣ 📜getUserILike.ts ┃ ┃ ┃ ┃ ┗ 📜getUserLikeMe.ts ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂messages ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂profile ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂promise ┃ ┃ ┃ ┣ 📂form ┃ ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┣ 📜PromiseCard.tsx ┃ ┃ ┃ ┃ ┣ 📜PromiseCardDropdown.tsx ┃ ┃ ┃ ┃ ┣ 📜PromiseCardLink.tsx ┃ ┃ ┃ ┃ ┣ 📜PromiseFormButton.tsx ┃ ┃ ┃ ┃ ┣ 📜promiseSection.module.css ┃ ┃ ┃ ┃ ┗ 📜PromiseSection.tsx ┃ ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┃ ┗ 📜getPromiseAll.ts ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂recommend ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┗ 📜RecommendSection.tsx ┃ ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┃ ┗ 📜getUserRecommends.ts ┃ ┃ ┃ ┣ 📜page.tsx ┃ ┃ ┃ ┗ 📜recommend.module.css ┃ ┃ ┣ 📂search ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┣ 📜SearchCard.tsx ┃ ┃ ┃ ┃ ┣ 📜SearchForm.tsx ┃ ┃ ┃ ┃ ┗ 📜SearchResult.tsx ┃ ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┃ ┗ 📜getSearchResult.ts ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂setting ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂[userId] ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┣ 📜Back.tsx ┃ ┃ ┃ ┣ 📜ImageWithPlaceholder.tsx ┃ ┃ ┃ ┣ 📜LogoutButton.tsx ┃ ┃ ┃ ┣ 📜MainTitle.tsx ┃ ┃ ┃ ┣ 📜MbtiRecommendSection.tsx ┃ ┃ ┃ ┣ 📜Modal.tsx ┃ ┃ ┃ ┣ 📜RQProvider.tsx ┃ ┃ ┃ ┣ 📜SearchForm.tsx ┃ ┃ ┃ ┣ 📜userCard.module.css ┃ ┃ ┃ ┣ 📜UserCard.tsx ┃ ┃ ┃ ┣ 📜UserCardArticle.tsx ┃ ┃ ┃ ┗ 📜UserRandomRecommendSection.tsx ┃ ┃ ┣ 📂_lib ┃ ┃ ┃ ┣ 📜getBase64.ts ┃ ┃ ┃ ┗ 📜getUserRandomRecommends.ts ┃ ┃ ┣ 📜layout.module.css ┃ ┃ ┣ 📜layout.tsx ┃ ┃ ┗ 📜page.tsx ┃ ┣ 📂(beforeLogin) ┃ ┃ ┣ 📂login ┃ ┃ ┃ ┣ 📜login.module.css ┃ ┃ ┃ ┗ 📜page.tsx ┃ ┃ ┣ 📂signup ┃ ┃ ┃ ┣ 📜page.tsx ┃ ┃ ┃ ┗ 📜signup.module.css ┃ ┃ ┣ 📂userSetting ┃ ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┃ ┣ 📜BirthdaySelect.tsx ┃ ┃ ┃ ┃ ┣ 📜DrinkSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜GenderSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜ImageSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜JobSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜MbtiSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜NicknameSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜RegionSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜ReligionSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜SchoolSelect.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUser.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUser2.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUserComplete.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUserProvider.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUserProvider2.tsx ┃ ┃ ┃ ┃ ┣ 📜SetUserTop.tsx ┃ ┃ ┃ ┃ ┣ 📜SmokeSelect.tsx ┃ ┃ ┃ ┃ ┗ 📜TallSelect.tsx ┃ ┃ ┃ ┣ 📜page.tsx ┃ ┃ ┃ ┗ 📜userSetting.module.css ┃ ┃ ┣ 📂_component ┃ ┃ ┃ ┣ 📜title.module.css ┃ ┃ ┃ ┗ 📜Title.tsx ┃ ┃ ┗ 📂_lib ┃ ┃ ┃ ┗ 📜login.ts ┃ ┣ 📂api ┃ ┃ ┗ 📂auth ┃ ┃ ┃ ┗ 📂[...nextauth] ┃ ┃ ┃ ┃ ┗ 📜route.ts ┃ ┣ 📂_component ┃ ┃ ┣ 📜AuthSession.tsx ┃ ┃ ┣ 📜BottomNav.tsx ┃ ┃ ┣ 📜LeftNav.tsx ┃ ┃ ┣ 📜MSWComponent.tsx ┃ ┃ ┣ 📜nav.module.css ┃ ┃ ┗ 📜TopNav.tsx ┃ ┣ 📜favicon.ico ┃ ┣ 📜globals.css ┃ ┗ 📜layout.tsx ┣ 📂components ┃ ┗ 📂ui ┃ ┃ ┣ 📜avatar.tsx ┃ ┃ ┣ 📜badge.tsx ┃ ┃ ┣ 📜button.tsx ┃ ┃ ┣ 📜card.tsx ┃ ┃ ┣ 📜carousel.tsx ┃ ┃ ┣ 📜dropdown-menu.tsx ┃ ┃ ┣ 📜form.tsx ┃ ┃ ┣ 📜input.tsx ┃ ┃ ┣ 📜label.tsx ┃ ┃ ┣ 📜progress.tsx ┃ ┃ ┣ 📜skeleton.tsx ┃ ┃ ┣ 📜textarea.tsx ┃ ┃ ┗ 📜tooltip.tsx ┣ 📂lib ┃ ┗ 📜utils.ts ┣ 📂mocks ┃ ┣ 📜browser.ts ┃ ┣ 📜handlers.ts ┃ ┗ 📜http.ts ┣ 📂model ┃ ┣ 📜Post.ts ┃ ┣ 📜postImage.ts ┃ ┣ 📜User.ts ┃ ┗ 📜UserImage.ts ┣ 📜auth.ts ┗ 📜middleware.ts 로컬에서 잘되다가 어느순간부터 됐다 안됐다 하다가 이제는 안되고 있습니다. 어느 부분을 좀 더 찾아보고 해야할지 조언주시면 정말 감사하겠습니다.
-
해결됨[React / VanillaJS] UI 요소 직접 만들기 Part 1
append와 insertAdjacentElement 차이가 무엇일까요?
insertAdjacentElement 를 사용해서 구현하셨는데저 부분을 append로 해도 똑같은 결과가 나옵니다.혹시 append말고 insertAdjacentElement를 사용하신 이유가 있을까요?개인적으로 검색해봤는데 append와 차이점이 insertAdjacentElement는 '지정한 위치에 요소를 삽입할 수 있다'는 점 이외에는 없더라구요..ㅠ어떤 장점때문에 insertAdjacentElement 를 사용한지 알고 싶습니다!
-
해결됨Next + React Query로 SNS 서비스 만들기
prefetchQuery 적용 후 Warning: Text content did not match. 오류가 발생합니다.
안녕하세요. 강의 잘 보고 있습니다.!!👍👍react-query를 적용 중 오류가 표시되어 오류와 궁금한 부분이 있어서 문의 드립니다.Text content did not match. 오류가 발생하는데 어느 부분이 문제인지 잘 모르겠습니다 ㅠㅠprefetchQuery 를 사용하여 서버에서 post(트윗)데이터를 프리패치 후 TweetList컴포넌트에서 useQuery를 사용하게 만들었습니다. Post의 데이터는 msw에서 알려주신 faker를 통해서 요청마다 content를 동적으로 생성하게 했구요. 콘솔로 출력한 데이터와 reactQuery개발 도구의 데이터는 동일한데 화면에 표기된 데이터는 전혀 다른 데이터로 표시되네요..그리고 방식이 prefetchQuery 를 사용한 데이터는 staleTime과 상관없이 Fresh상태로 되어 useQuery 호출 시 데이터가 아직 fresh한 상태이므로 서버를 호출하지 않고 캐쉬에서 가져와 보여주는 방식이 맞나요?오류로그app-index.js:33 Warning: Text content did not match. Server: "Tener adulatio decens conitor. Thesis apostolus ago at decerno. Iste uberrime commodo. Verus amitto quas cometes delicate. Cognomen alii curto ciminatio. Solitudo complectus tristis. Teneo sum vindico. Adhuc decimus triumphus ipsa arguo umquam addo adulatio cresco. Sponte degenero non trado cauda beneficium laboriosam approbo." Client: "Tremo collum sint terror templum summisse viduo usque unus benevolentia. Eligendi substantia concido amissio uredo turpis aperte. Admoneo titulus audeo conicio fuga." at div at div at div at article at TweetWrapper (webpack-internal:///(app-pages-browser)/./src/app/(afterLogin)/home/_components/TweetWrapper.tsx:11:11) at Tweet (webpack-internal:///(app-pages-browser)/./src/app/(afterLogin)/home/_components/Tweet.tsx:31:11) at TweetList (webpack-internal:///(app-pages-browser)/./src/app/(afterLogin)/home/_components/TweetList.tsx:16:93) at HydrationBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/@tanstack+react-query@5.39.0_react@18.2.0/node_modules/@tanstack/react-query/build/modern/HydrationBoundary.js:14:11) at HomeContextProvider (webpack-internal:///(app-pages-browser)/./src/app/(afterLogin)/home/_components/Provider.tsx:17:11) at InnerLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:242:11) at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:74:9) at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:82:11) at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:84:11) at LoadingBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:340:11) at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.js:162:11) at InnerScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:152:9) at ScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:227:11) at RenderFromTemplateContext (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/render-from-template-context.js:16:44) at OuterLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:359:11) at InnerLayoutRouter (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:242:11) at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:74:9) at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/redirect-boundary.js:82:11) at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/not-found-boundary.js:84:11) at LoadingBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:340:11) at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/error-boundary.js:162:11) at InnerScrollAndFocusHandler (webpack-internal:///(app-pages-browser)/./node_modules/.pnpm/next@14.1.4_@babel+core@7.24.4_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/client/components/layout-router.js:152:9) at ScrollAndFocu소스코드(Tweet이 Post입니다! 나중에 바꿔야겠네여)[page.tsx] /homeimport React from "react"; import HomeTopTab from "./_components/HomeTopTab"; import WriteForm from "./_components/WriteForm"; import TweetList from "./_components/TweetList"; import HomeContextProvider from "./_components/Provider"; import { QueryClient, dehydrate, HydrationBoundary, } from "@tanstack/react-query"; import getPostRecommends from "./_lib/getPostRecommends"; const HomePage = async () => { const queryClient = new QueryClient(); await queryClient.prefetchQuery({ queryKey: ["tweet", "recommends"], queryFn: getPostRecommends, }); const dehydratedState = dehydrate(queryClient); return ( <HomeContextProvider> <HomeTopTab /> <WriteForm /> <HydrationBoundary state={dehydratedState}> <TweetList /> </HydrationBoundary> </HomeContextProvider> ); }; export default HomePage; TweetList.tsx"use client"; import React from "react"; import Tweet from "./Tweet"; import getPostRecommends from "../_lib/getPostRecommends"; import { Post } from "./TweetWrapper"; import { QueryClient, useQuery } from "@tanstack/react-query"; const TweetList = () => { const { data: tweets } = useQuery<Post[]>({ queryKey: ["tweet", "recommends"], queryFn: getPostRecommends, enabled: false, }); console.log("🚀 _ TweetList _ tweets:", tweets); return tweets?.map((tweet) => <Tweet post={tweet} key={tweet.postId} />); }; export default TweetList;
-
미해결Next + React Query로 SNS 서비스 만들기
찜하기 관련 질문드립니다.
안녕하세요 상세페이지에서 찜하기 요청을 하게되면 users/favorite/${peopleId}로 post 요청을 보내게되고 그럼 users/favorite api에 찜한 사람들의 목록이 추가됩니다.찜하기를 눌렀을경우 아이콘의 컬러를 변경시켜야해서 getQueryData로 찜한 사람들의 목록 을 가져오려고 하였는데 const { data: likePeopleList } = useQuery<GetPeoples>({ queryKey: ["get", "likepeoples"], queryFn: getLikePeoples, staleTime: 60 * 1000, // fresh -> stale, 5분이라는 기준 gcTime: 300 * 1000, });위에 코드가 없으면 likeQuery를 가져오지 못하고있는것같습니다. getQueryData로 가져오는 방법이 잘못된걸까요? 다른 좋은 방법있다면 여쭤보고싶습니다.밑에는 전체 코드입니다. type Props = { peopleId: string; }; export default function PeoplePosts({ peopleId }: Props) { const { data } = useQuery< GetPeoplePost, Object, GetPeoplePost, [_1: string, _2: string, _3: string] >({ queryKey: ["get", "peoplesDetail", peopleId], queryFn: getPeopleDetail, }); const { data: likePeopleList } = useQuery<GetPeoples>({ queryKey: ["get", "likepeoples"], queryFn: getLikePeoples, staleTime: 60 * 1000, // fresh -> stale, 5분이라는 기준 gcTime: 300 * 1000, }); const { content, nickname, userFileUrl, favoriteCount, viewCount, softSkill, techStack, links, position, alarmStatus, year, } = data?.data ?? {}; const queryClient = useQueryClient(); const likeQuery = queryClient.getQueryData<GetPeoples>([ "get", "likepeoples", ]); console.log("likeQuery", likeQuery); const liked = !!likeQuery?.data.find( (item) => item.userId === Number(peopleId), ); console.log("liked", liked); const like = useMutation({ mutationFn: (peopleId: string) => { return fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/users/favorite/${peopleId}`, { method: "post", }, ); }, onMutate(peopleId: string) { const oldData = queryClient.getQueryData<GetPeoples>([ "get", "likepeoples", ]); if (oldData) { const newData = { ...oldData, data: oldData.data.map((item) => ({ ...item, userId: Number(peopleId), })), }; queryClient.setQueryData(["get", "likepeoples"], newData); } }, }); const unLike = useMutation({ mutationFn: (peopleId: string) => { return fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/users/favorite/${peopleId}`, { method: "delete", }, ); }, onMutate(peopleId: string) { const oldData = queryClient.getQueryData<GetPeoples>([ "get", "likepeoples", ]); if (oldData) { const deleteData = { ...oldData, data: oldData.data.filter((item) => item.userId !== Number(peopleId)), }; queryClient.setQueryData(["get", "likepeoples"], deleteData); } }, }); const onLike: MouseEventHandler<HTMLButtonElement> = (e) => { e.preventDefault(); if (liked) { unLike.mutate(peopleId); } else { like.mutate(peopleId); } }; return ( <div className="flex flex-col"> <div className="flex flex-col gap-4 border-b pb-10"> <div className="flex items-center gap-[10px]"> <Image src={`${userFileUrl}`} alt="유저프로필" width={30} height={30} /> <h1 className="text-[32px] font-bold">{nickname}</h1> <div> <BlueTextBox textSize="12px" textToShow={`${position}`} /> </div> </div> <div className="flex flex-col gap-2"> {softSkill ?.split(",") .map((skill, i) => <HashTag text={skill} key={i} />)} </div> <div className="flex gap-3"> <HeartEyeIconBox count={favoriteCount as number} icon={heartIcon} /> <HeartEyeIconBox count={viewCount as number} icon={eyeIcon} /> </div> </div> <div className="mt-[42px] flex flex-col gap-[50px]"> <div className="people-post-grid"> <h1 className="text-[22px] font-bold">경력</h1> <h3>{year}</h3> </div> <div className="people-post-grid"> <h1 className="text-[22px] font-bold">사용언어</h1> <div className="flex gap-2"> {techStack ?.split(",") .map((stack, i) => ( <TechStack techStack={stack} showText key={`stack${i}`} /> ))} </div> </div> <div className="flex flex-col gap-3"> <h1 className="text-[22px] font-bold">자기소개</h1> <p>{content}</p> </div> <div className="flex flex-col gap-3"> <h1 className="text-[22px] font-bold">Link</h1> <Link href={links as string}>{links}</Link> </div> </div> <div className="mt-[60px] flex gap-[13px] self-center"> <button className={`h-[58px] w-[142px] rounded-md bg-neutral-orange-500 font-bold ${alarmStatus ? "text-neutral-white-0" : "text-neutral-black-800"}`} > {alarmStatus ? "제안하기" : "제안불가"} </button> <button onClick={onLike} className="flex h-[58px] w-[58px] flex-col items-center justify-center rounded-md border" > {liked ? ( <Image src={fillHeartIcon} alt="하트아이콘" width={20} height={20} /> ) : ( <Image src={heartIcon} alt="하트아이콘" width={20} height={20} /> )} <h5 className="text-[12px]">{favoriteCount}</h5> </button> </div> </div> ); }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
geolocation부분에 대해 질문있습니다.
geolocation부분에const askForLocation = function () { navigator.geolocation.getCurrentPosition((position) => { console.log(position); }); }; askForLocation();이렇게 함수가 있는데 여기서 궁금한점이 askForLocation(); 함수 호출부분에 인자로 전달하는 것이 없는데 매개변수로 position에 위치정보 객체형식으로 콘솔에 나오는 이유가 뭔지 궁금합니다.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
3000번은 잘 들어가지는데 80번은 안됩니다.
sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000까지 하고 3000번 포트로 잘 들어가지는데 3000번포트를 지우고 들어가봐도 뒤에 :80을 붙여 넣어 들어가봐도 사이트에 연결할 수 없음이 뜨네요 왜 그럴까요??
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
함수, 변수의 호이스팅 부분 질문있습니다.
함수, 변수의 호이스팅 강의 영상 부분에if (storageData?.complete) { newLi.classList.add("complete"); }이 코드에서 storageData 뒤에 ? 이 물으표가 없으면 매개변수를 받아오지 않게 되니 undefined라고 강사님이 말씀하시는데 이게 무슨 말인지 잘 모르겠습니다. 그리고 ToDoList에서 리스트에 추가를 하고 더블클릭 삭제, 전체 삭제까지는 어느정도 이해가 되는데 로컬스트리지 전체 부분이 조금 이해도 어려운데 어떻게 이해를 하면서 공부를 하면 좋을까요?
-
해결됨Next + React Query로 SNS 서비스 만들기
spring boot를 사용한 social login
Next.js와 Spring boot를 사용해서 소셜 로그인을 추가하려고 합니다.소셜 로그인의 경우 코드와 토큰을 프론트에서 관리하지 않는 것이 좋다고 하여 백엔드에서 모든 로직을 처리하고 있습니다. 현재 프론트에서 window.location.href = "http://localhost:8080/oauth2/authorization/google";위와 같은 경로로 백엔드로 소셜 로그인 요청을 보내고 백엔드에서는 소셜 로그인을 진행한 뒤에 유저 정보를 생성하고 JWT를 생성한 뒤 응답의 헤더에 쿠키 형태로 JWT를 넣어 response.sendRedirect("http://localhost:3000/auth/social");로 리다이렉트하고 있습니다. 프론트에서는 /auth/social 페이지에서 쿠키에 있는 JWT를 꺼내 유저 정보를 가져오는 API를 다시 호출해 유저 정보를 가져오고 있습니다.이 후 auth.js 라이브러리를 통한 session 관리를 하고자 하는데 session을 업데이트 하려면 어떻게 해야 하나요..?(session.user가 있는지 없는지를 통한 로그인 체크를 위해 사용한다고 이해했습니다.) 서버 컴포넌트에서 import { auth } from "@/auth"; 를 통해 가져온 session에 데이터를 넣어보았지만 데이터가 제대로 들어가지 않는 것 같아 조언을 구해봅니다..
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
객체 localStorage 부분 질문있습니다.
현재 객체 localStorage저장 부분의 인강을 보다 궁금한점 있어 질문을 드립니다.const saveItemsFn = function () { const saveItems = []; for (let i = 0; i < todoList.children.length; i++) { const todoObj = { contents: todoList.children[i].querySelector("span").textContent, complete: todoList.children[i].classList.contains("complete"), }; saveItems.push(todoObj); } localStorage.setItem("saved-items", JSON.stringify(saveItems)); };현재 자바스크립트 코드 제일 마지막 부분에 saveItemsFn함수가 저장하는 부분이니 이 함수안에 localStorage.setItem을 사용해서 로컬스토리지에 저장을 하는것까지는 이해가 되었는데 저장된걸 가져오기 위해 const savedTodoList = localStorage.getItem("saved-items");localStorage.getItem을 사용하는데 여기서 첫번째로 이 코드 위치가 다시 제일 위쪽에 작성이 되었는지 궁금하고 두번째로 이 코드가 saveItemsFn 함수 안에 작성이 되지 않은 이유는 getItem이 저장하는 코드가 아니기때문에 saveItemsFn안이 아닌 따로 다시 작성이 된것인가요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
14강 로그아웃 안됨
logout을 send하면 해당 오류가 발생합니다. 코드가 잘못된건지 궁금해 git 도 올려드립니다. 감사합니다. https://github.com/bukwon/boiler-plate
-
해결됨Next + React Query로 SNS 서비스 만들기
로그인 관련 질문입니다.
https://www.inflearn.com/questions/1268586/%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%97%90%EB%9F%AC-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4여기에서 질문했던 로그인 관련 문제입니다.지금 현재 로그인 후 로그아웃 후 다른 아이디로 재로그인시에 로그아웃 버튼에 session정보가 제대로 안들어가는 것 같습니다.처음 로그인 했을 때 로그아웃에 session 정보가 들어가고, 로그아웃 후 다른 아이디로 재로그인시에 로그아웃버튼에 로그아웃전의 계정의 정보가 들어가 있습니다. 백엔드 서버 콘솔에는 정보가 제대로 들어가 있지만, let session = await auth(); 에서 session정보가 제대로 안 받아지는건지, 아니면 캐싱된 정보가 계속 쓰이는 건지 모르겠지만, 정보가 제대로 안들어갑니다.물론 새로고침시에 정상적으로 정보가 들어가집니다.제 프로젝트에서 문제가 있는건가 싶어서, 코드를 비교해보고 next, next-auth 버전을 바꿔보기도 했지만, 해결이 안되어서, 배포된 https://z.nodebird.com/ 링크로도 해보았는데 똑같은 버그가 있는 것 같아서 질문드립니다.무엇이 문제인지 궁금합니다. 좋은 하루 보내세요!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
graphql codegen 설치
섹션 10에서 graphql codegen 설치하려는데요강의에서는 yarn add -D @graphql-codegen/cli을 설치하라고 하는데 지금 설치하려고 사이트에 들어가니yarn add --dev typescript @graphql-codegen/cli로 되어 있는데 같은 건가요?그냥 아래 명령어 실행해도 되나요?
-
해결됨[React / VanillaJS] UI 요소 직접 만들기 Part 2
테일윈드로 포탈로 모달작성시 뒤에 클릭이 안됩니다
<div id='modalRoot' ref={ref} className='fixed inset-0 z-100 flex flex-col items-center justify-center' > {children} </div>테일 윈드로 선생님 모달 포탈로만드는것 해보고있는데.뒤에 클릭이 안되요 pointer-events-none 쓰지말고 하는법이 없을까요?!일단은 아래처럼 쓰고잇긴한데 ㅠㅠ 먼가 ...'use client'; import React, { useEffect, useRef } from 'react'; import { useModalStore } from '@/shared/models/modal/stores/modalStore'; const mutationObserverOption: MutationObserverInit = { childList: true, subtree: false, }; /** * 모달 컴포넌트를 렌더링하기 위한 전역 컨테이너 프로바이더입니다. * 모달이 열려 있을 때 body 요소에 'no-scroll' 클래스를 토글하여 스크롤을 비활성화합니다. * * @param {React.ReactNode} children - 모달 루트 내부에 렌더링할 자식 요소 * @returns {JSX.Element} 모달 루트 프로바이더 */ const ModalRootProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const ref = useRef<HTMLDivElement>(null); const { openedModalTypes, closeModal } = useModalStore(); // const isModalOpen = openedModalTypes.length > 0; useEffect(() => { let observer: MutationObserver; /** * MutationObserver를 사용하여 모달 루트 컨테이너의 자식 요소 변경을 감지합니다. * 모달이 열려 있는 경우 body 요소에 'no-scroll' 클래스를 추가하고, 모달이 닫혀 있는 경우 클래스를 제거합니다. */ if (ref.current) { observer = new MutationObserver(() => { const size = ref.current?.childNodes.length || 0; document.body.classList.toggle('no-scroll', size > 0); ref.current!.classList.toggle('bg-black/50', size > 0); ref.current!.style.pointerEvents = size > 0 ? 'auto' : 'none'; }); observer.observe(ref.current, mutationObserverOption); } // 컴포넌트 언마운트 시 MutationObserver 연결을 해제합니다. return () => { observer.disconnect(); }; }, []); return ( <div id='modalRoot' ref={ref} className='fixed inset-0 z-100 flex flex-col items-center justify-center pointer-events-none' // onClick={handleOutsideClick} > {children} </div> ); }; export default ModalRootProvider;