Skip to content

Conversation

@dasosann
Copy link
Contributor

@dasosann dasosann commented Nov 8, 2025

[7팀] {치이이즈} 프론트엔드 코드리뷰

✨ 리뷰를 요청드리는 주요 부분

가장 고민되는 지점을 아래 형식으로 정리했습니다.

파일경로 : 조언이 필요한 포인트

(zustand에 이미지 저장 후 화면에 불러오기 )

  • src\feature\create-album\utils\saveFilesToStore.ts
  • src\app\album\[albumId]\select\page.tsx
  • src\feature\album-select\components\SelectAlbumBody.tsx
    ( ncp에서 이미지 불러오기 )
  • src\app\album\detail\[albumId]\page.tsx

💬 치이이즈팀이 고민하고 있는 부분

  • 현재 zustand를 이용해 ncp storage에 이미지를 올리기 전에 사진들이 조건을 만족하지 않으면 zustand에 이미지들을 담고 있는데
    여기서 zustand에 저장된 이미지가 많아질 경우에 이 이미지들을 사용자에게 blob형태로 한번에 보여줄때 어떻게 최적화 시킬 수가 있을지가 궁금합니다.

💌 요청드리는 리뷰 방향

  1. 현재 폴더구조를 팀원과 조율 후 사용하고 있는데 사용하다 보니까 혼잡해지는 경향이 조금 있는 것 같아서 이런 상황에서 어떠한 폴더구조를 선택하는게 좋을지 조언을 듣고 싶습니다!
  2. 사진들을 불러와서 사용자에게 보여줄 때 필요한 이미지들을 어떻게 빠르게 보여줄 수 있을지 고민됩니다!
  3. PhotoBox 나 BottomSheetModal, ConfirmModal 와 같이 모듈화된 컴포넌트의 경우, props가 과도하고 난잡한 감을 없지않아 느꼈던 것 같습니다.
    a. 가장 고민이었던 ConfirmModal (혹은, 이 링크) 컴포넌트에서,
    b. 디자인 주입하는 상태 (cancelClassName, confirmClassName) 로 커스텀하는것이 맞을지, 혹은 디자인을 변경할것이라면 컴포넌트를 아예 새로만드는것이 맞았는지
    c. 재사용성과 책임 분리가 괜찮은 방향인지 피드백 해주시면 감사하겠습니다.

바쁘신 와중에 시간 내어 리뷰해주셔서 감사합니다.
편하게 피드백 부탁드립니다! 🥹

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @dasosann, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 치이이즈 프론트엔드 프로젝트의 초기 구조를 설정하고 핵심 기능을 구현하는 광범위한 변경 사항을 포함합니다. Next.js 15를 기반으로 프로젝트 환경을 구축하고, shadcn/ui 및 vaul과 같은 라이브러리를 활용하여 UI 컴포넌트를 통합했습니다. 앨범 생성, 사진 업로드, 상세 보기, 공유 등 서비스의 주요 흐름을 구성하는 페이지와 기능들이 추가되었으며, Zustand를 통한 상태 관리와 NCP Presigned URL을 이용한 효율적인 파일 업로드 시스템이 마련되었습니다. 또한, 온보딩 및 카카오 소셜 로그인 기능이 도입되어 사용자 경험의 시작점을 완성했습니다. 전반적으로 프로젝트의 기반을 다지고 핵심적인 사용자 상호작용을 가능하게 하는 데 중점을 둔 변경입니다.

Highlights

  • 초기 Next.js 프로젝트 설정: Next.js 15.5.4 기반의 프로젝트 초기 설정 파일들(.gitignore, .prettierrc, eslint.config.mjs, next.config.ts, package.json, postcss.config.mjs, Dockerfile)이 추가되었습니다.
  • 디자인 시스템 및 UI 컴포넌트 통합: components.json 파일과 src/components/ui 디렉토리에 alert-dialog.tsx, button.tsx, carousel.tsx, drawer.tsx와 같은 UI 컴포넌트들이 추가되어 shadcn/ui 및 vaul 라이브러리를 활용한 디자인 시스템 기반이 마련되었습니다.
  • 앨범 관련 페이지 및 기능 추가: 앨범 생성, 선택, 상세 보기, 사진 업로드, 공유 진입 등 다양한 앨범 관련 페이지(src/app/album, src/app/create-album, src/app/photo, src/app/photo-share-entry, src/app/image-test)와 기능(src/feature/create-album, src/feature/album-select, src/feature/album/detail, src/feature/photo-detail, src/feature/photo-share-entry, src/feature/upload)이 대거 추가되었습니다.
  • 상태 관리 및 API 연동: Zustand를 활용한 이미지 및 앨범 상태 관리(src/store/useImageStore.ts, src/store/useAlbumStore.ts)와 함께 Presigned URL을 이용한 NCP 파일 업로드(src/global/api/getPresignedUrl.ts, src/global/api/presignedAndUploadToNCP.ts, src/global/hooks/usePresignedAndUploadToNCP.ts, src/global/utils/uploadToNCP.ts) 기능이 구현되었습니다.
  • 온보딩 및 로그인 기능: 온보딩 페이지(src/app/onboarding)와 로그인 페이지(src/app/login)가 추가되었으며, 카카오 소셜 로그인 연동(src/app/oauth/callback/route.ts) 및 사용자 프로필 설정 기능이 포함되었습니다.
  • 정적 자산 및 유틸리티 추가: 다양한 SVG 이미지(앨범, 로그인, 온보딩 관련)와 Lottie 애니메이션 JSON 파일(public/assets)이 추가되었고, src/global/utils에 API 요청, 쿠키 관리, 이모지 변환 등 여러 유틸리티 함수들이 포함되었습니다.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/deploy.yml
    • .github/workflows/lint-checker.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

전반적으로 Next.js 프로젝트의 초기 설정이 매우 체계적으로 잘 구성되어 있습니다. Docker, ESLint, Prettier, TailwindCSS 설정과 함께 디자인 토큰 시스템을 자동화하는 스크립트까지 포함되어 있어 인상적입니다. 다만, 몇 가지 개선점을 제안드립니다. 주로 의존성 버전 관리, 서버 컴포넌트의 타입 안정성, 클라이언트 및 서버 환경에서의 인증 처리 일관성 등에 대한 내용입니다. 아래의 상세 리뷰를 확인해주세요.

Comment on lines 5 to 8
}: {
params: Promise<{ albumId: string }>;
}) {
const { albumId } = await params;
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

페이지 컴포넌트의 params prop 타입이 Promise로 잘못 지정되었습니다. App Router에서 paramsPromise가 아닌 일반 객체로 전달됩니다. 이로 인해 타입 에러가 발생하며, await params 구문도 올바르게 동작하지 않습니다. 아래와 같이 수정해야 합니다.

  params: { albumId: string };
}) {
  const { albumId } = params;

Comment on lines +4 to +8
params: Promise<{ albumId: string }>;
};

export default async function Page({ params }: PageProps) {
const { albumId } = await params;
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

페이지 컴포넌트의 params prop 타입이 Promise로 잘못 지정되었습니다. App Router에서 paramsPromise가 아닌 일반 객체로 전달됩니다. 이로 인해 타입 에러가 발생하며, await params 구문도 올바르게 동작하지 않습니다. 아래와 같이 수정해야 합니다.

Suggested change
params: Promise<{ albumId: string }>;
};
export default async function Page({ params }: PageProps) {
const { albumId } = await params;
params: { albumId: string };
};
export default async function Page({ params }: PageProps) {
const { albumId } = params;

Comment on lines +4 to +10
params: Promise<{
albumId: string;
}>;
};

export default async function Page({ params }: PageProps) {
const { albumId } = await params;
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

페이지 컴포넌트의 params prop 타입이 Promise로 잘못 지정되었습니다. App Router에서 paramsPromise가 아닌 일반 객체로 전달됩니다. 이로 인해 타입 에러가 발생하며, await params 구문도 올바르게 동작하지 않습니다. 아래와 같이 수정해야 합니다.

  params: {
    albumId: string;
  }>;
};

export default async function Page({ params }: PageProps) {
  const { albumId } = params;

"@radix-ui/react-slot": "^1.2.3",
"@tanstack/react-query": "^5.90.2",
"@tanstack/react-query-devtools": "^5.90.2",
"axios": "^1.12.2",
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

axios의 버전이 ^1.12.2로 지정되어 있습니다. axios의 최신 안정 버전은 1.7.2이며, 1.12.2는 존재하지 않는 버전이라 pnpm install 실행 시 오류가 발생할 수 있습니다. 최신 안정 버전으로 수정하는 것을 권장합니다.

Suggested change
"axios": "^1.12.2",
"axios": "^1.7.2",

Comment on lines +4 to +10
params: Promise<{
albumId: string;
}>;
}

export default async function Page({ params }: PageProps) {
const { albumId } = await params;
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

페이지 컴포넌트의 params prop 타입이 Promise로 잘못 지정되었습니다. App Router에서 paramsPromise가 아닌 일반 객체로 전달됩니다. 이로 인해 타입 에러가 발생하며, await params 구문도 올바르게 동작하지 않습니다. 아래와 같이 수정해야 합니다.

  params: {
    albumId: string;
  }>;
}

export default async function Page({ params }: PageProps) {
  const { albumId } = params;

Comment on lines 68 to 76
<body
className={`${pretendard.className} antialiased`}
style={{
maxWidth: '430px',
width: '100vw',
margin: '0 auto',
background: '#fff',
}}
>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

<body> 태그에 레이아웃 관련 스타일이 인라인으로 적용되어 있습니다. 유지보수성과 코드 가독성을 위해 이 스타일들을 globals.css 파일로 옮겨 클래스로 관리하는 것이 좋습니다.

예를 들어, globals.css에 아래와 같이 클래스를 정의하고,

.app-layout {
  max-width: 430px;
  width: 100vw;
  margin: 0 auto;
  background: #fff;
}

<body> 태그에 className으로 적용할 수 있습니다.

Suggested change
<body
className={`${pretendard.className} antialiased`}
style={{
maxWidth: '430px',
width: '100vw',
margin: '0 auto',
background: '#fff',
}}
>
<body
className={`${pretendard.className} antialiased app-layout`}
>

request.headers.get('host') ||
'localhost:3000';

const redirectPath = data.result.isOnboarded ? '/main' : '/create-album';
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

신규 사용자의 경우 (isOnboardedfalse) /create-album 페이지로 리디렉션하고 있습니다. 프로젝트에 /onboarding 페이지가 존재하는 것으로 보아, 신규 사용자는 온보딩 과정을 먼저 거치는 것이 의도된 흐름일 수 있습니다. 현재 로직이 의도된 동작이 맞는지 확인해보시고, 만약 온보딩 페이지로 보내는 것이 맞다면 리디렉션 경로를 /onboarding으로 수정하는 것을 고려해보세요.

Suggested change
const redirectPath = data.result.isOnboarded ? '/main' : '/create-album';
const redirectPath = data.result.isOnboarded ? '/main' : '/onboarding';

Comment on lines +7 to +34
function Drawer({
...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
useEffect(() => {
const cleanup = () => {
document.body.removeAttribute('data-scroll-locked');
document.body.style.paddingLeft = '';
document.body.style.paddingRight = '';
document.body.style.marginLeft = 'auto';
document.body.style.marginRight = 'auto';
document.body.style.overflow = '';
};

const observer = new MutationObserver(() => {
if (document.body.hasAttribute('data-scroll-locked')) {
cleanup();
}
});

observer.observe(document.body, { attributes: true });

return () => {
observer.disconnect();
cleanup();
};
}, []);
return <DrawerPrimitive.Root data-slot='drawer' {...props} />;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

useEffectMutationObserver를 사용하여 vaul 라이브러리가 추가하는 스크롤 잠금 관련 속성들을 강제로 제거하고 있습니다. 이는 라이브러리의 기본 동작을 우회하는 핵(hack)적인 방법으로, 예기치 않은 부작용을 일으킬 수 있습니다. vaul 라이브러리는 스크롤 방지 동작을 제어하는 preventScroll prop을 제공합니다. 이 prop을 DrawerPrimitive.Root에 전달하여 스크롤 방지 기능을 비활성화하는 것이 더 안정적인 방법입니다. useEffect 훅을 제거하고 아래와 같이 수정하는 것을 권장합니다.

function Drawer({
  ...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
  return <DrawerPrimitive.Root data-slot='drawer' preventScroll={false} {...props} />;
}

],
rules: {
'@typescript-eslint/no-empty-object-type': 'off', // 빈 타입 객체 허용
'@next/next/no-img-element': 'off',
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

@next/next/no-img-element 규칙을 비활성화하셨습니다. Next.js의 next/image 컴포넌트는 이미지 최적화(리사이징, 포맷 변환, 지연 로딩 등)를 자동으로 처리해주는 강력한 기능입니다. 이 규칙을 비활성화하면 이러한 최적화 이점을 포기하게 되는데, 특별한 이유가 있으신가요? 만약 <img> 태그를 직접 사용해야 하는 상황이 아니라면, 이 규칙을 활성화하여 next/image 사용을 권장하는 것이 성능에 도움이 될 것입니다.

Comment on lines +98 to +99
const parts = ref.split('.');
if (parts.length > 0) parts[0] = parts[0].trim();
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

토큰 참조 값을 파싱할 때 ref.split('.').map(p => p.trim())과 같이 각 부분을 trim()하는 것이 더 안전합니다. 현재 코드는 첫 번째 부분만 trim()하고 있어 Color . Blue . 500과 같이 중간에 공백이 있는 참조를 올바르게 처리하지 못할 수 있습니다.

Suggested change
const parts = ref.split('.');
if (parts.length > 0) parts[0] = parts[0].trim();
const parts = ref.split('.').map(p => p.trim());

@vercel
Copy link

vercel bot commented Nov 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
fe Ready Ready Preview, Comment Jan 1, 2026 7:26am

dasosann and others added 2 commits November 29, 2025 00:37
feat: 앨범 생성 lighthouse 개선
fix : 사진상세 하단 swiper가 pc에서도 잘 뜨게 수정
fix: 촬영 시각은 받은 시간 그대로 변환
refactor: lighthouse 성능개선
fix : next,react 보안문제 해결을 위해 안정적 패치 버전으로 변경
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.

3 participants