Info

  • MVVM ํŒจํ„ด ์‚ฌ์šฉ
    • Model
    • View
    • ViewModel

Structure

Model

class

  • ์ฃผ๋กœ constructor ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์— ์‚ฌ์šฉ๋จ.
  • get ๊ณผ ๊ฐ™์ด custom function์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋„ ์‚ฌ์šฉ๋จ.
  • ์˜ˆ์ œ์ธ DateOfCalendar.ts ์—์„œ๋Š” ์ƒ์„ฑ ๊ณผ์ •์—์„œ object to class ํ˜•ํƒœ ๊ตฌ์กฐ๋ฅผ class๋ฅผ ํ†ตํ•ด ์ •์˜ํ•˜๊ณ  ๊ฐ•์ œํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ.
  • ์œ ์—ฐํ•œ ์ƒ์„ฑ์ด ํ•„์š” ์—†์ด ์ •ํ•ด์ง„ ํŒจํ„ด์œผ๋กœ ์ƒ์„ฑํ•ด์•ผํ•˜๋Š” model์—์„œ ์‚ฌ์šฉ

example

import { getWeekdayByCode, weekday, Weekday } from '@typings/constants/Weekday'  
import dayjs from 'dayjs'  
import { ScheduleOfDate } from '@typings/ScheduleOfDate'  
import dateFormatUtil from '@utils/date/dateFormatUtil'  
  
export class DateOfCalendar {  
  year: number  
  month: number  
  day: number  
  weekday: Weekday  
  schedules: Array<ScheduleOfDate | null>  
  holiday: null | string = null  
  
  constructor(  
    date: {  
      year?: number  
      month?: number  
      day?: number  
      schedules?: Array<ScheduleOfDate | null>  
      holiday?: null | string  
    } = {},  
  ) {  
    const now = dateFormatUtil.getDate()  
  
    const {  
      year = now.year(),  
      month = now.month() + 1,  
      day = now.date(),  
      schedules = [],  
      holiday = null,  
    } = date  
  
    const targetDate = date ? dayjs(`${year}-${month}-${day}`) : now  
  
    this.year = targetDate.year()  
    this.month = targetDate.month() + 1  
    this.day = targetDate.date()  
    this.weekday = getWeekdayByCode(targetDate.day())  
    this.schedules = schedules  
    this.holiday = holiday  
  }  
  
  get isSaturday() {  
    return this.weekday.code == weekday.SATURDAY.code  
  }  
  
  get isSunday() {  
    return this.weekday.code == weekday.SUNDAY.code || this.holiday  
  }  
  
  isSameWeekday = (weekday: Weekday) => this.weekday.code == weekday.code  
  isToday = (now: dayjs.Dayjs) =>  
    now.year() == this.year && now.month() + 1 == this.month && now.date() == this.day  
}

type

  • ์ฃผ๋กœ ํ˜•ํƒœ๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ.
  • ์ถ”๊ฐ€์ ์ธ ๋กœ์ง ์ •์˜ ์—†๋Š” ์ˆœ์ˆ˜ํ•œ field์˜ ์ง‘ํ•ฉ.

example

Token.ts
export type Token = {  
  accessToken: string | null  
  refreshToken: string | null  
}

View

  • ์‹ค์ œ ํŽ˜์ด์ง€ ๊ตฌํ˜„๋ถ€
  • ์„œ๋น„์Šค ๋กœ์ง์€ ์ตœ์†Œํ™”ํ•˜๊ณ  ํ™”๋ฉด์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ Element ๊ตฌ์„ฑ ์š”์†Œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ.
    • utils์— ์ •์˜๋œ ๊ณตํ†ต util function๋“ค์€ ํ˜ธ์ถœ ๊ฐ€๋Šฅ
  • ํ•„์š”์— ๋”ฐ๋ผ component๋กœ ๋ถ„๋ฆฌ.
  • ์‚ฌ์šฉ์— ๋”ฐ๋ผ name ๋ถ„๋ฅ˜
    • page์ธ ๊ฒฝ์šฐ, {name}Page.tsx
    • component์ธ ๊ฒฝ์šฐ, {name}.tsx

example

MonthlyCalendarPage.tsx
import React, { useEffect } from 'react'  
import useMonthlyCalendarViewModel from '@views/calendar/monthlyCalendar/useMonthlyCalendarViewModel'  
import './monthlyCalendarPage.scss'  
import MonthlyCalendarHeader from '@views/calendar/monthlyCalendar/components/monthlyCalendarHeader/MonthlyCalendarHeader'  
import MonthlyCalendarBody from '@views/calendar/monthlyCalendar/components/monthlyCalendarBody/MonthlyCalendarBody'  
import SchedulePopups from '@views/calendar/popup/SchedulePopups'  
  
const MonthlyCalendarPage: React.FC = () => {  
  const { calculatedMonthlyCalendar: calendar, currentDate, init } = useMonthlyCalendarViewModel()  
  
  useEffect(() => {  
    if (  
      calendar == null ||  
      calendar.type != 'MONTHLY' ||  
      !(currentDate.year == calendar.year && currentDate.month == calendar.month)  
    )  
      init()  
  }, [currentDate])  
  
  if (!calendar) return null  
  
  return (  
    <>  
      <div className="monthly-calendar">  
        <MonthlyCalendarHeader />  
        <MonthlyCalendarBody calendar={calendar} />  
        <SchedulePopups />      </div>  
    </>  
  )  
}  
  
export default MonthlyCalendarPage

ViewModel

  • page ๋˜๋Š” component์—์„œ ํ•„์š”ํ•œ service ๋กœ์ง ๋˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ์ •๋ณด(store์™€์˜ ์ƒํ˜ธ์ž‘์šฉ)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ€๋ถ„
  • use{name}ViewModel.ts ๋กœ ์ •์˜
  • ํ™”๋ฉด์—์„œ ํ•„์š”ํ•œ store ์ •๋ณด๋ฅผ ์ „๋‹ฌํ•ด์ฃผ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋Š” ์ž‘์—…๋„ viewModel์—์„œ๋งŒ ๊ฐ€๋Šฅ

example

import calendarRepository from '@repositories/CalendarRepository'  
import { useMemo } from 'react'  
import { Calendar } from '@typings/Calendar'  
import dateFormatUtil from '@utils/date/dateFormatUtil'  
import { ScheduleOfDate } from '@typings/ScheduleOfDate'  
import scheduleRepository from '@repositories/ScheduleRepository'  
import useCalendarStore from '@stores/useCalendarStore'  
import useScheduleStore from '@stores/useScheduleStore'  
import { useShallow } from 'zustand/react/shallow'  
import useCalendarSelectStore from '@stores/useCalendarSelectStore'  
import { scheduleType } from '@typings/constants/ScheduleType'  
  
const useMonthlyCalendarViewModel = () => {  
  const currentDate = useCalendarSelectStore(useShallow(state => state.currentDate))  
  
  const { calendar, setCalendar } = useCalendarStore(  
    useShallow(state => ({  
      calendar: state.calendar,  
      setCalendar: state.setCalendar,  
    })),  
  )  
  
  const { schedules, setSchedules } = useScheduleStore(  
    useShallow(state => ({  
      schedules: state.schedules,  
      setSchedules: state.setSchedules,  
    })),  
  )  
  
  const { stringToDate } = dateFormatUtil  
  
  const calculatedMonthlyCalendar: null | Calendar = useMemo(() => {  
    if (!calendar) return null  
  
    const copyCalendar: Calendar = Object.assign(calendar)  
  
    const schedulesOfDate = schedules  
      .sort(a => (a.type == scheduleType.TASK ? 1 : -1))  
      .sort((a, b) => (stringToDate(a.startedAt).isAfter(stringToDate(b.startedAt)) ? 1 : -1))  
      .map((schedule, index) => schedule.getScheduleOfDateList(index + 1))  
      .flatMap(schedules => schedules)  
  
    let prevSchedules: ScheduleOfDate[] = []  
  
    copyCalendar.dates.forEach(date => {  
      const resultSchedules: ScheduleOfDate[] = []  
      const targetSchedules: ScheduleOfDate[] = schedulesOfDate.filter(  
        schedule =>  
          schedule.year == date.year && schedule.month == date.month && schedule.day == date.day,  
      )  
  
      let prevIndex = 0  
      // ๋นˆ ์š”์†Œ null ์„ธํŒ… => ํ™”๋ฉด์—์„œ ์‚ฌ์šฉ ํ•  ๊ฒฝ์šฐ, null ๊ฐ’์— ๋Œ€ํ•œ ๋Œ€์‘ ํ•„์š”.  
      targetSchedules.forEach(schedule => {  
        const prevScheduleIndex = prevSchedules.findIndex(  
          targetSchedule => targetSchedule != null && targetSchedule.id == schedule.id,  
        )  
        for (let i = prevIndex; i < prevScheduleIndex; i++) resultSchedules.push(null)  
  
        prevIndex = prevScheduleIndex + 1  
      })  
  
      // ์ผ์ • ์„ธํŒ…  
      targetSchedules.forEach(schedule => {  
        const prevScheduleIndex = prevSchedules.findIndex(  
          targetSchedule => targetSchedule != null && targetSchedule.id == schedule.id,  
        )  
        if (prevScheduleIndex != -1) {  
          resultSchedules[prevScheduleIndex] = schedule  
        } else {  
          const nullIndex = resultSchedules.findIndex(targetSchedule => targetSchedule == null)  
          if (nullIndex != -1) {  
            resultSchedules[nullIndex] = schedule  
          } else {  
            resultSchedules.push(schedule)  
          }        }  
      })  
  
      prevSchedules = resultSchedules  
      date.schedules = resultSchedules  
    })  
  
    return copyCalendar  
  }, [calendar, schedules])  
  
  const setMonthlyCalendar = async () => {  
    setCalendar(null)  
    setSchedules([])  
  
    const monthlyCalendar = await calendarRepository.getMonthlyCalendar({  
      year: currentDate.year,  
      month: currentDate.month,  
    })  
  
    const scheduleList = await scheduleRepository.findByDate({  
      year: currentDate.year,  
      month: currentDate.month,  
    })  
  
    setCalendar(monthlyCalendar)  
  
    setSchedules(scheduleList)  
  }  
  
  const init = async () => {  
    console.log('init!!')  
    await setMonthlyCalendar()  
  }  
  
  return {  
    calculatedMonthlyCalendar,  
    currentDate,  
    init,  
  }  
}  
  
export default useMonthlyCalendarViewModel