Skip to content

[12팀 금정민] Chapter 2-3. 관심사 분리와 폴더구조#48

Open
KumJungMin wants to merge 74 commits intohanghae-plus:mainfrom
KumJungMin:main
Open

[12팀 금정민] Chapter 2-3. 관심사 분리와 폴더구조#48
KumJungMin wants to merge 74 commits intohanghae-plus:mainfrom
KumJungMin:main

Conversation

@KumJungMin
Copy link

@KumJungMin KumJungMin commented May 1, 2025

과제 체크포인트

기본과제

목표 : 전역상태관리를 이용한 적절한 분리와 계층에 대한 이해를 통한 FSD 폴더 구조 적용하기

  • 전역상태관리를 사용해서 상태를 분리하고 관리하는 방법에 대한 이해
  • Context API, Jotai, Zustand 등 상태관리 라이브러리 사용하기
  • FSD(Feature-Sliced Design)에 대한 이해
  • FSD를 통한 관심사의 분리에 대한 이해
  • 단일책임과 역할이란 무엇인가?
  • 관심사를 하나만 가지고 있는가?
  • 어디에 무엇을 넣어야 하는가?

체크포인트

  • 전역상태관리를 사용해서 상태를 분리하고 관리했나요?
  • Props Drilling을 최소화했나요?
  • shared 공통 컴포넌트를 분리했나요?
  • shared 공통 로직을 분리했나요?
  • entities를 중심으로 type을 정의하고 model을 분리했나요?
  • entities를 중심으로 ui를 분리했나요?
  • entities를 중심으로 api를 분리했나요?
  • feature를 중심으로 사용자행동(이벤트 처리)를 분리했나요?
  • feature를 중심으로 ui를 분리했나요?
  • feature를 중심으로 api를 분리했나요?
  • widget을 중심으로 데이터를 재사용가능한 형태로 분리했나요?

심화과제

목표: 서버상태관리 도구인 TanstackQuery를 이용하여 비동기코드를 선언적인 함수형 프로그래밍으로 작성하기

  • TanstackQuery의 사용법에 대한 이해
  • TanstackQuery를 이용한 비동기 코드 작성에 대한 이해
  • 비동기 코드를 선언적인 함수형 프로그래밍으로 작성하는 방법에 대한 이해

체크포인트

  • 모든 API 호출이 TanStack Query의 useQuery와 useMutation으로 대체되었는가?
  • 쿼리 키가 적절히 설정되었는가?
  • fetch와 useState가 아닌 선언적인 함수형 프로그래밍이 적절히 적용되었는가?
  • 캐싱과 리프레시 전략이 올바르게 구현되었는가?

과제 셀프회고

작성 중...ing

KumJungMin added 28 commits May 1, 2025 20:04
@KumJungMin
Copy link
Author

KumJungMin commented May 1, 2025

✍️ 셀프 회고

🔄 리팩토링 방향에 대한 고민

  • 처음 코드를 분리할 때는 모달과 같은 덩어리 단위부터 별도 컴포넌트로 나누었습니다. 덕분에 전체 코드의 볼륨이 줄어든 듯한 느낌을 받아 작업이 수월해 보였지만,
    되려 관심사 분리의 기준이 흐려지는 문제가 발생했습니다.

  • 초기에 큰 단위를 분리하기보다는, 가장 작은 단위인 entity → api부터 출발했으면 더 체계적인 구조가 되었을 것이라 느꼈습니다.

  • 저는 리팩토링을 할 때 무의식적으로 '컴포넌트부터 분리'하는 습관이 있다는 것을 이번 기회에 자각하게 되었습니다.

  • 이번 주제에서는 '관심사 분리'의 개념을 중심에 두고 리팩토링을 시도해 보았으며, 실제 업무에도 적용해 보았습니다.

  • 처음부터 폴더를 나눈 후 코드를 작성하면, 각 모듈 간 인터페이스를 맞추기 위해 더 많은 시간이 소요되었습니다. 반면, 기존 코드에서 중복 제거 → 로직 병합/분리 → 최종 분리 흐름으로 접근했을 때 시간 대비 효율이 훨씬 좋았습니다.

🔍 작업하며 느낀 점

  • entity → page로 올라갈수록 마치 피라미드처럼 상위 계층일수록 코드량이 줄어든다는 구조적 특성을 체감했습니다.
  • 하지만 실제 작업에서는 widget 단위에서 feature들을 조합하다 보니 widget/model이 설명해주신 개념과 맞지 않다는 걸 느꼈습니다ㅜ(코드 분리를 잘못했다는 걸 뒤늦게 깨달아버렸습니다...ㅜ)
  • 처음이라 feature 폴더를 데이터 단위 기준으로 나눴는데, 그 결과 폴더 구조가 feature 개념과 맞지 않게 흘러갔다는 아쉬움이 있습니다.

❓궁금한 점

  • .tsx 파일에서 로직을 분리할 때, useXXXLogic.ts처럼 로직 전용 훅을 만드는 방식이 일반적인가요?
  • 저는 처음에 store, state, API 호출 등을 컴포넌트 내부에 조합해서 썼지만,
  • 파일 간 이동이 너무 많아져서 결국 로직 전용 훅을 만들어 model/usePostTableLogic.ts처럼 분리했습니다.
    이 방식은 괜찮은 접근인지, 현업에서는 어떻게 분리하고 있는지 궁금합니다 :)

Copy link

@suinkimme suinkimme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코드가 정말 잘 정리되어 있다는 인상을 받았습니다. 읽는 동안 흐름이 끊기는 부분도 없었고, 많은 걸 배울 수 있었습니다. 특히 useXXXLogic처럼 훅에 붙인 네이밍이 개인적으로 매우 인상 깊고 괜찮다고 느꼈습니다.

Comment on lines +13 to +81
export const usePostTableLogic = () => {
const { tag: selectedTag, search, set: setFilter } = usePostTableStore()

const { posts, setSelectedPost, selectedPost, setPosts, deletePost: removeFromStore } = usePostStore()
const { setComments } = useCommentStore()
const { setSelectedUser } = useUserStore()

const [userId, setUserId] = useState<string | null>(null)

const { data: user } = useUserQuery(userId)
const { data: comments } = useCommentsQuery(selectedPost?.id)

const { posts: fetchedPosts, isLoading } = usePostsData()
const { mutate: deletePostMutate } = useDeletePost()
const { toggle } = useDialogStore()

useEffect(() => {
if (!isLoading) setPosts(fetchedPosts)
}, [isLoading])

useEffect(() => {
if (comments?.comments && selectedPost?.id) {
setComments(selectedPost.id, comments.comments)
}
}, [comments, selectedPost?.id])

useEffect(() => {
if (user) {
setSelectedUser(user)
toggle("userDetail", true)
}
}, [user])

const handleTagClick = (tag: string) => {
setFilter({ tag })
}

const handleOpenDetail = (post) => {
setSelectedPost(post)
toggle("postDetail", true)
}

const handleOpenEdit = (post) => {
setSelectedPost(post)
toggle("editPost", true)
}

const handleDeletePost = (post) => {
deletePostMutate(post.id)
removeFromStore(post)
}

const handleOpenUser = (user) => {
setUserId(user.id)
}

return {
search,
posts,
isLoading,
selectedTag,

handleOpenUser,
handleTagClick,
handleOpenDetail,
handleOpenEdit,
handleDeletePost,
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여러 스토어를 하나의 훅에서 접근하는 방식은, 과제를 진행할 때는 잘못된 방식이라고 오해해서 피하려 했었습니다. 그런데 지금 보니 오히려 이 방식이 더 자연스럽고 효율적인 것 같네요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants