묻고 답해요
150만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
순위 정보를
불러오고 있어요
-
미해결Slack 클론 코딩[실시간 채팅 with React]
fetcher 함수의 data 값이 두번 찍히는 이유
Login.tsx에서 swr로 호출한 users의 data 값을 return 직전에 console 로그로 찍어봤는데요네트워크 탭에서는 users 요청은 한번 밖에 없었는데undefined와 false가 연달아서 찍히더라구요다른 질문에서 답변해주신 내용을 보니 데이터 로딩중엔 undefined라고 말씀해주셨는데, 맨 처음 컴포넌트가 렌더링될 때 useSWR이 api를 호출하게 되고 이때 console.log(data)는 아직 데이터가 로딩 중이라서 undefined가 찍히게 되고, 이후 데이터 로딩이 완료되면 useSWR이 다시 호출되어? false가 찍히는 프로세스로 이해했는데 맞는걸까요그렇다면 useSWR로 api호출 시 무조건 최소 2번 렌더링될 수 밖에 없는걸까요?// Login.tsx import React, { useState, useCallback, useEffect } from 'react'; import { TextField, Button } from '@components/index'; import { Link, Navigate } from 'react-router-dom'; import { useInput } from '@hooks/useInput'; import { LogoSlack } from '@assets/icons/'; import axios from 'axios'; import useSWR from 'swr'; import fetcher from '@utils/fetcher'; const Login = () => { // useSWR은 get으로 요청한 데이터를 받아와서 저장한다. // mutate : 내가 원할 때 SWR 호출하기 const { data, error, mutate } = useSWR('http://localhost:3095/api/users', fetcher, { dedupingInterval: 5000, // 주기적으로 호출하지만, dedupingInterval 기간 내에는 캐시에서 불러온다 }); const [logInError, setLogInError] = useState(false); const [email, setEmail, onChangeEmail] = useInput(''); const [password, setPassword] = useInput<string>(''); const onChangePassword = useCallback( (e: React.ChangeEvent<HTMLInputElement>) => { setPassword(e.target.value); }, [email, password, data], ); const onSubmit = useCallback( (e) => { setLogInError(false); axios .post( 'http://localhost:3095/api/users/login', { email, password }, { withCredentials: true, }, ) .then(() => { mutate(); }) .catch((error) => { setLogInError(error.response?.status === 401); }); }, [email, password], ); console.log(data); // if (data) return <Navigate to="/workspace/channel" />; return ( <div className="max-w-[400px] mx-auto px-[20px]"> <h1 className="flex justify-center pt-[60px] pb-[20px]"> <LogoSlack /> <span className="blind">Slack</span> </h1> <TextField label="이메일 주소" type="email" value={email} onChange={onChangeEmail} /> <TextField label="비밀번호" type="password" value={password} onChange={onChangePassword} /> {logInError && <p className="mb-[20px] mt-[-10px] text-red-500 font-normal">로그인 실패</p>} <Button text="로그인" onClick={onSubmit} /> <p className="mt-[10px] text-center"> Slack을 처음 사용하시나요? <Link to="/sign" className="ml-[4px] text-blue-600"> 회원가입 </Link> </p> </div> ); }; export default Login;
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
제네릭 질문
커스텀 훅에서 사용된 제네릭에 대해서 공부하다가 궁금한게 생겨서요.아래 테스트 코드에서'+' 연산자는 'T' 및 'T' 유형에 적용할 수 없습니다. 라는 에러 코드가 발생하는 이유가 이해가 안 가서요위 함수는 사용한다면 아래 처럼 숫자 또는 문자인 타입으로 쓰일텐데, 그러면 return 값에서 + 연산자가 number + number 또는 문자열 + 문자열로 실행되어 문제가 없을 것 같은데 에러가 뜨는 이유가 모르겠어서요add<number>(1, 2); add<string>('1', '2');지피티에 질문해보니 함수 오버로드를 쓰거나 return 값에 any를 쓰라곤 하는데 잘못된 방법 같고 extends로 타입 제한을 걸어도 같은 에러가 뜹니다function add<T extends number | string>(x: T, y: T): T { return x + y; }제가 참고한 제네릭 레퍼런스 자료입니다https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Generic-%ED%83%80%EC%9E%85-%EC%A0%95%EB%B3%B5%ED%95%98%EA%B8%B0#%EC%A0%9C%EB%84%A4%EB%A6%ADgenerics_%EC%86%8C%EA%B0%9C
-
미해결Slack 클론 코딩[실시간 채팅 with React]
ts-node 대신 tsx 사용여부
질문: 1.ts-node로 이어나가도 괜찮을까요?2. outdated 명령어 쳐보니 버전이 강의에서 한두개 추가설치했지만, 이제 빨간 글씨로 10개 정도 다 버전 업그레이드를 요구했습니다.제가 고쳐나가면서 나아가보면될지 궁금합니다.ㅡㅡㅡㅡㅡㅡㅡㅡ상황: 몇주전 타입스크립트를 다른 강의로 시작했습니다.최근 환경설정에서 ts-node 사용시 오류가 계속 떠서 그 강의가 tsx 사용을 권장했고, tsx로 입문했습니다.그래서 ts-node로 잘돌아갈지 의문입니다.
-
미해결웹 게임을 만들며 배우는 Vue
숫자 야구 npm run build시 오류
ERROR in app.js from TerserError: error:0308010C:digital envelope routines::unsupported구글링해보니 리액트밖에 안뜨네요.. 어떻게 해야 하나요?
-
미해결Slack 클론 코딩[실시간 채팅 with React]
str.toLowerCase is not a function
예상하지 못한 부분에서 에러가 나와서 질문 남겨드립니다 !ChatBox.tsximport React, { useCallback, useEffect, useRef, VFC } from 'react'; import { ChatArea, EachMention, Form, MentionsTextarea, SendButton, Toolbox } from './styles'; import autosize from 'autosize'; import { Mention, SuggestionDataItem } from 'react-mentions'; import { useParams } from 'react-router'; import useSWR from 'swr'; import { IUser } from '@typings/db'; import fetcher from '@utils/fetcher'; import gravatar from 'gravatar'; interface Props { chat: string; onSubmitForm: (e: any) => void; onChangeChat: (e: any) => void; placeholder?: string; } const ChatBox: VFC<Props> = ({ chat, onSubmitForm, onChangeChat, placeholder }) => { const { workspace } = useParams<{ workspace: string }>(); const { data: userData, error, revalidate, mutate, } = useSWR<IUser | false>('/api/users', fetcher, { dedupingInterval: 2000, // 2초 }); const { data: memberData } = useSWR<IUser[]>(userData ? `/api/workspaces/${workspace}/members` : null, fetcher); const textareaRef = useRef<HTMLTextAreaElement>(null); useEffect(() => { if (textareaRef.current) { autosize(textareaRef.current); } }, []); const onKeydownChat = useCallback( (e) => { if (e.key === 'Enter') { if (!e.shiftKey) { e.preventDefault(); onSubmitForm(e); } } }, [onSubmitForm], ); const renderSuggestion = useCallback( ( suggestion: SuggestionDataItem, search: string, highlightedDisplay: React.ReactNode, index: number, focus: boolean, ): React.ReactNode => { if (!memberData) return; return ( <EachMention focus={focus}> <img src={gravatar.url(memberData[index].email, { s: '20px', d: 'retro' })} alt={memberData[index].nickname} /> <span>{highlightedDisplay}</span> </EachMention> ); }, [memberData], ); return ( <ChatArea> <Form onSubmit={onSubmitForm}> <MentionsTextarea id="editor-chat" value={chat} onChange={onChangeChat} onKeyPress={onKeydownChat} placeholder={placeholder} inputRef={textareaRef} allowSuggestionsAboveCursor > <Mention appendSpaceOnAdd trigger="@" data={memberData?.map((v) => ({ id: v.id, display: v.nickname })) || []} renderSuggestion={renderSuggestion} /> </MentionsTextarea> <Toolbox> <SendButton className={ 'c-button-unstyled c-icon_button c-icon_button--light c-icon_button--size_medium c-texty_input__button c-texty_input__button--send' + (chat?.trim() ? '' : ' c-texty_input__button--disabled') } data-qa="texty_send_button" aria-label="Send message" data-sk="tooltip_parent" type="submit" disabled={!chat?.trim()} > <i className="c-icon c-icon--paperplane-filled" aria-hidden="true" /> </SendButton> </Toolbox> </Form> </ChatArea> ); }; export default ChatBox;혼자서 해결해보려다가 못찾고 있어서 질문 남겨드려요 ㅠㅠ
-
미해결Slack 클론 코딩[실시간 채팅 with React]
[공유] react-mention 항상 커서 위에 나오게 수정
이전 질문을 보면 react-mention 추천이 커서 아래로 나온다고 해서 공식 문서를 확인해봤습니다.결론적으로는 forceSuggestionsAboveCursor을 사용하면 됩니다. 해당 내용은 제로초님 sleact 레파지토리에 pull request 하였습니다.allowSuggestionsAboveCursor는 아래 공간이 부족하면 위에 배치가 '가능'하도록, 즉 워크스페이스 사람이 적으면 아래로 배치forceSuggestionsAboveCursor는 항상 위로 배치allowSuggestionsAboveCursor: Renders the SuggestionList above the cursor if there is not enough space belowforceSuggestionsAboveCursor: Forces the SuggestionList to be rendered above the cursor
-
미해결웹 게임을 만들며 배우는 React
webpack설정 시 json 파일 에러
안녕하세요 제로초님,제가 웹팩을 이용해서 리액트를 사용하면서 json 파일을 로컬에서 사용하고 싶어서 import를 해봤는데 세미콜론 에러가 나서 json-loader를 추가해보니 또 다른 에러가 나서.. 혹시 이 부분에 대한 해결 방법이나 방향을 알고 계신가요? import data from "./info.json"; // 에러=> json loader 추가 후webpack.config.jsconst path = require("path"); const RefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); module.exports = { name: "react-test-setting", mode: "development", devtool: "eval", resolve: { extensions: [".jsx", ".js"], }, entry: { app: ["./client"], }, // input module: { rules: [ { test: /\.jsx?/, loader: "babel-loader", options: { presets: ["@babel/preset-env", "@babel/preset-react"], plugins: ["react-refresh/babel"], }, }, { test: /\.json$/, loader: "json-loader", }, ], }, plugins: [new RefreshWebpackPlugin()], output: { path: path.join(__dirname, "/dist"), filename: "app.js", }, // output devServer: { devMiddleware: { publicPath: "/dist" }, static: { directory: path.resolve(__dirname) }, hot: true, }, }; info.json{ "items": [ { "name": "kim", "age": 21, "address": "seoul" }, { "name": "lee", "age": 23, "address": "seoul" }, { "name": "park", "age": 31, "address": "seoul" } ] }
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
안녕하세요.
안녕하세요. 질문 있습니다.강의에서는 SWR을 쓰시는데 공부할때는 react-query를 쓰려고 합니다. 이유는 생태계가 더 큰 라이브러리를 사용할 목적이었는데 알려주신 npm trends에 검색해보니 둘 다 비슷비슷하더라고요.SWR도 아직 많이 쓰이는 추세인가요??? 궁금한 이유는 react-query 외에도 추가로 하나의 라이브러리를 동시에 더 써볼 생각이라 추천해주시면 감사합니다. 추가로 수업 외 질문으로 유틸리티 라이브러리?를 하나 써보려고 합니다. 기존에는 fxjs를 사용 중이었습니다. npm trends를 보니 가장 많이 사용하는 라이브러리는 lodash, rxjs인 것 같고 lamda도 많이 쓰는거 같고 타입스크립트로 된 fp-ts도 있는거 같은데 fp-ts를 제외한 것들 중 추천해주시는게 있을까요? 감사합니다!!
-
미해결Slack 클론 코딩[실시간 채팅 with React]
Cannot read properties of undefined (reading 'map')
제로초님, 코드를 따라친 후에 로그아웃을 하고 다시 로그인 하면 이런 에러메세지가 뜹니다.그런데 네트워크 탭을 보면 로그인이 정상적으로 된거 같아서 새로고침을 하면 에러 메세지가 사라지고 슬랙에서 로그인된 화면이 제대로 뜹니다.근데 또 여기서 워크스페이스를 생성하려고 하면 콘솔에 axioserror메세지가 떠서 어떻게 해야될지 모르겠습니다..Workspace/index.tsximport axios from "axios"; import React, { FC, useCallback, useState } from "react"; import useSWR from 'swr'; import fetcher from "@utils/fetcher"; import { Navigate, Routes, Route } from "react-router-dom"; import { AddButton, Channels, Chats, Header, LogOutButton, MenuScroll, ProfileImg, ProfileModal, RightMenu, WorkspaceButton, WorkspaceName, Workspaces, WorkspaceWrapper } from "@layouts/Workspace/style"; import gravatar from 'gravatar'; import loadable from '@loadable/component'; import Menu from "../../components/Menu"; import Modal from "../../components/Modal"; import { Link } from "react-router-dom"; import { IUser } from "@typings/db"; import { Button, Input, Label } from "@pages/SignUp/styles"; import useInput from "@hooks/useInput"; import {toast} from 'react-toastify'; const Channel = loadable(() => import('@pages/Channel')); const DirectMessage = loadable(() => import('@pages/DirectMessage')); const Workspace: FC<React.PropsWithChildren<{}>> = ({children}) => { const [showUserMenu, setShowUserMenu] = useState(false); const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false); const [newWorkspace, onChangeNewWorkspace, setNewWorkspace] = useInput(''); const [newUrl, onChangeNewUrl, setNewUrl] = useInput(''); // revalidate = 서버로 요청 다시 보내서 데이터를 다시 가져옴 // mutate = 서버에 요청 안보내고 데이터를 수정 const {data: userData, error, mutate} = useSWR<IUser | false>('/api/users', fetcher, { dedupingInterval: 2000, }); const onLogout = useCallback(() => { axios.post('/api/users/logout', null , { withCredentials: true, }) .then(() => { mutate(false, false); }) }, []); const onClickUserProfile = useCallback((e: any) => { e.stopPropagation(); setShowUserMenu((prev) => !prev); }, []) const onClickCreateWorkspace = useCallback(() => { setShowCreateWorkspaceModal(true); }, []) const onCreateWorkspace = useCallback((e: any) => { 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); }, []) if(!userData) { return <Navigate to="/login" /> } if(!userData) return null; return( <div> <Header> <RightMenu> <span onClick={onClickUserProfile}> <ProfileImg src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.nickname} /> {showUserMenu && <Menu style={{ right: 0, top: 38 }} show={showUserMenu} onCloseModal={onClickUserProfile}> <ProfileModal> <img src={gravatar.url(userData.nickname, { s: '36px', d: 'retro' })} alt={userData.nickname} /> <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={`/workspace/${123}/channel/일반`}> <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton> </Link> ); })} <AddButton onClick={onClickCreateWorkspace}>+</AddButton> </Workspaces> <Channels> <WorkspaceName>Sleact</WorkspaceName> <MenuScroll>menu scroll</MenuScroll> </Channels> <Chats> <Routes> <Route path="/channel" element={<Channel />} /> <Route path="/dm" element={<DirectMessage />} /> </Routes> </Chats> {/* Input이 들어있으면 별도의 컴포넌트로 빼는 것을 추천(input에 글자를 입력할 때마다 여기 있는 함수들이 다 리렌더링 되기 때문에 비효율적) */} </WorkspaceWrapper> <Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}> <form onSubmit={onCreateWorkspace}> <Label id="workspace-label"> <span>워크스페이스 이름</span> <Input id="workspace" value={newWorkspace} onChange={onChangeNewWorkspace} /> </Label> <Label id="workspace-url-label"> <span>워크스페이스 이름</span> <Input id="workspace" value={newUrl} onChange={onChangeNewUrl} /> </Label> <Button type="submit">생성하기</Button> </form> </Modal> </div> ) } export default Workspace;Modal/index.tsximport React, { useCallback, FC } from "react"; import { CloseModalButton, CreateModal } from "./style"; interface Props { show: boolean; onCloseModal: () => void; children: React.ReactNode; } const Modal: FC<Props> = ({show, children, onCloseModal}) => { const stopPropagation = useCallback((e: any) => { e.stopPropagation() }, []); if(!show){ return null; } return( <CreateModal onClick={onCloseModal}> <div onClick={stopPropagation}> <CloseModalButton onClick={onCloseModal}>×</CloseModalButton> {children} </div> </CreateModal> ); }; export default Modal;swr2.0 버전, react v18, typescript v18swr을 최신버전 사용해서 revalidate대신 mutate를 사용했는데 제가 잘못 사용한건지 모르겠습니다.
-
해결됨프론트엔드 개발환경의 이해와 실습 (webpack, babel, eslint..)
개발환경에서 assets 파일 참조관련 질문
요약개발환경에서 src/assets/.... 에 있는 이미지 파일을 제대로 참조하는 방법이 궁금합니다. 구성요소프로젝트의 구성요소는 아래와 같습니다.public[index.html, favicon.ico]src[assets[image0, image1...], index.js 등] 설치된 패키지는 아래와 같습니다. "webpack": "^5.75.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.11.1" // 본 강의에선 4.x.x 버전을 사용하지만... // 5 version을 공부해야해서... 죄송합니다 😥 설명dev server를 실행시켜 개발할 때,js 파일을 수정하면 바로 반영이 되는 걸 확인했습니다. 그런데 이미지 파일의 경우 다른 파일을 참조하도록 하면 해당 파일을 불러오지 못합니다. 그리고 build된 파일을 참조합니다.예로들어 정적 이미지 파일이 ./src/assets/image_0.jpg 라면,dev server로 실행시켜 확인하면 HOST/dist/assets/images/[hash][ext][query].jpg 이렇게 되어있습니다. (경로가 다름)그리고 build를 하면 분명 assets 디렉토리엔 다수의 이미지 파일이 존재함에도 불구하고 코드에서 사용된 이미지 파일만 build됩니다.그러면 만약 코드내부에서 동적으로 다른 static image 파일을 참조하게 된다면 해당 이미지가 없기 때문에 오류가 날텐데 이런건 어떻게 처리해야하나요? 코드const path = require('path'); const { BannerPlugin, DefinePlugin } = require('webpack'); const childProcess = require('child_process'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const isDevMode = (process.env.NODE_ENV || 'development').trim() === 'development'; console.log('is DEV mode?', isDevMode); console.log('__dirname: ', __dirname); module.exports = { mode: isDevMode ? 'development' : 'production', // entry: webpack 시작되는 부분이라고 생각하면 된다. entry: { main: './src/index.js', }, /** * output * entry point를 기준으로 * 모든 .js 파일을 합쳐서 하나의 bundle 파일로 만드는데, * 이걸 어디에 저장할 것인지 지정하는 option */ output: { path: path.resolve(__dirname, 'dist'), filename: isDevMode ? '[name].js' : 'main.[contenthash].js', chunkFilename: '[id].chunk.js', assetModuleFilename: 'images/[hash][ext][query]', clean: true, }, devServer: { port: 3000, hot: true, client: { overlay: { errors: true, warnings: false, }, }, // static: { // directory: path.resolve(__dirname, './src/assets/'), // }, }, /** * module * test에 설정한 파일들을 inspect 하여, * 조건에 맞는 파일들에 대해 loader 들을 실행하여 해석함 */ module: { rules: [ { test: /\.(sa|sc|c)ss$/i, exclude: [/node_modules/], use: [ // creates 'style' nodes from JS strings isDevMode ? 'style-loader' : { loader: MiniCssExtractPlugin.loader, options: { publicPath: '', }, }, // translates css into common JS 'css-loader', 'postcss-loader', // complies sass to css 'sass-loader', ], }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, exclude: [/node_modules/], type: 'asset/resource', parser: { dataUrlCondition: { // 크기가 8kb 미만인 파일은 inline 모듈로 처리되고 그렇지 않으면 resource 모듈로 처리됩니다. maxSize: 4 * 1042, }, }, // generator: { // publicPath: './assets/', // outputPath: './assets/', // }, }, { test: /\.js$/, exclude: [/node_modules/], loader: 'babel-loader', }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, exclude: [/node_modules/], type: 'asset/resource', }, ], }, plugins: [ /** * 개발할 때 API 서버주소, * 배포했을 때 API 서버주소를 설정하는 Plugin */ // new DefinePlugin({ // NODE_ENV: 'development', // }), new BannerPlugin({ banner: `Build Date: ${new Date().toLocaleString()} Commit Version: ${childProcess.execSync('git rev-parse --short HEAD')} Author: ${childProcess.execSync('git config user.name')}`, }), new HtmlWebpackPlugin({ template: './public/index.html', templateParameters: { env: isDevMode ? '개발용' : '배포용', }, minify: !isDevMode ? { collapseWhitespace: true, removeComments: true, } : false, }), ...(!isDevMode ? [ new MiniCssExtractPlugin({ filename: isDevMode ? '[name].css' : '[name].[contenthash].css', chunkFilename: isDevMode ? '[id].css' : '[id].[contenthash].css', }), ] : []), ], }; 결론즉 정리하자면,개발모드일 때 정적 이미지 파일을 참조하도록 설정을 어떻게 해야하나요?왜 build할 땐 이미지 파일이 코드에서 사용중인 것만 빌드 되나요? 답변 주시면 감사하겠습니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
swr revalidate에 대해 질문드립니다.
안녕하세요 swr revalidate 가 deprecated 되어 mutate를 사용하면 된다는 답변을 확인하고 mutate를 썼는데 궁금한 점이 있습니다. mutate()를 하는 이유는 로그인 성공했을때 그 시점에 users api를 호출하기 위해서 인가요?그리고 mutate 와 무관하게 디폴트 설정에따라(화면전환등) SWR에서 userapi를 호출하고 있는것도 맞나요?1: 화면 첫 렌더링때 user api 콜로그인mutate실행으로 user api 콜화면전환했을때 다시 콜 제가 이해한게 맞는지 답변 부탁드립니다.감사합니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
회원가입 요청이 가지않는 이슈
회원가입을 눌렀으나 회원가입이 안되고 요청이 가지 않는 것 같습니다. 에러메세지를 긁어서 확인 해보았으나 어디가 잘못된건지 모르겠고, 서버 부분의 콘솔을 확인해 보았으나 에러메세지가 나오지 않았습니다. 에러 메세지 이미지와 본문입니다.react_devtools_backend.js:4012 Error: Minified React error #31; visit https://reactjs.org/docs/error-decoder.html?invariant=31&args[]=object%20with%20keys%20%7Bsuccess%2C%20code%2C%20data%7D for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at ka (react-dom.production.min.js:140:47) at react-dom.production.min.js:150:265 at Ml (react-dom.production.min.js:176:171) at Bi (react-dom.production.min.js:271:134) at Eu (react-dom.production.min.js:250:347) at wu (react-dom.production.min.js:250:278) at bu (react-dom.production.min.js:250:138) at pu (react-dom.production.min.js:243:163) at react-dom.production.min.js:123:115 at t.unstable_runWithPriority (scheduler.production.min.js:18:34 혹시나 ENV를 까먹었을까봐 다시 확인했지만 있었고,아래는 서버쪽 터미널 이미지입니다.감사합니다.
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
npx sequelize db:create 오류에 대한 질문입니다.
PS C:\Projects\sleact\back> npx sequelize db:createSequelize CLI [Node: 18.12.1, CLI: 6.2.0, ORM: 6.26.0]Loaded configuration file "config\config.js".Using environment "development".ERROR: Access denied for user 'root'@'localhost' (using password: YES)현재 node version은 18.12.1npm version은 8.19.2 입니다.squelize 는 npm 을 통해서 설치하였고 cli도 설치하였습니다.config/config.js 에 string으로도 넣어보았고.env 에도 넣어보았습니다. .env에 값이 나오는 것은 console.log로 확인하였습니다.MySQL commend clinent 에 들어가 password가 맞는지도 확인했는데 맞는 password 였습니다....!그럼에도 불구하고실행이 안됐습니다.. ㅠ 도움 주시면 감사하겠습니다.
-
미해결프론트엔드 개발자를 위한 웹팩
__dirname 질문입니당
선생님, output:{ fieldname : 'main.js', path: path.resolve( __dirname, 'dist') }여기에서요,__dirname 대신에 fieldname 을 적어줘야 할 것 같은데 __dirname 를 적은 이유가 뭐에욤?
-
미해결Slack 클론 코딩[실시간 채팅 with React]
채널 생성시 channelData.map is not a function
채널생성 클릭하면 channeldata.map is not a function이라고 에러가 뜨는데channelData뿌려지는곳에 ?옵셔널도 줬고..아래처럼 잘 작성한것같은데 어딜 놓쳤는지 모르겠습니다.새로고침하면 추가된 채널명이 출력됩니다. workspaceimport fetcher from '@utils/fetcher'; import axios from 'axios'; import React, { FC, useCallback, useState } from 'react'; import { Navigate, useParams } from 'react-router-dom'; import useSWR from 'swr'; import { AddButton, Channels, Chats, Header, LogOutButton, MenuScroll, ProfileImg, ProfileModal, RightMenu, WorkspaceButton, WorkspaceModal, WorkspaceName, Workspaces, WorkspaceWrapper, } from './styles'; import gravatar from 'gravatar'; import Menu from '@components/menu'; import { Link } from 'react-router-dom'; import { IChannel, IUser, IWorkspace } from '@typings/db'; import { Button, Input, Label } from '@pages/signup/styles'; import useInput from '@hooks/useInput'; import Modal from '@components/modal'; import { toast } from 'react-toastify'; import CreateChannelModal from '@components/createChannelModal'; const Workspace: FC = ({ children }) => { const { workspace, channel } = useParams<{ workspace: string; channel: string }>(); const { data: userData, error, mutate } = useSWR<IUser | false>('/api/users', fetcher, { dedupingInterval: 2000 }); const { data: channelData } = useSWR<IChannel[]>(userData ? `/api/workspaces/${workspace}/channels` : null, fetcher); if (!userData) { return <Navigate to="/login" />; } const [showUserMenu, setShowUserMenu] = useState(false); const [newWorkspace, onChangeNewWorkspace, setNewWorkspace] = useInput(''); const [newUrl, onChangeNewUrl, setNewUrl] = useInput(''); const [showWorkspaceModal, setShowWorkspaceModal] = useState(false); const [showCreateChannelModal, setShowCreateChannelModal] = useState(false); const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false); //functions const onLogout = useCallback(() => { axios .post('/api/users/logout', null, { withCredentials: true, }) .then((res) => { mutate(res.data); }); }, []); const onClickUserProfile = useCallback(() => { setShowUserMenu(!showUserMenu); }, [showUserMenu]); const onClickCreateWorkspace = useCallback(() => { setShowCreateWorkspaceModal(true); }, []); const onCreateWorkspace = useCallback( (e) => { e.preventDefault(); if (!newWorkspace || !newWorkspace.trim()) return; if (!newUrl || !newUrl.trim()) return; //trim ->띄어쓰기 하나도 통과 돼버리는걸 막는다. axios .post( '/api/workspaces', { workspace: newWorkspace, url: newUrl, }, { withCredentials: true, }, ) .then((res) => { mutate(res.data); setShowCreateWorkspaceModal(false); setNewWorkspace(''), setNewUrl(''); }) .catch((err) => { console.dir(err); toast.error(error.response?.data, { position: 'bottom-center' }); }); }, [newWorkspace, newUrl], ); const onCloseModal = useCallback(() => { setShowCreateWorkspaceModal(false); setShowCreateChannelModal(false); }, []); const toggleWorkspaceModal = useCallback(() => { setShowWorkspaceModal(!showWorkspaceModal); }, [showWorkspaceModal]); const onClickAddChannel = useCallback(() => { setShowCreateChannelModal(true); }, []); return ( <div> <Header> <RightMenu> <span onClick={onClickUserProfile}> <ProfileImg src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.nickname} /> {showUserMenu && ( <Menu style={{ right: 0, top: 38 }} onCloseModal={onClickUserProfile} show={showUserMenu}> <ProfileModal> <img src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.nickname} /> <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: IWorkspace) => { return ( <Link key={ws.id} to={`/workspace/${123}/channel/일반`}> <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton> </Link> ); })} <AddButton onClick={onClickCreateWorkspace}>+</AddButton> </Workspaces> <Channels> <WorkspaceName onClick={toggleWorkspaceModal}>Sleact</WorkspaceName> <MenuScroll> <Menu show={showWorkspaceModal} onCloseModal={toggleWorkspaceModal} style={{ top: 95, left: 80 }}> <WorkspaceModal> <h2>Sleact</h2> {/* <button onClick={onClickInviteWorkspace}>워크스페이스에 사용자 초대</button> */} <button onClick={onClickAddChannel}>채널 만들기</button> <button onClick={onLogout}>로그아웃</button> </WorkspaceModal> </Menu> {channelData?.map((v, idx) => ( <div key={idx}>{v.name}</div> ))} </MenuScroll> </Channels> <Chats> {children}</Chats> </WorkspaceWrapper> <Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}> <form onSubmit={onCreateWorkspace}> <Label id="workspace-label"> <span>워크스페이스 이름</span> <Input id="workspace" value={newWorkspace} onChange={onChangeNewWorkspace} /> </Label> <Label id="workspace-url-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; createChannelModalimport 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, VFC } from 'react'; import { useParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import useSWR from 'swr'; interface Props { show: boolean; onCloseModal: () => void; setShowCreateChannelModal: (flag: boolean) => void; } const CreateChannelModal: VFC<Props> = ({ show, onCloseModal, setShowCreateChannelModal }) => { const [newChannel, onChangeNewChannel, setNewChannel] = useInput(''); const { workspace, channel } = useParams<{ workspace: string; channel: string }>(); const { data: userData } = useSWR<IUser | false>(`/api/users`, fetcher); const { data: channelData, mutate } = useSWR<IChannel[]>( userData ? `/api/workspaces/${workspace}/channels` : null, fetcher, ); const onCreateChannel = useCallback( (e) => { e.preventDefault(); axios .post( `/api/workspaces/${workspace}/channels`, { name: newChannel, }, { withCredentials: true }, ) .then((res) => { setShowCreateChannelModal(false); mutate(res.data); setNewChannel(''); }) .catch((err) => { console.dir(err); toast.error(err.response?.data, { position: 'bottom-center' }); }); }, [newChannel], ); return ( <Modal show={show} onCloseModal={onCloseModal}> <form onSubmit={onCreateChannel}> <Label id="channel-label"> <span>채널</span> <Input id="channel" value={newChannel} onChange={onChangeNewChannel} /> </Label> <Button type="submit">생성하기</Button> </form> </Modal> ); }; export default CreateChannelModal;
-
미해결Slack 클론 코딩[실시간 채팅 with React]
npx sequelize db:create 오류
ws@DESKTOP-9H6S8B6 MINGW64 ~/Desktop/sleact/back$ npx sequelize db:createSequelize CLI [Node: 16.15.0, CLI: 6.4.1, ORM: 6.21.4]Loaded configuration file "config\config.js".Using environment "development".ERROR: Access denied for user 'root'@'localhost' (using password: NO) 이런 오류가 계속 뜨고 다른 분들께서 질문하신 답변을 봐도 모르겠습니다... mysql 비밀번호는 확실하게 맞습니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
npx sequelize db:create 입력시 에러 발생
back 폴더에 npm i 이후 npx sequelize db:create 입력시 npm ERR! could not determine executable to run npm ERR! A complete log of this run can be found in:npm ERR! /Users/eycha/.npm/_logs/2022-08-21T06_14_10_186Z-debug-0.log 라는 에러 발생합니다. mysql 과 node 정상적으로 설치했는데 관련되서 검색해도 해결책이 없어서 질문 남깁니다.
-
해결됨웹 게임을 만들며 배우는 Vue
app.vue안에 있는 nav를 따로 분리하고 싶은데 잘안됩니다..
안녕하세요. 현재 강의보고하다가 요즘은 책을 보고 공부중인데 app.vue안에 있는 nav를 따로 분리 하고 싶은데 방법이 있을까요? 이유는 특정 서브페이지에서는 nav가 안보여야하는데 그 특정 서브페이지에서도 nav가 보여서요 ㅠ css로 처리했더니 다시 돌아가면 그페이지들도 nav가 사라지더라구요 ㅠㅠ 혹시 방법이 잇을까요?
-
미해결웹 게임을 만들며 배우는 Vue
3-3 웹팹 로더 사용하기
asset app.js 325 KiB [compared for emit] (name: app)runtime modules 1.13 KiB 5 modulescacheable modules 303 KiB ./main.js 138 bytes [built] [code generated] ./NumberBaseball.vue 39 bytes [built] [code generated] [1 error] ./node_modules/vue/dist/vue.runtime.esm.js 303 KiB [built] [code generated] ERROR in ./NumberBaseball.vueModule build failed (from ./node_modules/vue-loader/dist/index.js):TypeError: Cannot read properties of undefined (reading 'styles') at Object.loader (C:\Users\samsung\OneDrive\바탕 화면\CODE\vue-webgame\3.숫자야구\node_modules\vue-loader\dist\index.js:70:34) @ ./main.js 2:0-50 4:8-22 webpack 5.74.0 compiled with 1 error in 506 ms --------------------------------------------NumberBaseball.vue 코드 //Vue 컴포넌트<template> <div> <h1>{{ result }}</h1> <form v-on:submit="onSubmitForm"> <input type="text" ref="answer" maxlength="4" v-model="value"> <button>입력</button> </form> </div></template><script> export default { data() { return { value: '', result: '', }; }, methods: { onSubmitForm(e) { e.preventDefault(); }, }, };</script><style></style>웹팹 로더에 실패해서.. 다음 단계로 못 넘어가고 있습니다.혹시 버전의 차이가 있을까 구글링 열심히 해봤지만 결국 에러 한 개는 해결하지 못했어요.
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
sleact/alecture/pages/login/styles.tsx ?
깃헙에서 sleact/alecture/pages/login/styles.tsx 파일이 비워 있는데 일부러 비워 두신거죠?
주간 인기글
순위 정보를
불러오고 있어요