묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
npm run dev 실행 오류
npm run dev 로 server.ts 실행시, 에러가 발생합니다.찾아보니, db 연결 시 인증에 관련된 오류 같은데... 어떻게 해야할지 잘 모르겠습니다.오타 꼼꼼히 확인하고 docker-compose도 다시 실행 해봤는데 해결이 되지 않네요.제가 수업 듣기 전에 pg-admin을 설치했었는데, 그것과 관련 있는 걸까요?
-
미해결Slack 클론 코딩[실시간 채팅 with React]
배포후 로컬에서 Index.html 열면 작동이 잘 안하는 이유
백엔드에 배포하려고 파일 넘기기 전에,,빌드가 잘 되었는지 테스트해보려고 (백엔드 먼저 켜두고)index.html을 그냥 브라우저로 열면 백엔드와 잘 연동될줄 알았는데, 첫 화면부터 보이지가 않아서요. 그 이유가 궁금합니다.이유가 혹시,,, 웹펙에서devServer: { historyApiFallback: true, // react router(원래 SPA에서는 3090/ 주소밖에 모른다. 뒤에 /login같은 경로는 우리가 가짜로 만들어낸건데 History api(History 기본함수)가 가짜주소를 만들어주는 거다.) port: 3090, devMiddleware: { publicPath: '/dist/' }, static: { directory: path.resolve(__dirname) }, proxy: { '/api/': { //프론트에서 /api/로 보내는 요청은 주소를 3095로 바꿔서 보내겠다 target: 'http://localhost:3095', changeOrigin: true, }, }, },위와 같이 dev모드일때는 3090포트 개발서버를 항상 켜두는데, 이처럼 뭔가 서버가 계속 켜있어야 프론트가 잘 작동 가능한건가요?? (그래서 배포할때 백엔드서버는 항상 켜있으니까 그냥 넘겨주면 된다는게 이해가 되는거 같아서요) 만약 그렇다면 프론트인데 빌드 결과물이 있는데 왜 계속 프론트만을 위한 서버가 켜있어야 하는지 궁금합니다,,저는 배포파일이 실행파일마냥 그냥 index.html 실행하면 (백엔드 켜져있어서 통신할 수 있다는 가정하에) 알아서 잘 되는줄 알았거든요,,
-
미해결한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
상태변화 이후 렌더링에 대해서
안녕하세요 🙋♂️-State, 9분 32초즈음State가 변화할 때 마다 그 컴포넌트가 계속 호출되어 새로운 요소(혹은 DOM)를 반환하는 것은 알겠습니다.그러나, 함수가 호출되어, return을 꾸준히 해준다 하더라도,App.js에서, import를 한번만 한다면, 변화 내용은 업데이트 되지 않을 것입니다. 또, index.js에서 변화한 App.js를 다시 import를 해야겠죠즉 State의 변화에 따라서 import도 계속해줘야 업데이트가 반영될 것입니다.우리는 import를 꾸준히 동작하도록 코드를 작성한 적이 없는데 불구하고, 업데이트가 반영되고 있습니다.어떻게 이것이 가능한 건지 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
error An unexpected error occurred: "/Users/leejeonghun/Desktop/ ... /class/package.json: Unexpected token } in JSON at position 670".
멘토님 안녕하세요,1) 웹페이지를 실행시키는 yarn dev 명령어를 입력했을때, 페이위 제목과 같은 에러가 떴는데 무엇이 문제일까요? 2) 인덱스와 홈.모듈.css 두 창을 비교 설명하시는 과정에서 강의 그대로 따라 했는데 저의 홈.모듈 창에는 title 이 아얘 뜨지 않았습니다. 이 경우는 혹시 제가 모르는 사이 리액트 17이 아니라 18이 깔려있어서 그런걸까요? 아니면 다른 문제일까요? (분명 리액트 17.0.2?로 수정해서 저장하고서 설치했었는데요ㅜㅜ)
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트
django 코드를 보려고 하는데 질문드립니다.
저는 클래스의 self가 클래스의 인스턴스를 만들었을때 그 인스턴스 자신을 self라고 하는것으로 알고있었습니다. 그런데 장고코드에는 클래스기반뷰에서 클래스의 인스턴스를 만들지 않음에(예를들어 a=RedirectVIew()어쩌구 하는게 아니라 a=RedirectView.as_view()를 통해서 함수를 넘기잖아요)도 불구하고 self가 다양하게 들어가는데 이건 어떤 의미인가요?^^;;
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
우분투에서 작업시 윈도우로 mysql 설치하는 것에 관해서
제로초쌤 제가 우분투에서 Nodebird를 진행하고 있는데 더북에 써져있는 거처럼 리눅스(우분투)에서 설치하는 방법 그대로 설치하다가 Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (13) 소켓 에러가 계속 나서 해결하려고 인터넷에 찾아보면서 따라하고 있는데 해결이 안되서 혹시 윈도우로 mysql설치해서 진행해도 크게 차이가 없을까요?
-
미해결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를 사용했는데 제가 잘못 사용한건지 모르겠습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
props.data?.fetchBoard?.writer 와 props.data?.fetchBoard.writer 차이
BoardDetail.presenter.js 파일에서 데이터 가져올 때,props.data?.fetchBoard?.writer로 가져오시는 데,props.data?.fetchBoard.writer로 해도 오류 안 나고 잘 작동하는데 한 번 더 ?를 사용해서 확인하는 이유가 궁금합니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
Error: error: Unexpected token `< (jsx tag start)`
수업 자료와 똑같이 코딩을 하였는데도 불구하고 해당 페이지로 접속을 하면 위와같은 오류가 출력됩니다. 무슨문제인지 알 수 있을까요..? ㅜㅠ
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
_app.js 수정 이후 오류
위와 같이 수정하고 난 후 yarn dev로 실행시켰는데 버튼이 안나옵니다..ㅠㅠ뭐가 문제인지 모르겠습니다
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
상품업로드 화면 구현 시 이미지 업로드 시점 관련
안녕하세요. 수업 잘 듣고 있습니다!수업관련 질문은 아니지만 일반적인 구현방법도 이런가해서 문의 남겨봅니다^^이미지 업로드 화면에서업로드 할 사진을 선택하면 서버측으로 먼저 이미지를 전송하는 방식으로 구현하셨는데 이미지 선택 시에는 로컬(클라이언트PC)의 이미지로 보여주고 [상품 등록하기] 버튼을 눌렀을 때 서버로 업로드 하면서 DB에 등록하는 것이 어떨까해서요.이미지를 계속 변경하면 서버에 업로드가 되는 듯 하여...문의한번 해봅니다^^ 다른 수강생분들에게도 문제 해결에 도움을 줄 수 있도록 좋은 질문을 남겨봅시다 :) 1. 질문은 문제 상황을 최대한 표현해주세요.2. 구체적이고 최대한 맥락을 알려줄 수 있도록 질문을 남겨 주실수록 좋습니다. 그렇지 않으면 답변을 얻는데 시간이 오래걸릴 수 있습니다 ㅠㅠex) A라는 상황에서 B라는 문제가 있었고 이에 C라는 시도를 해봤는데 되지 않았다!3. 먼저 유사한 질문이 있었는지 꼭 검색해주세요!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
"Error: The default export is not a React Component in page: "/qqq"
정확하게 같게 했는데 이게 왜이런지 모르겠습니다 host3000 패이지는 잘나옵니다
-
미해결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; 에러메세지입니다.혹시 몰라서 터미널에 뜬 에러메세지도 첨부하겠습니다
-
미해결만들면서 배우는 리액트 : 기초
cat-jjal-maker-cra 설치중
위와같은 진행상태로 error 시에 어떻게 해냐하나요?폴더 안에도 node_modules 와 packagej.son 두개만 만들어져 있습니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
reduxjs/toolkit 에서 loadPost 한번만 가게하기
제로초쌤 강의를 들으면서 redux, redux-saga로 작성하신걸 깃헙 소스코드 참고하고 스스로 해보면서 toolkit으로 변환하면서 해보고 있는데 toolkit에서는 따로 한번만 요청이 가게 할 수 없나요..?제 코드 첨부하겠습니다<pages > index.js> useEffect(() => { function onScroll(){ console.log(window.scrollY, document.documentElement.clientHeight, document.documentElement.scrollHeight); if(window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300){ if(hasMorePosts && !loadPostsLoading){ dispatch(loadPost(10)); console.log(loadPostsLoading); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); } }, [hasMorePosts])<actions > post.js>export const loadPost = createAsyncThunk('post/loadPosts' , async (data , thunkAPI) => { try{ const result = await delay(getDummyPost(data), 1000); return result; } catch(error){ console.log(error); } }); <reducers > postSlice.js> .addCase(loadPost.pending, (state, action) => { state.loadPostsLoading = true; state.loadPostsDone = false; state.loadPostsError = null; }) .addCase(loadPost.fulfilled, (state, action) => { state.loadPostsLoading = false; state.loadPostsDone = true; state.mainPosts = action.payload.concat(state.mainPosts); state.hasMorePosts = action.payload.length < 100; }) .addCase(loadPost.rejected, (state, action) => { state.loadPostsLoading = false; state.loadPostsError = action.error; })
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
[React.memo] useState의 setState는 변경되지 않는가요?
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. React.memo로 렌더링 최적화 강의를 듣다가 질문이 생겼습니다.React.memo는 props가 변하지 않으면 다시 렌더링 하지 않는다고 알고 있는데요. App.js에서 useState의 결과물인 setState(setTodoData 등)가 List.js의 props로 내려가는데, 이는 변화로 보지 않는 것으로 보았습니다.아래의 단순화된 useState의 구현을 보면 useState로 나온 state는 클로저를 통해 값이 변경되지 않으면 useState가 같은 값?참조?를 내려주는 것 같은데, setState는 매번 새로 생성되어 참조가 바뀔 것 같다는 생각을 하였습니다. 그래서 React.memo가 매번 새로 생성되는 setState를 보고 props가 바뀌는 것으로 인식하여 렌더링을 해주어야 하지 않을까 했는데... 실제로는 그렇지 않더라고요물론 단순화된 구현이고 실제 리액트의 구현은 복잡한 과정을 거쳐 같은 참조를 내려줄 것 같다고 생각이 드는데요.이 마법같은 과정이나 원리, 키워드에 대해서 아무리 찾아도 찾을 수가 없어서 도움을 청하고자 질문을 남깁니다...감사합니다. const MyReact = (function() { let _val // hold our state in module scope return { render(Component) { const Comp = Component() Comp.render() return Comp }, useState(initialValue) { _val = _val || initialValue function setState(newVal) { _val = newVal } return [_val, setState] } } })()출처: https://medium.com/hcleedev/web-usestate%EC%9D%98-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC%EC%99%80-%ED%95%A8%EC%A0%95-7b4825c16b9
-
해결됨파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트
BadHeaderError at /accounts/signup/
심지어는이런 에러까지 뜨더라구요.이것과 관련하여서는 장고 공식문서에 나온대로 예외처리를class User(AbstractUser): website_url = models.URLField(blank = True) bio = models.TextField(blank = True) def send_welcome_email(self): subject = render_to_string('accounts/welcome_email_subject.txt', { 'user': self }) content = render_to_string('accounts/welcome_email_content.txt',{ 'user': self }) # 여기서 settings는 django.conf라는 곳에서 임포트 해오는 것이고 # 기초 settings.py의 이름이 변하더라도 거기서 가져오는 것 같다. # 문제 생길 시, 추측이 아닐 수 있으니 settings를 common으로 변경해주자. sender_email = settings.WELCOME_EMAIL_SENDER if subject and content and sender_email: try: # 여기서 self.email의 의미는 User마다 email로 가입이 될텐데, 그 주소가 수신주소가 되는것이다. send_mail(subject, content, sender_email, [self.email], fail_silently=False) except BadHeaderError: return HttpResponse('Invalid Header found.') return HttpResponseRedirect('/') else: # In reality we'd use a form class # to get proper validation errors. return HttpResponse('Make sure all fields are entered and valid.') # save할때마다 호출 -> which mean is User가 생성될때마다 # 이런식의 로직 구현이 가능하다. def save(self, *args, **kwargs): is_created = (self.pk == None) super().save(*args, **kwargs) if is_created: pass이렇게 중간에 try, except로 처리해주었는데, 이렇게 해도 진행에 지장은 없을까요?애초에 이 에러가 왜 발생했는지도 이해가 잘갑니다.에러메세지를 읽어봐도 두루뭉술하게 Header values can't newlines 라고 되어있으니, 뭐가 문제인지도 파악이 어렵구요 ㅠㅠ
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트
NoReverseMatch at /accounts/signup/
안녕하세요 선생님 ㅠㅠ연달아서 에러가 빵빵 터지네요NoReverseMatch at /accounts/signup/Reverse for 'pydenticon_image' with keyword arguments '{'data': ''}' not found. 1 pattern(s) tried: ['identicon/image/(?P<data>.+)/\\Z']인데요.문제가 되는 부분은<img src="{% url 'pydenticon_image' data=user.username %}" style="width: 24px; height: 24px;">이 부분으로 나옵니다.Error during template rendering In template /Users/daniel_choi/Desktop/total_projects/instagram_second/templates/layout.html, error at line 77 Reverse for 'pydenticon_image' with keyword arguments '{'data': ''}' not found. 1 pattern(s) tried: ['identicon/image/(?P<data>.+)/\\Z']이게 에러 추가 메세지 이구요.하지만 이것과 관련하여는 잘 작동하다가 그냥 아무것도 만진것없이 echo 쪽만 들락거리고 signup 시도했던 것 밖에 없는데, 되다가 갑자기 에러가 나니깐 당황스럽네요;(물론 admin을 제외한 계정을 계속해서 삭제하고 시도하긴 했습니다.)from django_pydenticon.views import image as pydenticon_image urlpatterns = [ path('identicon/image/<path:data>/', pydenticon_image, name = 'pydenticon_image') ]이렇게 되어있구요.accounts/forms.py에서도 User모델 상속 받고, class Meta로fields 중에서 username을 분명히 오버라이드 시킨것도 확인이 되는데, 왜 이런 에러가 발생하는지 모르겠습니다! ㅠㅠ 이거는 추가적으로 몇번 테스트를해보니깐1.계정을 생성한다2.SMTP에러가 뜬다.3.admin페이지에 들어가서 방금 생성된 계정을 삭제한다.4.다시 /accounts/signup페이지에 접속시도한다.5.<img src="{% url 'pydenticon_image' data=user.username %}" style="width: 24px; height: 24px;">관련된 reversematch에러가 뜬다.6.admin페이지에서 admin계정으로 로그인을 하고 accounts/signup 페이지로 와야 비로소 이 reversematch 에러가 뜨지 않습니다. #번외번외 적으로는 분명 계정을 생성했고, admin 페이지에서도 2개의 추가계정이 확인되는데, Django administration(어드민페이지 로그인창)을 통해서 로그인하려고 하면 비밀번호를 맞게 입력해도 로그인이 안됩니다;
-
미해결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버전입니다.
-
미해결이미지 관리 풀스택(feat. Node.js, React, MongoDB, AWS)
Promise.all에 대한 질문
선생님 안녕하세요.이번 강의에서 사용된 Promise.all이 뭔지 몰라서찾아보니 async-await를 사용하면 비동기 동작의 상태가 완료될 때까지 기다린 후 다음 코드를 순차적으로 읽어나가다보니 이렇게 기다리는 시간을 개선하기 위해 Promise들을 병렬로 처리해 주는 것으로 확인됩니다.근데 이번 강의에서 Promise.all을 빼고 이미지를 업로드해보면 아래의 사진처럼 네트워크의 preview에서 아무 값도 담기지 않게 됩니다.Promise.all이 처리속도를 개선해 주기 위한 기능이라면 Promise.all을 사용하지 않아도 우선 값은 담겨야 하는게 아닐까 하는 생각이 드는데 왜 값이 담기지 않게 되는지 궁금합니다.