Skip to content

feat: 그로스팀 명예의전당, 아카이브, 나의 스터디 기록, 밸런스게임#353

Closed
Hyeonjun0527 wants to merge 52 commits intodevelopfrom
feat/merge/growth-team-new-feature
Closed

feat: 그로스팀 명예의전당, 아카이브, 나의 스터디 기록, 밸런스게임#353
Hyeonjun0527 wants to merge 52 commits intodevelopfrom
feat/merge/growth-team-new-feature

Conversation

@Hyeonjun0527
Copy link
Member

@Hyeonjun0527 Hyeonjun0527 commented Jan 30, 2026

🌱 연관된 이슈

☘️ 작업 내용

pr 리뷰 한번 부탁드립니다 신규기능들입니다. file changed 82, line +9100 -1627 입니다.로컬환경 프론트 로컬환경 백엔드로 전부 테스트가 끝났고, 신규기능 qa서버에 올리려고 합니다.기능을 나눠서 그로스팀이 개발하고 리뷰받고 개발하고 리뷰받고 이렇게 3번 4번 하면 너무 오래걸릴거 같아서요.커밋은 어느정도 나뉘어져 있습니다. 그거로 아주 간단하게 리뷰받고 qa 서버 올리고 qa팀 컨펌까지받아서 문제있는지 확인하고 운영서버에 올릴 계획입니다.첫 그로스팀 협업 작업인데, 한번 qa 서버 올려도 되는지 컨펌 부탁드립니다

실제로 배포할때는 이 하나의 커밋으로 뭉쳐서 올릴 것입니다.
이 file changed에 있는 도커파일 수정이랑 깃 이그노어 수정 xml 수정은 전부 잘못올려진거라서 그거 전부 제외한 뒤에 위 커밋으로 배포할 겁니다. 그 부분 감안하고 봐주시면 감사하겠습니다.

1차 리뷰 개선사항

밸런스게임/아카이브/명예의전당/나의 스터디 기록 화면에서 UI가 판단·API 선택·부수효과까지 맡던 구조를 정리하고, 도메인 로직은 훅(model)으로 이동했습니다.

Archive 화면을 Header / Filters / Grid / List로 분해하여 Composite UI 패턴으로 재구성하고, 상태 규칙(정렬·뷰모드 등)은 상위로 끌어올렸습니다.

반복되던 버튼·인풋·카드 패턴을 design token 기반 공통 UI 컴포넌트(components/ui)로 승격하여 스타일 중복을 제거했습니다.

Next.js App Router 페이지는 서버 컴포넌트로 유지하고, 실제 UI/상태 로직은 app 외부 클라이언트 컴포넌트로 분리해 SSR/SEO 이점을 보존했습니다.

balance-game / hall-of-fame 타입을 src/types로 통합하여 완전한 type-based 구조로 정리했습니다.

반복되던 catch + throw API 패턴은 TanStack Query 훅으로 흡수해 에러 처리 책임을 일관화했습니다.

네이밍 규칙(useXXXQuery)을 전수 점검해 one-to-one 도메인 훅 명칭을 정리했고, 임시 문서/계획 파일은 모두 제거했습니다.

🍀 참고사항

스크린샷 (선택)

image image image image image image image

@seong-jin-jo
Copy link
Contributor

.idea 폴더와 docker compose 등은 빠질거라는거죠? 리뷰받을 코드와 배포 커밋이 달라야할 이유가 있을까요?

Copy link
Contributor

@HA-SEUNG-JEONG HA-SEUNG-JEONG left a comment

Choose a reason for hiding this comment

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

voting-create-modal의 경우에도 기존 모달 패턴을 재사용하는게 좋을 것 같습니다.

create-mission-modal 참고

const response = await axiosInstance.get<ApiResponse<BalanceGame>>(
`/balance-games/${gameId}`,
);
console.log('getBalanceGameDetail response data:', response.data);
Copy link
Contributor

@HA-SEUNG-JEONG HA-SEUNG-JEONG Jan 30, 2026

Choose a reason for hiding this comment

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

정상적으로 데이터를 받아오는거면 콘솔로그는 지우셔도 될 거 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

components 폴더 내에 이미 card라는 폴더가 있어서 이 부분은 통합해도 좋을 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

[...ARCHIVE_QUERY_KEY.all, params] as const,
};

export const useArchive = (params: GetArchiveParams) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분은 useArchiveQuery로 네이밍하는게 더 적절해 보입니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@seong-jin-jo seong-jin-jo left a comment

Choose a reason for hiding this comment

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

고생하셨습니다. 코드량과 커밋으로 유추해봤을때 많은 고민과 시도가 있었을 것 같습니다. 프론트 슬랙채널에 release 용 flag나 롤백 전략에 대해 협의가 완료 안된 상태인데 추후 기능 변경이나 삭제 혹은 확장가능성 생각했을때 배포 전 프론트팀 최종협의가 필요할 것 같습니다

Comment on lines 3 to 35
## 🎯 설계 원칙

### 1. Type-based 구조

**핵심 개념**: 기능(feature/domain)이 아닌 **타입(역할)**을 기준으로 폴더 구조 구성

#### ✅ FSD에서 Type-based로의 변화

**Before (FSD - Feature-Sliced Design):**

```
entities/user/
├── api/ # 사용자 API
├── model/ # 사용자 쿼리
└── ui/ # 사용자 UI

features/study/
├── group/
├── interview/
└── participation/

widgets/home/ # 홈 위젯들
```

**After (Type-based):**

```
api/endpoints/ # 모든 API (open api로 관리 예정)
types/ # 모든 도메인 스키마, 모델, 타입
hooks/queries/ # 모든 쿼리 훅
components/ # 모든 컴포넌트 (UI 타입별 분류)
utils/ # 모든 유틸리티
```
Copy link
Contributor

Choose a reason for hiding this comment

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

이 파일은 프론트팀에서 FSD에서 Type-based로 리팩토링 계획담은 문서인데 삭제된 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

복구하겠습니다

Copy link
Contributor

Choose a reason for hiding this comment

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

Next.js App Router 라우팅 + 서버 경계만 담당하는 Next 전용 영역입니다.
Next를 사용하는 가장 큰 이유가 SEO를 위한 SSR인데 'use client' 를 상위에서 쓰게 되면 서버 컴포넌트의 장점이 소멸되고 번들이 커지는 사이드 이팩트도 있습니다.
이러한 이유로 현재 페이지는 서버컴포넌트로 유지하고 페이지 로직이나 UI는 app 바깥에 클라이언트 컴포넌트 형태로 존재해야하며 우리는 FSD 구조에서 Type based 구조로 전환중이기 때문에 components 폴더에서 관리하는게 적절해보입니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 208 to 232
{/* Dropdown */}
<div className="absolute top-full left-0 z-20 hidden w-[120px] pt-50 group-hover:block">
<div className="bg-background-default border-border-subtle rounded-100 shadow-2 overflow-hidden border">
<button
onClick={() => setSortMode('latest')}
className={cn(
'hover:bg-fill-neutral-subtle-hover font-designer-14r w-full px-200 py-150 text-left transition-colors',
sortMode === 'latest' &&
'bg-fill-neutral-subtle-default',
)}
>
최신순
</button>
<button
onClick={() => setSortMode('popular')}
className={cn(
'hover:bg-fill-neutral-subtle-hover font-designer-14r w-full px-200 py-150 text-left transition-colors',
sortMode === 'popular' &&
'bg-fill-neutral-subtle-default',
)}
>
인기순
</button>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

드롭다운이나 버튼같은경우 shadcn 을 베이스로한 headles UI 아키텍쳐위에 자체 디자인시스템을 구축해서 전역으로 쓰고 있습니다. 따로 정의한 이유가 있을까요? UXUI 일관성을 헤칠수있고 추후 디자인 리팩토링하기 어려운 구조가 될 것으로 예상돼 걱정됩니다.

Comment on lines 38 to 51
interface Ranker {
rank: number;
userId: number;
nickname: string;
profileImage: string | null;
score: number;
scoreLabel: string;
change?: 'up' | 'down' | 'same';
lastActive: string;
studyTime: string;
major?: string;
streak?: number;
changeValue?: number;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

같은이유로 현재 페이지도 서버컴포넌트로 유지하며 내용물은 따로 뺐으면 좋겠고 타입 인터페이스같은경우엔 기존 FSD 에서는 Features 도메인 models 에서, 변경될 Type-based 구조에서는 /types 에서 정의가 됩니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

// Main Page
// ----------------------------------------------------------------------

export default function OneOnOnePage() {
Copy link
Contributor

Choose a reason for hiding this comment

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

UI 코드리뷰할땐 스크린샷 있으면 좋겠습니다. 추후 vercel preview URL 가 도입되면 모르겠지만 코드만 봐서는 감을 잡기 어렵네요. 나중에 기능폐기를 염두해두고 원페이지에 모든 코드를 담은건지는 모르겠지만 weekly 쪽 커멘트처럼 코드를 찢어서 구조화하고 기존 디자인시스템 활용했으면 좋겠습니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

스샷포함하겟습니다.

const parts = token.split('.');
if (parts.length !== 3) {
return null; // 형식이 맞지 않으면 조용히 null 반환
}
Copy link
Contributor

Choose a reason for hiding this comment

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

이 코드의 목적이 무엇인지 궁금합니다. 형식이 안맞는 JWT 를 디코딩할 경우가 있나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

백엔드는 로컬에서 JWT가 아닌 순수 JSON형식으로 토큰을 주고 받아서 그 이유로 그냥 JSON 문자열이 들어와서 런타임에러로 화면이 크래시가 되었었습니다.

@@ -0,0 +1,62 @@
// Discussion 관련 타입 정의

export type DiscussionTopic =
Copy link
Contributor

Choose a reason for hiding this comment

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

hall-of-fame 같은경우엔 fsd 아키텍쳐에따라 model 내에서 types 정의가 이루어졌는데 이부분은 Type-based 구조에따라 types 에 정의가 되어있네요. app/ features 내 types / types 폴더 타입정의를 각각 다르게 둔 판단기준이 있는지 궁금합니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

공용이면 src/types/, 도메인 전용이면 features//types.ts
로 두는것이 어떨까 싶은데 어떻게 생각하시나요?
해당 타입의 소유권과 의존 범위를 기준으로 결정하는것이 좋아보입니다.
특정 feature 내부 규칙에 가까운 타입은 feature/model에,
여러 feature 및 UI/서버 계약으로 공유되는 타입은
src/types에 배치하는 것으로요.

Copy link
Contributor

Choose a reason for hiding this comment

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

FSD 아키텍쳐 -> Type-based 로 전환하면 features, entities 레이어 자체가 없어집니다.
아직 리팩토링이 점진적으로 진행되는 과정으로 파악되는데 미리 types 에 몰아넣는게 합리적이라고 생각합니다.

image

@Hyeonjun0527
Copy link
Member Author

.idea 폴더와 docker compose 등은 빠질거라는거죠? 리뷰받을 코드와 배포 커밋이 달라야할 이유가 있을까요?

.idea 폴더 docker compose, xml 많은 부분이 잘못 수정되었기때문에 이부분을 전부 롤백해야하는데, 깃 특성상 중간사이에 잇는 커밋만 롤백하기가 어렵습니다. 그렇다고 수동으로 하나하나 다시 복구하기도 불필요하고 실수유발이 됩니다.
그래서 수정하였기 전 커밋으로 되돌아가는 것이 복구가 하기가 편하여 그렇게 하려고 합니다.
커밋컨벤션도 잘못된 것이 있습니다. 그리고, 저는 오히려 마구 개발했던 중 많이 refactoring,feat,fix가 생기므로 바로 커밋하기보단 커밋을 합쳐 롤백가능한 단위로 커밋을 해야한다고 판단합니다. 이 커밋을 모두 하나로 스쿼시하는 것은 롤백하기 쉽게하여 서비스를 위험하지 않게 하기 위함입니다. 수십개의 커밋이 들어가도 되는 경우는 각 커밋 단위가 롤백가능한 단위여야한다고 생각합니다.

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.

4 participants