본문 바로가기

JS/React Project

🏃‍♀️ 목표 다이어리 만들기 - 기획 및 기초 세팅

기능 

1. HOME 화면 

 - 月별로 작성된 목표를 확인할 수 있다. 

 - 각 목표 아이템은 작성한 날짜, 주제, 목표, 메모(코멘트)가 나타나고 체크된 목표에 따라 달성률을 표시한다. 

 - 새로운 목표 작성 버튼 

 - ...을 누르면 수정 및 삭제가 가능하다. 

 

2. 상세조회 

 - 작성한 내용을 볼 수 있다. 

 - 버튼을 누르면 목표 완료/취소가 가능하다. 

 - 달성률에 따라 막대기가 변한다. 

 - ...을 누르면 수정 및 삭제가 가능하다. 

 

3. 작성 및 수정 

 - 날짜를 선택할 수 있다. 

 - 주제를 선택할 수 있다. 

 - 목표추가 버튼을 누르면 목표를 작성할 수 있다. 

 - 메모, 코멘트, 일기 등을 작성할 수 있는 공간이 있다. 

 - 작성을 취소할 수 있다.(홈화면으로 이동) 

 

4. 메모 아이템 구성 

 - Id

 - 주제 : 공부, 운동, 일상, 저축

 - 목표 : [{ goalId : 1, isComplete : true/false , goalContent : '' } ]

 - 메모/코멘트/일기

 

기초 세팅하기 

1. 공통 컴포넌트 

 - 버튼  

  1. 기본 버튼 
    • type : default / undifined
    • text : 화살표(< , > , 작성취소, ...)
    • onClick
  2. 작성 완료
    • type: positive
    • text : 새로운 목표, 목표 추가, 작성 완료 
    • onClick 
const MyButton = ({ text, type, onClick }) => {

    // 만약 type에 임의의 값이 들어갔을 경우에도 default로 지정해줌 
    // === type의 값이 배열['positive'] 안에 존재하는지 확인하여 존재한다면 그 값으로, 아니라면 default로 반환
    const btnType =  ['positive'].includes(type) ? type:'default';

    return (
        <button className={['MyButton', `MyButton_${btnType}`].join(" ")} onClick={onClick}>
            {text}
        </button>
    );
}

// type이 지정되지 않았다면 default로 지정
MyButton.defaultProps = {
    type : "default"
}

export default MyButton;

 

 

- 헤더  : 총 세 가지 섹션 ( 오른쪽, 가운데, 왼쪽) 

const  MyHeader = ({headText, leftChild, rightChild}) => {
    
    return <header>
        <div className="head_btn_left">{leftChild}</div>
        <div className="head_text">{headText}</div>
        <div className="head_btn_right">{rightChild}</div>
    </header>

}

export default MyHeader;

 

 

2. 상태관리 세팅 및 공급

App 컴포넌트에 상태를 관리하는 State를 만들고 하위 컴포넌트에서 받아서 사용해야 한다. 

 

Reducer를 사용하여 상태관리 함수를 만들어 관리하고 Context를 사용하여 data와 함수를 컴포넌트에 공급한다. 

const reducer = (state, action) => {
  let newState = [];

  switch(action.type) {
    case 'INIT' : {
      return action.data;
    }
    case 'CREATE' : {
      newState = [action.data, ...state];
      break;
    }
    case 'DELETE' : {
      newState = state.filter((it)=> it.id !== action.targetId);
      break;
    }
    case 'EDIT' : {
      newState = state.map((it)=> it.id === action.data.id ? {...action.data} : it);
      break;
    }
    default : return state;
  }

  return newState;
}

export const DiaryStateContext = React.createContext();
export const DiaryDispatchContext = React.createContext();


function App() {

  // State 관리 
  const [data, dispatch] = useReducer(reducer, []);

  // Id 생성
  const dataId = useRef(0);

  // reducer dispatch - CREATE 
  const onCreate = (date, subject, goal, content) => {
      dispatch({
        type : "CREATE", 
        data : {
          id : dataId, 
          date : new Date(date).getTime(),
          subject,
          goal,
          content
      }
    });
    dataId.current +=1 
  }

  // reducer dispatch - DELETE
  const onDelete = (targetId) => {
    dispatch({type:"DELETE", targetId});
  }

  // reducer dispatch - EDIT
  const onEdit = (targetId, date, subject, goal, content) => {
    dispatch({  
      type : "EDIT", 
      data : {
        id : targetId, 
        date : new Date(date).getTime(),
        subject, 
        goal,
        content
      }
    })
  }

  return (
    <DiaryStateContext.Provider value={data}>
      <DiaryDispatchContext.Provider value={{onCreate, onDelete, onEdit}}>
        <BrowserRouter>
          <div className="App">
            <Routes>
              <Route path="/" element={<Home />} />
              <Route path="/new" element={<NewDiary />} />  
              <Route path="/Edit/:id" element={<EditDiary />} />
              <Route path="/Diary/:id" element={<Diary />} />    
            </Routes>
          </div>
        </BrowserRouter>
      </DiaryDispatchContext.Provider>
    </DiaryStateContext.Provider>
  );
}

export default App;