Skip to content

Commit ff188b0

Browse files
authored
Style/칵테일 레시피 스켈레톤 작업#14 (#74)
* [style]칵테일 페이지 스켈레톤 UI * [style]레시피페이지 스켈레톤
1 parent ebc7fcf commit ff188b0

File tree

9 files changed

+208
-55
lines changed

9 files changed

+208
-55
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"dev": "next dev --turbopack",
6+
"dev": "next dev",
77
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx}\"",
88
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx}\"",
99
"prepare": "husky",

src/app/recipe/page.tsx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import Input from '@/shared/components/Input-box/Input';
55
import Accordion from '../../domains/recipe/components/main/Accordion';
66
import CocktailList from '@/domains/recipe/CocktailList';
77
import PageHeader from '@/domains/shared/components/page-header/PageHeader';
8+
import { Suspense } from 'react';
9+
import SkeletonRecipe from '@/domains/recipe/skeleton/SkeletonRecipe';
810

911
export const metadata: Metadata = {
1012
title: 'SSOUL | 칵테일레시피',
@@ -18,24 +20,26 @@ function Page() {
1820
<PageHeader title="Cocktail Recipes" description="다양하고 재밌는 칵테일 레시피" />
1921
</section>
2022
<div className="page-layout max-w-1224 mt-6">
21-
<section className="flex flex-col-reverse items-start gap-6 md:flex-row md:justify-between md:items-center ">
22-
<Accordion />
23-
<Input
24-
placeholder="내용을 입력해 주세요."
25-
id="search"
26-
variant="search"
27-
className="w-full md:max-w-80"
28-
/>
29-
</section>
30-
<section>
31-
<div className="h-10 flex justify-between items-center mt-3 border-b-1 border-gray-light">
32-
<p>n개</p>
33-
<SelectBox option={['', '댓글순', '인기순']} title="최신순" />
34-
</div>
35-
<section className="mt-5">
36-
<CocktailList />
23+
<Suspense fallback={<SkeletonRecipe />}>
24+
<section className="flex flex-col-reverse items-start gap-6 md:flex-row md:justify-between md:items-center ">
25+
<Accordion />
26+
<Input
27+
placeholder="내용을 입력해 주세요."
28+
id="search"
29+
variant="search"
30+
className="w-full md:max-w-80"
31+
/>
3732
</section>
38-
</section>
33+
<section>
34+
<div className="h-10 flex justify-between items-center mt-3 border-b-1 border-gray-light">
35+
<p>n개</p>
36+
<SelectBox option={['', '댓글순', '인기순']} title="최신순" />
37+
</div>
38+
<section className="mt-5">
39+
<CocktailList />
40+
</section>
41+
</section>
42+
</Suspense>
3943
</div>
4044
</div>
4145
);

src/domains/recipe/CocktailCard.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { StaticImageData } from 'next/image';
22
import Image from 'next/image';
33
import Img from '@/shared/assets/images/dummy/exampleCocktail.png';
4-
import Keep from '../shared/keep/Keep';
5-
import Label from '../shared/label/Label';
6-
4+
import Label from '../shared/components/label/Label';
5+
import Keep from '../shared/components/keep/Keep';
76

87
interface Props {
98
src?: StaticImageData;

src/domains/recipe/details/DetailList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import DetailRecommendList from "./DetailRecommendList";
1+
import DetailRecommendList from './DetailRecommendList';
22

33
function DetailList() {
44
return (
5-
<ul className='flex justify-between gap-2'>
5+
<ul className="flex justify-between gap-2">
66
<li>
77
<DetailRecommendList />
88
</li>

src/domains/recipe/details/DetailMain.tsx

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,45 +7,48 @@ import SsuryShake from '@/shared/assets/ssury/ssury_make.webp';
77
import SsuryDrink from '@/shared/assets/ssury/ssury_drink.webp';
88
import Image from 'next/image';
99
import DetailList from './DetailList';
10-
10+
import { Suspense } from 'react';
11+
import SkeletonDetail from '../skeleton/SkeletonDetail';
1112

1213
function DetailMain() {
1314
return (
14-
<div className="max-w-1024 page-layout py-12">
15-
<DetailsHeader />
16-
17-
<article className="flex flex-col items-center mt-4 lg:mt-0">
18-
<span className="md:bg-secondary w-1 h-100 -translate-y-19 absolute top-0 left-1/2 -translate-x-1/2 md: z-2"></span>
19-
<span className="h-3 w-3 rounded-full absolute top-80 left-1/2 -translate-x-1/2 z-99 md:bg-secondary"></span>
20-
<DetailItem />
21-
</article>
22-
23-
<section className="mt-20 flex flex-col gap-5">
24-
<div className="border-b-1 h-18 border-white">
25-
<div className="flex items-center gap-3">
26-
<Image src={SsuryShake} alt="" width="48" height="48" />
27-
<h3 className="text-3xl font-bold">레시피</h3>
15+
<Suspense fallback={<SkeletonDetail />}>
16+
<div className="max-w-1024 page-layout py-12">
17+
<DetailsHeader />
18+
19+
<article className="flex flex-col items-center mt-4 lg:mt-0">
20+
<span className="md:bg-secondary w-1 h-100 -translate-y-19 absolute top-0 left-1/2 -translate-x-1/2 md: z-2"></span>
21+
<span className="h-3 w-3 rounded-full absolute top-80 left-1/2 -translate-x-1/2 z-99 md:bg-secondary"></span>
22+
<DetailItem />
23+
</article>
24+
25+
<section className="mt-20 flex flex-col gap-5">
26+
<div className="border-b-1 h-18 border-white">
27+
<div className="flex items-center gap-3">
28+
<Image src={SsuryShake} alt="" width="48" height="48" />
29+
<h3 className="text-3xl font-bold">레시피</h3>
30+
</div>
2831
</div>
29-
</div>
30-
<DetailRecipe />
31-
</section>
32-
33-
<section className="mt-20" aria-labelledby="옆으로 슬라이드되는 리스트">
34-
<h2 className="sr-only">추천 칵테일 리스트</h2>
35-
<div className="border-b-1 h-18 border-white">
36-
<div className="flex items-center gap-3">
37-
<Image src={SsuryDrink} alt="" width="48" height="48" />
38-
<h3 className="text-3xl font-bold">추천리스트</h3>
32+
<DetailRecipe />
33+
</section>
34+
35+
<section className="mt-20" aria-labelledby="옆으로 슬라이드되는 리스트">
36+
<h2 className="sr-only">추천 칵테일 리스트</h2>
37+
<div className="border-b-1 h-18 border-white">
38+
<div className="flex items-center gap-3">
39+
<Image src={SsuryDrink} alt="" width="48" height="48" />
40+
<h3 className="text-3xl font-bold">추천리스트</h3>
41+
</div>
3942
</div>
40-
</div>
4143

42-
<div className="mt-5">
43-
<DetailList />
44-
</div>
45-
</section>
44+
<div className="mt-5">
45+
<DetailList />
46+
</div>
47+
</section>
4648

47-
<section>{/* 여기에 댓글 컴포넌트 */}</section>
48-
</div>
49+
<section>{/* 여기에 댓글 컴포넌트 */}</section>
50+
</div>
51+
</Suspense>
4952
);
5053
}
5154
export default DetailMain;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// DetailSkeleton.tsx
2+
// 한 페이지에 붙여넣기만 하면 되는 최소 스켈레톤 UI
3+
4+
export default function DetailSkeleton() {
5+
return (
6+
<div className="page-layout max-w-1024 py-12 animate-pulse">
7+
{/* 헤더 */}
8+
<div className="mt-4 flex items-center justify-between">
9+
<div className="h-8 w-16 rounded bg-gray animate-pulse " />
10+
<div className="flex items-center gap-3">
11+
<div className="h-8 w-8 rounded bg-gray animate-pulse " />
12+
<div className="h-8 w-8 rounded bg-gray animate-pulse " />
13+
</div>
14+
</div>
15+
16+
{/* 메인 아이템 */}
17+
<article className="flex flex-col items-center mt-8 ">
18+
{/* 타이틀/설명 */}
19+
<div
20+
className="flex flex-col items-center gap-3 relative md:flex-row md:justify-between
21+
md:ml-15 md:w-150
22+
lg:ml-30 lg:w-187.5 h-50"
23+
>
24+
<div className="flex flex-col md:ml-10 gap-3 items-center md:items-end">
25+
<div className="h-6 w-14 rounded bg-gray animate-pulse" />
26+
<div className="h-8 w-56 rounded bg-gray animate-pulse " />
27+
<div className="h-7 w-40 rounded bg-gray animate-pulse " />
28+
</div>
29+
<div className="w-70 h-20 rounded mr-5 md:mr-0 md:self-end md:w-70 lg:w-100 bg-gray animate-pulse" />
30+
</div>
31+
32+
{/* 이미지 */}
33+
<div className="mt-8 h-[375px] w-[300px] aspect-[3/4] rounded-2xl bg-gray animate-pulse" />
34+
35+
{/* 도수/글래스 정보 */}
36+
<dl className="flex flex-col mt-6 gap-3 w-72">
37+
<div className="flex items-center gap-3">
38+
<div className="h-5 w-22 rounded bg-gray animate-pulse" />
39+
40+
<div className="flex-1 h-3 rounded bg-gray animate-pulse" />
41+
</div>
42+
<div className="flex items-center gap-3">
43+
<div className="h-5 w-24 rounded bg-gray animate-pulse" />
44+
<div className="h-5 w-20 rounded bg-gray animate-pulse" />
45+
</div>
46+
</dl>
47+
</article>
48+
49+
{/* 레시피 섹션 */}
50+
<section className="mt-16 flex flex-col gap-5">
51+
<div className="border-b-1 h-18 border-white flex items-center gap-3">
52+
<div className="h-12 w-12 rounded bg-gray animate-pulse" />
53+
<div className="h-8 w-36 rounded bg-gray animate-pulse" />
54+
</div>
55+
56+
<div className="flex flex-col md:flex-row px-5 gap-5">
57+
{/* 재료 */}
58+
<article className="flex flex-col gap-3 md:w-1/2">
59+
<div className="h-7 w-16 rounded bg-gray animate-pulse" />
60+
<ul className="flex flex-col gap-2">
61+
{Array.from({ length: 4 }).map((_, i) => (
62+
<li key={i} className="h-5 w-56 rounded bg-gray animate-pulse" />
63+
))}
64+
</ul>
65+
</article>
66+
67+
{/* 만드는 법 */}
68+
<span className="border-t-1 pt-5 md:border-l-1 md:border-t-0 md:px-10 border-white">
69+
<article className="flex flex-col gap-3">
70+
<div className="h-7 w-24 rounded bg-gray animate-pulse" />
71+
<ol className="flex flex-col gap-2">
72+
{Array.from({ length: 4 }).map((_, i) => (
73+
<li key={i} className="h-5 w-72 rounded bg-gray animate-pulse" />
74+
))}
75+
</ol>
76+
</article>
77+
</span>
78+
</div>
79+
</section>
80+
81+
{/* 추천 리스트 */}
82+
<section className="mt-16">
83+
<div className="border-b-1 h-18 border-white flex items-center gap-3">
84+
<div className="h-12 w-12 rounded bg-gray animate-pulse" />
85+
<div className="h-8 w-28 rounded bg-gray animate-pulse" />
86+
</div>
87+
88+
<div className="mt-6">
89+
<ul className="flex justify-between gap-4">
90+
{Array.from({ length: 3 }).map((_, i) => (
91+
<li key={i} className="w-full max-w-[250px]">
92+
<div className="relative w-full aspect-[5/6] overflow-hidden rounded-lg">
93+
<div className="absolute inset-0 bg-gray animate-pulse"></div>
94+
</div>
95+
<div className="mt-3 space-y-2">
96+
<div className="h-5 w-3/5 rounded bg-gray animate-pulse"></div>
97+
<div className="h-4 w-2/5 rounded bg-gray animate-pulse"></div>
98+
</div>
99+
</li>
100+
))}
101+
</ul>
102+
</div>
103+
</section>
104+
105+
{/* 댓글 자리 */}
106+
<section className="mt-16">
107+
<div className="h-8 w-24 rounded bg-gray animate-pulse" />
108+
<div className="mt-4 h-24 w-full rounded bg-gray animate-pulse" />
109+
</section>
110+
</div>
111+
);
112+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// app/recipe/loading.tsx (예)
2+
function SkeletonRecipe() {
3+
return (
4+
<div className="animate-pulse">
5+
{/* Toolbar 자리 (Accordion + Input) */}
6+
<div className="flex flex-col-reverse items-start gap-6 md:flex-row md:justify-between md:items-center">
7+
<div className="h-10 w-40 bg-gray rounded-lg animate-pulse " />
8+
<div className="h-10 w-full md:max-w-80 bg-gray rounded animate-pulse" />
9+
</div>
10+
11+
{/* 상단 라인 */}
12+
<div className="h-10 flex justify-between items-center mt-3 border-b-1 border-gray-light">
13+
<div className="h-4 w-10 bg-gray rounded animate-pulse" />
14+
<div className="h-8 w-28 bg-gray rounded animate-pulse" />
15+
</div>
16+
17+
{/* 리스트 자리 */}
18+
<div className="mt-5 grid gap-8 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
19+
{Array.from({ length: 8 }).map((_, i) => (
20+
<div key={i} className="w-full min-w-0">
21+
<div className="relative w-full aspect-[3/4] max-w-80 max-h-75 md:max-w-62.5 overflow-hidden rounded-xl bg-gray animate-pulse" />
22+
<div className="mt-4.5 space-y-2">
23+
<div className="h-5 w-2/3 bg-gray rounded animate-pulse" />
24+
<div className="h-4 w-1/2 bg-gray rounded animate-pulse" />
25+
</div>
26+
</div>
27+
))}
28+
</div>
29+
</div>
30+
);
31+
}
32+
33+
export default SkeletonRecipe;

src/domains/shared/components/page-header/PageHeader.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client';
12
import StarBg from '../star-bg/StarBg';
23

34
interface Props {

src/shared/components/Input-box/Input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
'use client';
12
import tw from '@/shared/utills/tw';
23
import { cva } from 'class-variance-authority';
34
import { HTMLInputTypeAttribute, Ref } from 'react';

0 commit comments

Comments
 (0)