axios로 get, put, patch, delete 테스트 가능
| 관리 도구 | 관리 대상 | 설명 | |
|---|---|---|---|
| zustand | 선택된 chatSeq | 전역 선택 상태. 여러 컴포넌트에서 쉽게 가져다 씀 | chatStore |
| 선택된 사용자 | userStore | ||
| 모달 열림 여부, UI toggle | 모달 open/close 상태 등 | ||
| 현재 선택된 사이드 메뉴 | 메뉴 선택 상태 | ||
| React Query & lukemorales | 상담 리스트 | 서버 데이터 fetch 및 캐싱 | useChat |
| 상담 상세 데이터 | 선택된 채팅 번호로 상세 조회 | ||
| 채팅 데이터 | 채팅 기록 등 | ||
| 상담사 리스트 | 상담사 선택 셀렉트 | useMgr | |
| 로그인 정보 | useLogin |
lukemorales
별도의 키 팩토리 생성 후 쿼리키 부분에서 가져와서 사용
Text, List, Tag, Button, Table, message, popover, modal, select, search, avatar, badge 등
├── api/ ← API 호출 모듈 (axios 사용)
├── assets
│ ├── fonts
│ └── images
├── components/ ← UI 컴포넌트 (공통: form/, layout/, cmm/)
├── hooks ← 리액트쿼리 (비즈니스 로직 중심)
│ ├── cb
│ ├── login
│ ├── template
│ └── view
│ ├── code
│ ├── common
│ ├── chat
│ ├── menu
│ └── system
├── pages ← UI
│ ├── cb
│ ├── login
│ ├── template
│ └── view
│ ├── code
│ ├── common
│ ├── chat
│ ├── menu
│ └── system
├── query
│ ├── queryClient.ts
│ └── queryKeys.ts
├── stores
├── types
├── utils
api, hooks, stores, types 등 기능 단위로 분리되어 있어 유지보수에 용이함.pages/view/* 하위에 consult, menu, system 등 도메인 별로 나눈 것도 👍components/form 안에 CmmButton, CmmRadioGroup 등 커스텀화된 UI 컴포넌트가 잘 분리돼 있음.수정예정 : hook, query, stores 도 기능별로 폴더 생성(chat, menu, user 등)
const deleteChatMutation = useMutation({
mutationFn: ({ chatSeq }: { chatSeq: Chat['chatSeq']; }) =>
deleteChat(chatSeq),
onSuccess: () => {
clearChatSeq();
setUserId('');
queryClient.invalidateQueries({ queryKey: ['chat']});
message.success(`상담이 삭제되었습니다.`);
},
onError: () => {
message.error('상담 삭제에 실패했습니다.');
}
});
onSuccess에서 많은 일을 하고 있으니, 함수로 빼서 가독성 확보
useQuery, useMutation 규칙useQuery는 queryFn, enabled, keepPreviousData 중심으로 작성useMutation은 결과 후처리는 컴포넌트/서비스단에서 처리invalidateQueries, 간단한 message 정도는 onSuccess에 OK| 항목 | 규칙 |
|---|---|
| 파일 위치 | /hooks/chat/useChatQueries.ts, /useChatMutations.ts 등 도메인별 정리 |
| export 방식 | 각 함수 개별 export → 자동완성, 단독 import 유리 |
| 훅 이름 | useInsertChatFormTextMutation, useChatTextData 등 목적+역할 분명하게 |
export const insertChatFormTextMutation = () => {
return useMutation({
mutationFn: ({chatSeq, text}: { chatSeq: ChatFormData['chatSeq']; text: ChatFormData['text'] }) =>
insertChatFormText(chatSeq, text),
onSuccess: () => {
queryClient.invalidateQueries({queryKey: ['chat']});
message.success(`상담 내역이 등록되었습니다.`);
},
onError: () => {
message.error('상담 내역 등록에 실패했습니다.');
},
});
};
export const insertChatFormMemoMutation = useMutation({
mutationFn: ({chatSeq, text } :{ chatSeq: ChatFormData['chatSeq']; text:ChatFormData['text'] }) =>
insertChatFormMemo(chatSeq, text),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['chat']});
message.success(`상담 메모가 등록되었습니다.`);
},
onError: () => {
message.error('상담 메모 등록에 실패했습니다.');
}
});
| 이름 | 현재 구조 | 사용법 | 설명 |
|---|---|---|---|
insertChatFormTextMutation | 함수 () => useMutation(...) | insertChatFormTextMutation() | 사용하려면 함수 호출 필요 |
insertChatFormMemoMutation | 직접 useMutation(...) | insertChatFormMemoMutation | 이미 호출되어 있으므로 바로 사용 |
보통은
방법 1함수형 export로 통일하는 걸 추천
const updateChat = useUpdateChatStatusMutation();
const { clearChatSeq } = useChatStore();
const handleUpdateStatus = async () => {
try {
await updateChat.mutateAsync({ chatSeq, status: '완료' });
clearChatSeq(); // 상태 변경은 서비스 단
message.success('상태 업데이트 완료');
} catch {
message.error('상태 업데이트 실패');
}
};
📌 여러 뮤테이션/스토어 조합은 "서비스 함수 or 컴포넌트 단에서 일괄 처리"한다.
onSuccess → 뮤테이션 호출, zustand 상태관리 흐름은 ❌ 가급적 피함.
| 항목 | 규칙 |
|---|---|
| 타입 이름 | Chat, Mgr, ChatFormData 등 PascalCase |
| API 함수 이름 | getChatList, updateChatMgr 등 동작+대상 |
| 상태 초기화 함수 | clearChatSeq(), resetUserId() 등 명확하게 표현 |
| 메시지 | message.success('저장되었습니다.') – 전역 메시지는 utils/messageUtil.ts로 분리해도 좋음 |
| 영역 | 규칙 요약 |
|---|---|
| Zustand | set 함수만 정의, 상태 변경은 서비스단에서 |
| React Query | queryKey 상수화 + 개별 훅 export |
| 액션 처리 | 뮤테이션 후 상태 변경 및 메시지는 컴포넌트/서비스 단 |
| 구조 | 도메인 단위로 hooks/store/api/types 정리 |
| 컴포넌트 | 핸들러 함수명, 구조분해, 깔끔한 상태 접근 유지 |
