Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions public/assets/login/white-cheese-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/rending/new-swipe/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/rending/new-swipe/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/rending/new-swipe/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/rending/new-swipe/4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export const metadata: Metadata = {
},
};

export const viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

뷰포트 설정에서 userScalable: false를 사용하면 사용자가 페이지를 확대/축소할 수 없게 됩니다. 이는 접근성 가이드라인(WCAG 2.1 - 1.4.4 Resize text)에 위배될 수 있으며, 저시력 사용자에게 불편을 초래할 수 있습니다. 꼭 필요한 경우가 아니라면 이 설정을 제거하거나 true로 변경하는 것을 고려해주세요.

Suggested change
userScalable: false,
userScalable: true,

};

export default function RootLayout({
children,
}: Readonly<{
Expand Down
4 changes: 2 additions & 2 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ScreenRoot from '@/feature/root/components/ScreenRoot';
import ScreenNewRoot from '@/feature/root/components/ScreenNewRoot';

export default function Page() {
return <ScreenRoot />;
return <ScreenNewRoot />;
}
17 changes: 17 additions & 0 deletions src/feature/root/components/FlashRending.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Image from 'next/image';

const FlashRending = () => {
return (
<div className='flex min-h-screen items-center justify-center bg-[#FFCD14]'>
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

하드코딩된 배경색(bg-[#FFCD14])을 사용하고 있습니다. 프로젝트의 다른 부분에서는 bg-background-brand와 같이 Tailwind CSS 테마에 정의된 색상을 사용하고 있습니다. 일관성 있는 디자인 시스템을 유지하고 유지보수성을 높이기 위해, 이 색상도 Tailwind 설정 파일에 추가하고 시맨틱한 이름으로 사용하는 것을 권장합니다. 예를 들어 bg-brand-primary와 같이 사용할 수 있습니다.

<Image
src='/assets/login/white-cheese-logo.svg'
alt='Cheese Logo'
width={169}
height={36}
priority
/>
</div>
);
};

export default FlashRending;
153 changes: 153 additions & 0 deletions src/feature/root/components/RendingBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
'use client';

import {
Carousel,
CarouselContent,
CarouselItem,
type CarouselApi,
} from '@/components/ui/carousel';
import { buildQuery } from '@/global/utils/buildQuery';
import Image from 'next/image';
import { useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';

const KAKAO_AUTH_URL = `https://dev.say-cheese.me/oauth2/authorization/kakao`;
Copy link
Contributor

Choose a reason for hiding this comment

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

high

개발 환경의 URL이 하드코딩되어 있습니다. 배포 환경(production, development 등)에 따라 동적으로 URL을 설정할 수 있도록 .env 파일과 같은 환경 변수를 사용하는 것이 좋습니다. 이렇게 하면 환경별로 코드를 수정할 필요가 없어지고, 보안에도 더 안전합니다.

Suggested change
const KAKAO_AUTH_URL = `https://dev.say-cheese.me/oauth2/authorization/kakao`;
const KAKAO_AUTH_URL = `${process.env.NEXT_PUBLIC_API_URL}/oauth2/authorization/kakao`;


export const RendingBody = () => {
const [api, setApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
const searchParams = useSearchParams();
const redirect = searchParams.get('redirect');

const slides = [
{
src: '/assets/rending/new-swipe/1.png',
alt: '앨범 목록 화면',
width: 260,
height: 530,
title: '이벤트마다 만드는 공유앨범',
description: '감튀 모임부터 찐친 여행까지',
},
{
src: '/assets/rending/new-swipe/2.png',
alt: 'QR 코드 공유',
width: 260,
height: 530,
title: '이벤트마다 모인 자리에서 바로 공유',
description: '감튀 모임부터 앨범 만들고 초대까지 딱 10초',
},
{
src: '/assets/rending/new-swipe/3.png',
alt: '베스트컷',
width: 312.5,
height: 530.3,
title: '이벤트마다 한눈에 보는 베스트컷',
description: '사진 고르는 고민 이제 끝',
},
{
src: '/assets/rending/new-swipe/4.png',
alt: '네컷추억',
width: 289,
height: 530,
title: '딱 네컷으로 남는 추억',
description: '앨범이 닫히면 사라지는 원본 사진들',
},
];
Comment on lines +22 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

slides 배열은 컴포넌트가 렌더링될 때마다 재생성될 필요가 없는 정적인 데이터입니다. 컴포넌트 외부로 이동시켜 불필요한 재생성을 방지하면 성능을 개선할 수 있습니다.


// 캐러셀 API를 통해 현재 슬라이드 추적
useEffect(() => {
if (!api) return;

api.on('select', () => {
setCurrent(api.selectedScrollSnap());
});
}, [api]);
Comment on lines +58 to +64
Copy link
Contributor

Choose a reason for hiding this comment

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

high

useEffect에서 api.on('select', ...)로 이벤트 리스너를 등록한 후, 컴포넌트가 언마운트될 때 이를 정리(clean up)하는 로직이 없습니다. 이는 메모리 누수를 유발할 수 있습니다. useEffect의 반환값으로 cleanup 함수를 추가하여 이벤트 리스너를 제거해주세요.

  useEffect(() => {
    if (!api) return;

    const onSelect = () => {
      setCurrent(api.selectedScrollSnap());
    };

    api.on('select', onSelect);

    return () => {
      api.off('select', onSelect);
    };
  }, [api]);


const handleKakaoLogin = async () => {
try {
const kakaoUrl = redirect
? `${KAKAO_AUTH_URL}${buildQuery({ redirect })}`
: KAKAO_AUTH_URL;

window.location.href = kakaoUrl;
} catch (err) {
console.error('카카오 인증 GET 요청 실패:', err);
}
};
Comment on lines +66 to +76
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

handleKakaoLogin 함수가 async로 선언되었지만 await 키워드를 사용하지 않습니다. 비동기 작업이 없으므로 async 키워드를 제거하는 것이 좋습니다. 또한, window.location.href 할당은 일반적으로 예외를 발생시키지 않으므로 try...catch 블록도 제거할 수 있습니다.

  const handleKakaoLogin = () => {
    const kakaoUrl = redirect
      ? `${KAKAO_AUTH_URL}${buildQuery({ redirect })}`
      : KAKAO_AUTH_URL;

    window.location.href = kakaoUrl;
  };


return (
<section className='bg-background-white-muted relative flex h-screen w-full flex-col'>
{/* 캐러셀 영역 */}
<div className='flex h-screen w-full flex-col items-center justify-start px-4 pt-12'>
<Carousel
setApi={setApi}
className='w-full max-w-md'
opts={{
loop: true,
align: 'center',
}}
>
<CarouselContent>
{slides.map((slide, index) => (
<CarouselItem key={index}>
<div className='flex items-center justify-center overflow-hidden rounded-3xl'>
<Image
src={slide.src}
alt={slide.alt}
width={slide.width}
height={slide.height}
className='object-contain'
priority={index === 0}
/>
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</div>

{/* 하단 흰색 배경 영역 - 이미지 위에 오버레이 */}
<div className='absolute bottom-0 z-10 flex h-[242px] w-full flex-col items-center bg-white px-4 pt-8 pb-5'>
{/* 텍스트 영역 */}
<div className='mb-6 flex flex-col items-center text-center'>
<h2 className='text-text-basic text-[24px] font-[600]'>
{slides[current].title}
</h2>
<p className='mt-1 text-[16px] font-[500] text-[#746181]'>
{slides[current].description}
</p>
</div>

{/* 인디케이터 점들 */}
<div className='flex gap-2'>
{slides.map((_, index) => (
<button
key={index}
onClick={() => api?.scrollTo(index)}
className={`h-1.5 rounded-full transition-all ${
current === index ? 'w-6 bg-gray-700' : 'w-1.5 bg-gray-200'
}`}
aria-label={`슬라이드 ${index + 1}로 이동`}
/>
))}
</div>

{/* 카카오 로그인 버튼 */}
<div
className='mt-auto flex h-[56px] w-full cursor-pointer items-center justify-center gap-2 rounded-[6px] bg-[#FEE500]'
onClick={handleKakaoLogin}
>
Comment on lines +136 to +139
Copy link
Contributor

Choose a reason for hiding this comment

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

high

클릭 가능한 카카오 로그인 영역이 div 태그로 구현되어 있습니다. 스크린 리더 사용자나 키보드만으로 네비게이션하는 사용자를 위해 웹 접근성을 준수하는 것이 중요합니다. div 대신 시맨틱한 button 태그를 사용하고, type='button' 속성을 추가하는 것을 권장합니다. button으로 변경 후 className에서 cursor-pointer는 제거해도 됩니다.

        <button
          type='button'
          className='mt-auto flex h-[56px] w-full items-center justify-center gap-2 rounded-[6px] bg-[#FEE500]'
          onClick={handleKakaoLogin}
        >

<Image
src='/assets/login/kakao-logo.svg'
width={18}
height={18}
alt='카카오 로고'
/>
<span className='text-[15px] font-[600] text-[rgba(0,0,0,0.85)]'>
카카오 로그인
</span>
</div>
</div>
</section>
Comment on lines +110 to +151
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

이 영역 전반에 걸쳐 하드코딩된 색상 값(text-[#746181], bg-[#FEE500], text-[rgba(0,0,0,0.85)])이 사용되고 있습니다. 프로젝트의 다른 부분에서는 bg-background-white-muted와 같이 Tailwind CSS 테마에 정의된 색상을 사용하고 있습니다. 일관성 있는 디자인 시스템을 유지하고 유지보수성을 높이기 위해, 이 색상들도 Tailwind 설정 파일에 추가하고 시맨틱한 이름으로 사용하는 것을 권장합니다.

예를 들어,

  • text-[#746181] (116행) -> text-text-muted
  • bg-[#FEE500] (137행) -> bg-kakao (카카오 브랜드 색상이므로 명확한 이름 사용)
  • text-[rgba(0,0,0,0.85)] (146행) -> text-black/85 (Tailwind의 opacity modifier 사용)

);
};
37 changes: 37 additions & 0 deletions src/feature/root/components/ScreenNewRoot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client';

import { useCheckAuth } from '@/global/hooks/useCheckAuth';
import { useEffect, useState } from 'react';
import FlashRending from './FlashRending';
import { RendingBody } from './RendingBody';

const ScreenNewRoot = () => {
const { isAuthed } = useCheckAuth();
const [showSplash, setShowSplash] = useState(true);
Comment on lines +8 to +10
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

로그인된 사용자가 메인 페이지로 리디렉션되지 않는 문제가 있습니다. useCheckAuth 훅이 onAuthed 콜백 없이 호출되어, 인증된 사용자가 랜딩 페이지를 보게 됩니다. 이전 ScreenRoot 컴포넌트처럼 onAuthed 콜백을 사용하여 /main으로 리디렉션하는 로직을 추가해야 합니다.

아래와 같이 수정하고, 파일 상단에 import { useRouter } from 'next/navigation';을 추가해주세요.

Suggested change
const ScreenNewRoot = () => {
const { isAuthed } = useCheckAuth();
const [showSplash, setShowSplash] = useState(true);
const ScreenNewRoot = () => {
const router = useRouter();
const { isAuthed } = useCheckAuth({ onAuthed: () => router.push('/main') });
const [showSplash, setShowSplash] = useState(true);


console.log('ScreenNewRoot - isAuthed:', isAuthed, 'showSplash:', showSplash);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

디버깅 목적으로 사용된 console.log 문이 남아있습니다. 프로덕션 코드에 포함되지 않도록 병합 전에 제거해주세요.


// 비로그인 상태일 때만 2초 후 스플래시 숨김
useEffect(() => {
if (isAuthed === false) {
const timer = setTimeout(() => {
setShowSplash(false);
}, 2000);
return () => clearTimeout(timer);
}
}, [isAuthed]);

// 인증 확인 중이거나 비로그인 상태에서 스플래시 표시 중일 때
if (isAuthed === null || (isAuthed === false && showSplash)) {
return <FlashRending />;
}

// 로그인 상태이거나 스플래시 시간이 지난 비로그인 사용자
return (
<div className='bg-background-brand flex min-h-screen flex-col items-center justify-center'>
<RendingBody />
</div>
);
};

export default ScreenNewRoot;
147 changes: 0 additions & 147 deletions src/feature/root/components/ScreenRoot.tsx

This file was deleted.

Loading
Loading