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
5 changes: 1 addition & 4 deletions src/app/(auth)/login/_styles/Login.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ export const backgroundWrapper = style({
position: "relative",
width: "100%",
height: "100%",
backgroundImage: "url(\"/images/login-background.webp\")",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
Expand All @@ -27,6 +23,7 @@ export const textButton = style({
});

export const logoWrapper = style({
position: "relative",
width: "100%",
height: "100%",
display: "flex",
Expand Down
7 changes: 7 additions & 0 deletions src/app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export default function LoginPage() {
return (
<main className={styles.wrapper}>
<div className={styles.backgroundWrapper}>
<Image
src='/images/login-background.png'
alt='로그인 배경화면'
fill
priority
/>

Comment on lines +25 to +31
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

fill 이미지에 sizes 누락 – Next.js 경고 및 불필요한 이미지 다운로드 가능성
next/image에서 fill 사용 시 sizes 속성이 없으면 빌드/런타임 경고가 발생하고, 뷰포트보다 큰 이미지가 내려받혀 초기 로딩이 느려질 수 있습니다.

아래와 같이 sizes="100vw"(또는 적절한 미디어쿼리) 추가를 권장합니다.

         <Image
           src='/images/login-background.png'
           alt='로그인 배경화면'
           fill
+          sizes='100vw'
           priority
         />

fill 속성 사용 시 sizes 누락으로 인한 경고 및 불필요한 큰 이미지 다운로드 예방
Next.js에서 fill 레이아웃을 사용할 때 sizes를 지정하지 않으면 빌드/런타임 경고가 발생하고, 뷰포트보다 큰 이미지를 내려받아 초기 로딩 속도가 느려질 수 있습니다.
src/app/(auth)/login/page.tsx 파일의 <Image> 컴포넌트에 sizes="100vw"(또는 적절한 미디어쿼리)를 추가해주세요.

  • 파일: src/app/(auth)/login/page.tsx
  • 위치: <Image> 컴포넌트 (fill 속성 사용 부분`)
         <Image
           src='/images/login-background.png'
           alt='로그인 배경화면'
           fill
+          sizes='100vw'
           priority
         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Image
src='/images/login-background.png'
alt='로그인 배경화면'
fill
priority
/>
<Image
src='/images/login-background.png'
alt='로그인 배경화면'
fill
sizes='100vw'
priority
/>
🤖 Prompt for AI Agents
In src/app/(auth)/login/page.tsx around lines 25 to 31, the <Image> component
uses the fill attribute but lacks the sizes attribute, causing build/runtime
warnings and potentially downloading unnecessarily large images. Add
sizes="100vw" to the <Image> component to specify the image size relative to the
viewport width, preventing these issues and improving loading performance.

<GNB
align='left'
background='transparent'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,10 @@ export const content = style({
height: "54.8rem",
});

export const titleWrapper = style({
export const iconWrapper = style({
position: "relative",
});

export const title = style({
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: "0.8rem",
textAlign: "center",
});

export const cancelIconWrapper = style({
position: "absolute",
top: 0,
Expand All @@ -29,6 +21,14 @@ export const cancelIcon = style({
color: semantic.icon.gray,
});

export const title = style({
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: "0.8rem",
textAlign: "center",
});

export const mainTitle = style({
whiteSpace: "pre-line",
});
Expand All @@ -38,4 +38,15 @@ export const body = style({
flexDirection: "column",
alignItems: "center",
gap: "1.6rem",
padding: 0,
});

export const sliderContainer = style({
width: "100%",
});

export const imageWrapper = style({
display: "flex",
justifyContent: "center",
alignItems: "center",
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,47 @@

import { AnimatePresence, motion } from "motion/react";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import Slider from "react-slick";

import CancelIcon from "@/assets/cancel.svg";
import { BottomSheet } from "@/components/ui/BottomSheet";
import { Button } from "@/components/ui/Button";
import { Indicator } from "@/components/ui/Indicator";
import { Spacer } from "@/components/ui/Spacer";
import { Text } from "@/components/ui/Text";

import { INTRO_STEP_CONTENTS, type IntroStepContent } from "../../_constants";
import * as styles from "./ServiceIntroBottomSheet.css";

export const ServiceIntroBottomSheet = () => {
const slickRef = useRef<Slider>(null);
const [currentIndex, setCurrentIndex] = useState(0);
const [isOpen, setIsOpen] = useState(false);

useEffect(() => {
const hasWatchIntro = localStorage.getItem("watchIntro") === "true";

if (!hasWatchIntro) {
setIsOpen(true);
localStorage.setItem("watchIntro", "true");
}
}, []);

const sliderSettings = {
arrows: false,
beforeChange: (current: number, next: number) => {
setCurrentIndex(next);
},
};

const handleDotClick = (index: number) => {
setCurrentIndex(index);
slickRef.current?.slickGoTo(index);
};

const handleNext = () => {
if (currentIndex < INTRO_STEP_CONTENTS.length - 1) {
setCurrentIndex(currentIndex + 1);
slickRef.current?.slickNext();
} else {
setIsOpen(false);
}
Expand All @@ -47,7 +57,7 @@ export const ServiceIntroBottomSheet = () => {
return (
<BottomSheet.Root open={isOpen} onOpenChange={setIsOpen}>
<BottomSheet.Content className={styles.content}>
<BottomSheet.Title className={styles.titleWrapper}>
<BottomSheet.Title className={styles.iconWrapper}>
<div className={styles.cancelIconWrapper}>
<button
type='button'
Expand All @@ -61,43 +71,54 @@ export const ServiceIntroBottomSheet = () => {
/>
</button>
</div>

<AnimatePresence mode='wait'>
<motion.div
className={styles.title}
key={currentIndex}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Text typo='title1Bd' className={styles.mainTitle}>
{currentContent.title}
</Text>
<Text typo='body2Rg' color='neutral.50'>
{currentContent.subtitle}
</Text>
</motion.div>
</AnimatePresence>
</BottomSheet.Title>

<BottomSheet.Body className={styles.body}>
<AnimatePresence mode='wait'>
<motion.div
key={currentIndex}
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
>
<Image
width={335}
height={252}
src={currentContent.imageSrc}
alt={currentContent.imageAlt}
/>
</motion.div>
</AnimatePresence>
<div className={styles.sliderContainer}>
<Slider ref={slickRef} {...sliderSettings}>
{INTRO_STEP_CONTENTS.map(content => (
<>
<AnimatePresence mode='wait'>
<motion.div
className={styles.title}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Text typo='title1Bd' className={styles.mainTitle}>
{content.title}
</Text>
<Text typo='body2Rg' color='neutral.50'>
{content.subtitle}
</Text>
</motion.div>
</AnimatePresence>

<Spacer size={28} />

<AnimatePresence mode='wait'>
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className={styles.imageWrapper}
>
<Image
width={335}
height={252}
src={content.imageSrc}
alt={content.imageAlt}
priority
/>
</motion.div>
</AnimatePresence>
</>
))}
Comment on lines +79 to +118
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

슬라이더 내부 Fragment 및 애니메이션 구조 검토 필요

현재 구현에 몇 가지 잠재적 문제가 있습니다:

  1. Slider 컴포넌트의 직접 자식으로 Fragment(<>)를 사용하면 슬라이더가 제대로 작동하지 않을 수 있습니다.
  2. AnimatePresence가 map 내부에 있어 각 슬라이드마다 별도의 애니메이션 컨텍스트가 생성됩니다.

다음과 같이 수정을 제안합니다:

-              {INTRO_STEP_CONTENTS.map(content => (
-                <>
-                  <AnimatePresence mode='wait'>
-                    <motion.div
+              {INTRO_STEP_CONTENTS.map((content, index) => (
+                <div key={index}>
+                  <motion.div
                      className={styles.title}
                      initial={{ opacity: 0, y: 10 }}
                      animate={{ opacity: 1, y: 0 }}
                      exit={{ opacity: 0, y: -10 }}
                      transition={{ duration: 0.3 }}
                    >
                      <Text typo='title1Bd' className={styles.mainTitle}>
                        {content.title}
                      </Text>
                      <Text typo='body2Rg' color='neutral.50'>
                        {content.subtitle}
                      </Text>
                    </motion.div>
-                  </AnimatePresence>

                  <Spacer size={28} />

-                  <AnimatePresence mode='wait'>
-                    <motion.div
+                  <motion.div
                      initial={{ opacity: 0, scale: 0.95 }}
                      animate={{ opacity: 1, scale: 1 }}
                      exit={{ opacity: 0, scale: 0.95 }}
                      transition={{ duration: 0.3 }}
                      className={styles.imageWrapper}
                    >
                      <Image
                        width={335}
                        height={252}
                        src={content.imageSrc}
                        alt={content.imageAlt}
                        priority
                      />
                    </motion.div>
-                  </AnimatePresence>
-                </>
+                </div>
              ))}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{INTRO_STEP_CONTENTS.map(content => (
<>
<AnimatePresence mode='wait'>
<motion.div
className={styles.title}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Text typo='title1Bd' className={styles.mainTitle}>
{content.title}
</Text>
<Text typo='body2Rg' color='neutral.50'>
{content.subtitle}
</Text>
</motion.div>
</AnimatePresence>
<Spacer size={28} />
<AnimatePresence mode='wait'>
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className={styles.imageWrapper}
>
<Image
width={335}
height={252}
src={content.imageSrc}
alt={content.imageAlt}
priority
/>
</motion.div>
</AnimatePresence>
</>
))}
{INTRO_STEP_CONTENTS.map((content, index) => (
<div key={index}>
<motion.div
className={styles.title}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<Text typo='title1Bd' className={styles.mainTitle}>
{content.title}
</Text>
<Text typo='body2Rg' color='neutral.50'>
{content.subtitle}
</Text>
</motion.div>
<Spacer size={28} />
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className={styles.imageWrapper}
>
<Image
width={335}
height={252}
src={content.imageSrc}
alt={content.imageAlt}
priority
/>
</motion.div>
</div>
))}
🤖 Prompt for AI Agents
In
src/app/(home)/_components/ServiceIntroBottomSheet/ServiceIntroBottomSheet.tsx
lines 79 to 118, the current use of a Fragment as a direct child of the Slider
component and placing AnimatePresence inside the map causes animation and slider
functionality issues. To fix this, replace the Fragment with a single wrapper
element that has a unique key for each content item, and move AnimatePresence
outside the map so it wraps the entire list of slides. This ensures proper
animation context and slider behavior.

</Slider>
</div>

<Indicator
totalCount={INTRO_STEP_CONTENTS.length}
currentIndex={currentIndex}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const addButton = style({
left: "2.9rem",
width: "2.1rem",
height: "2.1rem",
padding: "0.5rem",
padding: "0.3rem",
borderRadius: radius.circle,
background: `${colors.neutral[30]}`,
border: `2px solid ${colors.common[100]}`,
Expand Down
2 changes: 1 addition & 1 deletion src/app/(home)/_components/Story/Story.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { radius } from "@/styles";

export const wrapper = style({
width: "100%",
height: "9rem",
height: "9.5rem",
overflowX: "auto",
"::-webkit-scrollbar": {
display: "none",
Expand Down
25 changes: 13 additions & 12 deletions src/app/(home)/_components/Story/StoryList/StoryList.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@ import { style } from "@vanilla-extract/css";

import { radius } from "@/styles";

export const wrapper = style({
width: "100%",
height: "9rem",
overflowX: "auto",
"::-webkit-scrollbar": {
display: "none",
},
export const storyItem = style({
flexShrink: 0,
padding: "0.3rem",
background:
"linear-gradient(45deg, #ff6f0f, #ff9047, #ffb07f, rgba(0, 255, 128, 0.7), #49e57d)",
borderRadius: radius.circle,
});
Comment on lines +5 to 11
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

스토리 아이템 구조 개선이 잘 되었으나 그라디언트 색상을 검토해 주세요.

CSS 구조를 분리하여 관심사를 명확히 구분한 것은 좋은 개선입니다. 하지만 그라디언트에서 오렌지/빨간색 톤과 녹색 톤을 함께 사용하는 것이 디자인 시스템과 일관성이 있는지 확인이 필요합니다.

다음 스크립트로 디자인 시스템의 색상 팔레트와 일관성을 확인해 주세요:


🏁 Script executed:

#!/bin/bash
# 디자인 시스템 색상 정의 확인
rg -A 10 -B 5 "colors.*=" src/styles/

Length of output: 637


다음 스크립트로 그라디언트에 사용된 각 헥스 값이 src/styles/colors.css.ts에 정의되어 있는지 확인해 주세요:


🏁 Script executed:

#!/bin/bash
for hex in ff6f0f ff9047 ffb07f 49e57d 00ff80; do
  echo -n "$hex: "
  rg -n "$hex" src/styles/colors.css.ts || echo "not found"
done

Length of output: 729


스토리 아이템 그라디언트 색상 팔레트 일관성 확인 및 수정 필요

구조 개선 사항은 승인하나, 현재 하드코딩된 그라디언트 색상들이 디자인 시스템에 정의되어 있지 않아 일관성 문제가 발생합니다. 아래 항목을 검토하고 조치해주세요.

• 문제 위치

  • 파일: src/app/(home)/_components/Story/StoryList/StoryList.css.ts
  • style 정의:
    export const storyItem = style({
      /* … */
      background: "linear-gradient(45deg, #ff6f0f, #ff9047, #ffb07f, rgba(0, 255, 128, 0.7), #49e57d)",
      /* … */
    });

• 미정의 컬러

  • #ff6f0f
  • #ff9047
  • #ffb07f
  • rgba(0, 255, 128, 0.7)
  • #49e57d

조치 제안

  1. 디자인 시스템(src/styles/colors.css.ts)에 해당 색상을 컬러 토큰으로 추가 후 사용
  2. 혹은 기존 정의된 토큰으로 유사한 색상을 매핑하여 사용
🤖 Prompt for AI Agents
In src/app/(home)/_components/Story/StoryList/StoryList.css.ts lines 5 to 11,
the linear-gradient background uses hardcoded colors not defined in the design
system, causing inconsistency. To fix this, either add these specific colors as
new color tokens in the design system file src/styles/colors.css.ts and then
reference those tokens here, or find existing similar color tokens in the design
system and replace the hardcoded colors with those tokens in the gradient
definition.


export const storyImage = style({
export const storyImageInner = style({
padding: "3px",
backgroundColor: "white",
borderRadius: radius.circle,
objectFit: "cover",
cursor: "pointer",
});

export const storyItem = style({
flexShrink: 0,
export const storyImage = style({
display: "block",
borderRadius: radius.circle,
cursor: "pointer",
});
17 changes: 10 additions & 7 deletions src/app/(home)/_components/Story/StoryList/StoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ export const StoryList = () => {
className={styles.storyItem}
onClick={() => handleStoryClick(story.storyId)}
>
<Image
src={story.imageUrl}
width={80}
height={80}
alt={`스토리 이미지 ${story.storyId}`}
className={styles.storyImage}
/>
<div className={styles.storyImageInner}>
<Image
src={story.imageUrl}
width={80}
height={80}
alt={`스토리 이미지 ${story.storyId}`}
className={styles.storyImage}
objectFit='cover'
/>
</div>
</div>
))}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { style } from "@vanilla-extract/css";

import { colors } from "@/styles";
import { colors, semantic } from "@/styles";

export const contentWrapper = style({
height: "calc(100dvh - 52px)",
Expand All @@ -23,6 +23,15 @@ export const icon = style({
color: colors.neutral[90],
});

export const helperTextWrapper = style({
display: "flex",
gap: "0.4rem",
});

export const infoIcon = style({
color: semantic.icon.gray,
});

export const searchResultItems = style({
display: "flex",
flexDirection: "column",
Expand Down
Loading