묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
메인페이지 출력 오류
안녕하세요 메인 페이지 출력이 안되어서 질문 남깁니다해당 코드와 실행결과 사진으로 첨부했습니다
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
searchbar 포트폴리오 리뷰 질문
searchbar 포트폴리오 리뷰에서 props로 searchbar 컴포넌트에 refechBoardsComment넘겨서 searchbar 컴포넌트에서 refechBoardsComment이용하여 페이지네이션을 수정없이 lastpage를변경할 수 있는데const { data: dataBoardsCount, refetch: refechBoardsCount } = useQuery<Pick<IQuery, "fetchBoardsCount">,IQueryFetchBoardsCountArgs>(FETCH_BOARDSCOUNT,{variables:{search:keyword}}); 이렇게 FETCH_BOARDSCOUNT에 variables 값의 search를 keyword(검색어)로 주어도 똑같이 작동하는데 이 방법이 refechBoardsComment props로 넘겨줄 필요없어 간단한거 같은데 refechBoardsComment이용한 것과 같은거 맞나요? 아님 refechBoardsComment를 이용한 것과 차이가 있는 건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
section 04 퀴즈 질문입니다 ㅠ
삭제하기 버튼을 눌럿을때 이런 에러가 뜨는데 이유를 모르겠습니다 ㅜㅠ
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
안녕하세요.
안녕하세요. 질문 있습니다.강의에서는 SWR을 쓰시는데 공부할때는 react-query를 쓰려고 합니다. 이유는 생태계가 더 큰 라이브러리를 사용할 목적이었는데 알려주신 npm trends에 검색해보니 둘 다 비슷비슷하더라고요.SWR도 아직 많이 쓰이는 추세인가요??? 궁금한 이유는 react-query 외에도 추가로 하나의 라이브러리를 동시에 더 써볼 생각이라 추천해주시면 감사합니다. 추가로 수업 외 질문으로 유틸리티 라이브러리?를 하나 써보려고 합니다. 기존에는 fxjs를 사용 중이었습니다. npm trends를 보니 가장 많이 사용하는 라이브러리는 lodash, rxjs인 것 같고 lamda도 많이 쓰는거 같고 타입스크립트로 된 fp-ts도 있는거 같은데 fp-ts를 제외한 것들 중 추천해주시는게 있을까요? 감사합니다!!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
graphql 관련 질문이 있습니다.
안녕하세요 수업 내용 중, graphql 실습 관련하여 궁금한 것이 있습니다. 실습 과정에서 createProduct API에 파라미터 값을 저장해서 보내려 하는 과정에서 mutation을 구성할 때createProductInput 이라는 부분은 REST API의 다중 JSON 방식으로 보내는 것으로 해석하면 될까요?그런데 해당 데이터를 fetchProduct로 조회 시다중 JSON과 같은 그림이 아닌, 그냥 같은 한 덩어리로 넘어오는데 왜 저장할 때는 다중 JSON과 같은 방식으로 넣고 조회 시에는 그냥 뭉쳐져서 조회 되는지 차이가 궁금합니다. (그럼 굳이 CREATE 시 다중 JSON과 같은 방식으로 넣는 이유가 궁금합니다)그리고 중간에 createProductInput 을 사용하는 이유에 대해서 오해를 방지하기 위함이라고 말씀해주셨는데 키 값도 있는데 왜 오해를 불러 일으킨다는 것인지 잘 이해를 못했습니다.질문 요약첫 사진의 createProductInput 부분이 REST API의 다중 JSON 같은 방식이라고 보면 될까요?createProduct로 값을 넣을 때는 이중 JSON과 같은 방식으로 파라미터를 넣고 API를 넘겨줬는데 데이터를 조회하면 그냥 1차원 JSON으로 넘어오는데 그럼 왜 굳이 createProduct 에서 이중 JSON 방식으로 구성해서 넣는 것인지 궁금합니다. createProduct에서 createProductInput로 한 번 더 감싸는 자세한 이유 (수업 중간에 number라는 키 값으로 보이는 것도 있는데 오해를 불러 일으킬 수도 있다는 말씀에서 왜 오해인지 자세히 이해를 못함)입니다. 감사합니다.
-
해결됨따라하며 배우는 리액트 A-Z[19버전 반영]
맥용, prettier 단축키
안녕하세요vscode에서 Prettier 설치를 했는데shift + option + f단축키가 먹히질 않네요ㅠ 왜 그런 건지 알 수 있을까요..?
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
setProducts 질문
위 사진처럼 setProducts 밑에 console.log를 해봤는데한번 출력될 줄 알았는데 왜 두 번 출력되는 건가요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
TypeORM 에러 및 middleware 설치 이후 cors 에러 문의드립니다.
안녕하세요, 강사님!강의 듣던 도중 해결하지 못한 에러가 있어 조언해주실 수 있으신지 문의드립니다. 1 TypeORM에서 다음과 같은 에러가 뜹니다.Server running at http://localhost:4000 QueryFailedError: tables can have at most 1600 columns at PostgresQueryRunner.query (생략) { query: 'ALTER TABLE "posts" ADD "title" character varying NOT NULL', parameters: undefined, driverError: error: tables can have at most 1600 columns at Parser.parseErrorMessage (생략) { length: 100, severity: 'ERROR', code: '54011', detail: undefined, hint: undefined, position: undefined, internalPosition: undefined, internalQuery: undefined, where: undefined, schema: undefined, table: undefined, column: undefined, dataType: undefined, constraint: undefined, file: 'tablecmds.c', line: '6819', routine: 'ATExecAddColumn' }, length: 100, severity: 'ERROR', code: '54011', detail: undefined, hint: undefined, position: undefined, internalPosition: undefined, internalQuery: undefined, where: undefined, schema: undefined, table: undefined, column: undefined, dataType: undefined, constraint: undefined, file: 'tablecmds.c', line: '6819', routine: 'ATExecAddColumn' } <== 이 부분이 계속 반복됩니다 (반복적으로 테이블이 생성되는 듯)stackoverflow를 찾아보니 oneToMany relation이 문제가 된다는 글을 봤는데... 제가 백앤드쪽 지식이 전무하다보니, oneToMany 데코레이터로 설정된 Entity를 어떻게 수정해야하는건지 잘 모르겠고, 강사님 코드와 동일하게 작성한 것 같은데 뭐가 문젠지 잘 모르겠습니다.특히나 아래 코드 중,{ length: 100, severity: 'ERROR', .... }이 부분이 반복적으로 로그에 찍히는데 어떻게 해결해야할지 잘 모르겠습니다.이 글을 보니, nomarlization을 진행하라는데 이게 맞는 솔루션인가요?백앤드 해보고 싶어서 아는 것 없이 무작정 따라하는 중인데, 지식이 부족해서 제대로 된 해결방법이 뭔지 알 수가 없네요.. 2middleware 설치 전까지는 회원가입 기능이 잘 되었었는데, middleware 설치 이후로는, 로컬호스트 3000에서 cors 에러가 뜹니다. (아래 이미지 첨부)로그인, 회원가입 기능 어떤것도 되지 않는 상태이고, token도 생성되지 않습니다.일단 계속해서 강의를 듣고 있는데 에러 해결이 안되어 더이상 진도를 나갈 수가 없어서 문의남깁니다 ㅠㅠAccess to XMLHttpRequest at 'http://localhost:4000/api/auth/me' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. { "message": "Network Error", "name": "AxiosError", "stack": "AxiosError: Network Error\n at XMLHttpRequest.handleError (webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:168:14)", "config": { "transitional": { "silentJSONParsing": true, "forcedJSONParsing": true, "clarifyTimeoutError": false }, "adapter": [ "xhr", "http" ], "transformRequest": [ null ], "transformResponse": [ null ], "timeout": 0, "xsrfCookieName": "XSRF-TOKEN", "xsrfHeaderName": "X-XSRF-TOKEN", "maxContentLength": -1, "maxBodyLength": -1, "env": {}, "headers": { "Accept": "application/json, text/plain, /" }, "baseURL": "http://localhost:4000/api", "withCredentials": true, "method": "get", "url": "/auth/me" }, "code": "ERR_NETWORK", "status": null } 나름대로 검색도 해보고, node_modules 싹 날리고 재설치해봤으나.. ㅠㅠ 답변 기다리겠습니다 감사합니다
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
5분13초 부분에서 삭제버튼을 누르면 onDelete가 함수가 아니라고 에러메세지가 뜨는데요.
5분13초 부분에서 삭제버튼을 누르게 되면 다음과 같은 에러가 발생하여, 원인을 찾고 있는데Uncaught TypeError: onDelete is not a function강의영상 화면의 소스코드와 비교를 해봐도 원인을 찾기가 어려워, 강의 진행에서 사용된 소스를 직접보려고 합니다. (Props으로 넘기는 부분을 몇번을 확인했는데 강의영상과 다른 부분을 찾을 수 없었습니다.)구글 리서치중 import관련된 부분의 가능성을 제시한 글이 있어 찾아보다가,import관련된 소스코드부분이 이번회 강의영상에서 화면에 보이지 않아,밑의 강의 진행 중 사용된 소스코드 링크를 타고 들어가봤는데, 소스가 현재부분의 소스가 아닙니다.(DiaryItem.js, DiaryList.js 등의 파일이 빠져있습니다.)현재강의(6-5 리스트 데이터 삭제하기) 기준으로, 소스코드 링크를 받을 수 있을까요?(아니면 완성본이라도... ^^;)감사합니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
EachDM의 왼쪽에 있는 동그라미의 색깔이 초록색으로 변하지 않음
[제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
DM List와 Channel List에서 CollapseButton을 누르면 CollapseButton의 왼편에 있는 화살표가 아래로 향해야 하는데 변하지 않습니다.
[제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
제너레이터 함수 안 yield 구문 실행에대해 질문드립니다..
function* test() { yield 1; yield 2;}===============const gen = test()===============gen.next(){ "value": 1, "done": false}보통 제너레이터 함수를 설명할때, 개발자 도구에서 이런식으로 설명을 많이 하는것같은데...설명만 보면 yield 구문실행을 위해선, next() 라는 함수를 사용해야된다는건 알겠습니다..근데 실제코드에서는 next() 함수를 실행하지않는데, 어떻게 yield 구문들이 실행되는지 이해가 안됩니다..
-
미해결Slack 클론 코딩[실시간 채팅 with React]
react-router-dom v6 오류
다른분들의 질문들을 보면서 수정을 해보았지만 해결되지 않아 질문 올립니다. -react-router-dom 버전으로 인해 swtch는 Routes로 바꾸었으며 worspace뒤에 *을 붙였습니다.//App.ts <Routes> <Route path="/" element={<Navigate replace to="/login" />} /> <Route path="/login" element={<LogIn />} /> <Route path="/signup" element={<SignUp />} /> <Route path="/workspace/:workspace/*" element={<Workspace />} /> </Routes>workspace.tsx 에서는 밑에 코드로 바꾸었습니다. //Workspace.tsx <Routes> <Route path="channel/:channel" element={<Channel />} /> <Route path="dm/:id" element={<DirectMessage />} /> </Routes> 그리고 나서 login 은 잘 되고 쿠키도 잘 저장되지만 http://localhost:3090/workspace/sleact/channel/%EC%9D%BC%EB%B0%98 이 주소로 가면 아래 같은 에러가 뜨면서 창이 뜨지 않습니다. 어떤게 잘못된 걸까요 ㅠ TypeErrorCannot read properties of undefined (reading 'match')Call Stack useParams alecture/./node_modules/react-router/esm/react-router.js:760:34 ChannelList alecture/./components/ChannelList/index.tsx:40:75 renderWithHooks alecture/./node_modules/react-dom/cjs/react-dom.development.js:14985:18 mountIndeterminateComponent alecture/./node_modules/react-dom/cjs/react-dom.development.js:17811:13 beginWork alecture/./node_modules/react-dom/cjs/react-dom.development.js:19049:16 HTMLUnknownElement.callCallback alecture/./node_modules/react-dom/cjs/react-dom.development.js:3945:14 Object.invokeGuardedCallbackDev alecture/./node_modules/react-dom/cjs/react-dom.development.js:3994:16 invokeGuardedCallback alecture/./node_modules/react-dom/cjs/react-dom.development.js:4056:31 beginWork$1 alecture/./node_modules/react-dom/cjs/react-dom.development.js:23959:7 performUnitOfWork alecture/./node_modules/react-dom/cjs/react-dom.development.js:22771:12
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
Wsl mysql 질문
오늘 하루 mysql 설정하는거때문에 다 날려먹으면서 혼자서 해보고있는데 우여곡절 끝에 WSL에 mysql까지 실행 성공시켜서 그안에 react-nodebird database가 있는건 확인했는데 윈도우에 깐 mysql-workbench랑 연동을 못하고있는데 크게 상관 없을까요 제로초쌤..?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
react-netflix-clone 오류 문의
안녕하세요!react-netflix-clone의 메인 페이지에서 발생한 오류에 대해 문의 드립니다.문의1) key={movies.id} 에 대해 "list should have a unique "key" prop" 오류가 발생합니다.Row.js ... <div id={id} className='row__posters'> {movies.map((movie) => ( <SwiperSlide> <img key={movie.id} style={{ padding: "25px 0" }} className={`row__poster ${isLargeRow && "row__posterLarge"}`} src={`https://image.tmdb.org/t/p/original/${ isLargeRow ? movie.poster_path : movie.backdrop_path }`} alt={movie.name} onClick={() => { imageClickHandler(movie) }} /> </SwiperSlide> ))} </div>MainPage.js 에서 Row 컴포넌트를 하나만 정의해도 동일한 오류가 발생합니다. 문의2) useEffect eslint warning이 안나게 하려면 어떻게 해야 하나요?useEffect(() => { fetchMovieData(); }, []); ==> warning WARNING in [eslint] src/components/Row.js Line 24:8: React Hook useEffect has a missing dependency: 'fetchMovieData'. Either include it or remove the dependency array react-hooks/exhaustive-deps 문의3) autoprefixer warning이 안나게 하려면 어떻게 해야 하나요?warningWARNING in ./src/components/Banner.css (./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].oneOf[5].use[2]!./node_modules/source-map-loader/dist/cjs.js!./src/components/Banner.css) Module Warning (from ./node_modules/postcss-loader/dist/cjs.js): Warning (75:5) autoprefixer: start value has mixed support, consider using flex-start insteadBanner.css (제공 소스 그대로 적용함).banner { color: white; object-fit: contain; height: 448px; } @media (min-width: 1500px) { .banner { position: relative; height: 600px; } .banner--fadeBottom { position: absolute; bottom: 0; width: 100%; height: 40rem; } } @media (max-width: 768px) { .banner__contents { width: min-content !important; padding-left: 2.3rem; margin-left: 0px !important; } .banner__description { font-size: 0.8rem !important; width: auto !important; } .info { text-align: start; padding-right: 1.2rem; } .space { margin-left: 6px; } .banner__button { font-size: 0.8rem !important; border-radius: 4px !important; } } .banner__contents { margin-left: 40px; padding-top: 140px; height: 190px; } .banner__title { font-size: 3rem; font-weight: 800; padding-bottom: 0.5rem; } .banner__description { width: 45rem; line-height: 1.3; padding-top: 1rem; font-weight: 500; font-size: 1rem; max-width: 400px; height: 80px; } .banner--fadeBottom { height: 7.4rem; background-image: linear-gradient( 180deg, transparent, rgba(37, 37, 37, 0.61), #111 ); } .banner__buttons { display: flex; flex-direction: row; } .banner__button { display: flex; flex-direction: row; justify-content: start; align-items: center; cursor: pointer; outline: none; border: none; font-size: 1rem; font-weight: 700; border-radius: 0.2vw; padding: 0.4rem 1.8rem 0.4rem 1rem; margin-right: 1rem; } .banner__button:hover { color: #000; background-color: rgba(170, 170, 170, 0.9); transition: all 0.2s; } .play { background-color: white; color: black; } .info { background-color: rgba(109, 109, 110, 0.7); color: white; } .info:hover { background-color: rgb(74, 74, 74); color: white; } .space { margin-left: 4px; }
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
강의를 보며 작업한 코드를 개인 깃허브에 올려도 되나요?
강의를 보며 작업한 코드(실습코드, 일기장 등)를 깃허브 공개 레포에 올려도 되나요?
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
router.js:954 No routes matched location 에러가 발생합니다.
안녕하세요 제로초님, 현재 react-router-dom v6.6.2로 강의를 수강하고 있습니다. 앞부분에서 잘 따라하다가 어느 부분에서 잘못된 것인지 로그인을 했을 때 흰 화면과 함께 아래 첨부한 사진과 같은 에러가 발생하했습니다.API를 받아올 때 사용하는 params에 문제가 있나 싶었지만, http://localhost:3095/api/workspaces/sleact/channels 이 주소로 들어갔을 때 아래와 같은 데이터를 받아오는 것을 확인할 수 있었습니다.[ { "id": 1, "name": "일반", "private": false, "createdAt": "2023-01-26T08:07:33.000Z", "updatedAt": "2023-01-26T08:07:33.000Z", "WorkspaceId": 1, "Members": [ { "id": 2, "ChannelMembers": { "UserId": 2 } } ] } ] 데이터가 문제인가 싶어서 테이블도 삭제했다가 다시 만들어봤지만 해결할 수 없었습니다ㅠㅠ 어떻게 해결할 수 있을까요?? 혹시 몰라 코드는 모두 첨부하겠습니다! // App/index.tsx import React from 'react'; import loadable from '@loadable/component'; import { Routes, Route, Navigate } from 'react-router-dom'; const Login = loadable(() => import('@pages/Login')); const SignUp = loadable(() => import('@pages/SignUp')); const Workspace = loadable(() => import('@layouts/Workspace')); const App = () => { return ( <Routes> <Route path="/" element={<Navigate to="/login" />} /> <Route path="/login" element={<Login />} /> <Route path="/signup" element={<SignUp />} /> <Route path="/workspace/:workspace" element={<Workspace />} /> </Routes> ); }; export default App;// Workspace/index.tsx import React, { useCallback, useEffect, useState } from 'react'; import useSWR from 'swr'; import axios from 'axios'; import fetcher from '@utils/fetcher'; import gravatar from 'gravatar'; import { Navigate, Route, Routes } from 'react-router'; import { AddButton, Channels, Chats, Header, LogOutButton, MenuScroll, ProfileImg, ProfileModal, RightMenu, WorkspaceButton, WorkspaceModal, WorkspaceName, Workspaces, WorkspaceWrapper, } from './styles'; import loadable from '@loadable/component'; import Menu from '@components/Menu'; import { Link } from 'react-router-dom'; import { IChannel, IUser } from '@typings/db'; import Modal from '@components/Modal'; import { Button, Input, Label } from '@pages/SignUp/styles'; import useInput from '@hooks/useInput'; import { toast } from 'react-toastify'; import CreateChannelModal from '@components/CreateChannelModal'; import { useParams } from 'react-router'; const Channel = loadable(() => import('@pages/Channel')); const DirectMessage = loadable(() => import('@pages/DirectMessage')); const Workspace = () => { const [showUserMenu, setShowUserMenu] = useState(false); const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false); const [showWorkspaceModal, setShowWorkspaceModal] = useState(false); const [showCreateChannelModal, setShowCreateChannelModal] = useState(false); const [newWorkspace, onChangeNewWorkspace, setNewWorkspace] = useInput(''); const [newUrl, onChangeNewUrl, setNewUrl] = useInput(''); const params = useParams<{ workspace?: string }>(); const { workspace } = params; const { data: userData, error, mutate } = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher); const { data: channelData } = useSWR<IChannel[]>( userData ? `http://localhost:3095/api/workspaces/${workspace}/channels` : null, fetcher, ); const onLogout = useCallback(() => { axios .post('http://localhost:3095/api/users/logout', null, { withCredentials: true, }) .then(() => { mutate(false, false); }); }, []); const onClickUserProfile = useCallback(() => { setShowUserMenu((prev) => !prev); }, []); const onCloseUserProfile = useCallback((e: React.MouseEvent) => { e.stopPropagation(); setShowUserMenu(false); }, []); const onClickCreateWorkspace = useCallback(() => { setShowCreateWorkspaceModal((prev) => !prev); }, []); const onCreateWorkspace = useCallback( (e: React.FormEvent) => { e.preventDefault(); // 필수 값이 들어있는지 검사 if (!newWorkspace || !newWorkspace.trim()) return; if (!newUrl || !newUrl.trim()) return; axios .post( 'http://localhost:3095/api/workspaces', { workspace: newWorkspace, url: newUrl, }, { withCredentials: true }, ) .then(() => { mutate(); setShowCreateWorkspaceModal(false); setNewWorkspace(''); setNewUrl(''); }) .catch((error) => { console.dir(error); toast.error(error.response?.data, { position: 'bottom-center' }); }); }, [newWorkspace, newUrl], ); const onCloseModal = useCallback(() => { setShowCreateWorkspaceModal(false); setShowCreateChannelModal(false); }, []); const onClickAddChannel = useCallback(() => { setShowCreateChannelModal(true); }, []); const toggleWorkspaceModal = useCallback(() => { setShowWorkspaceModal((prev) => !prev); }, []); if (!userData) { return <Navigate to="/login" />; } return ( <div> <Header> <RightMenu> <span onClick={onClickUserProfile}> <ProfileImg src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.email} /> <Menu style={{ right: 0, top: 38 }} show={showUserMenu} onCloseModal={onCloseUserProfile}> <ProfileModal> <img src={gravatar.url(userData.email, { s: '36px', d: 'retro' })} alt={userData.email} /> <div> <span id="profile-name">{userData.nickname}</span> <span id="profile-active">Active</span> </div> </ProfileModal> <LogOutButton onClick={onLogout}>로그아웃</LogOutButton> </Menu> </span> </RightMenu> </Header> <WorkspaceWrapper> <Workspaces> {userData?.Workspaces?.map((ws) => { return ( <Link key={ws.id} to={`${ws.url}/channel/일반`}> <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton> </Link> ); })} <AddButton onClick={onClickCreateWorkspace}>+</AddButton>; </Workspaces> <Channels> <WorkspaceName onClick={toggleWorkspaceModal}>Sleact</WorkspaceName> <MenuScroll> <Menu style={{ top: 95, left: 80 }} show={showWorkspaceModal} onCloseModal={toggleWorkspaceModal}> <WorkspaceModal> <h2>Sleact</h2> <button onClick={onClickAddChannel}>채널 만들기</button> <button onClick={onLogout}>로그아웃</button> </WorkspaceModal> </Menu> {channelData?.map((v) => ( <div>{v.name}</div> ))} </MenuScroll> </Channels> <Chats> <Routes> <Route path="/:workspace/channel/:channel" element={<Channel />} /> <Route path="/:workspace/dm/:id" element={<DirectMessage />} /> </Routes> </Chats> </WorkspaceWrapper> <Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}> <form onSubmit={onCreateWorkspace}> <Label id="workspace-name"> <span>워크스페이스 이름</span> <Input id="workspace" value={newWorkspace} onChange={onChangeNewWorkspace} /> </Label> <Label id="workspace-label"> <span>워크스페이스 url</span> <Input id="workspace" value={newUrl} onChange={onChangeNewUrl} /> </Label> <Button type="submit">생성하기</Button> </form> </Modal> <CreateChannelModal show={showCreateChannelModal} onCloseModal={onCloseModal} setShowCreateChannelModal={setShowCreateChannelModal} /> </div> ); }; export default Workspace; // CreateChannel/index.tsx import Modal from '@components/Modal'; import useInput from '@hooks/useInput'; import { Button, Input, Label } from '@pages/SignUp/styles'; import { IChannel, IUser } from '@typings/db'; import fetcher from '@utils/fetcher'; import axios from 'axios'; import React, { useCallback } from 'react'; import { useParams } from 'react-router'; import { toast } from 'react-toastify'; import useSWR from 'swr'; interface Props { show: boolean; onCloseModal: () => void; setShowCreateChannelModal: (flag: boolean) => void; } const CreateChannelModal: React.FC<Props> = ({ show, onCloseModal, setShowCreateChannelModal }) => { const [newChannel, onChangeNewChannel, setNewChannel] = useInput(''); const params = useParams<{ workspace?: string }>(); const { workspace } = params; const { data: userData, error, mutate } = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher); const { mutate: mutateChannel } = useSWR<IChannel[]>( userData ? `http://localhost:3095/api/workspaces/${workspace}/channels` : null, fetcher, ); const onCreateChannel = useCallback( (e: React.FormEvent) => { e.preventDefault(); axios .post( `http://localhost:3095/api/workspaces/${workspace}/channels`, { name: newChannel, }, { withCredentials: true }, ) .then(() => { setShowCreateChannelModal(false); mutateChannel(); setNewChannel(''); }) .catch((error) => { console.dir(error); toast.error(error.response?.data, { position: 'bottom-center' }); }); }, [newChannel, workspace], ); return ( <Modal show={show} onCloseModal={onCloseModal}> <form onSubmit={onCreateChannel}> <Label id="channel-label"> <span>채널 이름</span> <Input id="workspace" value={newChannel} onChange={onChangeNewChannel} /> </Label> <Button type="submit">생성하기</Button> </form> </Modal> ); }; export default CreateChannelModal;
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
_app.tsx 에러
_app.tsx 로 바꾸고나서 <Component/>에서 에러가 나는데 어떻게 해야하나요...?
-
미해결처음 만난 리액트(React)
(실습) Context를 사용하여 테마 변경 기능 만들기 에서 테마 변경이 안 돼요.
테마 변경 버튼을 눌러도 테마 색 변경이 안 됩니다. 이전에 카드 만들기 실습에서도 색이 안 나오는 걸 보면 제 습관적인 실수인데... 제 실수를 못 찾겠습니다...
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
section02 quiz - 3번째 퀴즈 관련 질문입니다.
첫번째 문제에서 게시글을 등록하라고 하는데, 정보를 입력했더니 해당 오류가 발생합니다. 어떻게 해결해야 하나요?