[SideProject] - 개인 프로젝트 - TeddyLog 03. 습관 가져오기


이제는 Habit을 생성하고 수정하는 기능을 만들어보자.

기본적인 투두리스트와 다른점은

생성을 할 경우 firestore 서버에 저장되며 GoalHabits에 저장되고 수정되며

실제 Habit의 카운팅 및 기록은 HabitsLog에 저장된다.

다음날이 되면 GoalHabits를 기준으로 새로운 HabitsLog를 count 0 으로 자동생성한다.

해야할 것


  • 목표 습관 생성 (Fab 버튼으로 생성)
    • 습관 기록 오늘 습관에서도 추가
  • 목표 습관 삭제 (롱프래스로 삭제)
    • 습관 기록 오늘 습관에서도 삭제
  • 습관 기록 조회하여 현재 날짜 기준 있는지 확인 (첫 화면 시작하면 불러올 데이터)
    • 없으면 목표 습관 내용으로 오늘 신규 습관 생성
    • 있으면 습관 목록 반환
  • 습관 count update

로직 Flow


1. 습관 목록 가져오기

스크린샷 2022-08-20 오후 4.36.45.png

2. 습관 생성

스크린샷 2022-08-20 오후 4.43.26.png

3. 습관 count update

스크린샷 2022-08-20 오후 4.43.42.png

4. 습관 삭제

스크린샷 2022-08-20 오후 4.43.56.png

이제 해당 Flow를 개발을 진행해보자

초기 설정


habitSlice.ts 를 만들어주자.

export interface IHabit {
    name: string;
    goalCount: number;
    currentCount: number | undefined;
}

export interface HabitState {
    habits: IHabit[];
}

const initialState: HabitState = {
    habits: [],
};

export const habitSlice = createSlice({
    name: 'habit',
    initialState,
    reducers: {},
    extraReducers: (builder) => {
    }
})

그리고 configureStore에 등록해주자.

export const store = configureStore({
    reducer: {
        auth: authSlice.reducer,
        user: userSlice.reducer,
        habit: habitSlice.reducer,
    },
    middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

이제 기능을 구현하면 된다!

1. 습관 목록 가져오기


먼저 습관 목록을 가져오는 기능을 만들어보자.

기능에서 해야하는 순서로는

  1. Date()를 활용해 오늘 날짜 추출
  2. Users/{uid}/HabitsLog/{date} 로 doc 가져오기
  3. doc이 있으면
    1. Users/{uid}/HabitsLog{date}/Habits 의 doc을 반환하기
  4. doc이 없으면
    1. Users/{uid}/HabitsLog/{date} 에 초기값으로 doc 생성하기
    2. Users/{uid}/GoalHabit 에서 docs가져오기
    3. 해당 docs로 Users/{uid}HabitsLog/{date}/Habits 에 목록 만들기
    4. 해당 docs 반환하기

뼈대를 만들어보자

createAsyncThunks를 활용해서 함수를 생성한다.

export const fetchHabitsRequest = createAsyncThunk(
  'habit/fetchHabitsRequest',
  async ({ uid }: IFetchHabitsRequest, { rejectWithValue }) => {
    try {
 
    } catch (e) {
      console.error(e);
    }
  }
);

해당 정보를 가져올때 필요한 uid 정보만 받도록 하였다.

  1. date 생성

     const now = new Date();
     const date = `${now.getFullYear()}-${now.getMonth()}-${now.getDay()}`;console.log(date);
    
  2. Users/{uid}/HabitsLog/{date} 로 doc 가져오기

     const habitLogRef = doc(db, 'Users', uid, 'HabitsLog', date);
     const docSnapshot = await getDoc(habitLogRef);
        
     if(docSnapshot.exists()) {
     ...
     } else {
     ...
     }
    
  3. doc이 존재하면? Users/{uid}/HabitsLog/{date}/Habits 의 doc을 반환하기

     const habitsCollection = collection(
               db,
               'Users',
               uid,
               'HabitsLog',
               date,
               'Habits'
             );
     const habits = await getDocs(habitsCollection);
        
     return habits.docs.map((e) => ({
               id: e.id,
               habitRef: e.data().habitRef,
               name: e.data().name,
               goalCount: e.data().goalCount,
               currentCount: e.data().currentCount,
             }));
    
  4. doc이 없으면? Users/{uid}/HabitsLog/{date} 에 초기값으로 doc 생성하기

     await setDoc(habitLogRef, {
               date,
               success: false,
             });
    
  5. doc이 없으면? Users/{uid}/GoalHabit 에서 docs가져오기

     const todayHabitLogCollection = collection(
               db,
               'Users',
               uid,
               'HabitsLog',
               date,
               'Habits'
             );
        
     const goalHabitCollection = collection(db, 'Users', uid, 'GoalHabits');
     const goalHabits = await getDocs(goalHabitCollection);
    
  6. 해당 docs로 Users/{uid}HabitsLog/{date}/Habits 에 목록 만들기

     const result = [];
     for (const goalhabit of goalHabits.docs) {
       const { name, goalCount } = goalhabit.data();
        
       const ref = await addDoc(todayHabitLogCollection, {
                 name,
                 goalCount,
                 currentCount: 0,
                 habitRef: goalhabit.ref,
               });
       result.push({
                 id: ref.id,
                 name,
                 goalCount,
                 currentCount: 0,
                 habitRef: goalhabit.ref,
               });
     }
     return result;
    

마지막으로 reducer에 연결해주자.

export const habitSlice = createSlice({
  name: 'habit',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchHabitsRequest.fulfilled, (state, action) => {
        state.habitFetchError = null;
        state.habitFetchLoading = 'succeeded';
        state.habits = action.payload ?? [];
      })
      .addCase(fetchHabitsRequest.rejected, (state, action) => {
        state.habitFetchError = action.payload as string;
        state.habitFetchLoading = 'failed';
      })
      .addCase(fetchHabitsRequest.pending, (state, action) => {
        state.habitFetchLoading = 'pending';
        state.habitFetchError = null;
      });
  },
});

해당 내용을 firestore에 dummy데이터를 넣어놓고 테스트해보자.

스크린샷 2022-08-20 오후 5.38.56.png

위와 같이 목표 습관을 만들어 놓고 실행해보았다.

스크린샷 2022-08-20 오후 5.39.06.png

아주 잘 생성된다.

스크린샷 2022-08-20 오후 5.41.43.png

화면 또한 아주 잘 보인다!

다음에는 목표 습관 생성을 개발해보자.