Skip to content

Code review caesar#3

Open
caesar1030 wants to merge 112 commits into
ericjypark:mainfrom
caesar1030:code-review-caesar
Open

Code review caesar#3
caesar1030 wants to merge 112 commits into
ericjypark:mainfrom
caesar1030:code-review-caesar

Conversation

@caesar1030
Copy link
Copy Markdown

Pull Request 템플릿

배포

배포된 페이지가 있다면 링크를 남겨주세요

  • 미배포입니다.

로컬 환경

로컬 환경 확인이 필요하다면 예시와 같이 남겨주세요

# install
npm i

# run
npm run dev

리뷰 요청 ✍️

- 신청하게 된 계기, 간단한 사연

관심사 분리와 수정에 용이한 코드를 짜보고 싶어서 클린 아키텍쳐를 웹 FE에 적용해봤습니다.
일반적이지 않은 코드 형태이기도 하고, 레퍼런스도 많이 부족하다보니 현업 개발자 분에게 꼭 코드리뷰를 받아보고 싶었습니다.

- 중점적으로 리뷰받고 싶은 부분

  • 아키텍쳐가 잘 구성되어있는지 ( 관심사 분리가 잘 되어있고 코드 수정이 쉬운 코드인지 궁금합니다.)
  • 프레젠테이션 레이어(사실상 리액트 코드)에서 컴포넌트가 역할에 따라 잘 분리되어 있는지 (리액트 컴포넌트를 구분하는 저만의 기준이 생겼는데 이게 올바른 기준인지 궁금합니다.)
  • 타입스크립트를 잘 사용하고 있는지 (타입스크립트를 사용한 프로젝트가 처음이라.. 막연히 잘 사용하고 있는지 궁금합니다.)

- 나누고 싶은 고민

  • 이런 다소 일반적이지 않은 프로젝트를 취업 포트폴리오로 사용할 수 있는지 여쭈고 싶습니다.
  • 라이브러리를 사용하지 않아도 되는 부분에서(이 프로젝트에서는 react hook form) 어느정도 실제로 현업에서 많이 사용된다고 생각한 라이브러리를 학습용으로 사용하는게 좋을지, 아니면 정말 실무처럼 필요하지 않다고 판단되면 라이브러리를 사용하지 않는게 좋을지 여쭈고 싶습니다.

@caesar1030
Copy link
Copy Markdown
Author

혹시 .env에서 관리되는 변수들은 어디에 말씀드리면 될까요?

@ericjypark
Copy link
Copy Markdown
Owner

안녕하세요!
eric010506@naver.com로 메일 보내주시면 됩니다!

@caesar1030
Copy link
Copy Markdown
Author

감사합니다.
확인 부탁드리겠습니다!

@ericjypark
Copy link
Copy Markdown
Owner

신청해주셔서 감사합니다!
늦어도 일주일 내로 리뷰해드릴게요!!

Copy link
Copy Markdown
Owner

@ericjypark ericjypark left a comment

Choose a reason for hiding this comment

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

안녕하세요 완섭님! 코드리뷰 신청해주셔서 감사합니다!

타입스크립트를 분명 처음 사용하신다고 말씀하신 것 같은데, 정말 잘 사용하시는 것을 보고 놀랐네요.
그냥 간단하게 변수와 함수들의 타입을 지정해주는 것은 많이들 할줄 알지만, Utility Type들과 Generic을 적재적소에 사용하는 사람들은 생각보다 많지 않더라구요. 물론 제 이야기이기도 합니다 ㅎㅎ,,

클린 아키텍쳐를 신경쓰시면서 개발을 하셨다고 하셨는데, 제 시선에서 봤을 때는 정말 흠 잡을 곳이 없습니다.
비정상적으로 긴 컴포넌트가 존재하지도 않고, 그렇다고 과할 정도로 재사용에 집착을 하시지도 않으셨구요.
더불어 Supabase와의 통신을 이렇게 깔끔한 구조로 구현을 하신 분은 처음 뵙네요. 사실상 하나의 백엔드를 구축하신거나 다름 없어보이는데, 심지어 그 구조가 NestJS같은 프레임워크를 이용한 것 처럼 구현이 되어있다는게 정말로 놀랍네요.

그래서 사실 아키텍쳐적인 측면에서는 제가 드릴 조언이 별로 없어보이네요. 오히려 제가 코드 리뷰를 하면서 많이 배웠습니다 감사합니다 🙇‍♂️


리액트 코드에 대해서 조금 얘기를 해보자면, 프로젝트 전반적으로 리액트의 최적화 훅들을 사용하시지 않으셨다는 점에 대해서 얘기를 할 수 있을 것 같습니다.

최적화에 대한 과한 집착은 좋지 않은 것이 맞기에, 저도 취준하던 시절에는 useCallbackuseMemo를 사용해본 적이 없었습니다. 필요한 상황이 생긴다면 저절로 사용하게 될거라고 생각했기 때문입니다.

하지만 막상 면접을 보러 다닐 때나, 회사에 입사하고 나서 가장 부족함을 느꼈던 부분이 바로 이러한 최적화 훅들의 활용이었습니다. 사용법을 잘 알지 못해 면접에서 탈락한 적이 있기도 하고, 회사에 입사하고 나서 개발을 할 때도 정확히 언제 어떻게 사용해야할지를 몰라서 골치 아팠던 적이 한두번이 아니었습니다.

분명 useCallbackuseMemo가 무엇을 하는 훅들인지는 알고 있었지만, 아는 것과 활용할줄 아는 것에는 꽤나 큰 차이가 있더라구요. 완섭님은 저와 같은 어려움을 겪지 않으셨으면 하는 바램에 말씀드립니다!

다른 의미로 얘기를 하자면, 최적화를 제외하면 따로 얘기 드릴 만한 점이 없습니다. 이미 어느정도 경력이 있으신게 아닐까 싶을 정도로 실력이 좋으신 것 같네요.


이런 다소 일반적이지 않은 프로젝트를 취업 포트폴리오로 사용할 수 있는지 여쭈고 싶습니다.
라이브러리를 사용하지 않아도 되는 부분에서(이 프로젝트에서는 react hook form) 어느정도 실제로 현업에서 많이 사용된다고 생각한 라이브러리를 학습용으로 사용하는게 좋을지, 아니면 정말 실무처럼 필요하지 않다고 판단되면 라이브러리를 사용하지 않는게 좋을지 여쭈고 싶습니다.

이런 두 고민이 있다고 말씀해주셨는데,
우선 첫 고민에 대해서 말씀을 조금 드리자면, 이 프로젝트는 충분히 취업 포트폴리오에 사용할만한 프로젝트라고 생각이 됩니다.

분명 일반적이지는 않은 프로젝트가 맞지만, 다른 의미로는 백엔드 개발자들이 신경쓰는 아키텍쳐에 대한 고민을 공감할 수 있다는 점에서도 분명 플러스가 될 것 같습니다.

더불어 프론트엔드 코드의 구조 역시 너무나도 깔끔합니다.
useQuery를 따로 커스텀훅으로 뺀 것도, 공통 컴포넌트들을 구현하신 방식들도, 모두 현업에서 되게 중요한 부분들이기에 이러한 역량을 갖추고 있으시다는 점이 취업하실 때 분명 엄청난 경쟁력이 될 것 같다고 생각이 드네요.

두번째 고민은 react-hook-form을 활용한 것에 대해서 말씀해주셨는데, 개인적으로는 전혀 문제가 되지 않는다고 생각합니다. 특히 react-hook-form같은 경우 실무에서도 많이 사용되는 라이브러리이기에 사용법을 잘 안다면 오히려 플러스가 될 수도 있을 것 같네요.
다만 이 라이브러리를 사용하지 못하는 상황일 때는 어떻게 비슷하게 구현을 할 수 있을까에 대한 고민을 조금 해보신다면 더욱 많은 도움이 되실 것 같습니다!

코드리뷰를 하면서 저도 정말 많이 배웠습니다 감사합니다.

좋은 프로젝트 만드시느라 고생 많으셨습니다!

Comment thread src/App.tsx
Comment on lines +8 to +12
defaultOptions: {
queries: {
staleTime: 0,
},
},
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Default 설정에 staleTime: 0 을 추가하신 이유가 있으실까요?? 기본 설정이 staleTime: 0으로 알고 있어서 불필요한 옵션이 아닐까 싶습니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

앗 지우도록 하겠습니다 😅

Comment on lines +11 to +12
const base =
'flex gap-1 justify-center items-center flex-shrink-0 hover:opacity-80 active:opacity-[64] disabled:opacity-[32]';
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

잘못된 부분은 전혀 아니고, 개인적인 의견을 드리자면, 저는 이렇게 tailwind 스타일을 따로 뺄때는 *Style이라는 컨벤션을 가져가곤 합니다.

그리고 이 컨벤션과 tailwind intellisense를 같이 묶어서 편하게 사용할 수가 있어요!

예를 들어, 완섭님께서 하신 방식으로는 아마도 tailwind 자동완성이 안먹었을텐데,

"tailwindCSS.classAttributes": [
  "class",
  "className",
  "clsx",
  "cn",
  ".*Style*" // Add ".*Style*" (or whatever matches your naming scheme)
],

vscode에 이 설정을 추가하면 외부 변수에 tailwind 스타일을 작성할 때도 자동완성이 뜨도록 설정할 수 있어요.

살짝 꿀팁같은 느낌입니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

와 항상 className에서 확인하고 복사 붙여넣기(ㅎㅎ..) 했는데 감사합니다!!

Comment on lines +3 to +9
interface ButtonProps extends ComponentPropsWithoutRef<'button'> {
variant: keyof typeof variants;
size: keyof typeof sizes;
children: React.ReactNode;
flexible?: boolean;
active?: boolean;
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Typescript를 처음 사용하신다고 하셨는데, 되게 잘활용하시네요! 👍 👍

Comment on lines +38 to +41
[base, variants[variant], sizes[size]].join(' ') +
`${flexible ? ' flex w-fit h-fit ' : ''}` +
`${active ? ' text-neutral-text-strong ' : ''}` +
rest.className
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

이부분은 clsx라는 라이브러리를 찾아보시면 도움이 많이 되실거에요.
clsx에 twMerge를 얹어서 보통 util함수를 따로 만들어서 cn()을 많이들 사용하시더라구요.
https://github.com/shadcn-ui/ui/blob/main/apps/www/lib/utils.ts
이 코드를 참고하시면 도움이 되실 것 같습니다!

Comment on lines +32 to +38
function focus() {
setIsFocused(true);
}

function blur() {
setIsFocused(false);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

이 함수들을 각각 useCallback()에 감싸면 좋을 것 같아요! 이유는 아마 높은 확률로 FilterBar 컴포넌트는 매번 리렌더링이 되지 않을까 싶습니다. React.memo()로 감싸더라도요!

그러면 사소하지만 매번 이 함수들이 다시 생성이 되게 될텐데, 만약 이 함수 참조하는 어떤 한 비싼 컴포넌트가 존재한다면 골치 아플수도 있습니다.

https://tkdodo.eu/blog/the-uphill-battle-of-memoization#children

이 부분을 참고하시면, {children} 을 받는 컴포넌트들이 리렌더링에 취약하다는 사실을 알 수 있습니다.

따라서 FilterBar 컴포넌트의 리렌더링을 제대로 방지하지는 못하더라도, 리렌더링 될 때 이 두 함수들이 다시 만들어지는 것은 막을 수 있다는 점에서 도움이 될 것 같습니다!

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

되게 흥미로운 코드네요!
사실 이 코드는 프론트엔드보다는 백엔드에 가까운 코드로 보이긴 합니다.

Database (이 경우엔 Supabase) 와 통신을 하는 하나의 Repository를 구현하신걸로 보이는데, 단순 supabase와의 통신을 이렇게 아키텍쳐적으로 고민을 하셨다는게 진짜 대단하시네요. 👍

const ref = useRef<T>(null!);

useEffect(
function () {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Arrow Function을 별로 선호하지 않으시는걸로 보이는데, 따로 이유가 있으실까요??

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

사실 이 부분이 저도 아직도 긴가민가 하는 부분입니다.

간혹가다가 핸들러를 커스텀 훅에 전달해야 하는 경우가 생기더라구요. (이 프로젝트에는 없습니다.)
이 경우 arrow function으로 핸들러를 정의하면 tdz 때문에 핸들러 선언 이후에 커스텀 훅을 사용하는 경우가 있어서

코드의 일관성을 위해 무조건 function 키워드를 쓰자!라고 생각했는데 이번 프로젝트를 하면서 핸들러를 또 한 곳에 모두 모아넣은 경우는 가독성이 떨어져서 차마 function키워드를 사용하기는 좀 그래서 화살표 함수를 사용했더니 일관성이 또 깨지고.. 좀 고민인 부분입니다.

혹시 준열님은 위의 경우시라면 어떻게 하실지 의견 여쭐 수 있을까요!?😅

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

사실 저는 function을 사용하는 경우가 거의 없는 것 같아요.
어떤 함수를 선언하기 전에 그 함수를 호출할 수 있다는 그런 특징이 과연 자연스러운가에 대해 고민을 해봤을 때 아니라는 결론이 나와서 저는 대부분 다 Arrow Function을 사용하는 것 같아요. 물론 회바회, 팀바팀이라는 점!

다만 저도 Arrow Function 때문에 어떤 커스텀 훅 호출하는 위치가 꼬이게 된다면 되게 신경쓰여하긴 합니다. 그럴 때는 그 함수를 아예 컴포넌트 외부로 뺄 수 있는지를 고민해보고, 만약 불가능하다면 어쩔 수 없이 그냥 커스텀 훅 호출을 아래로 내리는 것 같습니다.

Comment thread src/pages/Issues.tsx
Comment on lines +5 to +15
function Issues() {
return (
<>
<IssueFilterBar />

<IssueFilterResetButton />

<IssueTable />
</>
);
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

깔끔하네요!! 👍

Comment on lines +14 to +16
useEffect(() => {
navigate('/issues?isOpen=open', { replace: true });
}, [navigate]);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

path에 직접 queryParam을 추가하는 방법은 조금 위험할 수도 있을 것 같습니다!

만약 queryParam이 하나가 아니라 여러개인 상황이라면, 다른 queryParam들이 의도치 않게 지워질 수도 있지 않을까요?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

만약 queryParam이 하나가 아니라 여러개인 상황이라면, 다른 queryParam들이 의도치 않게 지워질 수도 있지 않을까요?

이 부분에 대해서 이해하지 못했습니다. 혹시 어떤 경우인지 질문 드릴 수 있을까요?😂

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

예를 들어서, /issues?isDummy=true라는 query param을 들고 있다고 가정을 하면,
위 effect가 돌게 된다면 isDummy는 사라지고 isOpen만 남게 되겠죠.

당장은 문제가 되지 않을수도 있겠지만, 만약 나중에 서비스가 커져서 query param을 여러개 다뤄야하는 경우가 있을 수도 있으니까요!

Comment on lines +170 to +191
getOpenStatusSearchParam,
getLabelSearchParam,
getMilestoneSearchParam,
getLikeSearchParams,

setOpenStatusSearchParam,
toggleLabelSearchParam,
toggleMilestoneSearchParam,
setLikeSearchParams,
initSearchParams,

isOpenStatus,
isCloseStatus,
isUnLabeld,
isNotWithMilestone,
hasLabelSearchParam,
hasMilestoneSearchParam,
hasLikeSearchParam,

applySearchQuery,
getFilterOptions,
convertParamsToQuery,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

정말 사소하지만 꽤 가독성에 도움을 주는 디테일인데, 신경쓰신게 보이네요! 👍 👍

@caesar1030
Copy link
Copy Markdown
Author

안녕하세요 완섭님! 코드리뷰 신청해주셔서 감사합니다!

타입스크립트를 분명 처음 사용하신다고 말씀하신 것 같은데, 정말 잘 사용하시는 것을 보고 놀랐네요. 그냥 간단하게 변수와 함수들의 타입을 지정해주는 것은 많이들 할줄 알지만, Utility Type들과 Generic을 적재적소에 사용하는 사람들은 생각보다 많지 않더라구요. 물론 제 이야기이기도 합니다 ㅎㅎ,,

클린 아키텍쳐를 신경쓰시면서 개발을 하셨다고 하셨는데, 제 시선에서 봤을 때는 정말 흠 잡을 곳이 없습니다. 비정상적으로 긴 컴포넌트가 존재하지도 않고, 그렇다고 과할 정도로 재사용에 집착을 하시지도 않으셨구요. 더불어 Supabase와의 통신을 이렇게 깔끔한 구조로 구현을 하신 분은 처음 뵙네요. 사실상 하나의 백엔드를 구축하신거나 다름 없어보이는데, 심지어 그 구조가 NestJS같은 프레임워크를 이용한 것 처럼 구현이 되어있다는게 정말로 놀랍네요.

그래서 사실 아키텍쳐적인 측면에서는 제가 드릴 조언이 별로 없어보이네요. 오히려 제가 코드 리뷰를 하면서 많이 배웠습니다 감사합니다 🙇‍♂️

리액트 코드에 대해서 조금 얘기를 해보자면, 프로젝트 전반적으로 리액트의 최적화 훅들을 사용하시지 않으셨다는 점에 대해서 얘기를 할 수 있을 것 같습니다.

최적화에 대한 과한 집착은 좋지 않은 것이 맞기에, 저도 취준하던 시절에는 useCallbackuseMemo를 사용해본 적이 없었습니다. 필요한 상황이 생긴다면 저절로 사용하게 될거라고 생각했기 때문입니다.

하지만 막상 면접을 보러 다닐 때나, 회사에 입사하고 나서 가장 부족함을 느꼈던 부분이 바로 이러한 최적화 훅들의 활용이었습니다. 사용법을 잘 알지 못해 면접에서 탈락한 적이 있기도 하고, 회사에 입사하고 나서 개발을 할 때도 정확히 언제 어떻게 사용해야할지를 몰라서 골치 아팠던 적이 한두번이 아니었습니다.

분명 useCallbackuseMemo가 무엇을 하는 훅들인지는 알고 있었지만, 아는 것과 활용할줄 아는 것에는 꽤나 큰 차이가 있더라구요. 완섭님은 저와 같은 어려움을 겪지 않으셨으면 하는 바램에 말씀드립니다!

다른 의미로 얘기를 하자면, 최적화를 제외하면 따로 얘기 드릴 만한 점이 없습니다. 이미 어느정도 경력이 있으신게 아닐까 싶을 정도로 실력이 좋으신 것 같네요.

이런 다소 일반적이지 않은 프로젝트를 취업 포트폴리오로 사용할 수 있는지 여쭈고 싶습니다. 라이브러리를 사용하지 않아도 되는 부분에서(이 프로젝트에서는 react hook form) 어느정도 실제로 현업에서 많이 사용된다고 생각한 라이브러리를 학습용으로 사용하는게 좋을지, 아니면 정말 실무처럼 필요하지 않다고 판단되면 라이브러리를 사용하지 않는게 좋을지 여쭈고 싶습니다.

이런 두 고민이 있다고 말씀해주셨는데, 우선 첫 고민에 대해서 말씀을 조금 드리자면, 이 프로젝트는 충분히 취업 포트폴리오에 사용할만한 프로젝트라고 생각이 됩니다.

분명 일반적이지는 않은 프로젝트가 맞지만, 다른 의미로는 백엔드 개발자들이 신경쓰는 아키텍쳐에 대한 고민을 공감할 수 있다는 점에서도 분명 플러스가 될 것 같습니다.

더불어 프론트엔드 코드의 구조 역시 너무나도 깔끔합니다. useQuery를 따로 커스텀훅으로 뺀 것도, 공통 컴포넌트들을 구현하신 방식들도, 모두 현업에서 되게 중요한 부분들이기에 이러한 역량을 갖추고 있으시다는 점이 취업하실 때 분명 엄청난 경쟁력이 될 것 같다고 생각이 드네요.

두번째 고민은 react-hook-form을 활용한 것에 대해서 말씀해주셨는데, 개인적으로는 전혀 문제가 되지 않는다고 생각합니다. 특히 react-hook-form같은 경우 실무에서도 많이 사용되는 라이브러리이기에 사용법을 잘 안다면 오히려 플러스가 될 수도 있을 것 같네요. 다만 이 라이브러리를 사용하지 못하는 상황일 때는 어떻게 비슷하게 구현을 할 수 있을까에 대한 고민을 조금 해보신다면 더욱 많은 도움이 되실 것 같습니다!

코드리뷰를 하면서 저도 정말 많이 배웠습니다 감사합니다.

좋은 프로젝트 만드시느라 고생 많으셨습니다!

안녕하세요 준열님!
이렇게 좋은 기회 선물해주셔서 다시 한 번 진심으로 감사드립니다. (강조 하고 싶습니다. 코드 리뷰가 항상 절실했어서요..ㅎㅎ)

취준을 하면서 일반적이지 않은 포맷을 시도하다 보니 걱정이 많이 앞서고 불안해서 포기도 많이 고려하던 상황이었는데 이렇게 긍정적으로 반응해주셔서 끝까지 프로젝트 마무리할 힘이 생겼습니다. 감사합니다 ( _ _ )

성급한 최적화를 피하라는 말이 마치 부적처럼 최적화에 대한 고민을 막아왔던것 같습니다. ㅎㅎ;
API만 알면 필요할 때 쓸 수 있겠지라는 생각이었는데 준열님 말씀처럼 면접 그리고 실무에서 기준이 생기지 않을 것 같아 가장 시간이 많은 취준기간동안 많이 고민해보겠습니다.

리뷰 확인하며 질문 주셨던 부분에 대해 제 나름대로의 짧은 생각이나 더 궁금한 부분에 대해 코멘트 달아놨습니다! 혹시 확인 가능하시다면 확인 부탁드릴게요! 😁

다시 한번 좋은 기회 주셔서 정말정말 감사드립니다. 저도 좋은 개발자가 되어 누군가에게 비슷한 도움을 줄 수 있으면 좋겠습니다. 감사합니다!

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