typings/db.ts
// 백엔드에서 보내는 데이터에 대한 타입
export interface IUser {
id: number;
nickname: string;
email: string;
Workspaces: IWorkspace[];
}
...
(생략)
...
export interface IWorkspace {
id: number;
name: string;
url: string;
OwnerId: number;
}
Workspace/index.tsx
import {IUser} from '@typings/db';
...(생략)...
// useSWR 뒤에 붙이는건 위에서 본 데이터의 타입이다. 로그인에 안 되어 있으면 false 를 전달한다.
const {data:userData,error,mutate} = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher)
Typescript를 이용해서 Props를 보낼 때 익명함수 e도 아래처럼 타입을 표시해두자!
Menu/index.tsx
interface Props {
show:boolean;
// ★ onCloseModal:() => void; 로 하면 안됨 !! ★ //
onCloseModal:(e:any) => void;
style:CSSProperties;
closeButton?:boolean;
}
const Menu:FC<Props> = ({children,style,show,onCloseModal,closeButton}) => {
...(생략)...
Workspace/index.tsx
...(생략)...
const onCloseUserProfile = useCallback((e)=>{
e.stopPropagation();
setShowUserMenu((prev)=>!prev);
},[])
...(생략)...
<RightMenu>
<span onClick={onCloseUserProfile}>
<ProfileImg src={gravatar.url(userData.nickname,{s:'28px',d:'retro'})} alt={userData.nickname}/>
{showUserMenu && (
<Menu style={{right:0, top:38}} show={showUserMenu} onCloseModal={onCloseUserProfile}>
<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>
Workspace/index.tsx
...(생략)...
const onCreateWorkspace = useCallback((e)=>{
e.preventDefault()
// 필수값들 다 들어있나 검사
if(!newWorkspace || !newWorkspace.trim()) return;
if(!newUrl || !newUrl.trim()) return;
// if(!newWorkspace) return;
// 위에 주석처리한 것처럼 단순히 검사하는 경우가 많은데,
// 이 경우 띄어쓰기가 하나도 없을 때도 검사가 통과 된다.
// 띄어쓰기까지 막으려면 위에처럼 trim() 을 넣어주자.
axios.post('api/workspaces',{
workspace:newWorkspace,
url:newUrl,
})
.then(()=>{
mutate();
// 아래는 데이터 전송 했으면 초기값으로 설정
setShowCreateWorkspaceModal(false);
setNewWorkspace('')
setNewUrl('');
},{
//이 값을 true 로 해줘야 내가 로그인 된 상태인 걸 쿠키로 알 수 있다!
withCredentials:true,
})
.catch((error)=>{ // 에러난 경우 콘솔로그 찍기
console.dir(error);
// 아래 코드는 사용자에게 오류날 경우 왜 오류났는지 표시해주는 코드이다.
// npm i react-toastify 한 후에
// import {toast} from 'react-toastify' 로 불러온다.
toast.error(error.response?.data,{position:'bottom-center'});
});
},[newWorkspace,newUrl]);
...(생략)...
<input>
이 있으면 따로 별도의 컴포넌트로 빼주자. 안그러면 리렌더링이 빈번하게 일어나게 된다. 큰 문제는 아닌데 성능 최적화를 위해 해주자.
form
을 onSubmit 할 때 e.preventDefault()
를 항상 써주자.