[TIL] 230706

이세령·2023년 7월 6일
0

TIL

목록 보기
50/118

API 명세서

gitbook을 사용해서 api 명세서를 정리해보았다.
https://seryeongs-organization.gitbook.io/api-docs/

GTTP Methods

  1. GET
    데이터를 서버에서 받아올 때 사용하는 메소드이다.

  2. POST
    데이터를 수정 및 생성할 때 사용하는 메소드이다.
    보통 request body를 담아서 보낸다.

  3. PUT
    데이터를 수정 및 생성할 때 사용한다.
    post와 마찬가지로 request body에 수정 내용을 담아 보낸다.

  4. PATCH
    데이터를 수정할 때 사용하는 메소드이다.
    수정 데이터를 request body에 수정 내용을 담아 보낸다.
    put, post와 다르게 자원의 일부분을 업데이트한다.

  5. DELETE
    데이터를 삭제할 때 사용하는 메소드이다.
    url에 index를 지정하거나 header에 pk를 넣어 지정하지만, 최근에는 테이블에 status속성을 지정하여 삭제 상태로 업데이트한다.

React Icons

아이콘과 styled-component를 사용하여 직접 UI를 구현해보기 위해 사용하기 쉬운 react icons을 사용하기로 했다.

  • 설치
    npm install react-icons --save
    yarn add react-icons --save
  • 사용법
    https://react-icons.github.io/react-icons
    공식에서 사용하고 싶은 것을 import 해주면 된다!
    import { IoHomeSharp } from "react-icons/io5";
    원하는 아이콘의 이름을 import하고 해당 아이콘이 어디 있는지 공식문서를 통해 찾아보고 경로설정을 해줬다.
  • 크기 변경하기
<IoHomeSharp IoIosClose size="24" />
  • 색상 변경하기
 <IoHomeSharp color="#fff" />

메인 페이지 버튼 구성

  1. 전체보기
    todolist의 전체를 보여줍니다.
  2. Mypost (로그인 구현 후)
    내가 작성한 글만 보여줍니다.
    해당 버튼이 활성화 상태이면 색이 변합니다.
  3. 진행중
    3-1. Mypost가 비활성화인 경우
    전체글 중에서 진행중만 보여줍니다.
    3-2. Mypost가 활성화인 경우 (로그인 구현 후)
    Mypost 중에서 진행중만 보여줍니다.
  4. 완료목록
    4-1. Mypost가 비활성화인 경우
    전체글 중에서 완료된 것만 보여줍니다.
    4-2. Mypost가 활성화인 경우 (로그인 구현 후)
    Mypost 중에서 완료된 것만 보여줍니다.
  5. 로그인
    로그인 페이지로 이동합니다.
  6. 위 화살표
    가장 하단으로 내려가면 나타나는 버튼입니다.
    클릭 시 가장 상단으로 올라갑니다.
  7. 작성하기
    클릭 시 작성하는 페이지로 이동합니다.
    항상 옆에 위치합니다.

기본 구성 코딩


집 모양 아이콘을 누르면 Home으로 돌아갈 수 있도록 구현했고, 컴포넌트 분리를 위해 영역만 분리 해두었다.

선발대 수업

좋아요 구현하기

like를 별도의 문서로 관리해서 postId를 외래키로 사용하여 참조하는 방식으로 구현하는 것을 배웠다.

export const fetchPosts = createAsyncThunk(
  `${name}/fetchPosts`,
  async (_, thunkAPI) => {
    try {
      const postsRef = collection(db, "posts");
      const querySnapshot = await getDocs(postsRef);
      const postPromises = querySnapshot.docs.map(async (document) => {
        const likesCollectionRef = collection(db, "likes");
        const q = query(likesCollectionRef, where("postId", "==", document.id));
        const likesDocSnapShot = await getDocs(q);
        const likesCount = likesDocSnapShot.size;

        return {
          id: document.id,
          likes: likesCount,
          ...document.data(),
        };
      });

      const posts = await Promise.all(postPromises);

      return thunkAPI.fulfillWithValue(posts);
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);

export const likePost = createAsyncThunk(
  `${name}/likePost`,
  async (postId, thunkAPI) => {
    try {
      const currentUserId = thunkAPI.getState().userReducer.currentUser.uid;

      const likesCollectionRef = collection(db, "likes");
      const q = query(
        likesCollectionRef,
        where("uid", "==", currentUserId),
        where("postId", "==", postId)
      );
      const querySnapshot = await getDocs(q);

      const likeDocSnapShot = querySnapshot.docs[0];

      if (likeDocSnapShot?.exists()) {
        await deleteDoc(likeDocSnapShot.ref);
        return thunkAPI.fulfillWithValue({ postId, isLike: false });
      } else {
        await addDoc(likesCollectionRef, { uid: currentUserId, postId });
        return thunkAPI.fulfillWithValue({ postId, isLike: true });
      }
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);

const postSlice = createSlice({
  name,
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.posts = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      })
      .addCase(addPost.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(addPost.fulfilled, (state, action) => {
        state.loading = false;
        state.posts.push(action.payload);
      })
      .addCase(addPost.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      })
      .addCase(deletePost.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(deletePost.fulfilled, (state, action) => {
        state.loading = false;
        state.posts = state.posts.filter((post) => post.id !== action.payload);
      })
      .addCase(deletePost.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      })
      .addCase(likePost.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(likePost.fulfilled, (state, action) => {
        state.loading = false;
        state.posts = state.posts.map((post) => {
          if (post.id === action.payload.postId) {
            if (action.payload.isLike) {
              post.likes += 1;
            } else {
              post.likes -= 1;
            }
          }
          return post;
        });
      })
      .addCase(likePost.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  },
});

이번에 API명세서를 다시 작성해보면서 url을 역할에 알맞게 적용하려고 해봤다. 반복하여 작성하다보니 늘고 있는 것 같다.
firebase를 다시 사용하려니 헷갈리는 것도 있었고, thunk를 하루만 공부하고 적용하려니 정리해둔 것을 보면서 작업해야 했다.
firebase에는 query 관련해서 where문이 있다는 것도 처음 알았다.. 문서를 봤다고 생각했는데 아니였다.
firebase 문서
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ko&authuser=0
where 뿐만 아니라 limit으로 검색된 문서 수를 제한할 수 있는 등 다양한 기능이 있다.

profile
https://github.com/Hediar?tab=repositories

0개의 댓글