[React] 작업정리 및 코드 컨벤션(나름..?!)

DaramGee·2025년 6월 19일

TIL

목록 보기
14/17

세팅

  • React 18
  • TypeScript
  • Vite
  • Ant Design
  • Toast UI
  • ESLint + Airbnb 스타일

json-server 구축(백 CRUD로 사용)

axios로 get, put, patch, delete 테스트 가능

  • /chat(채팅정보) - 19 items
  • /users(사용자정보) - 5 items
  • /mgr(직원정보) - 5 items
  • /loginMgr(로그인정보) - 1 item
  • /code(코드성 데이터) - 6 items
  • /chatFormData(상담내역) - 0 item
  • /chatData(채팅내역) - 31 items
  • /menus(메뉴정보) - 3 items

상태관리

관리 도구관리 대상설명
zustand선택된 chatSeq전역 선택 상태. 여러 컴포넌트에서 쉽게 가져다 씀chatStore
선택된 사용자userStore
모달 열림 여부, UI toggle모달 open/close 상태 등
현재 선택된 사이드 메뉴메뉴 선택 상태
React Query & lukemorales상담 리스트서버 데이터 fetch 및 캐싱useChat
상담 상세 데이터선택된 채팅 번호로 상세 조회
채팅 데이터채팅 기록 등
상담사 리스트상담사 선택 셀렉트useMgr
로그인 정보useLogin

lukemorales
별도의 키 팩토리 생성 후 쿼리키 부분에서 가져와서 사용

현재까지 Ant 활용

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

1. 기능 중심 디렉토리 구조

  • api, hooks, stores, types 등 기능 단위로 분리되어 있어 유지보수에 용이함.
  • pages/view/* 하위에 consult, menu, system 등 도메인 별로 나눈 것도 👍

2. 공통 컴포넌트 모듈화

  • components/form 안에 CmmButton, CmmRadioGroup 등 커스텀화된 UI 컴포넌트가 잘 분리돼 있음.

3. React Query + Zustand 병행

  • 서버 상태는 React Query, 클라이언트 상태는 Zustand로 구분한 점은 좋고, 이 흐름이 FSD(Folder-based Software Design) 접근과 맞음

수정예정 : 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 규칙

  • useQueryqueryFn, enabled, keepPreviousData 중심으로 작성
  • useMutation은 결과 후처리는 컴포넌트/서비스단에서 처리
    단, invalidateQueries, 간단한 message 정도는 onSuccess에 OK

hooks 작성 규칙

항목규칙
파일 위치/hooks/chat/useChatQueries.ts, /useChatMutations.ts 등 도메인별 정리
export 방식각 함수 개별 export자동완성, 단독 import 유리
훅 이름useInsertChatFormTextMutation, useChatTextData 등 목적+역할 분명하게

hooks 작성법 차이

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로 분리해도 좋음

마무리 요약

영역규칙 요약
Zustandset 함수만 정의, 상태 변경은 서비스단에서
React QueryqueryKey 상수화 + 개별 훅 export
액션 처리뮤테이션 후 상태 변경 및 메시지는 컴포넌트/서비스 단
구조도메인 단위로 hooks/store/api/types 정리
컴포넌트핸들러 함수명, 구조분해, 깔끔한 상태 접근 유지

0개의 댓글