Conversation
🎨 Storybook 배포 완료PR 작성자: @jyeon03 |
There was a problem hiding this comment.
저는 publicRoutes, privateRoutes로 라우트를 분리하는 방식이 유지보수성과 가독성 측면에서 더 낫다고 생각해요 !!
인증 필요/불필요라는 기준이 명확한 도메인 규칙이기 때문에 오히려 지금처럼 라우트를 한 배열에 섞어두면 규모가 커질수록 분류 비용과 실수 가능성이 크다고 생각합니다 !
특히 새로운 페이지 추가 시에 이 페이지가 인증이 필요한가?를 판단하고 그 결과에 따라 private/public 중 한 곳에만 넣으면 되기 때문에 변경 범위가 작고 리뷰 포인트도 명확해진다고 생각하는데 지연님의 생각은 어떨까요 ?
또한 SSoT 위배 라고 말씀해주셨는데 분리 자체는 중복 정의가 아니라 책임 분리에 가깝다고 생각해요 !
SSoT가 실제로 깨지는 케이스는 이 페이지가 private인지 여부와 같은 동일한 규칙이 routes 파일/guard/메뉴 매핑 등 여러 곳에서 중복으로 관리되어 불일치가 생길 때인데 현재 구조는 접근 정책을 privateRoutes/publicRoutes라는 한 기준으로 모아두는 형태라 오히려 정책을 추적하기 쉽다고 생각합니다 !!
| Component: PublicLayout, | ||
| children: publicRoutes, | ||
| Component: GuardedPublicLayout, | ||
| children: routes.filter((r) => !r.auth), |
There was a problem hiding this comment.
이런식으로 filter 함수를 사용하면 가독성이 떨어질 것 같아요 ! routes 파일을 다시 분리하게 된다면 해결될 문제 같아요 !
| const { pathname } = useLocation(); | ||
|
|
||
| const publicOnlyPaths: string[] = [ | ||
| PATH.LANDING, | ||
| PATH.LOGIN, | ||
| PATH.LOGIN_CALLBACK, | ||
| ]; | ||
|
|
||
| if (accessToken && publicOnlyPaths.includes(pathname)) { |
There was a problem hiding this comment.
const GuardedPrivateLayout = () => (
<PrivateRouteGuard>
<PrivateLayout />
</PrivateRouteGuard>
);PublicRouteGuard를 PublicLayout 전체에 감싸면
PublicLayout 아래에 있는 모든 라우트가 public 영역이 되고 있기 때문에
현재 pathname이 publicOnlyPaths에 해당하나를 검사할 이유가 없을 것 같아요 !!
jm8468
left a comment
There was a problem hiding this comment.
상세하게 pr을 써주셔서 더 편하게 리뷰할 수 있었던 것 같아요👍👍👍
수고 많으셨어요!!
There was a problem hiding this comment.
저는 publicRoutes, privateRoutes로 라우트를 분리하는 방식이 유지보수성과 가독성 측면에서 더 낫다고 생각해요 !!
인증 필요/불필요라는 기준이 명확한 도메인 규칙이기 때문에 오히려 지금처럼 라우트를 한 배열에 섞어두면 규모가 커질수록 분류 비용과 실수 가능성이 크다고 생각합니다 !
특히 새로운 페이지 추가 시에 이 페이지가 인증이 필요한가?를 판단하고 그 결과에 따라 private/public 중 한 곳에만 넣으면 되기 때문에 변경 범위가 작고 리뷰 포인트도 명확해진다고 생각하는데 지연님의 생각은 어떨까요 ?
메타데이터에 대해 잘 몰라서 메타데이터 사용과 관련해서 찾아봤습니다😊
인증 여부, 비로그인 전용, 유료 구독 여부 등으로 다양하게 메타 데이터를 입력하고, 이러한 메타데이터들을 사용해 유지보수성도 높이고, 페이지 별로 어떤 권한을 가지는 지 알 수 있어서 메타 데이터 이용에 확실한 장점이 있다고 생각해요
반대로, 저희는 현재 인증 여부(auth) 만으로 페이지를 나누고 있어서, 현 상황에서는 오히려 더 명확하게 구분이 되도록 private-route.ts, public-route.ts로 나누는 것도 장점이 있다고 생각해요
저도 다른 분들 생각이 궁금한데, 프로젝트 규모가 커질 수록 페이지 별로 역할이 어느정도 다양해질 거고, 페이지를 인증 여부 별로만 분류하지는 않을 거라고 생각합니다. 그래서 결국에 메타데이터를 사용하게 될 것 같은데, 미래를 보고 우선적으로 해야할 지, 현재 상황에 맞게 가야할 지 궁금해요!!😊😊
메타데이터 사용 시 실수를 줄이기 위해서는 작성하신 것처럼 타입을 작성해주고, 자주 사용되는 메타데이터를 default로 상정하고 코드를 작성해주는 등의 방법을 취해주면 좋을 것 같습니다!
| { | ||
| path: PATH.NEW_MEMO, | ||
| Component: NewMemoPage, | ||
| auth: true, |
There was a problem hiding this comment.
auth-guard 방식을 auth: true일 때가 아닌 auth: false일 때를 기준으로 하는 건 어떻게 생각하시나요??
Private Routes에 있는 auth: true를 아예 없애고, Public Routes에 auth: false를 작성하는 방식입니다!
아무래도 auth: true일 때 사용되는 페이지가 많으니, 적게 사용되는 비인증 전용 페이지에만 메타데이터를 작성하는 것이 추후 생길 휴먼 에러를 더 줄일 수 있다고 생각해요😊😊👍
// Public Routes
{
path: PATH.LOGIN,
Component: LoginPage,
meta: { auth: false },
},
{
path: PATH.LOGIN_CALLBACK,
Component: LoginCallbackPage,
meta: { auth: false },
},
{
path: PATH.LANDING,
Component: LandingPage,
meta: { auth: false },
},
There was a problem hiding this comment.
PrivateRouteGuard, PublicRotueGuard로 나눠주셨는데, 만약 페이지가 더 늘어나고, 페이지가 인증 여부 외에도 다른 요소에 따라 보이는게 달라져야 한다면, 하나로 합치는 것이 좋을 것 같은데 어떻게 생각하시나여??
그리고 AuthGuard 내에서 토큰 유무, 메타데이터 유무를 조건으로 제어문을 작성한다면 한 곳에서 작성하므로, 분산이 적어지고, 유지보수 비용 또한 낮아질 수 있다고 생각해요
단일책임원칙에 대해서도 얘기해주셨는데, AuthGurad를 'routes/index.ts에 정의된 내용을 실행하는 역할'로 본다면 하나로 관리하는 것도 단일책임원칙에 부합하지 않을까라고 생각하고 있습니당.
책임을 더 세세하게 보고, 각각 분산한다면 오히려 응집도가 떨어지는게 아닐까라고 생각하고 있는데 다른 분들 의견도 궁금하네용
이 코멘트도 확장성을 보고 얘기드리는 거여서 오버 엔지니어링 여부를 봐야할 것 같습니다!😊😊
| * - requireAuth: 인증이 필요한 라우트 (인증 없으면 랜딩으로 리다이렉트) | ||
| * - requireAuth 없음: 인증 도메인 (인증 있으면 메인으로 리다이렉트) | ||
| */ | ||
| export const RouteGuard = ({ requireAuth = false, children }: GuardProps) => { |
There was a problem hiding this comment.
requureAuth라는 변수명이 직관적이여서 좋네요!!👍👍
requireAuth의 기본값이 false로 되어있는데 false로 설정하신 이유가 궁금해요!
There was a problem hiding this comment.
기본값을 false로 설정한 이유는 진입점(랜딩, 로그인 등)을 실수로 차단하는 것을 방지하고, 인증이 꼭 필요한 곳에만 명시적으로 설정하는 Opt-in 방식이 보안 및 관리 측면에서 더 안전하기 때문입니당! 또한 false 상태에서도 이미 로그인한 사용자가 로그인 페이지에 머무는 것을 막는 리다이렉트 로직이 기본으로 작동하게 하여, 컴포넌트 하나로 인증 보호와 게스트 전용 처리를 모두 유연하게 관리하기 위함이었습니다!
| const isAuthenticated = !!getAccessToken(); | ||
|
|
||
| if (requireAuth && !isAuthenticated) { | ||
| return <Navigate to={PATH.LANDING} replace />; |
There was a problem hiding this comment.
이 제어문에만 state={{ from: location }} 패턴을 적용하는 것도 좋아보여요! UX가 향상될 것 같네요😊😊
There was a problem hiding this comment.
routes 파일들에 전체적으로 메타데이터를 빼주셨는데, 제외하신 이유가 궁금해요!
이미 routes 파일을 각각 나눈 것만으로도 메타데이터를 대신할 수 있기 때문일까요??
There was a problem hiding this comment.
PR에서도 언급했다시피 route는 기능을 기준으로 분리하는 게 좋아요! 그래서 route에서 인증 유무를 알 필요가 없는 거죠!!
각 라우트 파일은 순수한 도메인별 경로 정보만 담고, 인증 제어는 상위 레이어인 GuardedLayout에서 일괄적으로 담당하게 되어 역할이 더 깔끔해지도록 설계했습니다!
| } | ||
|
|
||
| if (!requireAuth && isAuthenticated) { | ||
| if (GUEST_ONLY_PATHS.includes(pathname)) { |
There was a problem hiding this comment.
이 부분을 삭제하고, !requireAuth나 다른 메타데이터 추가를 통해 해결하는 건 어떻게 생각하시나용??
저희가 만약에 새로운 페이지를 routes/xxxx-routes.tsx에 추가하게 되면, path.ts의 GUEST_ONLY_PATHS에도 해당 페이지에 대한 내용을 추가해야할지 말아야할지 고민해야 합니다.
페이지를 추가할 때 2개의 파일을 고려하기보다는 1개의 파일을 고려하는게 좋을 것 같아서요!!
routes에서 메타데이터를 통해 다루거나,
router.tsx에서 등을 추가하는 등의 방식이 있을 것 같습니다😊👍
There was a problem hiding this comment.
좋은 의견 감사합니다! 👍
말씀하신 대로 페이지가 추가될 때마다 path.ts의 GUEST_ONLY_PATHS까지 수정해야 하는 의존성을 끊는 것이 유지보수 측면에서 훨씬 좋을 것 같네욥!
다만, routes/*.tsx 파일들은 순수한 도메인별 라우팅 정보만 가지도록 설계했기 때문에, 여기에 다시 메타데이터를 추가하기보다는 router.tsx 레벨에서 구조적으로 해결하는 방향으로 수정해보면 좋을 것 같네욤😊
jm8468
left a comment
There was a problem hiding this comment.
라우팅 구조를 설계하면서 꼼꼼하게 해주시려고 한 것 같아서 너무 좋네요!!!😊😊
수고 많으셨어용
📌 Summary
이번 리팩토링 수정사항은 라우트를
한 파일로 합쳤다수준이 아니라,로 역할을 재정의한 구조 개선입니다. route, guard, router가 무엇인지부터 다시 생각해보고 여러 레포를 참고하여 재설계했습니다!!!
-> 이를 해결하기 위해 라우팅을 기능 중심 + 정책 조합 구조로 재설계했습니다.
📚 Tasks
🔍 Describe
기존 구조의 한계현실에는 다음과 같은 페이지들이 존재합니다.
기존 구조에서는
Public이지만 Private 레이아웃을 사용해야 하는 페이지같은 요구를 처리하기 매우 어렵습니다. Public/Private 분류에 레이아웃과 인증 정책이 함께 묶여 있기 때문입니다.새로운 설계의 핵심👀 To Reviewer