project : post-mobism

55555-Jyeonยท2024๋…„ 2์›” 18์ผ
0

Memoirs

๋ชฉ๋ก ๋ณด๊ธฐ
3/7
post-thumbnail
post-custom-banner

post-mobism(ํฌ์ŠคํŠธ ๋ชจ๋น„์ฆ˜, ์ดํ•˜ ๋ชจ๋น„์ฆ˜)์€ mobithon API๋ฅผ ํ™œ์šฉํ•ด ๋งŒ๋“  ๊ธฐ๋ณธ CRUD ๊ธฐ๋Šฅ ๊ตฌํ˜„์— ์ถฉ์‹คํ•œ ๋ฏธ๋‹ˆ ํ† ์ด ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

  • ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ๊ธฐ๊ฐ„ : 2024.02.03 ~ 2024.02.17
  • ๋ฆฌํŒฉํ„ฐ๋ง ๊ธฐ๊ฐ„ : 2024.02.16 ~ 2024.02.17

์„ค ์—ฐํœด 3์ผ์„ ํฌํ•จํ•ด ์•ฝ 12์ผ๋™์•ˆ ์ง„ํ–‰ํ•œ ํ”„๋กœ์ ํŠธ post-mobism์˜ ๋ชฉํ‘œ๋Š” ์•„๋ž˜ ๋‘ ๊ฐ€์ง€์˜€์Šต๋‹ˆ๋‹ค.

  1. backend API ๋ถ„์„ ์ œ๋Œ€๋กœ ํ•ด๋ณด๊ธฐ
  2. Redux Toolkit(RTK)๋ฅผ ์‚ฌ์šฉํ•ด ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌํ•˜๊ธฐ

์ด์ „ ํ”„๋กœ์ ํŠธ Chap-Chap์—์„œ ๋ถ€์กฑํ–ˆ๋˜ ๋ถ€๋ถ„์€ ๋ฐฑ์—”๋“œ API์˜ ๋ถ„์„ ๋ถ€์กฑ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
๋ฐฑ์—”๋“œ API ๋ถ„์„์„ ์ œ๋Œ€๋กœ ํ•˜์ง€ ์•Š๊ณ  ๋“ค์–ด๊ฐ€ ๋‹น์‹œ mobithon API๋ฅผ ํ™œ์šฉํ•˜์ง€ ๋ชปํ–ˆ๊ณ 
ํ”„๋ก ํŠธ ๋‹จ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋กœ์ง ์ˆ˜์ •์„ ๊ต‰์žฅํžˆ ์ž์ฃผํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋ฒˆ์—๋Š” ๊ฐ™์ด API๋ฅผ ๋ถ„์„ํ•ด๋ณด๊ณ  ๋ถ„์„ํ•œ ๋‚ด์šฉ์„ ํ† ๋Œ€๋กœ ๋””์ž์ธ์„ ์ง„ํ–‰ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

API ๋ถ„์„์€ sequence diagram์„ ํ†ตํ•ด ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ฐ™์ด ์ง„ํ–‰ํ•œ ๋ถ€๋ถ„์€ gitHub์˜ readme ๋งˆํฌ๋‹ค์šด ํŒŒ์ผ์— ์ฒจ๋ถ€๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


์ฒ˜์Œ์— ํŽ˜์ด์ง€ ๋ณ„๋กœ ํ”„๋ก ํŠธ ๋‹จ์„ ๋‚˜๋ˆ„์–ด ์ด 5๋‹จ๊ณ„(ํด๋ผ์ด์–ธํŠธ-ํ”„๋ก ํŠธ(3ํŽ˜์ด์ง€)-๋ฐฑ์—”๋“œ)๋กœ ๋‚˜๋ˆ„์–ด ๋ถ„์„์„ ์ง„ํ–‰ํ–ˆ์œผ๋‚˜ ํ”„๋กœ์ ํŠธ์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์ดํŠธํ•˜๊ฒŒ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ณ  ์œ„์™€ ๊ฐ™์ด ๊ฐ„์†Œํ™”ํ•ด ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ ธ์Šต๋‹ˆ๋‹ค.

์ฒ˜์Œ์— ์ž‘์—… ์ˆœ์„œ๋ฅผ ์ž˜๋ชป ์งœ์„œ ์‚ด์ง ๋”œ๋ ˆ์ด๊ฐ€ ๋์Šต๋‹ˆ๋‹ค.
์„ค๊ณ„ โ†’ ์™€์ดํ”„๋ ˆ์ž„ โ†’ ๋””์ž์ธ โ†’ ๊ฐœ๋ฐœ ์ˆœ์œผ๋กœ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ž sequence diagram์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ  ๊ทธ๋ ค๋ณด๋Š” ์‹œ๊ฐ„์„ ๊ฐ–๊ธฐ๋กœ ํ–ˆ์œผ๋‚˜ ์„ค๊ณ„๋ฅผ ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋‹ˆ ๋””์ž์ธ์ด ์—†์ด ์ง„ํ–‰ํ•˜๊ธฐ๊ฐ€ ์• ๋งคํ•œ ๋ถ€๋ถ„์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋””์ž์ธ์— ๊ด€๋ จ๋œ ์–˜๊ธฐ๋ฅผ ์‚ฌ์ „์— ํ•˜์ง€ ์•Š์•„ ๋‹ค์‹œ ์™€์ด์–ดํ”„๋ ˆ์ž„ โ†’ ์„ค๊ณ„ โ†’ ๋””์ž์ธ โ†’ ๊ฐœ๋ฐœ ์ˆœ์œผ๋กœ ๊ฐ™์ด ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.



Designing... ๐ŸŽจ

post-mobism์€ post-modernism(ํฌ์ŠคํŠธ ๋ชจ๋”๋‹ˆ์ฆ˜)์—์„œ ์ฐฉ์•ˆํ•œ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

๋ณธ ํ”„๋กœ์ ํŠธ์˜ ์ฃผ ๊ธฐ๋Šฅ์ด post์™€ comment์˜ CRUD์ด๊ธฐ ๋•Œ๋ฌธ์— 'post'๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ๋„ฃ๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ”๋กœ post-modernism(ํฌ์ŠคํŠธ ๋ชจ๋”๋‹ˆ์ฆ˜)์ด ๋– ์˜ฌ๋ผ mobi์ฃผ์˜(mobi์‚ฌ์ƒ)์— ๋งž๊ฒŒ code๋ฅผ postํ•œ๋‹ค๋Š” ์ฃผ์ œ๋กœ ์ด๋ฆ„์„ ๋ถ™์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

post-mobism์€ mobi ์ƒˆ์‹น๋“ค์˜ ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์„œ๋กœ ๋ฆฌ๋ทฐํ•ด์ฃผ๋Š” ์‚ฌ์ดํŠธ์ž…๋‹ˆ๋‹ค.


| "Less is Bore"

์•„๋ž˜๋Š” ์™€์ด์–ดํ”„๋ ˆ์ž„์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค.
์˜ฌํ•ด์˜ ์ปฌ๋Ÿฌ์ธ peach fuzz(ํ”ผ์น˜ํผ์ฆˆ)๋ฅผ ๋ฒ ์ด์Šค ์ปฌ๋Ÿฌ๋กœ ์‚ฌ์šฉ, ํฌ์ธํŠธ ์ปฌ๋Ÿฌ๋Š” ์ด์™€ ์ž˜ ์–ด์šธ๋ฆฌ๋Š” ๋ฐ์€ ๋ ˆ๋“œ ์ปฌ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ–ผ๏ธ ์ฐธ๊ณ  ๋ธ”๋กœ๊ทธ



Developing... ๐Ÿ’ป


๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด...
์ด๋ฒˆ ํ† ์ด ํ”„๋กœ์ ํŠธ์—์„œ API ๋ถ„์„์€ ์ œ๋Œ€๋กœ ์ด๋ค„์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๐Ÿฅฒ

๋ถ„์„๊ณผ ์„ค๊ณ„ ๊ณผ์ •์—์„œ ์–ด๋–ค ์–˜๊ธฐ๋“ค์„ ํ–ˆ์–ด์•ผ ํ•˜๋Š”์ง€, ๋ถ„์„์„ ํ† ๋Œ€๋กœ ์–ด๋–ค ๋ถ€๋ถ„๋“ค์„ ์‚ฌ์ „์— ์˜๋…ผํ–ˆ์–ด์•ผ ๊ฐœ๋ฐœ์„ ๋” ํšจ์œจ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ์ œ๋Œ€๋กœ ๋ชจ๋ฅด๊ณ  ์ง„ํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ๋„ ๋งŽ์ด ํ—ค๋งธ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” mobism์„ ์ง„ํ–‰ํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ฒช์€ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ํ† ๋Œ€๋กœ ์•ž์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ง„ํ–‰ํ•˜๋ฉด ์ข‹์„์ง€ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๋” ์ƒ๊ฐํ•ด๋ณธ ๋’ค ์ž‘์„ฑํ•ด๋ดค์Šต๋‹ˆ๋‹ค.



โš™๏ธ basic-settings

ํ˜‘์—…์„ ์œ„ํ•ด ์‚ฌ์šฉ ์˜ˆ์ •์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ˜‘์—… ํˆด์„ ๊น”์•„์ค๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ์„ธํŒ…์€ ํ”„๋กœ์ ํŠธ ํด๋” ์ƒ์„ฑ ํ›„ API ๊ด€๋ จ ๋กœ์ง ์ž‘์„ฑ ์ „์— ์ง„ํ–‰ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์„ธํŒ…์ด ๊ถ๊ธˆํ•˜๋‹ค๋ฉด? ๐Ÿ‘‰ 55555-jyeon ๊ฒŒ์‹œ๊ธ€ ์ฐธ๊ณ ํ•˜๊ธฐ


๐Ÿ“Š API

๐Ÿ”Ž API ๋ถ„์„ ๋ฐ ์„ค๊ณ„

sequence diagram์œผ๋กœ API ๋ฌธ์„œ์™€ wire-frame์„ ํ† ๋Œ€๋กœ
๊ธฐ๋Šฅ์˜ ๊ตฌํ˜„ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ๋”ฐ์ ธ๋ด…๋‹ˆ๋‹ค.



โšก๏ธ thunder client๋กœ response.data ๋ฐ›์•„๋ณด๊ธฐ

BE ๊ฐœ๋ฐœ์ž๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ API ์•ˆ๋‚ด์„œ(?)๋ฅผ ํ† ๋Œ€๋กœ ์ฌ๋” ํด๋ผ์ด์–ธํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ด…๋‹ˆ๋‹ค.
ํ™•์ธ ๋ฐ ์‘๋‹ต์œผ๋กœ ์–ด๋–ค ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ผญ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ฌ๋” ํด๋ผ์ด์–ธํŠธ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋ณด๋ฉด์„œ ์–ด๋–ค ๊ฐ’์„ ํ•„์š”๋กœ ํ•˜๋Š”์ง€, ์–ด๋–ป๊ฒŒ ๋ณด๋‚ผ์ง€ ๋“ฑ์„ ํ™•์‹คํžˆ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ ์š”์ฒญ ์ง„ํ–‰ ์ค‘ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•œ ๊ฒฝ์šฐ, BE ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋น ๋ฅธ notice๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


sign-up & sign-in

get & post post data

`post comment`



๐Ÿ“ž response.data๋ฅผ ํ† ๋Œ€๋กœ type๊ณผ mockData ์ƒ์„ฑ

์œ„์—์„œ thunder client๋กœ ํ™•์ธํ•œ ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์™€ ๋ชฉ๋ฐ์ดํ„ฐ์™€ type์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

form type

export type SignUpUser = {
  userId: string
  password: string
  nickname: string
}

export type SignInUser = {
  userId: string
  password: string
  nickName: string
  profileUrl: string
  token?: string
}
  
export type User = {
  id: string
  nickName: string
  profileImg: string
}

post type

export type TPostsResponse = {
  data: Post[] | []
  pageNation?: Pagination
}

export type Post = {
  id: string
  data: { title: string; content: string }
  dataUser: { data: { nickName: string }; profile_url: string; userId: string }
  dataImage?: []
  createdAt: string
}

export type Pagination = {
  start: number
  end: number
  total: number
  set: number
  current: number
}

comment type

export type TCommentsResponse = {
  data: Comment[]
  pageNation?: Pagination
}

export type Comment = {
  id: string
  data: { content: string }
  dataUser: { data: { nickName: string }; profile_url: string; userId: string }
  createdAt: string
}


๐Ÿ› ๏ธ API ๋กœ์ง ๊ตฌํ˜„ํ•˜๊ธฐ

๋จผ์ € ๊ฒน์น˜๋Š” ๋กœ์ง์„ core.ts์— ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๊ฐ๊ฐ ๊ธฐ๋Šฅ๋ณ„๋กœ ํŒŒ์ผ์„ ๋‚˜๋ˆ  ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

features/core.ts

const token = localStorage.getItem(ACCESS_TOKEN)

export const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_BACKEND_URL,
  headers: {
    authorization: token ? `Bearer ${token}` : null,
  },
  params: {
    apiKey: import.meta.env.VITE_API_KEY,
    pair: import.meta.env.VITE_PAIR,
  },
  withCredentials: true,
})
// and others ...

features/user/auth.api.ts

const PATH = "/user"

export const AuthApi = {
	// sign-up
  	// sign-in
  	// sign-out
  	// refresh
}
  async SignIn(data: SignInType) {
    const res = await axiosInstance.post(PATH + "/sign-in", data)
    const userInfo = {
      userId: res.data.userId,
      nickName: res.data.info.nickName,
      profileUrl: res.data.info?.profileUrl,
    }
    localStorage.setItem("userInfo", JSON.stringify(userInfo))
    return res
  },
}
// and others ...

features/post/post.api.ts

const POST_PATH = "/data/post"

export const PostApi = {
	// getPost
  	// postPost
  	// editPost
  	// deletePost
}
  /**
   * @function getPost
   * @method GET
   * @params dataName: string
   * @queries parentId: string, page: number, limit: boolean
   */
  async getPosts(pageParam: number) {
    const res = await axiosInstance.get<TPostsResponse>(POST_PATH, {
      params: {
        page: pageParam,
      },
    })
    console.log("getPosts", res.data)
    return res.data
  },
 // and others ...


๐ŸŒ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” RTK๋กœ

๋ณธ ํ”„๋กœ์ ํŠธ๋Š” ์ „์—ญ ์ƒํƒœ๋Š” ๋น„๊ต์  ์ž์ฃผ ์‚ฌ์šฉํ–ˆ๋˜ recoil, ๋น„์Šทํ•œ jotai ๋“ฑ์€ ๋’ค๋กœ ํ•˜๊ณ  ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณด๋Š” Redux Toolkit์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ธ๊ธฐ ์žˆ๋Š” ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ด์ „๋ถ€ํ„ฐ ํ˜„์žฌ๊นŒ์ง€(ํ˜„์žฌ๋Š” ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด์„œ ์ฃผ๋กœ ์‚ฌ์šฉ) ์˜ค๋žœ ๊ธฐ๊ฐ„๋™์•ˆ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ €ํฌ๋„ ํ•œ ๋ฒˆ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ์„ธํŒ… ๋ฐ ์‚ฌ์šฉ๋ฒ•์€ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.


| redux์˜ ๊ธฐ๋ณธ ๊ฐœ๋…

redux๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ํ•˜๋‚˜์˜ JavaScript ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌํ•˜๊ณ , ์•ก์…˜(Action)์„ ํ†ตํ•ด ์ƒํƒœ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. store๋Š” ์ด ์ƒํƒœ๋ฅผ ๋ณด์œ ํ•˜๋ฉฐ, ์•ก์…˜์„ ๋””์ŠคํŒจ์น˜(dispatch)ํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

Redux Toolkit์—์„œ createSlice ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ๋“€์„œ(reducer)๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ configureStore ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด store๋กœ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. store๋Š” ์ด๋ ‡๊ฒŒ ์ƒ์„ฑ๋œ ๋ฆฌ๋“€์„œ์™€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜์—ฌ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

redux-toolkit์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ์„ค๋ช…๊ณผ ์„ธํŒ… ๊ณผ์ •์€ redux-toolkit ๊ฒŒ์‹œ๊ธ€์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.



โœจ OUTCOME

sign in & up

sign-up

sign-in


post CRUD

create-post

read-post : pagination

update-post

delete-post



figma ๐Ÿ‘‰ ๋” ์ž์„ธํ•œ ๋””์ž์ธ์€ ํ”ผ๊ทธ๋งˆ ๋งํฌ์—์„œ ํ™•์ธํ•˜๊ธฐ
vercel ๐Ÿ‘‰ ๊ตฌํ˜„๋œ ์‚ฌ์ดํŠธ์—์„œ ํŽธํ•˜๊ฒŒ ํ™•์ธํ•˜๊ธฐ
github ๐Ÿ‘‰ ์ƒ์„ธํ•œ ์ฝ”๋“œ ํ™•์ธํ•˜๊ธฐ

๋ณธ ์‚ฌ์ดํŠธ๋Š” 1920px ๊ธฐ์ค€์œผ๋กœ ๋””์ž์ธ๋˜์—ˆ์œผ๋ฉฐ ๋ฐ˜์‘ํ˜•์ด ์ ์šฉ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Memoirs ๐Ÿ’ฌ


๐Ÿชจ ๋ถ„์„๊ณผ ์„ค๊ณ„์— ๋Œ€ํ•ด ๋ฌด์ง€ํ–ˆ๋˜...

API ๋ถ„์„๊ณผ ์„ค๊ณ„๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด sequence diagram์— ๋Œ€ํ•ด ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์–˜๊ธฐ๋กœ๋งŒ ๋‚˜๋ˆด๋˜ ๋ถ€๋ถ„๋“ค์„ ํ‘œ๋กœ ๊ทธ๋ ค๋ณด๋ฉด์„œ ์ด์ „๋ณด๋‹ค๋Š” api์˜ ํ๋ฆ„์— ๋Œ€ํ•ด์„œ ์ž˜ ํŒŒ์•…์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ฐ™์ด ์ง„ํ–‰ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์ด๋‹ค ๋ณด๋‹ˆ ๊ฐ™์ด ์–˜๊ธฐ๋ฅผ ํ•˜๋ฉด์„œ ๋” ๋งŽ์€ ๊ฒƒ์„ ๊ณต์œ ํ–ˆ์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด ๋ถ€๋ถ„์„ ๊ฐ„๊ณผํ–ˆ์Šต๋‹ˆ๋‹ค.

์™€์ด์–ดํ”„๋ ˆ์ž„์„ ๋ณด๋ฉด์„œ "์ด ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•  ๊ฒƒ์ธ๊ฐ€?"์— ๋Œ€ํ•ด ์–˜๊ธฐ๋ฅผ ๋‚˜๋ˆด์–ด์•ผ ํ•œ๋‹ค๋Š” ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ณ  ์ด ๋ถ€๋ถ„์„ ์ค‘์ ์ ์œผ๋กœ ๋Œ€ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ ์™ธ์—๋„ ์–ด๋–ค ๋ฐ์ดํ„ฐ์˜ ๊ฐ’๋“ค์ด ์‘๋‹ต์œผ๋กœ ์˜ฌ ๊ฒƒ ๊ฐ™์€์ง€ thunder client๋กœ ์ฐ์–ด๋ดค์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์— ๋งž๊ฒŒ type์„ ์ •์˜ํ•˜๊ณ , ๋ชฉ๋ฐ์ดํ„ฐ๋„ ์ „๋‹ฌ๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ์™€ ๋™์ผํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ์–ด์•ผ api ์—ฐ๊ฒฐ ์‹œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•œ ๋ถ€๋ถ„์„ ๊ฑด๋“œ๋ฆด ์ผ์ด ์—†๋Š”๋ฐ...

response.data์˜ ํ˜•ํƒœ๋ฅผ ์ž„์˜๋กœ ์ƒ์ƒํ•ด ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด ํ™”๊ทผ์ด์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿšฎ ๋ฏธํกํ•œ ์ค€๋น„๋กœ ์ธํ•œ over engineering...

thunder client๋กœ api ํ˜ธ์ถœ ์‹œ ์–ด๋–ค ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋Š”์ง€ ํ™•์ธ์„ ํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋ชฉ๋ฐ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๊ฐœ๋ฐœ ์ค‘ ๋กœ์ง์„ ๋’ค์ง‘์–ด ์—Ž๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ดˆ๋ž˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

refactor๋ฅผ ์ง„ํ–‰ํ•œ PR์„ ์‚ดํŽด๋ณด๋ฉด ์ •๋ง ๋งŽ์€ ํŒŒ์ผ๋“ค์— ๋ณ€ํ™”๊ฐ€ ์žˆ์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹จ์ˆœ ๊ธฐ๋Šฅ์ ์ธ ๋ถ€๋ถ„์˜ ํ•˜๋“œ ์ฝ”๋”ฉ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ API๋ฅผ ์ •๋ง ์ž˜ ๋ถ„์„ํ–ˆ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์„ ๋ฐฉ๋ฒ•์œผ๋กœ ์–ด๋ ต๊ฒŒ(?) ๊ฐœ๋ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ main-page์˜ post๋“ค์„ pagination์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ํŒŒ์ผ์—์„œ ๊ณผ๋„ํ•œ ์—”์ง€๋‹ˆ์–ด๋ง์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. BE์—์„œ pagination๊ณผ ๊ด€๋ จ๋œ api๋ฅผ ๋ณด๋‚ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํ™œ์šฉํ–ˆ๋‹ค๋ฉด ์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ณ  ์‰ฝ๊ฒŒ ๋กœ์ง์„ ์งค ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


โ›”๏ธ over engineering์˜ ์›ํ‰

  • ์ฒ˜์Œ ๋งŒ๋“  mockData์˜ ํ˜•ํƒœ
{
	id: "",
    title : "",
    content : "",
    Post_img: ["", ""],
    Comments: [{
    	id: "",
      	content: "",
      	User: {
        	id: "",
          	nickName: "",
          	profileImg: "",
        },
      	createdAt: ""
    }],
    createdAt: ""
}
  • BE๋กœ๋ถ€ํ„ฐ ์‘๋‹ต๋ฐ›์€ data์˜ ํ˜•ํƒœ
// method : GET   (url : data/post)
{
	data: {
      title: "",
      content: ""
    },
    id: "",
    createdAt: "",
    dataImage: [{"url": ""},{"url": ""}],
    dataUser: {
      data: { nickName: ""},
      userId: "",
      profile_url: ""
    }
  },
}
  • TS์˜ type์ •์˜

// before

export type Post = {
  id: string
  title: string
  content: string
  User: User
  Post_img?: string[]
  Comments?: Comment[]
    createdAt: string
}

export type Comment = {
  id: string
  content: string
  User: User
  createdAt: string
}



//after

export type Post = {
  id: string
  data: { title: string; content: string }
  dataUser: { data: { nickName: string }; profile_url: string; userId: string }
  dataImage?: []
  createdAt: string
}

export type Comment = {
  id: string
  data: { content: string }
  dataUser: { data: { nickName: string }; profile_url: string; userId: string }
  createdAt: string
}

export type Pagination = {
  start: number
  end: number
  total: number
  set: number
  current: number
}

๋ฐ์ดํ„ฐ์˜ ๊ตฌ์กฐ/ํ˜•ํƒœ์™€ type์„ BE์—์„œ ์ „๋‹ฌํ•ด์ฃผ๋Š” ๊ฒƒ๊ณผ ๋„ˆ๋ฌด ์ฐจ์ด๊ฐ€ ์ƒ๊ฒจ ์ฝ”๋“œ์˜ ๊ฑฐ์˜ ์ „๋ถ€๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ๋์Šต๋‹ˆ๋‹ค. ์œ„ ๊ธฐ๋ณธ type์œผ๋กœ ๋ณ€๊ฒฝ ํ›„ ์‚ฌ์šฉํ•œ type์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

export type TPostsResponse = {
  data: Post[] | []
  pageNation?: Pagination
}

export type TCommentsResponse = {
  data: Comment[]
  pageNation?: Pagination
}

๐Ÿ†– ๋Œ€ํ‘œ์ ์ธ ์‚ฌ๋ก€๋ฅผ ๊ผฝ์ž๋ฉด...

| getPost

main์—์„œ pagination์œผ๋กœ ๊ฐ’์„ props๋กœ ์ „๋‹ฌํ•ด์ค„ ๋•Œ์˜ ๊ฐ’์— ๋ณ€ํ™”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

MockData๋กœ ์ง„ํ–‰ํ•  ๋•Œ์—๋Š” ๊ฐ’์„ ํ•˜๋‚˜ ํ•˜๋‚˜ ์„ ์–ธํ•ด ์ „๋‹ฌํ•ด์ฃผ์—ˆ๋˜ ๋ฐ˜๋ฉด API๋กœ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๋ณ„๋„๋กœ ์„ ์–ธํ•œ ๊ฐ’๋“ค ๋Œ€์‹  postList(data)์—์„œ pagination ๊ฐ’์—์„œ ๋ฐ”๋กœ ์ „๋‹ฌ๋งŒ ํ•ด์ค„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

// develop with MockData
<Pagination 
    listLength={listLength} 
    currentPage={currentPage} 
    perPage={perPage} 
    onPageChange={onPageChange} 
/>
      
// develop with API
<Pagination
    startPage={postList.pageNation?.start}
    endPage={postList.pageNation?.end}
    currentPage={postList.pageNation?.current}
    totalPage={postList.pageNation?.total}
    onPageChange={onPageChange}
/>

์•„๋ž˜๋Š” pagination.tsx์—์„œ์˜ ๋ณ€ํ™”์ž…๋‹ˆ๋‹ค.

// develop with MockData
const NumberButtons: number[] = Array.from({length: Math.min(pagesPerGroup, totalPage)}, (_, index) => startPage + index).filter(pageNumber => pageNumber <= totalPage);

// develop with API
const NumberButtons: number[] = Array.from({ length: endPage - startPage + 1 }, (_, index) => startPage + index)

pagination ๊ฐ’์„ BE์—์„œ ๋”ฐ๋กœ ์ „๋‹ฌํ•ด์ฃผ๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ œ๊ฐ€ ํ•˜๋‚˜ํ•˜๋‚˜ ์„ ์–ธํ•  ํ•„์š”๊ฐ€ ์—†๋Š” ๊ฐ’๋“ค์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— refactor ๊ณผ์ •์—์„œ type ๋ณ€๊ฒฝ ํ›„ pageination ๊ฐ’์˜ ์‚ฌ์šฉ์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๊ณ  ๊น”๋”ํ•ด์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


| postPost (createPost)

์ƒˆ๋กœ์šด ๊ฒŒ์‹œ๊ธ€์„ POSTํ•  ๋•Œ ์ด๋ฏธ์ง€์˜ ์ „์†ก ์œ„์น˜ ๋•Œ๋ฌธ์— ๊ฑฐ์˜ ํ•˜๋ฃจ๋ฅผ ๊ณ ๊ตฐ๋ถ„ํˆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
formData์—์„œ ์ด๋ฏธ์ง€ ๋ฐฐ์—ด๋„ ์ž˜๋ชป๋œ ์œ„์น˜๋กœ ๋ณด๋‚ด๊ณ  ์žˆ์–ด post๋œ ๊ฒŒ์‹œ๊ธ€์˜ ์ด๋ฏธ์ง€๊ฐ€ ํ™”๋ฉด์— render ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. mockData์˜ ์œ„์น˜๋กœ ๋ณด๋‚ด๋˜ ๊ฒฝ๋กœ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์•„ ์ž˜๋ชป๋œ ๊ฒฝ๋กœ๋กœ ๋ณด๋‚ด๊ณ  ์žˆ๋‹ค๋Š” 400๋ฒˆ๋Œ€ ์—๋Ÿฌ๋ฅผ ๋งž๋‹ฅ๋œจ๋ ธ๋˜ ๊ฑฐ์˜€์Šต๋‹ˆ๋‹ค.

๊ฒฐ์ •์ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ๋๋˜ ์ฝ”๋“œ๋Š” ์•„๋ž˜์˜€์Šต๋‹ˆ๋‹ค.

        <PostDetailContent
          postId={postDetail.data.id}
          title={postDetail.data.data.title}
          content={postDetail.data.data.content}
          postImages={postDetail.data.dataImage} โœ”๏ธ
          nickName={postDetail.data.dataUser.data.nickName}
          profileImage={postDetail.data.dataUser.profile_url} 
          weekday={postDetail.data.createdAt}
          isEditMode={isEditMode}
          setIsEditMode={setIsEditMode}
        />

props๋กœ postImages๋ฅผ ์ „๋‹ฌํ•ด์ฃผ๋Š”๋ฐ ์ž˜๋ชป๋œ ์œ„์น˜์— ์ „์†ก์ด ๋˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— post๋œ ๊ฒŒ์‹œ๊ธ€์˜ ์ด๋ฏธ์ง€๊ฐ€ get์œผ๋กœ ๊ฐ€์ ธ์™”์„ ๋•Œ ์ฝํžˆ์ง€ ์•Š๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜๋„ post๋ฅผ ํ•˜๋ฉด์„œ formData์˜ ์œ ์šฉํ•œ ์‚ฌ์šฉ๋ฒ• ํ•˜๋‚˜๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ๋˜๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

// upload image : preview
  const onUploadImage = (e: React.ChangeEvent<HTMLInputElement>) => {
    const imageLists = e.target.files as FileList
    let imageUrlLists: string[] = [...showImages]

    for (let i = 0; i < imageLists.length; i++) {
      const currentImageUrl = URL.createObjectURL(imageLists[i])
      imageUrlLists.unshift(currentImageUrl)
    }
    if (imageUrlLists.length > 5) {
      imageUrlLists = imageUrlLists.slice(0, 5)
      alert("ํ•œ ๋ฒˆ์— ์ด๋ฏธ์ง€๋ฅผ 5๊ฐœ ์ด์ƒ ์ถ”๊ฐ€ํ•˜์‹ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    }
    setHasImage(true)
    setShowImages([...imageUrlLists])
  }
// send new post's data to BE 
//before
const { handleCreatePost } = usePostActions()
const currentUser = useAppSelector(state => state.user[0])

const onSubmitCreatePost = async (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault()

   const formData = new FormData()
    formData.append("title", (e.currentTarget.elements.namedItem("title") as HTMLInputElement)?.value || "")
    formData.append("content", (e.currentTarget.elements.namedItem("content") as HTMLInputElement)?.value || "")
    for (let i = 0; i < showImages.length; i++) {
      formData.append("images[]", showImages[i])
    }

  try {
    await handleCreatePost(formData)
  } catch (error) {
    console.error("๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ:", error)
  }
}

before

- ์ง์ ‘ ํผ ๋ฐ์ดํ„ฐ์— ๊ฐ ํ•„๋“œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ถ”๊ฐ€
- ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜๋ณต๋ฌธ์„ ํ†ตํ•ด ์ˆ˜๋™์œผ๋กœ ํผ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€

after

- FormData ์ƒ์„ฑ์ž์— FormElement๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ž๋™์œผ๋กœ ํผ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์„ฑ
- ์ด๋ฏธ์ง€๋ฅผ ์ž๋™์œผ๋กœ ํผ ๋ฐ์ดํ„ฐ์— ์ถ”๊ฐ€
// after
const onSubmitCreatePost = async (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault()
  const formData = new FormData(e.currentTarget)
  try {
    await handleCreatePost(formData)
  } catch (error) {
    alert("๊ฒŒ์‹œ๊ธ€์„ ๋“ฑ๋กํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค! ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.")
    console.error("๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ:", error)
  }
}

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ฒ˜์Œ ์•Œ๊ฒŒ ๋œ ๋ถ€๋ถ„์œผ๋กœ FormElement๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ํ•ด๋‹น ํผ์˜ ๋ชจ๋“  field๊ฐ€ FormData์— ์ž๋™์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. form์— ์žˆ๋Š” ๋ชจ๋“  ์ž…๋ ฅ field, file ๋“ฑ์„ FormData์— ํฌํ•จํ•˜์—ฌ ์ „์†กํ•˜๊ณ ์ž ํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
๊ฐœ๋ณ„์ ์œผ๋กœ field๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ ์ž ํ•  ๋•Œ๋Š” append ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์œ ์šฉํ•˜๊ฒ ์ง€๋งŒ, ๋ชจ๋“  field๋ฅผ ํ•œ๊บผ๋ฒˆ์— ์ถ”๊ฐ€ํ•  ๋•Œ์—๋Š” ์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒ˜์Œ์— MMM ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ ํ•˜๋‚˜ ํ•˜๋‚˜ append๋กœ ์ „์†กํ–ˆ์—ˆ๋Š”๋ฐ... ๋‚˜์ค‘์— ๋ฆฌํŒฉํ„ฐ๋งํ•  ๋•Œ ์ด ๋ถ€๋ถ„์„ ๊ผญ ํ•ด์ฃผ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. JavaScript์—์„œ ํ•œ ๋ฒˆ์— ๋ณด๋‚ผ ๋•Œ์—๋Š” type๋งŒ ์ œ๊ฑฐํ•ด์ฃผ๋ฉด ๋™์ผํ•˜๊ฒŒ ์ ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

const onSubmitCreatePost = async (e) => {
  e.preventDefault();
  const formData = new FormData(e.currentTarget);
  try {
    await handleCreatePost(formData);
  } catch (error) {
    alert("๊ฒŒ์‹œ๊ธ€์„ ๋“ฑ๋กํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค! ์ž ์‹œ ํ›„ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.");
  }
};

API๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›๋Š” data์˜ ํ˜•ํƒœ๋ฅผ ์ œ๋Œ€๋กœ ํ™•์ธํ•˜์ง€ ์•Š์Œ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์„ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋Š๋‚€ ํ”„๋กœ์ ํŠธ์˜€๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์— mockData์™€ type๋“ค์„ BE๊ฐ€ ์ „๋‹ฌํ•ด์ฃผ๋Š” data์˜ ํ˜•ํƒœ์™€ ๋‹ค๋ฅด๊ฒŒ ๊ตฌ์„ฑ์„ ํ•˜๊ณ  ์ง„ํ–‰ํ•˜๋‹ค๋ณด๋‹ˆ ํผ๋ธ”๋ฆฌ์‹ฑ ๋‹จ๊ณ„์—์„œ ๋””์ž์ธ๊ณผ ๋กœ์ง์„ ๊ฐœ๋ฐœํ–ˆ๋Š”๋ฐ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ๋‹ค์‹œ ๋กœ์ง์„ ์ˆ˜์ •ํ•˜๊ณ  ์žˆ๋Š” ์ƒํ™ฉ์ด ๋ฒŒ์–ด์กŒ์Šต๋‹ˆ๋‹ค.

API ๋ถ„์„๊ณผ ์„ค๊ณ„๋ฅผ ์ œ๋Œ€๋กœ ํ–ˆ๋‹ค๋ฉด ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ๋Š” ๋ชฉ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ–ˆ๋˜ ๋ถ€๋ถ„๋งŒ API๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” ๋“ฑ์˜ ์ผ๋งŒ ์ง„ํ–‰ํ•˜๋ฉด ๋ผ์„œ ๊ธˆ๋ฐฉ ํ”„๋กœ์ ํŠธ๋ฅผ ๋๋‚ผ ์ˆ˜ ์žˆ์—ˆ์„ํ…๋ฐ...

๊ฐœ๋ฐœ ์‹œ๊ฐ„๋งŒ ๋”ฐ์ ธ๋ณธ๋‹ค๋ฉด ์ •๋ง ์‹œ๊ฐ„์ด ์•„๊นŒ์šด ํ”„๋กœ์ ํŠธ์˜€์ง€๋งŒ API ๋ถ„์„๊ณผ ์„ค๊ณ„์— ๋Œ€ํ•œ ์ค‘์š”์„ฑ์„ ๊ทธ ์–ด๋Š๋•Œ๋ณด๋‹ค ํ™•์‹คํžˆ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ์•ž์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ง„ํ–‰ํ•ด์•ผ ํ• ์ง€ ํ™•์‹คํžˆ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“† over engineering์œผ๋กœ ์ธํ•œ ETA delay...

ETA๊ฐ€ ๊ณ„์† ๋’ค๋กœ ๋ฐ€๋ฆฐ ๋ฐ์—๋Š” ํฌ๊ฒŒ 3๊ฐ€์ง€์˜ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค :

  1. ํ•™์Šต์„ ๋ณ‘ํ–‰ํ•œ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„
  2. ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณด๋Š” Redux ToolKit
  3. API ๋ถ„์„ ๋ฏธํก์œผ๋กœ ์ธํ•œ over engineering

๏นข ์„ค ์—ฐํœด๊ฐ€ ํฌํ•จ๋œ ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„ (3์ผ)


๋จผ์ € API ์„ค๊ณ„์™€ ๋ถ„์„ ๊ทธ๋ฆฌ๊ณ  auth ๋กœ์ง์— ๋Œ€ํ•œ ๊ณต๋ถ€๊ฐ€ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„ ์•ˆ์— ํฌํ•จ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
์ด์ „ ํ”„๋กœ์ ํŠธ์—์„œ pain-point๋ฅผ ๋Š๊ผˆ๋˜ ๋ถ€๋ถ„๋“ค์— ๋Œ€ํ•œ ๊ณต๋ถ€๋ฅผ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋ณ‘ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. sequence-diagram์ด๋ผ๋Š” ๊ฒƒ๋„ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ ๋˜์–ด ๋งŒ๋“ค์–ด ๋ณด๊ณ  auth ๋กœ์ง์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๋Š” ๊ธฐ๊ฐ„์ด ํฌํ•จ๋˜์–ด ์žˆ์–ด ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ์— ๋น„ํ•ด ๊ธธ๊ฒŒ ETA๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

post-mobism์—์„œ ์ƒˆ๋กญ๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด 2๊ฐœ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
Redux Toolkit์œผ๋กœ ์ „์—ญ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ ReactHookForm์˜ controller + Zod
Redux Toolkit์— ๋Œ€ํ•ด ํ•™์›์„ ๋‹ค๋‹ˆ๋ฉด์„œ ๊ฐ„๋‹จํžˆ ๋ฐฐ์šด ์ ์€ ์žˆ์ง€๋งŒ ์ œ๊ฐ€ ์ง์ ‘ ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉํ•ด๋ณด๋Š” ๊ฒƒ์€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  TS์™€์˜ ํ˜ธํ™˜์„ฑ์ด ์ข‹์ง€ ์•Š์•„ type๊ณผ ๊ด€๋ จ๋œ ์—๋Ÿฌ๋„ ์ •๋ง ๋งŽ์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

API ๋ถ„์„ ๋ฏธํก์œผ๋กœ ์ธํ•œ over engineering์œผ๋กœ ์ธํ•ด ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„๋„ ์˜ˆ์ƒ๋ณด๋‹ค 2๋ฐฐ ์ •๋„ ๊ธธ์–ด์กŒ๊ณ 
์„ค ์—ฐํœด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ธฐ๊ฐ„์—๋Š” ํ‰์†Œ๋ณด๋‹ค ์ ์€ ์‹œ๊ฐ„์„ ํˆฌ์žํ•ด ์กฐ๊ธˆ ๊ธธ์–ด์ง€๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ’ช๐Ÿป ์ „๋ฐ˜์ ์œผ๋กœ ์•„์‰ฌ์› ๋˜ ์ ๊ณผ ๋‹ค์ง

" post-mobism" ํ”„๋กœ์ ํŠธ๋Š” API ๋ถ„์„๊ณผ ์„ค๊ณ„๊ฐ€ ์–ผ๋งˆ๋‚˜ ์ค‘์š”ํ•œ ๊ณผ์ •์ธ์ง€, ์ด ๋ถ€๋ถ„์ด ์ž˜๋ชป ๋˜์—ˆ์„ ๋•Œ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„์— ์–ด๋– ํ•œ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. ์‹œ๊ฐ„์„ ๊ณผํ•˜๊ฒŒ ํˆฌ์žํ•œ ๋งŒํผ sequence diagram๋„ ๊ทธ๋ ค๋ณด๋ฉด์„œ ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž์™€ ์–ด๋–ค ์–˜๊ธฐ๋“ค์„ ๋‚˜๋ˆ ์•ผ ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ๋”์šฑ ํ™•์‹คํžˆ response.data์˜ ํ˜•ํƒœ๋ฅผ ํŒŒ์•…ํ•ด ํผ๋ธ”๋ฆฌ์‹ฑ์—์„œ ๊ฐœ๋ฐœํ•œ ์ฝ”๋“œ๋ฅผ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ๊ณผ๋„ํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ผ์ด ์—†๊ฒŒ ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค...!

์ด ์™ธ์—๋„ ์ •๋ง ๋งŽ์€ ์‹œ๊ฐ„์„ ๊ฐˆ์•„ ๋„ฃ์œผ๋ฉด์„œ ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์„ ์ด์ „๋ณด๋‹ค ์ž˜ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
thunder client์™€ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ network, application์˜ localStorage์™€ cookie ํƒญ์„ ํ†ตํ•ด response๋ฅผ ํ™•์ธํ•˜๋Š” ์ผ๋“ค์ด ํŠนํžˆ ๊ทธ๋Ÿฐ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ฒซ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„ console ์ฐฝ์œผ๋กœ ํ™•์ธ์„ ํ–ˆ๊ฑฐ๋“ ์š”...๐Ÿ‘€
์™œ ์•ˆ ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด network, application์„ ์ˆ˜์‹œ๋กœ ๋“ค์–ด๊ฐ”๋”๋‹ˆ ์ด์ œ์•ผ ๋น„๋กœ์†Œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์— ์ต์ˆ™ํ•ด์กŒ์Šต๋‹ˆ๋‹ค. ๐Ÿฅฒ

profile
๐Ÿฅž Stack of Thoughts
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€