• 카테고리

    질문 & 답변
  • 세부 분야

    프론트엔드

  • 해결 여부

    미해결

채널 생성시 404오류

22.02.23 02:09 작성 조회수 192

0

안녕하세요!!! 강좌 열심히 따라하면서 배우는 중, 해결점을 찾을수 없는 부분이 있어서 질문합니다.
*우선 리액트 v6를 사용하여 route가 아래와 같이 되었다는 것을 참고해주세요.
**SWR역시 revalidate가 작동이 안되어, mutate사용하였습니다.
 
[이슈사항]
채널 리스트는 잘 받아오는 것 같습니다.
하지만, 채널생성시 404에러가 발생합니다.
서버는 "존재하지 않는 워크스페이스입니다."라는 응답을 줍니다.
 
의심되는 것은 버전6 라우팅설정에 문제가 있지 않을까 생각되는데... 검색해봐도 딱히 잘못된것을 못 발견해서 이렇게 질문남깁니다.
//CreateChannelModal 코드
import Modal from '@components/Modal';
import React, { CSSProperties, useCallback, VFC } from 'react';
import { Button, Input, Label } from '@pages/SignUp/styles';
import useInput from '@hooks/useinput';
import axios from 'axios';
import { useParams } from 'react-router';
import { toast } from 'react-toastify';
import useSWR from 'swr';
import { IChannel, IUser } from '@typings/db';
import fetcher from '@utils/fetcher';

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, error, mutate } = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher);
const { data: channelData, mutate: mutateChannel } = useSWR<IChannel[]>(
// 로그인하지 않은상태이면 호출안하고, null
userData ? `http://localhost:3095/api/workspaces/${workspace}/channels` : null,
fetcher,
);
const onCreateChannel = useCallback(
(e) => {
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],
);
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;
 
//Workspace 코드

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

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

const Workspace: VFC = () => {
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>('http://localhost:3095/api/users', fetcher);
const { data: channelData } = useSWR<IChannel[]>(
// 로그인하지 않은상태이면 호출안하고, null
userData ? `http://localhost:3095/api/workspaces/${workspace}/channels` : null,
fetcher,
);

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

const onCloseUserProfile = useCallback((e) => {
e.stopPropagation();
setShowUserMenu(false);
}, []);
const onClickUserProfile = useCallback(() => {
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(
'http://localhost:3095/api/workspaces',
{
workspace: newWorkspace,
url: newUrl,
},
{
withCredentials: true,
},
)
.then((response) => {
mutate(response.data);
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 toggleWorkspaceModal = useCallback(() => {
setShowWorkspaceModal((prev) => !prev);
}, []);

const onClickAddChannel = useCallback(() => {
setShowCreateChannelModal(true);
}, []);

if (!userData) {
return <Navigate replace to="/login" />;
}

return (
<div>
<Header>
<RightMenu>
<span onClick={onClickUserProfile}>
<ProfileImg src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.email} />
{showUserMenu && (
<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.email}</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>
{/*<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={onClickInviteWorkspace}>워크스페이스에 사용자 초대</button>*/}
<button onClick={onClickAddChannel}>채널 만들기</button>
<button onClick={onLogout}>로그아웃</button>
</WorkspaceModal>
</Menu>
{channelData?.map((v) => (
<div>{v.name}</div>
))}
</MenuScroll>
</Channels>
<Chats>
chats
<Routes>
<Route path="/channel/:channel" element={<Channel />} />
<Route path="/dm/:id" element={<DirectMessage />} />
</Routes>
</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;
 
//App코드입니다

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={<LogIn />} />
<Route path="/login" element={<LogIn />} />
<Route path="/signup" element={<SignUp />} />
<Route path="/workspace/:workspace/*" element={<Workspace />} />
</Routes>
);
};

export default App;
 

 

답변 1

답변을 작성해보세요.

0

개발자도구 네트워크탭에서 채널 생성 요청을 보낼 때 요청 주소를 한 번 확인해보세요. 혹시 workspace가 undefined가 아닌가요? 맞다면 라우터 설정 문제이고, undefined가 아니라 sleact인데 404가 발생하는 것이면 db:seed 명령어 문제일 것 같습니다.

최인설님의 프로필

최인설

질문자

2022.02.23

말씀해주신 undefine은 아닌,

sleact대신 $%~~~와 같은 이상한 주소가 나와서  post 호출시 문제인것 같아서... 만지다 보니 해결되었습니다.

감사합니다 :)

 

(해결방법)

post요청시... 주소상에 ${workspace}가 들어가서 그런지,

작은 따옴표가 아닌, 백틱을 써야 됩니다!!!

 

비슷한 이슈있으시면 참고하세용 :)

에러발생!

axios
.post(
'http://localhost:3095/api/workspaces/${workspace}/channels',
{
name: newChannel,
},

에러해소(백틱사용)

axios
.post(
`http://localhost:3095/api/workspaces/${workspace}/channels`,
{
name: newChannel,
},