STUDY/[ TypeScript ]

redux

Lim임 2025. 11. 19. 03:09

프로젝트 추가 기능 정리

📋 목차

  1. EditModal 컴포넌트
  2. LoggerModal 컴포넌트
  3. LogItem 컴포넌트
  4. 게시판 삭제 기능
  5. Redux State 구조

1. EditModal 컴포넌트

📌 개념

EditModal은 Task(작업)를 수정할 수 있는 모달 컴포넌트입니다.

🎯 주요 기능

  • Task의 정보 수정 (이름, 설명, 담당자)
  • 모달 열기/닫기 제어
  • Redux를 통한 상태 관리

📁 파일 구조

EditModal/
├── EditModal.tsx       # 컴포넌트 로직
└── EditModal.css.ts    # Vanilla Extract 스타일

💡 사용 방법

import EditModal from './components/EditModal/EditModal';

// Task 수정 시 모달 표시
<EditModal />

🔧 주요 Props (예상)

  • isOpen: 모달 표시 여부
  • task: 수정할 Task 객체
  • onClose: 모달 닫기 핸들러
  • onSave: Task 저장 핸들러

2. LoggerModal 컴포넌트

📌 개념

LoggerModal은 사용자의 모든 액션(추가, 수정, 삭제)을 기록하고 표시하는 모달입니다.

🎯 주요 기능

  • 액션 히스토리 표시
  • 시간순 정렬된 로그 목록
  • LogItem 컴포넌트를 사용한 개별 로그 렌더링

📁 파일 구조

LoggerModal/
├── LoggerModal.tsx       # 메인 모달 컴포넌트
├── LoggerModal.css.ts    # 모달 스타일
└── LogItem/
    ├── LogItem.tsx       # 개별 로그 아이템
    └── LogItem.css.ts    # 로그 아이템 스타일

💾 Redux State

type loggerState = {
    logArray: ILogItem[]  // 모든 로그 배열
}

📊 로그 데이터 구조

interface ILogItem {
    logId: string;         // 로그 고유 ID
    logAuthor: string;     // 액션 수행자
    logMessage: string;    // 액션 내용
    logTimestamp: string;  // 액션 발생 시간
}

🔄 Redux Actions

// 로그 추가
addLog(payload: ILogItem)

💡 사용 예시

import { useDispatch } from 'react-redux';
import { addLog } from './store/slices/loggerSlice';

const dispatch = useDispatch();

// Task 추가 시 로그 기록
dispatch(addLog({
    logId: 'log-1',
    logAuthor: 'John',
    logMessage: 'Task "개발하기" 추가됨',
    logTimestamp: new Date().toISOString()
}));

3. LogItem 컴포넌트

📌 개념

LoggerModal 내부에서 개별 로그를 표시하는 하위 컴포넌트입니다.

🎯 주요 기능

  • 단일 로그 항목 렌더링
  • 작성자, 메시지, 시간 정보 표시
  • 시각적 구분을 위한 스타일링

🔧 Props

interface ILogItemProps {
    logItem: ILogItem;
}

🎨 스타일 요소

  • 로그 작성자 강조
  • 타임스탬프 포맷팅
  • 액션 타입별 색상 구분 (추가/수정/삭제)

4. 게시판 삭제 기능

📌 개념

List(게시판)를 삭제하는 기능으로 Redux를 통해 상태를 업데이트합니다.

🔄 Redux Action: deleteList

Action 타입 정의

type TDeleteListAction = {
    boardId: string;  // 보드 ID
    listId: string;   // 삭제할 리스트 ID
}

Reducer 로직

deleteList: (state, {payload}: PayloadAction<TDeleteListAction>) => {
    state.boardArray = state.boardArray.map(
        board =>
        board.boardId === payload.boardId
        ? {
            ...board,
            lists: board.lists.filter(
                list => list.listId !== payload.listId
            )
          }
        : board
    )
}

동작 원리

  1. boardId 찾기: 해당 Board를 찾습니다
  2. List 필터링: 해당 Board의 lists 배열에서 listId가 일치하지 않는 것만 남깁니다
  3. 불변성 유지: map과 filter로 새로운 배열을 반환합니다

💡 사용 예시

import { useDispatch } from 'react-redux';
import { deleteList } from './store/slices/boardsSlice';

const dispatch = useDispatch();

const handleDeleteList = (boardId: string, listId: string) => {
    dispatch(deleteList({ boardId, listId }));

    // 로그 기록
    dispatch(addLog({
        logId: `log-${Date.now()}`,
        logAuthor: 'User',
        logMessage: `리스트 삭제됨`,
        logTimestamp: new Date().toISOString()
    }));
};

5. Redux State 구조

📊 전체 State 구조

{
    boards: {
        modalActive: boolean,      // 모달 활성화 상태
        boardArray: IBoard[]       // 모든 보드 배열
    },
    logger: {
        logArray: ILogItem[]       // 모든 로그 배열
    },
    modal: {
        boardId: string,
        listId: string,
        task: ITask                // 현재 편집 중인 Task
    }
}

🔄 주요 Actions 목록

boardsSlice

  • addBoard: 새 보드 추가
  • addList: 보드에 리스트 추가
  • addTask: 리스트에 Task 추가
  • deleteList: 리스트 삭제 ⭐
  • setModalActive: 모달 표시 상태 변경

loggerSlice

  • addLog: 새 로그 추가 ⭐

modalSlice

  • 편집할 Task 정보 관리

📈 데이터 흐름

사용자 액션 (예: List 삭제)
    ↓
dispatch(deleteList({ boardId, listId }))
    ↓
Reducer에서 State 업데이트
    ↓
컴포넌트 리렌더링
    ↓
dispatch(addLog({ ... }))
    ↓
Logger State 업데이트
    ↓
LoggerModal에 히스토리 표시

🎨 스타일링

모든 컴포넌트는 Vanilla Extract를 사용하여 타입 안전한 CSS-in-JS를 구현합니다.

장점

  • TypeScript 완전 지원
  • 빌드 타임 CSS 생성
  • 제로 런타임 오버헤드
  • 자동완성 및 타입 체크

예시

// EditModal.css.ts
import { style } from '@vanilla-extract/css';
import { vars } from '../../App.css';

export const modalOverlay = style({
    position: 'fixed',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
});