묻고 답해요
158만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[React / VanillaJS] UI 요소 직접 만들기 Part 2
useCallback, useMemo의 차이에 대해서 궁금합니다.
[셀렉트박스 (3/5) headless #2 hook 적용] 부분에서 getTriggerProps/getListProps 처럼 각 컴포넌트가 필요한 것들을 useCallback으로 감싸고 함수형태로 제공해주셨는데요, 아래처럼 useMemo를 이용해서 객체에 값을 담아서 전달해주는 방식은 다른 걸까요??const getTriggerProps = useCallback( () => ({ selectedItem: items[selectedIndex], toggle, }), [selectedIndex, items, toggle] ); const TriggerpropsValue = useMemo(() => { return { selectedItem: items[selectedIndex], toggle, }; }, [items, selectedIndex, toggle]);
-
해결됨Next + React Query로 SNS 서비스 만들기
실무에서 리버스 인피니트 스크롤링 최적화 방법
채팅을 계속 올려서 몇개월 치를 사용자가 본다면 위로 채팅 기록이 엄청 쌓일텐데, 실무에서 메모리가 넘치는걸 방지하는 방법으로 사용하시는게 있는지 어떤걸 사용하시는지 궁금합니다.찾아보니 react-window 같은 라이브러리를 사용해서 실제 랜더링 하는 요소 갯수를 제한하고 스크롤을 유지 시켜서 성능 향상을 하는 글을 봤습니다. 실제로 실무에서 이런 방법을 주로 쓰는지 궁금합니다.
-
해결됨Next.js 시작하기
스페이스 + 컨트롤 눌러도 활성화 안되시는분 참고하세요...
인텔리센스 단축키 (Intellisense shortcut)¶https://stackoverflow.com/questions/51341555/visualcode-shortcut-to-display-autocomplete-list-before-typing-in-macos인텔리센스 사용시 한영 전환(ctrl + space)와 스팟라이트(cmd + space) 단축키와 충돌이 발생하므로 (option + esc) 조합을 사용합니다.메인 메뉴 Code > Preferences > Keyboard Shortcuts (cmd + K, cmd + S)에서 단축키(Trigger Suggest)를 (ctrl + .)로 변경할 수 있습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
section11 - event-bubbling 에러 문의있습니다
새 pc에서 강의를 듣는 중에 yarn dev 후 웹페이지에서 이런 에러가 뜹니다 ㅜㅜ node_modules 삭제 후 재설치 해도 이렇게 뜨는데 어떻게 해결해야 할까요?
-
해결됨Supabase, Next 풀 스택 시작하기 (feat. 슈파베이스 OAuth, nextjs 14)
혹시 ssr과 csr을 병행하여 사용하고 싶을 땐
api/(site)/page.tsx에 actions/todo/todo.actions.ts를 사용하고하위컴포넌트들에서는 ex) api/(site)/compoents/...container.ts 내부에서는 hook을 임포트한것들이 apis/todo-no-rls.ts를 사용하면 될까요? *위 예시는 강의에서 다룬 파일만을 예를 들었보았습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
배포 중 문제
ec2 터미널에서 node를 14버전으로 설치 후에 강의대로 따라가는 중인데요git clone해와서 yarn install시에 node의 버전이 맞지 않아서 설치가 안되고 있습니다ec2의 node 버전을 업그레이드하면 /lib64/libm.so.6: version GLIBC_2.27 not found(required by node) 가 뜨면서 ec2의 버전이 맞지 않는거같고 14버전으로 진행하면 설치한 프로젝트의 버전이 달라서 안되는데 class_build의 버전은 이런데 react의 버전을 바꿔야 해결되는걸까요? "dependencies": { "react": "^18", "react-dom": "^18", "next": "14.2.5" }
-
미해결Next + React Query로 SNS 서비스 만들기
프로필 업로드시 파일명 한글깨짐 현상
회원가입 페이지에서 프로필 이미지를 한글이 포함된 이미지명로 업로드했을때,nextjs의 서버 Formdata에서 한글 파일명이 깨지는 현상이 있습니다. 해결방법이 있을까요??Step1. 회원가입 페이지Step2. 서버 에러 결과회원가입 페이지 경로 : D:\z-com\src\app\(beforeLogin)\_lib\signup.ts업로드시 한글 파일명 출력 확인을 위해 아래코드를 추가해봤습니다. formData.getAll("image").map((data, idx) => { console.log(data); });업로드 이미지명에 한글이 포함되었을때 출력 -- 한글파일명이 깨져서 출력되고 데이터베이스에 저장됩니다.
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트 (장고 4.2 기준)
abstract=True를 사용한 상속 시 컬럼 순서
07-07 강의에서 상속을 써서 중복 컬럼을 제거하는데요상속을 먼저 받기 때문에 날짜 컬럼 순서가 앞에 위치합니다.순서를 뒤로 바꾸려면 어떻게 해야하나요?
-
해결됨Next + React Query로 SNS 서비스 만들기
require 문법 질문드립니다.
제가 알기로는 require는 commonjs에서 쓰는걸로 알고 있는데 useEffect내부에서 쓰시는걸 보고 궁금점이 생겨 질문드립니다.require 대신에 dynamic import를 쓰는건 보았는데 require도 클라이언트에서 사용이 가능한가요?
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
useNavigate 질문입니다
안녕하세요 선생님.useNavigate 질문이 있습니다.새 일기쓰기 페이지에서 '취소하기'를 누르면 뒤로 돌아갈때 nav(-1)을 쓰셨는데 사실 nav(-1)도 할 수 있지만 nav('/')를 할 수도 있는데 성능상의 차이가 있을까요?? 반응 이라던지 좀 더 큰 프로젝트라면 어떤걸 쓰는게 안전하다던지 하는 차이가 있는지 궁금합니다. 감사합니다
-
해결됨손에 익는 Next.js - 공식 문서 훑어보기
route.ts에 async 키워드가 필요한가요?
/app/api/revalidate/route.ts 파일 내 POST 함수가 async로 선언되어 있습니다. 본문에 await이 없는데 async로 선언되어야 하나요?
-
해결됨코드로 배우는 React 19 with 스프링부트 API서버
querydsl 에러
(사진)
-
미해결코드로 배우는 React 19 with 스프링부트 API서버
안녕하세요 로그인 비밀번호 관리 질문드려용....
안녕하세요 로그인 질문 드립니다. 강의와 크게 상관은 없지만 강의 듣다가 궁금한게 생겨서요..현재 시큐리티 강의 듣고 있습니다. DB에 암호화해서 저장하는건 이해하였습니다. 사용자가 암호를 잊었을때 시큐리티 암호 같은 방식은 암호 재발급 하면 되지만 복원 하는건 힘들어 보이네요 .예를 들어서 이런 방식입니다. 암호의 3째자리 까진 알려주는 방식 암호의 흰트를 입력하면 가입한 이메일에 암호를 보내준다던지...ms sqlserver에는 암호 복원 기능 있는거 같던데 ...암튼 강의듣다가 생각나서 질문 드려요
-
미해결React 기반 Gatsby로 기술 블로그 개발하기 v2
렌더링 문제
pages/index.tsx 파일이 먼저 렌더링이 되어야되는데pages/{contentfulPost.slug}.tsx 파일 이 먼저 렌더링 되는 현상이 생겼는데 이거는 무슨경우인가요 ? 갑자기 이렇게 되었습니다. 캐시 제거랑 재부팅까지했지만 해결을 못하고있습니다..
-
해결됨Supabase, Next 풀 스택 시작하기 (feat. 슈파베이스 OAuth, nextjs 14)
타입에러에 관해 질문있습니다
아래와 같은 타입에러가 발생했습니다.데이터베이스와 todoDto모두 설정 및 정상적으로 가져온거 같은데요마우스를 올려보고 에러내용도 확인해보면 타입은 다 같게 설정되어있는거 같습니다.타입자체를 비교해서 대입 하려해도 에러가나오네요..플러그인을 통해서 시각화해서 좀 더 자세하게 머가 문제인지 비교해봐도 타입이 같습니다..혹시 뭐가 문제인지 알 수 있을까요?...
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
강사님 REST API와 GraphQL에 관하여 질문이 있습니다.
graphql의 장점이 딱 원하는 데이터만 가져온다는 것은 이해가 되는데, REST API도 딱 원하는 DB 컬럼만 요청해서 가져오거나 할 수도 있지 않나요? 이게 어떠한 차이가 있나 궁금합니다. ㅎㅎ
-
해결됨기초부터 배우는 Next YTMusic 클론 코딩 (with next.js 14, UI 마스터)
11.5 Channel 노래,앨범 section 강의중
안녕하세요 선생님 ㅎchannel쪽 진행하고 있었는데 앨범 밑에 PlayListCarousel이 크기가 이상하게 나오네여 ㅠㅠ 코드를 다똑같이 써도 안되서 질문드립니다! channel/[id]/page.tsximport HeaderBgChanger from "@/components/HeaderBgChanger"; import PagePadding from "@/components/PagePadding"; import PlayListCarousel from "@/components/PlayListCarousel"; import SongCardRowExpand from "@/components/SongCardRowExpand"; import DarkButton from "@/components/elements/DarkButton"; import WhiteButton from "@/components/elements/WhiteButton"; import { getChannelById } from "@/lib/dummyData"; import { getRandomElementFromArray } from "@/lib/utils"; import { permanentRedirect } from "next/navigation"; import React from "react"; import { FiMusic, FiShuffle } from "react-icons/fi"; interface ChannelPageProps { params: { id: string; }; } const page = async (props: ChannelPageProps) => { const channel = await getChannelById(Number(props.params.id)); if (!channel) permanentRedirect("/"); const imageSrc = getRandomElementFromArray(channel.songList)?.imageSrc; return ( <PagePadding> <HeaderBgChanger imageSrc={imageSrc} /> <div className="mt-[150px]"></div> <section> <div className=" text-[28px] font-bold">{channel.name}</div> <article className="mt-4 lg:hidden"> <div> <DarkButton className={"w-[230px] flex justify-center"} label={"구독중 99만"} /> </div> <div className="flex flex-row gap-4 mt-4"> <WhiteButton label={"셔플"} icon={<FiShuffle size={16}></FiShuffle>} /> <WhiteButton label={"뮤직 스테이션"} icon={<FiMusic size={16} />} /> </div> </article> <div className="hidden lg:flex flex-row items-center gap-4 text-[14px] mt-4"> <WhiteButton label={"셔플"} icon={<FiShuffle size={16}></FiShuffle>} /> <WhiteButton label={"뮤직 스테이션"} icon={<FiMusic size={16} />} /> <DarkButton className={"w-[230px] flex justify-center"} label={"구독중 99만"} /> </div> </section> <section className="mt-[80px]"> <div className=" text-[28px] font-bold">노래</div> <div className="mt-[20px]"> <ul className="flex flex-col gap-2"> {channel.songList.map((song, key) => { return <SongCardRowExpand song={song} key={key} />; })} </ul> </div> </section> <section className="mt-[80px]"> <div className=" text-[28px] font-bold">앨범</div> <PlayListCarousel playlistArray={channel.playlistArray} /> </section> <section className="mt-[80px]"></section> </PagePadding> ); }; export default page;PlayListCarousel.tsximport { Playlist } from "@/types"; import React from "react"; import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; import PlayListCard from "./PlayListCard"; interface PlayListCarouselProps { title?: string; subTitle?: string; Thumbnail?: React.ReactNode; playlistArray?: Playlist[]; } const PlayListCarousel: React.FC<PlayListCarouselProps> = ({ title, subTitle, Thumbnail, playlistArray, }) => { return ( <div className="w-full"> <Carousel> <div className="flex flex-row justify-between items-end my-2"> <article className="flex flex-row gap-3"> {Thumbnail} <div className="flex flex-col justify-center"> <div> {subTitle && ( <div className=" text-neutral-500">{subTitle}</div> )} </div> <div className="text-[34px] font-bold leading-[34px] "> {title} </div> </div> </article> <div className="relative left-[-45px]"> <div className="absolute bottom-[20px]"> <CarouselPrevious className="right-2" /> <CarouselNext className=" left-2" /> </div> </div> </div> <CarouselContent className="mt-4"> {playlistArray?.map((playlist, index) => { return ( <CarouselItem key={index} className="basis-1/2 md:basis-1/3 lg:basis-1/4 xl:basis-1/5" > <PlayListCard playlist={playlist} /> </CarouselItem> ); })} </CarouselContent> </Carousel> </div> ); }; export default PlayListCarousel;
-
해결됨[React / VanillaJS] UI 요소 직접 만들기 Part 2
createPortal 활용해서 modal 만들 때 활용한 MutationObserver 코드 관련 질문
import { useEffect, useRef } from "react" const mutationObserverOption: MutationObserverInit = { childList: true, subtree: true } const ModalRoot = () => { const ref = useRef<HTMLDivElement>(null); useEffect(() => { let observer: MutationObserver if (ref.current) { observer = new MutationObserver(() => { const size = ref.current?.childNodes.length || 0 document.body.classList.toggle('no-scroll', size > 0) }) observer.observe(ref.current, mutationObserverOption) } return () => { observer.disconnect() } }, []) return (<div id="modalRoot" ref={ref}/>) } export default ModalRoot; 수업시간에 구현 되었던 코드가 어떤 순서로 동작하는지 콘솔로 확인해봤습니다. 제가 확인해 봤을 땐 처음 페이지가 렌더링 될 때 1. ModalRoot 컴포넌트가 실행 2. modalRoot div가 생성 3. useEffect가 실행4. useEffect의 Clean Up 작동까지는 예상대로 진행되었습니다. 하지만 이후에 이해가 안되는 부분이 있습니다.질문 1) 이후 useEffect가 다시 실행되는데, 의존성도 없는데 어떻게 다시 실행되는지 궁금합니다.질문 2) 영상에서 모달 버튼을 누르면 useEffect의 조건문에서 size를 콘솔로 확인하셨는데, ModalRoot 컴포넌트가 재실행되지 않고 어떻게 size를 확인할 수 있는지 궁금합니다.
-
해결됨React 기반 Gatsby로 기술 블로그 개발하기 v2
깃 액션 활용
name: Deploy Blog on: push: branches: develop jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 # 원하는 Github Secrets을 불러와 env 파일을 생성해줍니다. - name: Generate env file run: | echo "CONTENTFUL_ACCESS_TOKEN=$CONTENTFUL_ACCESS_TOKEN" >> .env echo "CONTENTFUL_SPACE_ID=$CONTENTFUL_SPACE_ID" >> .env env: CONTENTFUL_ACCESS_TOKEN: ${{ secrets.CONTENTFUL_ACCESS_TOKEN }} CONTENTFUL_SPACE_ID: ${{ secrets.CONTENTFUL_SPACE_ID }} - name: Deploy gatsby uses: enriikke/gatsby-gh-pages-action@v2 with: access-token: ${{ secrets.DEPLOYMENT_ACCESS_TOKEN }} deploy-branch: main gatsby-args: --verbosedevelop 브런치에서 위 코드를 작성하고 git push origin develop 을했는데 액션에 동작을 안합니다.https://github.com/Choi-jeonghoon/Jeong-hoon.github.io/actions/new
-
해결됨React 기반 Gatsby로 기술 블로그 개발하기 v2
code 강조가 안됨니다..
코드블럭 테스트language::typescriptexport const { auth, signIn, signOut, handlers } = NextAuth({등등을 존재하는데 아래와같이 코드는 작성했는데 코드 강조가 안되고있습니다. 뭐가문제일까요? import React from 'react' import { ContentfulRichTextGatsbyReference, renderRichText, } from 'gatsby-source-contentful/rich-text' import { getImage } from 'gatsby-plugin-image' import { NodeRenderer, Options } from '@contentful/rich-text-react-renderer' import { BLOCKS, INLINES, MARKS } from '@contentful/rich-text-types' import { Blockquote, Heading, Image, HorizontalRule, OrderedList, UnorderedList, Link, Code, } from './node' export const HEADERS = [ BLOCKS.HEADING_1, BLOCKS.HEADING_2, BLOCKS.HEADING_3, ] as const const CODE_METADATA_REGEX = /^language::(\w+)/ const options: Options = { renderMark: { [MARKS.CODE]: text => { const isBlock = !!text && CODE_METADATA_REGEX.test(text.toString()) if (!isBlock) return <Code>{text}</Code> else return ( <Code isBlock className={`language-${ CODE_METADATA_REGEX.exec(text.toString())?.[1] }`} > {text.toString().replace(CODE_METADATA_REGEX, '').trimStart()} </Code> ) }, }, renderNode: { ...HEADERS.reduce<{ [block: string]: NodeRenderer }>((nodes, header) => { nodes[header] = (node, children) => ( <Heading type={header}>{children}</Heading> ) return nodes }, {}), [BLOCKS.OL_LIST]: (_node, children) => ( <OrderedList>{children}</OrderedList> ), [BLOCKS.UL_LIST]: (_node, children) => ( <UnorderedList>{children}</UnorderedList> ), [BLOCKS.HR]: () => <HorizontalRule />, [BLOCKS.QUOTE]: (_node, children) => <Blockquote>{children}</Blockquote>, [BLOCKS.EMBEDDED_ASSET]: node => { const { gatsbyImageData, description } = node.data.target const image = getImage(gatsbyImageData) if (image) return <Image image={image} alt={description} /> }, [INLINES.HYPERLINK]: (node, children) => ( <Link href={node.data.uri as string} target="_blank" rel="noopener noreferrer" > {children} </Link> ), }, } export default function useRenderRichText({ raw, references, }: Queries.ContentfulPostContent) { if (!raw) return null return renderRichText( { raw, references: references as unknown as ContentfulRichTextGatsbyReference[], }, options, ) }import React from 'react' import { GatsbyBrowser } from 'gatsby' import Layout from './src/components/common/Layout' import 'prismjs/themes/prism-tomorrow.min.css' export const wrapPageElement: GatsbyBrowser['wrapPageElement'] = ({ element, props, }) => { return <Layout {...props}>{element}</Layout> } import React from 'react' import { GatsbyBrowser } from 'gatsby' import Layout from './src/components/common/Layout' import 'prismjs/themes/prism-tomorrow.min.css' export const wrapPageElement: GatsbyBrowser['wrapPageElement'] = ({ element, props, }) => { return <Layout {...props}>{element}</Layout> }import React, { useEffect } from 'react' import styled from 'styled-components' import { TPostBodyProps } from '../../types/PostBody' import Prism from 'prismjs' import 'prismjs/components/prism-typescript' import useRenderRichText from '../../hooks/useRenderRichText' const Wrapper = styled.div` position: relative; display: grid; grid-template-columns: 1fr 220px; grid-gap: 30px; justify-content: space-between; align-items: flex-start; padding-top: 100px; ` const Content = styled.div` overflow: auto; display: flex; flex-direction: column; gap: 100px; font-size: 16px; line-height: 2; word-break: break-word; ` export default function PostBody({ content }: TPostBodyProps) { const richText = useRenderRichText(content) useEffect(() => { Prism.highlightAll() }, []) return ( <Wrapper> <Content> <div id="content">{richText}</div> {/* 댓글 컴포넌트가 들어갈 자리 */} </Content> {/* 플로팅 목차 컴포넌트가 들어갈 자리 */} </Wrapper> ) }