묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Slack 클론 코딩[실시간 채팅 with React]
Module not found
제로초님 영상을 보고 따라하는 도중 Menu의 index.tsx작성 후에 Workspace로 옮겼는데, Module을 찾을 수 없다고 뜨는데, 어디가 잘못 된건지 모르겠습니다... components/Menu/index.tsximport React, { FC } from 'react'; import { CreateMenu } from './style'; const Menu: FC<React.PropsWithChildren<{}>> = ({ children }) => { return ( <CreateMenu> <div>menu</div> {children} </CreateMenu> ); }; export default Menu; Workspace/index.tsximport React, { FC } from 'react'; import { CreateMenu } from './style'; const Menu: FC<React.PropsWithChildren<{}>> = ({ children }) => { return ( <CreateMenu> <div>menu</div> {children} </CreateMenu> ); }; export default Menu; 에러메세지입니다.혹시 몰라서 터미널에 뜬 에러메세지도 첨부하겠습니다
-
미해결Slack 클론 코딩[실시간 채팅 with React]
Request failed with status code 404
제로초님, layouts폴더에 App.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 Channel = loadable(() => import('@pages/Channel')); const App = () => { return ( <Routes> <Route path="/" element={<Navigate replace to="/login" />} /> <Route path="/login" element={<LogIn />} /> <Route path="/signup" element={<SignUp />} /> <Route path="/workspace/channel" element={<Channel />} /> </Routes> ) } export default App;Route의 4번째줄 path에 /workspace로 하면 로그아웃 할 때 제대로 작동하는데 저렇게 workspace/channel로 코드를 작성하면 로그아웃 할 때, 아래처럼 뜹니다/를 하나만 붙여야 되는건가요?나머지 코드들은 변경하지 않았습니다.Login 폴더 index.tsximport useInput from "@hooks/useInput"; import axios from "axios"; import React, { useCallback, useState } from "react"; import { Form, Label, Input, LinkContainer, Button, Header, Error} from './styles'; import {Link, Navigate} from 'react-router-dom'; import useSWR from 'swr'; import fetcher from "@utils/fetcher"; const LogIn = () => { const {data, error, mutate} = useSWR('/api/users', fetcher); const [logInError, setLogInError] = useState(false); const [email, onChangeEmail] = useInput(''); const [password, onChangePassword] = useInput(''); const onSubmit = useCallback((e: any) => { e.preventDefault(); setLogInError(false); axios .post( '/api/users/login', {email, password}, {withCredentials: true}, ) .then((response) => { mutate(response.data, false); }) .catch((error) => { setLogInError(error.response?.data?.statusCode === 401); }) }, [email, password, mutate]); if(data === undefined) { return <div>로딩중...</div> } if(data) { return <Navigate to="/workspace/channel" /> } return ( <div id="container"> <Header>Sleact</Header> <Form onSubmit={onSubmit}> <Label id="email-label"> <span>이메일 주소</span> <div> <Input type="email" id="email" name="email" value={email} onChange={onChangeEmail} /> </div> </Label> <Label id="password-label"> <span>비밀번호</span> <div> <Input type="password" id="password" name="password" value={password} onChange={onChangePassword} /> </div> {logInError && <Error>이메일과 비밀번호 조합이 일치하지 않습니다.</Error>} </Label> <Button type="submit">로그인</Button> </Form> <LinkContainer> 아직 회원이 아니신가요? <Link to="/signup">회원가입 하러가기</Link> </LinkContainer> </div> ); }; export default LogIn; workspace.tsximport axios from "axios"; import React, { FC, useCallback } from "react"; import useSWR from 'swr'; import fetcher from "@utils/fetcher"; import { Navigate } from "react-router-dom"; const Workspace: FC<React.PropsWithChildren<{}>> = ({children}) => { // revalidate = 서버로 요청 다시 보내서 데이터를 다시 가져옴 // mutate = 서버에 요청 안보내고 데이터를 수정 const {data, error, mutate} = useSWR('/api/users', fetcher); const onLogout = useCallback(() => { axios.post('api/users/logout', null , { withCredentials: true, }) .then(() => { mutate(false, false); }) }, []); if(data === false) { return <Navigate to="/login" /> } return( <div> <button onClick={onLogout}>로그아웃</button> {children} </div> ) } export default Workspace;swr은 2버전입니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
Cannot read properties of undefined (reading 'data')
제로초님, 전 강의 듣고 할 때만 해도 잘 됐는데 오늘 쉬고 다시 해보니까 이런 오류 메세지가 뜹니다.해결해보려고 해도 잘 안되네요..Cannot read properties of undefined (reading 'data')SignUp 폴더에서 index.tsx에 문제가 있다고 떠서 코드 올립니다import useInput from "@hooks/useInput"; import React, { useCallback, useState } from "react"; import { Form, Label, Input, LinkContainer, Button, Header, Error, Success} from './styles' import axios from "axios"; import {Link} from 'react-router-dom'; // 타입스크립트는 간단하게 말하면 변수, 매개변수, 리턴 값에 타입을 붙여줌 // 변수에는 딱히 타입을 붙여주지 않아도 된다 타입스크립트가 알아서 추론하기 때문에(리턴값도 마찬가지) 그러나 매개변수에는 붙여주어야됨 const SignUp = () => { const [email, onChangeEmail] = useInput('');//useInput은 커스텀 훅 const [nickname, onChangeNickName] = useInput(''); const [password, setPassword] = useState(''); const [passwordCheck, setPasswordCheck] = useState(''); const [mismatchError, setMismathError] = useState(false); const [signUpError, setSignUpError] = useState(''); const [signUpSuccess, setSignUpSuccess] = useState(false); const onChangePassword = useCallback((e: any) => { setPassword(e.target.value); setMismathError(e.target.value !== passwordCheck); // 함수 기준으로 외부 변수만 deps에 적어줌 내부 변수는 x }, [passwordCheck]); const onChangePasswordCheck = useCallback((e: any) => { setPasswordCheck(e.target.value); setMismathError(e.target.value !== password) }, [password]); const onSubmit = useCallback( (e: any) => { e.preventDefault(); if (!mismatchError && nickname) { console.log('서버로 회원가입하기'); setSignUpError(''); setSignUpSuccess(false); axios .post('/api/users', { email, nickname, password, }) .then((response) => { console.log(response); setSignUpSuccess(true); }) .catch((error) => { console.log(error.response); //여기가 문제가 있다고 뜹니다. setSignUpError(error.response.data); }) .finally(() => {}); } }, [email, nickname, password, passwordCheck, mismatchError], ); return ( <div id="container"> <Header>Sleact</Header> <Form onSubmit={onSubmit}> <Label id="email-label"> <span>이메일 주소</span> <div> <Input type="email" id="email" name="email" value={email} onChange={onChangeEmail} /> </div> </Label> <Label id="nickname-label"> <span>닉네임</span> <div> <Input type="text" id="nickname" name="nickname" value={nickname} onChange={onChangeNickName} /> </div> </Label> <Label id="password-label"> <span>비밀번호</span> <div> <Input type="password" id="password" name="password" value={password} onChange={onChangePassword} /> </div> </Label> <Label id="password-check-label"> <span>비밀번호 확인</span> <div> <Input type="password" id="password-check" name="password-check" value={passwordCheck} onChange={onChangePasswordCheck} /> </div> {mismatchError && <Error>비밀번호가 일치하지 않습니다.</Error>} {!nickname && <Error>닉네임을 입력해주세요.</Error>} {signUpError && <Error>{signUpError}</Error>} {signUpSuccess && <Success>회원가입되었습니다! 로그인해주세요.</Success>} </Label> <Button type="submit">회원가입</Button> </Form> <LinkContainer> 이미 회원이신가요? <Link to="/login">로그인 하러가기</Link> </LinkContainer> </div> ); }; export default SignUp;
-
미해결Slack 클론 코딩[실시간 채팅 with React]
초기 새팅에 어려움을 가져서 질문드립니다. config.js 파일이 없습니다
https://github.com/ZeroCho/sleact에서 code로 가서 zip파일을 다운 받았습니다.이후 git clone https://github.com/zerocho/sleact 명령어를 입력한 후, back 폴더에서 npm i까지 마쳤는데, 저는 강의 내용과 다르게 config 폴더가 비어 있습니다.어떤 부분에서 잘못 따라 하였는이해를 못해서 깃 관련 구글링을 해보다가 결국 질문 드립니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
tsconfig.json과 webpack.config.ts의 연동구간?
tsconfig.json(ts -> js)로 바꿔주고,그 후에 webpack.config.ts가 js,css,html들을 하나로 번들링해주는 순서가 맞나요??$webpack 실행하면 웹팩 프로그램이 그냥 알아서 tsconfig.json이 있으면 먼저 적용한후 webpack.config.ts를 적용하는건가요?
-
미해결Slack 클론 코딩[실시간 채팅 with React]
npx sequelize db:seed:all 시, ERROR: Validation error 이슈
안녕하세요 강사님,강의 중 채널 생성과정에서 오류가 있어 문의를 남깁니다.먼저, 채널 데이터를 가져오지 못해 다른 문의를 찾아보니 seed 설정을 해주지 않은거 같아 npx sequelize db:create 진행하였고, 이어서npx sequelize db:seed:all 과정을 진행했습니다npx sequelize db:seed:all 을 진행하니 Validation error가 발생했습니다.다른 문의를 보니 db에 sleact table이 생성되지 않아 발생하는 문의글이 많아서 db 사진 올려드릴게요추가적으로, back 파일에서 npm run dev를 실행하면 db 연결까지 잘 됩니다.도대체 어디가 잘 못 된 건지 감이 오지 않습니다..감사합니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
POST http://localhost:3090/api/users net::ERR_INTERNET_DISCONNECTED
제로초님, 코드는 정확히 따라 한 거 같은데, 무엇이 문제인지 판단이 안되네요...리액트 v18, 타입스크립트 v18, axios는 1.2.3 입니다. webpackimport path from 'path'; import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; import webpack, { Configuration as WebpackConfiguration } from "webpack"; import { Configuration as WebpackDevServerConfiguration } from "webpack-dev-server"; interface Configuration extends WebpackConfiguration { devServer?: WebpackDevServerConfiguration; } import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; const isDevelopment = process.env.NODE_ENV !== 'production'; const config: Configuration = { name: 'sleact', mode: isDevelopment ? 'development' : 'production', devtool: !isDevelopment ? 'hidden-source-map' : 'eval', resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], alias: { '@hooks': path.resolve(__dirname, 'hooks'), '@components': path.resolve(__dirname, 'components'), '@layouts': path.resolve(__dirname, 'layouts'), '@pages': path.resolve(__dirname, 'pages'), '@utils': path.resolve(__dirname, 'utils'), '@typings': path.resolve(__dirname, 'typings'), }, }, entry: { app: './client', }, module: { rules: [ { test: /\.tsx?$/, loader: 'babel-loader', options: { presets: [ [ '@babel/preset-env', { targets: { browsers: ['IE 10'] }, debug: isDevelopment, }, ], '@babel/preset-react', '@babel/preset-typescript', ], env: { development: { plugins: [['@emotion', { sourceMap: true }], require.resolve('react-refresh/babel')], }, production: { plugins: ['@emotion'] } }, }, exclude: path.join(__dirname, 'node_modules'), }, { test: /\.css?$/, use: ['style-loader', 'css-loader'], }, ], }, plugins: [ new ForkTsCheckerWebpackPlugin({ async: false, // eslint: { // files: "./src/**/*", // }, }), new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }), ], output: { path: path.join(__dirname, 'dist'), filename: '[name].js', publicPath: '/dist/', }, devServer: { historyApiFallback: true, // react router port: 3090, devMiddleware: { publicPath: '/dist/' }, static: { directory: path.resolve(__dirname) }, proxy: { '/api/': { target: 'http://localhost:3095', changeOrigin: true, }, }, }, }; if (isDevelopment && config.plugins) { config.plugins.push(new webpack.HotModuleReplacementPlugin()); config.plugins.push(new ReactRefreshWebpackPlugin()); } if (!isDevelopment && config.plugins) { } export default config; index.tsximport useInput from "@hooks/useInput"; import React, { useCallback, useState } from "react"; import { Form, Label, Input, LinkContainer, Button, Header, Error} from './styles' import axios from "axios"; const SignUp = () => { const [email, onChangeEmail] = useInput('');//useInput은 커스텀 훅 const [nickname, onChangeNickName] = useInput(''); const [password, setPassword] = useState(''); const [passwordCheck, setPasswordCheck] = useState(''); const [mismatchError, setMismathError] = useState(false); const onChangePassword = useCallback((e: any) => { setPassword(e.target.value); setMismathError(e.target.value !== passwordCheck); // 함수 기준으로 외부 변수만 deps에 적어줌 내부 변수는 x }, [passwordCheck]); const onChangePasswordCheck = useCallback((e: any) => { setPasswordCheck(e.target.value); setMismathError(e.target.value !== password) }, [password]); const onSubmit = useCallback((e: React.FormEvent) => { e.preventDefault(); if(!mismatchError && nickname){ console.log('서버로 회원가입하기'); axios.post('/api/users', { email, nickname, password, }) .then((response) => { console.log(response); })//요청이 성공하면 실행 .catch((error) => { console.log(error.response); })//요청이 실패하면 실행 .finally(() => {});//성공하든 실패하든 실행시키고 싶은 것 } console.log(email, nickname, password, passwordCheck) }, [email, nickname, password, passwordCheck, mismatchError]); return ( <div id="container"> <Header>Sleact</Header> <Form onSubmit={onSubmit}> <Label id="email-label"> <span>이메일 주소</span> <div> <Input type="email" id="email" name="email" value={email} onChange={onChangeEmail} /> </div> </Label> <Label id="nickname-label"> <span>닉네임</span> <div> <Input type="text" id="nickname" name="nickname" value={nickname} onChange={onChangeNickName} /> </div> </Label> <Label id="password-label"> <span>비밀번호</span> <div> <Input type="password" id="password" name="password" value={password} onChange={onChangePassword} /> </div> </Label> <Label id="password-check-label"> <span>비밀번호 확인</span> <div> <Input type="password" id="password-check" name="password-check" value={passwordCheck} onChange={onChangePasswordCheck} /> </div> {mismatchError && <Error>비밀번호가 일치하지 않습니다.</Error>} {!nickname && <Error>닉네임을 입력해주세요.</Error>} {/* {signUpError && <Error>{signUpError}</Error>} */} {/* {signUpSuccess && <Success>회원가입되었습니다! 로그인해주세요.</Success>} */} </Label> <Button type="submit">회원가입</Button> </Form> <LinkContainer> 이미 회원이신가요? <a href="/login">로그인 하러가기</a> </LinkContainer> </div> ); }; export default SignUp;이렇게 따라 한 후에 회원 가입 누르면 아래 같은 화면이 뜹니다.
-
미해결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 였습니다....!그럼에도 불구하고실행이 안됐습니다.. ㅠ 도움 주시면 감사하겠습니다.
-
미해결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]
안녕하세요. invailhost header 질문
현재 슬랙클론 수강중인 학생입니다.저는 군대에서 독학으로 공부중입니다.현재 제로초님 깃허브에서 git clone 하여 그대로 데모버전을 사용하고 있습니다.제가 사용하는 에디터는 구름ide를 사용하고 있는데구름 ide 에서는 localhost를 제공해주지 않고, 제가 port를 설정하면 구름에서 제공해주는 사이트 주소를 사용하는 식으로 운영됩니다.제가 본 영상에서와 같이 dependency까지 그대로 다운받고,npm run dev 명령어를 실행하면 당연히 localhost가 없기때문에 사이트 접속이 힘듭니다....그래서 port 3090에 맞게 url에 접속하면 invaild host header 에러가 나옵니다..전에 위 에러를 만났을 때는 항상 cra로 개발했기 때문에 금방 구글링으로 해결을 했는데 현재는 어떻게 해결해야 할지 모르는 상황입니다..
-
미해결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]
500에러 질문
제로초님 깃헙코드 보고 리액트쿼리로 공부하다가 DM 보내는 로직에서 500에러가 떳습니다~!! api 문서대로 요청보냈고, 분명 서버쪽은 문제가 없을텐데 싶어서 며칠 째 고민하다 질문드려요!!! NaN으로 뜨는 부분이 백엔드 코드에서 콘솔 찍어보니까 req.query.perPage 이게 언디파인드로 전달 되더라구요. 근데 저는 perPage를 제로초님처럼 20으로 고정해서 전달하고 있는데 언디파인드로 뜨는게 이상하더라구요.ㅜㅜ +) 그리고 뮤테이션 쿼리키를 ["workspace", workspace, "dm", id, "chat"] 이렇게 주신 이유가 궁금해요!! 제가 공식문서 읽고 이해한게 배열로 줄 경우 첫번째 값이 캐싱할 때 쓰이는 이름이고 그 위에있는 애들은 mutation 안에서 사용될 외부 값을 넣어준다고 이해했었거든요!! 근데 dm 이나 chat은 사용되지 않는 것 같아서요!!
-
미해결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 정상적으로 설치했는데 관련되서 검색해도 해결책이 없어서 질문 남깁니다.
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
sleact/alecture/pages/login/styles.tsx ?
깃헙에서 sleact/alecture/pages/login/styles.tsx 파일이 비워 있는데 일부러 비워 두신거죠?
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
안녕하세요 웹팩 관련질문입니다.
안녕하세요! 웹팩 관련 질문이있습니다. 제꺼 빌드용량이 3MB로 엄청크더라구요. 그래서 원인을 알아보니 isDevelopment가 계속 development 모드로 빌드 되는게 이유였습니다. devtool설정은 아래와 같이했는데 development이다 보니까 계속 inline source map 으로 작동해서 파일크기가 큰것같더라고요. isDevelopment ? 'inline-source-map' : 'hidden-source-map' 차이점을 보니 npm script가 start는 webpack serve, build는 webpack만 되어있는 상태였어요. 그래서 start 에는 webpack serve --env development build에는 NODE_ENV=production webpack 를 적어주니까 production모드로 되고, 300kb로 떨어진걸 확인했어요. 그런데 start는 --env development이고, build는 NODE_ENV=production인 이유가 있나요? 둘다 --env development, --env production을 적거나 NODE_ENV=development, NODE_ENV=production으로 하는것과 차이가 있나요? 공식문서에서는 webpack dev와 prod로 파일을 다르게하는 아래방법밖에 못찾았는데, "start": "webpack serve --open --config webpack.dev.js", "build": "webpack --config webpack.prod.Js 제로초님은 어떤걸 보고 하셨는지 궁금합니다. 그리고 EnvironmentPlugin은 적지 않아도 console.log(isDevelopment)를 찍어보면 process.env.NODE_ENV 상태가 출력되고 빌드/실행도 잘되던데, 아래와같이 추가해야하는 이유가 있는지도 궁금합니다. new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production',
-
미해결Slack 클론 코딩[실시간 채팅 with React]
[06:26 부분] 정규표현식으로 문자열 변환하기 부분 질문할게요
정규식으로 닉네임 찾는 부분에서요 match.match(/@\[(.+?)]\((\d+?)\)/)! 여기 이부분에서느낌표(!)를 왜 붙여준건가요? str.match(정규표현식) 이런 형태로 작성하는 건줄 알았는데 느낌표가 붙여있어서 궁금해서 질문올립니다.
-
미해결Slack 클론 코딩[실시간 채팅 with React]
tsconfig관련 질문 있습니다
슬리액트 강의 내용 일렉트론으로 작업해 보려고 따라하는 중에 tsconfig path 설정에서 오류가 생겼습니다. import 경로 위에 컨트롤 + 마우스클릭 할 경우 정상적으로 해당 파일로 이동합니다. 근데 막상 서버 실행 시에는 아래와 같은 오류가 나더라구요. 참고로 저는 CRA를 사용했습니다!! 스택오버플로우 아래 해결책도 해보았는데 해결이 안되어서 질문드립니다ㅠㅠ 제 tsconifg.json 입니다~!! 방향 잡아주시면 또 열심히 찾아보겠습니다 감사합니다!! ㅠㅠ { "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", "baseUrl": "./src", "paths": { "@hooks/*": ["src/hooks/*"], "@components/*": ["src/components/*"], "@pages/*": ["src/pages/*"], "@utils/*": ["src/utils/*"], "@types/*": ["src/types/*"] }, "plugins": [{ "transform": "typescript-transform-paths" }] }, "include": ["src"] }
-
해결됨Slack 클론 코딩[실시간 채팅 with React]
웹팩 한가지 질문이 있습니다.
안녕하세요! 웹팩설정에서 질문이있습니다. 알거같다가도 헷갈려서 질문드려요! "webpack.config.ts 파일 내에서 const require 방식이 아닌 Import를 사용가능한 이유"가 어느부분때문인가요? tsconfig.json 에서 module을 esnext로 최신으로 쓰겠다고 설정했으므로 tsconfig를 웹팩이 먼저 읽어서, 웹팩 파일내부에서도 commonjs방식이아닌 import 방식이 가능한것이라고 이해하면 맞을지 궁금합니다. 그런데 이렇게 이해하면 tsconfig-for-webpack-config 파일에서는 또 module을 commonJs 로 해주기때문에 조금 헷갈립니다,,