inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Slack 클론 코딩[실시간 채팅 with React]

라우터 주소 설계(라우트 파라미터)

로그인 화면에서 리다이렉트 시 workspace 목록이 표시되지 않는 문제

725

김호준

작성한 질문수 2

0

안녕하세요. 강의 잘 듣고 있습니다.

다름이 아니라 login 한 후 workspace로 리다이렉트 된 후, workspace 목록이 아래와 같이 나타나지 않는 현상이 발생합니다.

하지만 다른 탭을 다녀오거나, workspace 추가를 하면 다시 정상적으로 아래와 같이 workspace 목록이 나타납니다.

아마 userData를 리다이렉트하면서 불러오지 않아 생기는 문제 같습니다. mutate()를 사용하고, dedupinginterval을 줄여도 문제 해결이 안되는 것 같아 리다이렉트됨과 동시에 다시 userData를 불러오도록 수정해야 할 것 같은데, 이를 구현할 수 있는 방법이 생각이 나지 않습니다. 현재 제 코드 일부 아래에 첨부합니다.

App.js

const App: FC = () => {
  return (
    <Switch>
      <Redirect exact path="/" to="/login" />
      <Route path="/login" component={LogIn} />
      <Route path="/signup" component={SignUp} />
      <Route path="/workspace/:workspace" component={Workspace} />
    </Switch>
  );
};

Login/index.tsx

const LogIn = () => {
    const {data, error, mutate} = useSWR('/api/users', fetcher, {
        dedupingInterval: 100000,
    });
    const [logInError, setLogInError] = useState(false);
    const [email, onChangeEmail] = useInput('');
    const [password, onChangePassword] = useInput('');
    const onSubmit = useCallback(
    (e) => {
      e.preventDefault();
      setLogInError(false);
      axios
        .post(
          '/api/users/login',
          { email, password }, 
          {
            withCredentials: true
          },
        )
        .then((response) => {
            mutate(response.data, false); //Optimistic UI
        })
        .catch((error) => {
          setLogInError(error.response?.status === 401);
        });
    },
    [email, password],
  );

  if (data === undefined) {
    return <div>로딩중...</div>;
  }

  if (data) {
    return <Redirect to="/workspace/sleact/channel/일반" />;
  }

Workspace/index.tsx

import Menu from "@components/Menu";
import Modal from "@components/Modal";
import CreateChannelModal from "@components/CreateChannelModal";
import useInput from "@hooks/useinput";
import fetcher from "@utils/fetcher";
import axios from "axios";
import React, { FunctionComponent, useCallback, useState } from "react"
import { Link, Redirect, Route, Switch, useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import useSWR from "swr";
import { 
    AddButton, 
    Channels, 
    Chats, 
    Header, 
    LogOutButton, 
    MenuScroll, 
    ProfileImg, 
    ProfileModal, 
    RightMenu, 
    WorkspaceButton, 
    WorkspaceModal,
    WorkspaceName, 
    WorkspaceWrapper, 
    Workspaces 
} from "@layouts/Workspace/style";
import gravatar from 'gravatar';
import { Button, Input, Label } from '@pages/SignUp/style';
import { IChannel, IUser } from "@typings/db";
import loadable from "@loadable/component";

const Channel = loadable(() => import('@pages/SignUp'));
const DirectMessage = loadable(() => import('@layouts/Workspace'));

const Workspace: FunctionComponent = () => {
    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 { workspace } = useParams<{workspace: 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
    );

    const onLogout = useCallback(() => {
        axios
        .post('/api/users/logout', null, {
            withCredentials: true,
        })
        .then(() => {
            mutate(false, false);
        });
    }, [])

    const onClickUserProfile = useCallback((e) => {
        e.stopPropagation();
        setShowUserMenu((prev) => !prev);
    }, []);

    const onClickCreateWorkSpace = useCallback(() => {
        setShowCreateWorkspaceModal(true);
    }, []);

    const onCreateWorkspace = useCallback((e) => {
        e.preventDefault();
        if(!newWorkspace || !newWorkspace.trim()) return;
        if(!newUrl || !newUrl.trim()) return;
        axios
        .post(
        '/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);
    }, []);

    if(!userData) {
        return <Redirect to="/login"/>
    }

    const toggleWorkspaceModal = useCallback(()=> {
        setShowWorkspaceModal((prev) => !(prev));
    },[]);

    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}} show={showUserMenu} onCloseModal={onClickUserProfile}>
                            <ProfileModal>
                                <img src={gravatar.url(userData.email, {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 onClick={toggleWorkspaceModal}>
                        Sleact
                    </WorkspaceName>
                    <MenuScroll>
                        <Menu show={showWorkspaceModal} onCloseModal={toggleWorkspaceModal} style={{ top: 95, left: 80}}>
                            <WorkspaceModal>
                                <h2>Sleact</h2>
                                <button onClick={onClickAddChannel}>채널 만들기</button>
                                <button onClick={onLogout}>로그아웃</button>
                            </WorkspaceModal>
                        </Menu>
                        {channelData?.map((v) => (<div>{v.name}</div>))}
                    </MenuScroll>
                </Channels>
                <Chats>
                    <Switch>
                        <Route path="/workspace/:workspace/channel/:channel" component={Channel} />
                        <Route path="/workspace/:workspace/dm/:id" component={DirectMessage} />
                    </Switch>
                </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-url" value={newUrl} onChange={onChangeNewUrl}/>
                    </Label>
                    <Button type="submit">생성하기</Button>
                </form>
            </Modal>
            <CreateChannelModal 
                show={showCreateChannelModal} 
                onCloseModal={onCloseModal}
                setShowCreateChannelModal={setShowCreateChannelModal}
            />
        </div>
    )
}

export default Workspace

react 웹팩 typescript socket.io babel 클론코딩

답변 1

0

제로초(조현영)

음.. 사실 Login/index.tsx에서 로그인 후 mutate하는 부분에서 데이터가 들어가 있어야 합니다. 여기서 console.log(response)를 해서 서버에서 유저 로그인 정보가 들어가는지 확인해보세요.

.then((response) => { mutate(response.data, false); //Optimistic UI })

0

김호준

빠른 답변 정말 감사드립니다. 말씀해주신 대로 console.log(response.data) 찍어보았는데 아래와 같이 response.data는 잘 찍히는 것을 확인했습니다. 그럼에도 같은 문제가 동일하게 발생합니다ㅠㅠ

image

0

제로초(조현영)

네트워크탭에서 login 요청 다음에 users 요청이 가고 그 응답이 사용자 정보인게 맞나요? false가 끼어들 데가 없을것같은데...

1

제로초(조현영)

질문 첫 번째 스크린샷에서 login 요청 전 users는 당연히 false일수밖에 없습니다. 로그인 하기 전에 내 정보를 가져온것이니까요. 로그인 후에 users를 mutate로 response.data로 바꾼거라서 돼야합니다. 실제로 강좌 소스코드도 정상 작동하고요.

mutate에서 false 빼보세요.

0

김호준

mutate에서 false 빼고 mutate(respose.data)로 수정하니까 해결되었습니다. 감사합니다!!

기본 셋팅과 관련하여

0

92

1

초기 셋팅 back과 front만 남겨두고 다 지운 후 진행 방법

0

96

2

focus 시에만 화면 업데이트 되는 이유 + 해결방법

0

150

2

useEffect 개수 관리

0

110

2

라이브러리 서치 방법

0

104

2

함수 정의 패턴

0

77

1

npm run dev 에러

0

152

3

npx webpack 후 에러

0

178

2

'void' 형식 식의 truthiness를 테스트할 수 없습니다.ts(1345)

0

144

2

사용자 가입시 에러발생 (TypeError: Cannot read properties of null (reading 'addMembers')

1

178

2

초기세팅중 packge.json 에러떠요

0

156

2

CORS - Access-Control-Allow-Origin 누락 문제

0

431

3

로그인 페이지 무한 새로고침 현상

0

598

2

Module not found: Error: Can't resolve './App' 에러

0

959

1

배포 방법

0

297

2

npm run dev 시 빌드가 매우 느려졌습니다

0

990

2

alias 경로 설정 오류

0

451

2

fetcher 함수의 data 값이 두번 찍히는 이유

0

277

1

제네릭 질문

0

218

2

ts-node 대신 tsx 사용여부

0

373

1

배포 관련 질문

0

247

1

[nginx + https] 서비스를 실행하면 niginx가 아닌 서비스 화면을 보여주게 하고 싶습니다.

0

385

2

[배포하기] webpack에 aws 퍼블릭 IPv4 주소 와 포트 주소를 작성하고 나서 빌드후 실행하면 오류가 발생합니다.

0

336

1

users 호출 시 쿠키가 담기지 않는 이슈 질문드립니다.

0

247

2