Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

컴포넌트에서 책임 분리: id만 넘기기 vs 데이터 직접 전달하기#175

gronxb started this conversation inA vs B
Discussion options

비즈니스 데이터를 사용하는 컴포넌트를 만들 때, 데이터 패칭의 책임을 어디에 둘지 고민이 됩니다.
특히, 비즈니스 데이터와 관련된 컴포넌트를 만들 때 다음 두 가지 패턴이 있는데요.

1안: 부모로부터 id만을 전달받고, Global Store에서 꺼내오기

functionPost({ postId}:{postId:string}){const{ username, comment, likeCount}=usePost(postId);return(<div><h1>{username}</h1><p>{comment}</p><p>{likeCount}</p></div>);}

2안: 부모로부터 데이터를 직접 전달받기

functionPost({ username, comment, likeCount}:{username:string;comment:string;likeCount:number}){return(<div><h1>{username}</h1><p>{comment}</p><p>{likeCount}</p></div>);}

두개의 컴포넌트는 같은 컴포넌트이지만, 다른 Prop을 가지고 있습니다.

저는 GraphQL을 사용해 온 경험이 있어서인지 서버에서 오는 데이터와 관련된 컴포넌트라면 1안을 선호하는 편입니다. 하지만 2안을 선택해야 하는 경우가 있을 것 같은데,어떤 상황에서 2안이 더 적합한 경우일까요 ?

컴포넌트에서 책임 분리: id만 넘기기 vs 데이터 직접 전달하기
부모로부터 id만을 전달받고, Global Store에서 꺼내오기
51%
부모로부터 데이터를 직접 전달받기
48%

94 votes

You must be logged in to vote

Replies: 5 comments 5 replies

Comment options

저라면 개인적으로 2안을 선택할 것 같습니다.

데이터 페칭 훅인 usePost는 Post 컴포넌트에서 분리가 가능해 보입니다. 만약 Post 컴포넌트를 다른 사용처에서 재사용 할 때 부모 컴포넌트에서 데이터를 이미 들고 있다면, username, comment, likeCount를 props로 주입해야 하는 경우가 생길 수 있겠다는 생각이 듭니다!

You must be logged in to vote
2 replies
@gronxb
Comment options

Post가 재사용이 된다고 가정했을 때, 추가로 설명 파트를 그리는 요구사항이 들어온다고 가정한다면, description이라는 추가 필드가 생길 수 있을텐데요. 2안의 경우 해당 컴포넌트를 사용하는 모든 곳에 Prop을 바꿔줘야하는 경우가 생기지 않을까요? 1안에선 Post 컴포넌트 안에서 usePost 훅에서 description 필드를 가져와 재사용되는 컴포넌트는 id만 넘기고 신경 쓰면 될 것 같다고 생각했어요 !

@gronxb
Comment options

Post 컴포넌트는 어떻게 보면 비즈니스 로직 컴포넌트인데, Post 컴포넌트가 usePost에서 오는 데이터 없이 (서버에서 주는 데이터 없이) 사용할 일이 있을까 ? 에 대한 관점도 존재하는 것 같아용☺️

Comment options

  1. 컴포넌트 책임의 분리 관점
  • 1안처럼 Post 컴포넌트 내부에서 usePost 훅을 사용하면 컴포넌트가 데이터 페칭과 표시 두 가지 책임을 모두 갖게 됩니다.
    단일 책임 원칙에 따르면 이러한 결합은 컴포넌트의 재사용성과 테스트 용이성을 저하시킬 수 있습니다.
  1. 관심사의 분리
  • Post 컴포넌트는 데이터를 어디서 가져오는지 알 필요가 없습니다. 단지 받은 데이터를 표시하는 역할만 하는게 맞다고 생각합니다.
  1. OCP 준수를 위한 컴포넌트 설계
  • OCP에 따르면, Post 컴포넌트는 데이터를 표시하는 기본 역할(닫힘)을 유지하면서도 다양한 데이터 소스나 컨텍스트에서 사용될 수 있도록(열림) 설계되어야 합니다.

이에 맞춰서 2안이 조금 더 유연한 사고 및 확장이 가능하다고 생각이 드네요!

You must be logged in to vote
1 reply
@gronxb
Comment options

  1. 단일 책임 원칙은 해석하기에 따라 달라질 수 있습니다.
    단일 책임 원칙(SRP)이라는 게 무조건 “데이터 가져오기 vs 렌더링”으로 나뉘어야 한다는 건 아닙니다.
    Post 컴포넌트가 ‘Post라는 개념’을 책임진다고 보면, 데이터를 가져오는 것도 포함될 수 있습니다.
    오히려 2안처럼 데이터 로직을 부모로 올리면, 책임이 여기저기 흩어져서 유지보수가 더 어려워질 수도 있어요.
  2. 관심사의 분리는 현실적인 응집도를 봐야 합니다.
    2안처럼 부모가 데이터를 내려주는 방식이면, Post를 쓰는 모든 부모 컴포넌트가 데이터를 미리 가져와야 합니다.
    그러면 데이터 관리 책임이 부모로 분산되면서, Post를 사용할 때마다 중복 코드가 생기고 관리 포인트가 늘어날 가능성이 큽니다.
    반대로 1안처럼 Post 내부에서 데이터를 가져오면, Post는 Post만 신경 쓰면 되고, 부모는 Post의 데이터 구조를 몰라도 됩니다.
    결과적으로 오히려 관심사가 더 깔끔하게 분리되는 효과가 있을 수 있어요.
  3. OCP(개방-폐쇄 원칙)도 1안도 유효한 경우가 있습니다 !
    2안처럼 부모에서 데이터를 내려주는 방식이면, 새로운 필드가 필요할 때마다 부모도 바꿔야 합니다.
    이러면 Post를 쓰는 부모 컴포넌트가 많을수록 변경 범위가 커지고 유지보수가 힘들어지겠죠.
    반면 1안처럼 usePost(postId) 내부에서 처리하면, Post를 사용하는 부모들은 신경 쓸 필요가 없습니다.
    “확장에는 열려 있고 변경에는 닫혀 있어야 한다”는 OCP 원칙에도 1안이 더 적합할 가능성이 높습니다.

결국, Post는 “Post를 책임지는 컴포넌트”이니, 데이터를 가져오는 것까지 맡는 게 자연스럽지 않을까요?
이렇게 하면 Post는 독립적으로 동작할 수 있고, 부모도 불필요한 데이터 처리에서 자유로워질 수 있습니다.
그래서 저는 1안이 더 현실적인 선택이라고 생각했어용

Comment options

저도 1안을 선호합니다.
위 글 "컴포넌트 책임/관심사" 내용을 보니 10년전 dan abramov 의 글Presentational and Container Components
이 떠오르네요.
(벌써 10년이나 됐다니.. 🤯)

react hook 이 나온뒤 dan abramov 는 아래 코멘트와 함께 글을 업데이트했습니다.

Update from 2019: I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component.Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.

2019 업데이트: 이 글은 오래전에 작성한 것이며, 그 이후 제 관점도 많이 바뀌었습니다. 특히, 이제는 컴포넌트를 이렇게 나누는 방식을 권장하지 않습니다. 만약 코드베이스에서 자연스럽게 느껴진다면 이 패턴이 유용할 수도 있지만, 불필요하게, 거의 독단적으로 강요되는 경우를 너무 많이 보았습니다. 이 방식을 유용하게 느낀 주된 이유는 복잡한 상태 로직을 컴포넌트의 다른 부분과 분리할 수 있었기 때문입니다. 하지만 Hooks를 사용하면 임의의 분리 없이도 같은 작업을 수행할 수 있습니다. 이 글은 역사적 이유로 그대로 남겨두었으니, 너무 진지하게 받아들이지 않으시길 바랍니다. (GPT 번역)

즉 Custom hook 으로 비즈니스 로직을 충분히 잘 분리하면 컴포넌트 하나에서 props 로 모두 내려주는것과 거의 동일하게 관심사가 분리된다고 생각해요

  • Container component ~= Custom hook
  • Presentational component ~=return 문 아래의 JSX
functionPost({ postId}:{postId:string}){const{ username, comment, likeCount}=usePost(postId);// <- Container component 의 역할// Presentational component 의 역할return(<div><h1>{username}</h1><p>{comment}</p><p>{likeCount}</p></div>);}

특히 swr, react-query 같은 data fetching, global state 역할을 어느정도 같이 할 수 있는 좋은 라이브러리를 많이 사용할 때에는 더욱 유용하다고 생각해요.

You must be logged in to vote
1 reply
@kickbelldev
Comment options

저도 정확히 이렇게 생각했어요.

Comment options

저는 배열을 순회하는 경우인지, 단일 컴포넌트를 렌더링하는 경우인지에 따라 데이터 전달 방식이 달라져야 한다고 생각합니다.

배열을 순회하는 경우에는, 데이터 페칭의 책임이 Posts(또는 PostList) 컴포넌트에 있다고 보기 때문에,
Post 목록을 렌더링할 때는 데이터를 prop으로 전달하는 방식을 선호합니다.

반면, Post 목록을 렌더링할 때 id만 전달하는 방식처럼 Post 컴포넌트 내부에서 데이터를 다시 가져오게 되면,
하나의 도메인 데이터에 대해 데이터 페칭용 hook과 id 기반으로 데이터를 추출하는 context hook 두 가지를 모두 관리해야 하므로,
오히려 복잡성이 증가할 수 있다고 생각합니다.

하지만 상세 페이지처럼 단일 컴포넌트를 렌더링하는 경우에는, id만 전달하고 내부에서 데이터를 조회하는 방식이 더 자연스럽게 느껴집니다. ( 해당 게시글 내의 댓글과 동일하게 생각합니다. )

You must be logged in to vote
1 reply
@dngur9801
Comment options

저도 이 의견에 동의합니다!

데이터 패칭의 책임이 Post의 부모에 있을 경우, 아래처럼 PostList에서 usePostList를 사용하여 데이터를 가져오고, Post 컴포넌트에 개별 데이터를 props로 전달하는 방식이 적절합니다.

functionPostList(){const{ posts}=usePostList();return(<div>{posts.map((post)=>(<Postkey={post.id}username={post.username}comment={post.comment}likeCount={post.likeCount}/>))}</div>);}

즉, 게시물 목록을 보여주는 페이지에서는 2안처럼 부모에서 데이터를 패칭하고 자식 컴포넌트로 전달하는 방식이 적합하다고 생각합니다.
반면, 게시물 상세 페이지처럼 단일 데이터를 조회하는 경우, Post 컴포넌트 내부에서 직접 데이터를 패칭하는 방식이 더 적절하다고 봅니다.

정리하자면

  • 데이터 패칭이 부모 컴포넌트에서 이루어지고, 배열을 순회하며 여러 개의 컴포넌트를 렌더링하는 경우 → 2안(부모에서 데이터 전달) 사용
  • 단일 데이터를 조회하는 경우 → 1안(컴포넌트 내부에서 데이터 패칭) 사용
Comment options

약간React Server Component에서 Data Fetching을 어느 단계에서 해야 할 지 고민입니다랑 비슷한 고민으로 바라봤는데요!

저는 컴포넌트를 추상화하지 않아야 할 때, 즉부모 컴포넌트가 더 직접적인 제어권을 가져야 하는 상황 에서는 2안을 고려해볼 만하다고 생각해요.
ID만 전달하여 내부적으로 비즈니스 훅을 통해 데이터를 패칭하는 방식(1안)은 컴포넌트의 독립성을 높이지만, 코드를 읽는 사람이 데이터 흐름과 맥락을 파악하는 데 제약이 있다고 판단했어요.
예를 들어 여러 결합도가 있는 컴포넌트가 동일한 데이터를 다른 관점에서 표시하거나 조작해야 할 때 (하나의 데이터가 여러 View단을 거칠 때), 중앙에서 데이터를 관리하고 분배하는 것이 일관성을 유지하는 데 도움이 될 수도 있다는 생각은 듭니다.

다만 이 중앙에 배치하는 관점이@ysm-dev님이 언급해주셨듯이 훅이 도입된 이후로 컴포넌트 단위의 엄격한 Presentational-Container 패턴을 중시하지 않게 된 거 같아요. 컴포넌트 재사용성 관점에서 설계를 바라보는 경향을 데이터 패칭과 묶어서 바라보는 관점으로 바뀌어 가고 있다고 봅니다.

개인적으론 1번 방식을 선호합니다! 비즈니스 로직과 View 응집도를 부모단에서 관리하기보단 내부로 캡슐화 하는게 더 관리적인 측면에서 좋다고 느껴요. 2안을 사용한 컴포넌트를 수정한다면 어느 부모 컴포넌트까지 수정 범위에 들어가는지 판단하는 게 어려움을 느낀 적이 많았어요.

훅으로 관리하게 된다면 훨씬 수정하기에 쉬울 수 있습니다. 누구는 전역 상태로, 혹은 데이터 패칭을 묶어서 커스텀 훅을 만들어서 사용할 수도, 누구는 그냥 부모단에서 지역적인 Context 훅을 만들어 내려줄 수도 있구요. 어떤 방법을 쓰든 비즈니스 로직 자체가 훅에 묶여 관리되기에 이점이 있다 생각합니다.

다만 이제 구현할 당시의 유행하는 디자인 패턴도 어느 정도 영향이 있다 생각이 듭니다. 리액트의 자유분방함으로 인해 여러 패턴이 뜨고 지지만 또 몇 년 뒤엔 앵귤러처럼 엄격히 관심사를 분리하고 의존성을 주입하는 형태로 갈 수도 있으니까요!

You must be logged in to vote
0 replies
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
A vs B
Labels
None yet
8 participants
@gronxb@ysm-dev@kickbelldev@jin-Pro@Wisesaturn@dngur9801@YESHYUNGSEOK@jingjing2222

[8]ページ先頭

©2009-2025 Movatter.jp