vercel
github
figma

ν”„λ‘œμ νŠΈ κ°œμš”

PostMobism 은 BE Apiλ₯Ό ν™œμš©ν•˜μ—¬ νšŒμ›κ°€μž…, 둜그인, κ²Œμ‹œκΈ€ CRUD 그리고 λŒ“κΈ€ μž‘μ„±μ„ ν•  수 μžˆλŠ” μ‚¬μ΄νŠΈμž…λ‹ˆλ‹€

총 2λͺ…이 μ°Έμ—¬ν•˜μ˜€κ³ , μ„€ μ—°νœ΄ κΈ°κ°„ μ œμ™Έ 2024-02-03 ~ 2024-02-17 μ•½ 12일 λ™μ•ˆ μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€
ν›„μ—λŠ” 배포 및 μžμž˜ν•œ 였λ₯˜ μˆ˜μ • 및 λ¦¬νŒ©ν† λ§μ„ μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€


μ—­ν•  λΆ„λ‹΄ 및 λͺ©ν‘œ

μ—­ν•  λΆ„λ‹΄ 방식

βœ“ νš¨μœ¨λ³΄λ‹€λŠ” "λ§Žμ€ 것을 μ–»μ–΄κ°€λŠ” 것" 에 μ΄ˆμ μ„ 맞좘 μ—­ν•  λΆ„λ‹΄

이전에 μ§„ν–‰ν•˜λ©΄μ„œ μ•„μ‰¬μ› λ˜ 것듀을 μ΄λ²ˆμ—” μ œλŒ€λ‘œ μ±™κΈ°λŠ” 것이 λͺ©ν‘œμ˜€κΈ° λ•Œλ¬Έμ—,
μ΄μ „μ—λŠ” 보톡 둜직이 κ²ΉμΉ˜κΈ°μ— ν•œ μ‚¬λžŒμ΄ νšŒμ›κ°€μž…/λ‘œκ·ΈμΈμ„ λͺ¨λ‘ μ§„ν–‰ν–ˆλ‹€λ©΄ μ΄λ²ˆμ—λŠ” κ·Έ μ•ˆμ—μ„œ λ‚˜λˆ μ„œ μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€
ex) you: νšŒμ›κ°€μž…/포슀트, me: 둜그인/λŒ“κΈ€

μ΄λ•Œ μ„œλ‘œ ν•¨μˆ˜λͺ…μ΄λ‚˜ 폴더λͺ… λ“± 둜직이 κ²ΉμΉ˜μΉ˜κ±°λ‚˜ ν˜Όλ™μ΄ 올 수 μžˆμ–΄μ„œ PR reviewλ₯Ό κΌΌκΌΌν•˜κ²Œ μ§„ν–‰ν•˜κΈ°λ‘œ ν–ˆμŠ΅λ‹ˆλ‹€


λ‚˜μ˜ μ—­ν• 

Login, Comment, postDetailModal

λͺ©ν‘œ

1. μ •ν™•ν•˜κ³  효율적인 λ°±μ—”λ“œ Api 뢄석

  • μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨
  • Thunder Client

2. μ œλŒ€λ‘œ! 둜직 μ΄ν•΄ν•˜κΈ° + ν˜‘μ—…!!

  • auth, refreshToken 둜직 이해 및 κ΅¬ν˜„
  • λ¦¬λ‹ˆμ–΄, githubIsuue, prReview, JS DOCSλ₯Ό ν†΅ν•œ 체계적이고 효율적인 ν˜‘μ—…

3. RTK

  • RTKλ₯Ό μ‚¬μš©ν•œ μ „μ—­μƒνƒœκ΄€λ¦¬
  • RTK Queryλ₯Ό μ‚¬μš©ν•œ λ¬΄ν•œμŠ€ν¬λ‘€λ§ κ΅¬ν˜„

λͺ©ν‘œ 달성 μ—¬λΆ€ 및 μ„±κ³Ό

1. μ •ν™•ν•˜κ³  효율적인 λ°±μ—”λ“œ Api 뢄석 ❌

  • μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨
  • Thunder Client

μœ„λŠ” νŒ€μ›λΆ„κ»˜μ„œ λ°±μ—”λ“œ Api뢄석을 μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨μž…λ‹ˆλ‹€

뢄석이 처음이라 μ–΄λ–»κ²Œ 해야할지 λͺ¨λ₯΄κ² μ–΄μ„œ κ·Έλƒ₯ 생각을 μ •λ¦¬ν•˜λ“―μ΄ ν•΄κ°”λŠ”λ°,
νŒ€μ›λΆ„ 덕뢄에 μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨μ— λŒ€ν•΄μ„œ μ•Œκ²Œλ˜μ–΄ ν•¨κ»˜ λ‹€μ‹œ μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€!


πŸ”΄ μ‹€νŒ¨ μš”μΈ 뢄석

1. input output 연상 λΆ€μ‘±

μž˜ν•œ 것 κ°™μ§€λ§Œ, 빠진 것이 μžˆλ‹€λ©΄ Thunder Client둜 μ‹€μ œ ν™œμš©ν•  data의 인풋 아웃풋 값을 μ œλŒ€λ‘œ μ—°μƒν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€
λ°±μ—”λ“œ API 뢄석을 μ˜ˆμƒμœΌλ‘œλ§Œ μ§„ν–‰ν•˜λ‹€ λ³΄λ‹ˆ, μ‹€μ§ˆμ μœΌλ‘œ κ΅¬ν˜„ν•  λ•Œ μ˜ˆμƒλŒ€λ‘œ λ˜μ§€ μ•Šμ•˜λ˜ 뢀뢄듀이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€

2. μ΅μˆ™ν•˜μ§€ μ•Šμ€ 툴, 효율적인 μ§„ν–‰μ˜ 어렀움

λ˜ν•œ Api 뢄석, μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨ 그리기, ν”Όκ·Έλ§ˆ λͺ¨λ‘ μ΅μˆ™ν•˜μ§€ μ•Šλ‹€λ³΄λ‹ˆ 효율적인 진행이 잘 λ˜μ§€ μ•Šμ•˜λ˜ κ±° κ°™μŠ΅λ‹ˆλ‹€


πŸ”΅ 극볡 방법

1. ThunderClient둜 μ •ν™•ν•œ data μš”μ²­ 및 뢄석

νšŒμƒ‰ μ‚¬κ°ν˜•μœΌλ‘œ 가렀진 뢀뢄은 backendURL μž…λ‹ˆλ‹€

⚑️ SignUp/In

⚑️ postPost

⚑️ getPost

⚑️ patchPost

⚑️ deletePost

⚑️ getPostDetail

⚑️ postComment

⚑️ getComment

2. μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨ νˆ΄μ„ μ‚¬μš©

draw.io (무료)

ThunderClient둜 λΆ„μ„ν•œ 것을 λ°”νƒ•μœΌλ‘œ μš”μ²­ μ‹€νŒ¨μ™€ μ„±κ³΅μ‹œ 값을 λ‚˜λˆ μ„œ λ‚˜μ˜¬ 수 μžˆλŠ” 버그듀에 λŒ€ν•΄ μ˜ˆμΈ‘ν–ˆμŠ΅λ‹ˆλ‹€

μ™Όμͺ½μ—λŠ” λΆˆν•„μš”ν•œ μš”μ²­μ΄λ‚˜ 토큰 μ„ΈνŒ… 등에 λŒ€ν•œ 상세 λ‚΄μš©λ“€μ„ μ–΄λ–»κ²Œ μ²˜λ¦¬ν• μ§€ μ˜ˆμƒν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€



2. μ œλŒ€λ‘œ! 둜직 μ΄ν•΄ν•˜κΈ° + ν˜‘μ—…!! βœ…

ν˜‘μ—…μ€ chap-chap ν”„λ‘œμ νŠΈμ—μ„œ μ—΄κ³Ό 성을 λ‹€ν–ˆλ˜λ§ŒνΌ, κ·Έ λ•Œ ν–ˆλ˜ 것듀을 λ°”νƒ•μœΌλ‘œ 잘 μ§„ν–‰ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

🀝 λ¦¬λ‹ˆμ–΄

🀝 GitHub Issue

🀝 PR Review

🀝 PR Template


3. RTK βœ…

comment.slice.ts

import { TCommentsResponse } from "@/type/type"
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import { CommentApi } from "./comment.api"
import { CommentDataType } from "@/pages/main/components/post-detail-modal/components/comment/comment-form"

type CommentState = {
  commentList: TCommentsResponse | null
  loading: boolean
  error: string | null
}

const initialState: CommentState = {
  commentList: {
    data: [],
    pageNation: undefined,
  },

  loading: false,
  error: "",
}

// getComments : read
export const getComments = createAsyncThunk<TCommentsResponse, { page: number; postId: string }>(
  "comment/getComments",
  async ({ page, postId }) => {
    try {
      const posts = await CommentApi.getComment({ page, postId })
      return posts
    } catch (error) {
      throw new Error("κ²Œμ‹œκΈ€ 데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” 데 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€!")
    }
  },
)

export const postComment = createAsyncThunk("comment/postComment", async ({ parentId, content }: CommentDataType) => {
  try {
    const commentData = { parentId, content }
    const res = await CommentApi.postComment(commentData)
    return res.data
  } catch (error) {
    throw new Error("λŒ“κΈ€ μž‘μ„±μ— μ‹€νŒ¨ν•˜μ˜€μŠ΅λ‹ˆλ‹€")
  }
})

//Reducer와 action μ •μ˜
export const commentSlice = createSlice({
  name: "comment",
  initialState,
  reducers: {},
  //extraReducersλ₯Ό μ‚¬μš©ν•˜μ—¬ 비동기 μ•‘μ…˜(getPosts)의 성곡, μ‹€νŒ¨ 및 보λ₯˜ μƒνƒœμ— 따라 μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈ
  extraReducers(builder) {
    builder
      // getPost : read
      .addCase(getComments.pending, state => {
        state.loading = true
      })
      .addCase(getComments.fulfilled, (state, action) => {
        state.loading = false
        state.error = null
        state.commentList = {
          data: state.commentList?.data.concat(action.payload.data)!,
          pageNation: action.payload.pageNation,
        }
      })
      .addCase(getComments.rejected, (state, action) => {
        state.loading = false
        state.error = action.error.message || "예기치 λͺ»ν•œ μ—λŸ¬λ‘œ κ²Œμ‹œκΈ€ 데이터λ₯Ό λΆˆλŸ¬μ˜€μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€!"
        state.commentList = null
      })
      // postComment: create
      .addCase(postComment.pending, state => {
        state.loading = true
      })
      .addCase(postComment.fulfilled, (state, action) => {
        state.loading = false
      })
      .addCase(postComment.rejected, (state, action) => {
        state.loading = false
        state.error = action.error.message || "λŒ“κΈ€ μž‘μ„±μ— μ‹€νŒ¨ν•˜μ˜€μŠ΅λ‹ˆλ‹€"
      })
  },
})

// export const { setPostList } = postSlice.actions

export default commentSlice.reducer

store.ts

import userReducer from "@/features/user/user.slice"
import postReducer from "@/features/post/post.slice"
import commentReducer from "@/features/comment/comment.slice"
import { configureStore } from "@reduxjs/toolkit"
import { commentApi } from "@/hooks/use-get-comment-list-query"

export const store = configureStore({
  reducer: {
    user: userReducer,
    post: postReducer,
    comment: commentReducer,
    [commentApi.reducerPath]: commentApi.reducer,
  },
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      serializableCheck: false, // λΆˆν•„μš”ν•œ κ²½κ³ λ₯Ό ν”Όν•˜κΈ° μœ„ν•΄ μΆ”κ°€
    }).concat(commentApi.middleware),
})

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

RTK Query μ΄μš©ν•œ λ¬΄ν•œμŠ€ν¬λ‘€λ§ κ΅¬ν˜„


import { TCommentsResponse } from "@/type/type"
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"

const token = localStorage.getItem("access_token")

export const commentApi = createApi({
  reducerPath: "commentApi",
  baseQuery: fetchBaseQuery({
    baseUrl: import.meta.env.VITE_BACKEND_URL,
    headers: {
      authorization: `Bearer ${token}`,
    },
    credentials: "include",
  }),
  tagTypes: ["Comment"],
  endpoints: builder => ({
    getCommentList: builder.query<TCommentsResponse, { postId: string; pageParam: number }>({
      query: ({ postId, pageParam }) => ({
        url: `/data/comment?parentId=${postId}&page=${pageParam}&apiKey=${import.meta.env.VITE_API_KEY}&pair=${import.meta.env.VITE_PAIR}`,
        method: "GET",
      }),
      providesTags: ["Comment"],
      serializeQueryArgs: ({ queryArgs }) => {
        const newQueryArgs = { ...queryArgs }
        if (newQueryArgs.pageParam) {
          newQueryArgs.pageParam = 0
        }
        return newQueryArgs
      },
      merge: (currentCacheData, responseData) => {
        if (currentCacheData.data) {
          return {
            ...currentCacheData,
            ...responseData,
            data: [...currentCacheData.data, ...responseData.data],
          }
        }
      },
    }),
  }),
})

export const { useGetCommentListQuery } = commentApi

μ–΄λ €μ› λ˜ 점 & μ•Œκ²Œ 된 점 & κ°œμ„  λ°©μ•ˆ

1. BE Api μ œλŒ€λ‘œ 된 뢄석

μœ„μ— μ“΄λŒ€λ‘œ thunder client, draw.ioλ₯Ό 톡해 극볡!

2. RTKQueryλ₯Ό ν†΅ν•œ λ¬΄ν•œμŠ€ν¬λ‘€λ§

κ³΅μ‹λ¬Έμ„œλ₯Ό 읽어봐도 많이 μ–΄λ €μ› μŠ΅λ‹ˆλ‹€..
남은 λ¦¬νŒ©ν† λ§μ„ μ§„ν–‰ν•˜λ©° λ‹€μ‹œν•œ 번 잘 λ˜μ§€ μ•Šμ•˜λ˜ 뢀뢄을 보고 정리할 κ²ƒμž…λ‹ˆλ‹€
μ „μ—­ μƒνƒœ 관리와 λ¬΄ν•œ μŠ€ν¬λ‘€λ§μ€ react-query, useInfiniteQueryλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ–΄λ–¨κΉŒ..^^


마무리

νƒ€μž…μŠ€ν¬λ¦½νŠΈ, RTK, λ°±μ—”λ“œ API뢄석을 μœ„ν•œ ν”„λ‘œμ νŠΈμ˜€λ˜ κ±° κ°™μŠ΅λ‹ˆλ‹€
아직 μ–΄λ ΅κΈ΄ν•˜μ§€λ§Œ κ·Έλž˜λ„ 점점 μ΅μˆ™ν•΄μ Έκ°€λŠ” 게 λŠκ»΄μ‘ŒμŠ΅λ‹ˆλ‹€

λ‹€ μ²˜μŒν•  λ•ŒλŠ” μ–΄λ ΅μ§€λ§Œ, ν•œ 번 ν•˜κ³  λ‹€μŒμ— 또 μ‚¬μš©ν•˜λ©΄ κ·Έλž˜λ„ μ΅μˆ™ν•΄μ§„λ‹€λŠ” 것을 μ•Œκ²Œλ˜μ–΄ μƒˆλ‘œμš΄ Library 및 Tool에 λŒ€ν•œ 두렀움이 μ’€ μ‚¬λΌμ‘ŒμŠ΅λ‹ˆλ‹€

λ°±μ—”λ“œ API 뢄석은 μ•„λ¬΄λž˜λ„ 아직 λ°±μ—”λ“œμ™€μ˜ ν˜‘μ—…κ²½ν—˜μ΄ λͺ¨μžλΌλ‹€λ³΄λ‹ˆ μ–΄μ©” 수 μ—†λŠ” 뢀뢄이긴 ν•˜μ§€λ§Œ, ThunderClient + μ‹œν€€μŠ€ λ‹€μ΄μ–΄κ·Έλž¨ + 개발자 도ꡬ 검사 창에 μ΅μˆ™ν•΄μ Έμ„œ 뢀쑱함을 λ©”κΏ”λ‚˜κ°€μ•Όκ² μŠ΅λ‹ˆλ‹€!

특히 이번 ν”„λ‘œμ νŠΈ λ•Œ μ˜ˆμƒμΉ˜ λͺ»ν•˜κ²Œ μ•„ν”„κ³ , 또 μ—°νœ΄κ°€ λΌλ©΄μ„œ ν”„λ‘œμ νŠΈκ°€ 쑰금 λ°€λ¦¬λŠ” 일이 μžˆμ—ˆλŠ”λ° μ»¨λ””μ…˜ 건강 관리λ₯Ό μž˜ν•˜κ³ ... κ³„νšμ„ ν”„λ‘œμ νŠΈμ— 차질이 없도둝 μ„Έμ›Œμ„œ 더 μ±…μž„κ°μžˆκ²Œ μž„ν•΄μ•Όκ² λ‹€λŠ” 생각이 λ“€μ—ˆμŠ΅λ‹ˆλ‹€.

profile
no namae wa

0개의 λŒ“κΈ€