Skip to content

Commit b3ceb95

Browse files
committed
2 parents c4b7b8b + 85690d2 commit b3ceb95

File tree

16 files changed

+234
-284
lines changed

16 files changed

+234
-284
lines changed

README.md

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# SSOUL 프로젝트 인수인계 문서
2+
3+
## �� 프로젝트 개요
4+
5+
**프로젝트명**: SSOUL (칵테일을 좋아하는 사람들을 위한 서비스)
6+
**기술 스택**: Next.js 15, React 19, TypeScript, Tailwind CSS
7+
**저장소**: https://github.com/prgrms-web-devcourse-final-project/WEB5_6_HaeDokCoding_FE
8+
9+
## �� 시작하기
10+
11+
### 개발 환경 설정
12+
```bash
13+
# 의존성 설치
14+
npm install
15+
16+
# 개발 서버 실행
17+
npm run dev
18+
19+
# 빌드
20+
npm run build
21+
22+
# 코드 포맷팅
23+
npm run format
24+
25+
# 린트 검사
26+
npm run lint
27+
```
28+
29+
### 환경 변수
30+
프로젝트는 개발/운영 환경에 따라 다른 API URL을 사용합니다:
31+
- `NEXT_PUBLIC_API_URL_DEV`: 개발 환경 API URL
32+
- `NEXT_PUBLIC_API_URL_PROD`: 운영 환경 API URL
33+
34+
## �� 프로젝트 구조
35+
36+
```
37+
src/
38+
├── app/ # Next.js App Router 페이지
39+
│ ├── layout.tsx # 루트 레이아웃
40+
│ ├── page.tsx # 메인 페이지
41+
│ ├── community/ # 커뮤니티 관련 페이지
42+
│ ├── recipe/ # 칵테일 레시피 페이지
43+
│ ├── recommend/ # 취향 추천 페이지
44+
│ ├── login/ # 로그인 관련 페이지
45+
│ ├── mypage/ # 마이페이지
46+
│ └── api/ # API 설정
47+
├── domains/ # 도메인별 컴포넌트
48+
│ ├── community/ # 커뮤니티 도메인
49+
│ ├── recipe/ # 레시피 도메인
50+
│ ├── recommend/ # 추천 도메인
51+
│ ├── login/ # 로그인 도메인
52+
│ ├── mypage/ # 마이페이지 도메인
53+
│ └── shared/ # 공통 도메인 컴포넌트
54+
└── shared/ # 공통 컴포넌트 및 유틸
55+
├── components/ # 공통 UI 컴포넌트
56+
├── styles/ # 글로벌 스타일
57+
├── assets/ # 이미지, 아이콘 등
58+
├── hook/ # 공통 훅
59+
├── types/ # 타입 정의
60+
└── utills/ # 유틸리티 함수
61+
```
62+
63+
## �� 주요 기능
64+
65+
### 1. 인증 시스템
66+
- **소셜 로그인**: Google, Kakao, Naver 지원
67+
- **상태 관리**: Zustand + localStorage persist
68+
- **주요 파일**:
69+
- `src/domains/shared/store/auth.ts`: 인증 상태 관리
70+
- `src/domains/login/hook/useAuthHooks.ts`: 로그인 관련 훅
71+
- `src/app/api/config/appConfig.ts`: API 설정
72+
73+
### 2. 페이지별 기능
74+
75+
#### �� 메인 페이지 (`/`)
76+
- 현재 기본 구조만 구현됨
77+
- 추후 확장 예정
78+
79+
#### �� 칵테일 레시피 (`/recipe`)
80+
- **주요 컴포넌트**:
81+
- `CocktailList`: 칵테일 목록 표시
82+
- `Accordion`: 필터링 옵션
83+
- `SelectBox`: 정렬 옵션
84+
- **기능**: 검색, 필터링, 정렬
85+
- **상세 페이지**: `/recipe/[id]` - 개별 칵테일 상세 정보
86+
87+
#### �� 커뮤니티 (`/community`)
88+
- **주요 컴포넌트**:
89+
- `PostCard`: 게시물 카드
90+
- `CommunityTab`: 카테고리 탭
91+
- `WriteBtn`: 글쓰기 버튼
92+
- **카테고리**: 레시피, 팁, 질문, 자유
93+
- **글쓰기**: `/community/write`
94+
- **상세 페이지**: `/community/[id]`
95+
96+
#### �� 취향 추천 (`/recommend`)
97+
- **챗봇 기반 추천**: `ChatSection` 컴포넌트
98+
- **주요 컴포넌트**:
99+
- `BotMessage`, `UserMessage`: 메시지 컴포넌트
100+
- `BotOptions`: 선택 옵션
101+
- `MessageInput`: 입력창
102+
- `TypingIndicator`: 타이핑 효과
103+
104+
#### �� 마이페이지 (`/mypage`)
105+
- **기본 리다이렉트**: `/mypage``/mypage/mybar`
106+
- **주요 섹션**:
107+
- `/mypage/mybar`: 나만의 바
108+
- `/mypage/my-active`: 활동 내역 (게시물, 댓글, 좋아요)
109+
- `/mypage/my-alarm`: 알림 설정
110+
- `/mypage/my-setting`: 계정 설정
111+
112+
#### �� 로그인 (`/login`)
113+
- **소셜 로그인**: `SocialLogin` 컴포넌트
114+
- **성공 페이지**: `/login/success`
115+
- **신규 사용자**: `/login/user/first-user`
116+
117+
## �� 기술적 세부사항
118+
119+
### 상태 관리
120+
- **Zustand**: 클라이언트 상태 관리
121+
- **Persist**: localStorage를 통한 상태 영속화
122+
- **주요 스토어**:
123+
- `auth.ts`: 사용자 인증 상태
124+
- `accordionStore.ts`: 아코디언 UI 상태
125+
126+
### UI/UX
127+
- **Tailwind CSS**: 스타일링
128+
- **React Hot Toast**: 토스트 알림
129+
- **Lottie**: 로딩 애니메이션
130+
- **GSAP**: 고급 애니메이션
131+
- **Responsive**: 모바일/데스크톱 대응
132+
133+
### 개발 도구
134+
- **ESLint**: 코드 품질 관리
135+
- **Prettier**: 코드 포맷팅
136+
- **Husky**: Git hooks
137+
- **Lint-staged**: 커밋 전 검사
138+
139+
## �� 주요 설정 파일
140+
141+
- `next.config.ts`: Next.js 설정
142+
- `tailwind.config.js`: Tailwind CSS 설정
143+
- `eslint.config.mjs`: ESLint 설정
144+
- `tsconfig.json`: TypeScript 설정
145+
146+
## �� 반응형 디자인
147+
148+
프로젝트는 모바일 우선(Mobile-first) 접근 방식을 사용합니다:
149+
- **모바일**: 기본 스타일
150+
- **태블릿**: `md:` prefix
151+
- **데스크톱**: `lg:`, `xl:` prefix
152+
153+
## �� 주의사항
154+
155+
1. **환경 변수**: 개발/운영 환경에 맞는 API URL 설정 필요
156+
2. **인증 토큰**: localStorage에 저장되므로 보안 고려 필요
157+
3. **API 통신**: `credentials: 'include'` 설정으로 쿠키 기반 인증
158+
4. **Git Hooks**: Husky 설정으로 커밋 전 자동 검사
159+
160+
## �� 추가 문의
161+
162+
- **저장소 이슈**: https://github.com/prgrms-web-devcourse-final-project/WEB5_6_HaeDokCoding_FE/issues
163+
- **주요 브랜치**: `main` (메인), `dev` (개발)
164+
165+
---
166+
167+
**작성일**: 2025-10-14
168+
**작성자**: 이성헌
169+
**버전**: 1.0
7.95 MB
Binary file not shown.

src/app/(main)/layout.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import FooterWrapper from '@/shared/components/footer/FooterWrapper';
21
import Header from '@/shared/components/header/Header';
32

43
function NoLayout({ children }: { children: React.ReactNode }) {
54
return (
65
<>
7-
<Header className="bg-transparent w-full h-[44px] md:h-[60px] flex items-center justify-between px-[12px] fixed top-0 left-0 z-50 transition-transform duration-200 ease-in-ou" />
6+
<Header
7+
isMain={true}
8+
className="w-full h-[44px] md:h-[60px] flex items-center justify-between px-[12px] fixed top-0 left-0 z-50 transition-transform duration-200 ease-in-ou"
9+
/>
810
<main className="flex flex-1">{children}</main>
9-
<FooterWrapper />
1011
</>
1112
);
1213
}

src/domains/main/cocktailDrop/CocktailDrop.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,23 @@ function CocktailDrop({ isDesktop = false }: CocktailDropProps) {
4444
);
4545

4646
// 로고 위에서 아래로 자연스럽게 등장
47+
const screenWidth = window.innerWidth;
48+
const viewportHeight = window.innerHeight;
49+
const isTablet = screenWidth >= 640 && screenWidth < 1024;
50+
const isMobile = screenWidth < 640;
51+
52+
// 뷰포트 높이 기반으로 로고 위치 계산
53+
const logoFinalY = isMobile
54+
? `-${viewportHeight * 0.3}px`
55+
: isTablet
56+
? `-${viewportHeight * -0.8}px`
57+
: '0px';
58+
4759
gsap.fromTo(
4860
logoRef.current,
4961
{ y: -300, opacity: 0 },
5062
{
51-
y: !isDesktop ? -230 : -18, // 데스크톱이 아닐 때 더 위로
63+
y: logoFinalY, // 뷰포트 높이 기반 계산
5264
opacity: 1,
5365
duration: 3,
5466
ease: 'power3.out',
@@ -69,7 +81,7 @@ function CocktailDrop({ isDesktop = false }: CocktailDropProps) {
6981
return (
7082
<div
7183
ref={containerRef}
72-
className="relative w-full lg:min-h-[110vh] min-h-[89vh] flex flex-col md:justify-center justify-end items-center mt-10 overflow-hidden"
84+
className="relative w-full lg:min-h-[120vh] md:min-h-[95vh] min-h-[87vh] flex flex-col lg:justify-center md:justify-center justify-end items-center mt-10 overflow-hidden"
7385
id="scroll-fixed"
7486
>
7587
{/* 대각선 줄 1 */}
@@ -84,7 +96,7 @@ function CocktailDrop({ isDesktop = false }: CocktailDropProps) {
8496
/>
8597

8698
{/* 로고 */}
87-
<div ref={logoRef} className="absolute md:w-115 w-85 md:h-90 h-40">
99+
<div ref={logoRef} className="absolute z-4 md:w-115 w-65 md:h-90 h-40">
88100
<Image
89101
src="/logo.svg"
90102
alt="로고 이미지"
@@ -95,20 +107,17 @@ function CocktailDrop({ isDesktop = false }: CocktailDropProps) {
95107
/>
96108
</div>
97109

98-
<div className="w-full md:h-90 h-30"></div>
99-
100110
{/* 컵 이미지 - 모바일에서 바닥에 붙도록 */}
101-
<div className="md:relative absolute bottom-0">
111+
<div className="z-5 absolute bottom-0">
102112
<Image
103113
src={Cocktailcup}
104114
alt="칵테일 컵"
105-
width={900}
106-
height={700}
107115
priority
108-
className="md:w-auto md:h-auto w-[500px] h-[400px] object-cover"
116+
style={{ height: 'auto' }}
117+
className="md:w-[700px] w-[500px] object-contain"
109118
/>
110119
</div>
111-
<div className="absolute md:bottom-35 bottom-20 flex items-center justify-center z-3 w-full">
120+
<div className="absolute md:bottom-35 bottom-20 flex items-center justify-center z-10 w-full">
112121
<PassBtn />
113122
</div>
114123
</div>

src/domains/main/components/3d/HomeLogo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import Image from 'next/image';
33
function HomeLogo({ isDesktop }: { isDesktop: boolean }) {
44
return (
55
<div
6-
className="z-5 absolute md:top-8 md:left-10 md:translate-none top-13 left-1/2 -translate-x-1/2"
7-
style={{ width: !isDesktop ? 400 : 700, height: !isDesktop ? 70 : 240 }}
6+
className="z-5 absolute md:top-22 md:left-10 md:translate-none top-30 left-1/2 -translate-x-1/2"
7+
style={{ width: !isDesktop ? 400 : 580, height: !isDesktop ? 70 : 210 }}
88
>
99
<Image src={'/logo.svg'} alt="로고 이미지" fill priority className="object-contain" />
1010
</div>

src/domains/main/components/3d/HomeModel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ function Model({ onLoaded }: Props) {
4242
return (
4343
<primitive
4444
object={scene}
45-
scale={5.8}
46-
position={[0, -1.2, 0]}
45+
scale={4.6}
46+
position={[0, -0.6, 0]}
4747
rotation={[-0.15, Math.PI + 3, 0]}
4848
/>
4949
);

src/domains/main/components/3d/HomeText.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ function HomeText({ isDesktop }: { isDesktop: boolean }) {
22
return (
33
<>
44
{!isDesktop ? (
5-
<p className="absolute top-32 text-sm left-1/2 -translate-x-1/2 whitespace-nowrap">
6-
어떤 칵테일이 끌리시나요? SSoul이 쉽게 골라드릴게요.
5+
<p className="absolute top-48 text-sm left-1/2 -translate-x-1/2 text-center mt-4">
6+
어떤 칵테일이 끌리시나요? <br />
7+
SSoul이 쉽게 골라드릴게요.
78
</p>
89
) : (
910
<p className="absolute bottom-45 right-12 font-serif text-xl text-right font-normal z-20">

src/domains/main/components/3d/ModelImage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function ModelImage({ onLoaded }: Props) {
1919
width={260}
2020
height={290}
2121
priority
22-
className="object-cover w-[300px] h-[390px]"
22+
className="object-cover w-[300px] h-[350px]"
2323
/>
2424
</div>
2525
</div>

src/domains/main/components/3d/StarMain.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ function StarMain() {
1414
useEffect(() => {
1515
if (!background.current || !foreground.current) return;
1616

17+
// 모바일에서는 별 애니메이션 비활성화
18+
const isMobile = window.innerWidth < 768;
19+
if (isMobile) return;
20+
1721
const bgX = gsap.quickSetter(background.current, 'x', 'px');
1822
const bgY = gsap.quickSetter(background.current, 'y', 'px');
1923
const bgRotate = gsap.quickSetter(background.current, 'rotate', 'deg');
@@ -43,20 +47,11 @@ function StarMain() {
4347
mouse.current = { x, y };
4448
};
4549

46-
const handleTouchMove = (e: TouchEvent) => {
47-
const touch = e.touches[0];
48-
const x = (touch.clientX / window.innerWidth - 0.5) * 2;
49-
const y = (touch.clientY / window.innerHeight - 0.5) * 2;
50-
mouse.current = { x, y };
51-
};
52-
5350
window.addEventListener('mousemove', handleMouseMove);
54-
window.addEventListener('touchmove', handleTouchMove);
5551
rafId.current = requestAnimationFrame(update);
5652

5753
return () => {
5854
window.removeEventListener('mousemove', handleMouseMove);
59-
window.removeEventListener('touchmove', handleTouchMove);
6055
if (rafId.current) cancelAnimationFrame(rafId.current);
6156
};
6257
}, []);

src/domains/mypage/components/pages/my-active/MyLike.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ function MyLike() {
2020
fetchLike();
2121
}, []);
2222

23+
useEffect(() => {
24+
console.log(myLike);
25+
}, [myLike]);
26+
2327
return (
2428
<section className="flex justify-center">
2529
{myLike.length > 0 ? (

0 commit comments

Comments
 (0)