diff --git a/backend/src/main/java/com/funeat/product/domain/Category.java b/backend/src/main/java/com/funeat/product/domain/Category.java index 03d1d4f52..5d6c62a08 100644 --- a/backend/src/main/java/com/funeat/product/domain/Category.java +++ b/backend/src/main/java/com/funeat/product/domain/Category.java @@ -1,11 +1,6 @@ package com.funeat.product.domain; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; +import javax.persistence.*; @Entity public class Category { @@ -19,12 +14,15 @@ public class Category { @Enumerated(EnumType.STRING) private CategoryType type; + private String image; + protected Category() { } - public Category(final String name, final CategoryType type) { + public Category(final String name, final CategoryType type, final String image) { this.name = name; this.type = type; + this.image = image; } public Long getId() { @@ -38,4 +36,8 @@ public String getName() { public CategoryType getType() { return type; } + + public String getImage() { + return image; + } } diff --git a/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java index ebf70e602..3ea9e59a5 100644 --- a/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java @@ -6,14 +6,16 @@ public class CategoryResponse { private final Long id; private final String name; + private final String image; - public CategoryResponse(final Long id, final String name) { + public CategoryResponse(final Long id, final String name, final String image) { this.id = id; this.name = name; + this.image = image; } public static CategoryResponse toResponse(final Category category) { - return new CategoryResponse(category.getId(), category.getName()); + return new CategoryResponse(category.getId(), category.getName(), category.getImage()); } public Long getId() { @@ -23,4 +25,8 @@ public Long getId() { public String getName() { return name; } + + public String getImage() { + return image; + } } diff --git a/backend/src/test/java/com/funeat/fixture/CategoryFixture.java b/backend/src/test/java/com/funeat/fixture/CategoryFixture.java index b41401ce3..9d6da56ab 100644 --- a/backend/src/test/java/com/funeat/fixture/CategoryFixture.java +++ b/backend/src/test/java/com/funeat/fixture/CategoryFixture.java @@ -7,42 +7,42 @@ public class CategoryFixture { public static Category 카테고리_간편식사_생성() { - return new Category("간편식사", CategoryType.FOOD); + return new Category("간편식사", CategoryType.FOOD, "siksa.jpeg"); } public static Category 카테고리_즉석조리_생성() { - return new Category("즉석조리", CategoryType.FOOD); + return new Category("즉석조리", CategoryType.FOOD, "direct.jpeg"); } public static Category 카테고리_과자류_생성() { - return new Category("과자류", CategoryType.FOOD); + return new Category("과자류", CategoryType.FOOD, "snack.jpeg"); } public static Category 카테고리_아이스크림_생성() { - return new Category("아이스크림", CategoryType.FOOD); + return new Category("아이스크림", CategoryType.FOOD, "ice.jpeg"); } public static Category 카테고리_식품_생성() { - return new Category("식품", CategoryType.FOOD); + return new Category("식품", CategoryType.FOOD, "food.jpeg"); } public static Category 카테고리_음료_생성() { - return new Category("음료", CategoryType.FOOD); + return new Category("음료", CategoryType.FOOD, "drink.jpeg"); } public static Category 카테고리_CU_생성() { - return new Category("CU", CategoryType.STORE); + return new Category("CU", CategoryType.STORE, "cu.jpeg"); } public static Category 카테고리_GS25_생성() { - return new Category("GS25", CategoryType.STORE); + return new Category("GS25", CategoryType.STORE, "gs25.jpeg"); } public static Category 카테고리_EMART24_생성() { - return new Category("EMART24", CategoryType.STORE); + return new Category("EMART24", CategoryType.STORE, "emart.jpeg"); } public static Category 카테고리_세븐일레븐_생성() { - return new Category("세븐일레븐", CategoryType.STORE); + return new Category("세븐일레븐", CategoryType.STORE, "seven.jpeg"); } } diff --git a/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java b/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java index f68b15d33..1b9b09aba 100644 --- a/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java +++ b/backend/src/test/java/com/funeat/recipe/application/RecipeServiceTest.java @@ -1,28 +1,5 @@ package com.funeat.recipe.application; -import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; -import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; -import static com.funeat.fixture.ImageFixture.여러_이미지_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버1_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버2_생성; -import static com.funeat.fixture.MemberFixture.멤버_멤버3_생성; -import static com.funeat.fixture.PageFixture.페이지요청_생성_시간_내림차순_생성; -import static com.funeat.fixture.PageFixture.페이지요청_생성_시간_오름차순_생성; -import static com.funeat.fixture.PageFixture.페이지요청_좋아요_내림차순_생성; -import static com.funeat.fixture.ProductFixture.레시피_안에_들어가는_상품_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점2점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격1000원_평점5점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점1점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격2000원_평점3점_생성; -import static com.funeat.fixture.ProductFixture.상품_삼각김밥_가격3000원_평점4점_생성; -import static com.funeat.fixture.RecipeFixture.레시피_생성; -import static com.funeat.fixture.RecipeFixture.레시피이미지_생성; -import static com.funeat.fixture.RecipeFixture.레시피좋아요요청_생성; -import static com.funeat.fixture.RecipeFixture.레시피추가요청_생성; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.SoftAssertions.assertSoftly; - import com.funeat.common.ServiceTest; import com.funeat.common.dto.PageDto; import com.funeat.member.domain.Member; @@ -38,12 +15,24 @@ import com.funeat.recipe.dto.RecipeDetailResponse; import com.funeat.recipe.dto.RecipeDto; import com.funeat.recipe.exception.RecipeException.RecipeNotFoundException; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; + +import static com.funeat.fixture.CategoryFixture.카테고리_간편식사_생성; +import static com.funeat.fixture.CategoryFixture.카테고리_즉석조리_생성; +import static com.funeat.fixture.ImageFixture.여러_이미지_생성; +import static com.funeat.fixture.MemberFixture.*; +import static com.funeat.fixture.PageFixture.*; +import static com.funeat.fixture.ProductFixture.*; +import static com.funeat.fixture.RecipeFixture.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.SoftAssertions.assertSoftly; @SuppressWarnings("NonAsciiCharacters") class RecipeServiceTest extends ServiceTest { @@ -88,7 +77,7 @@ class create_성공_테스트 { @Test void 레시피의_상세_정보를_조회할_수_있다() { // given - final var category = 카테고리_추가_요청(new Category("간편식사", CategoryType.FOOD)); + final var category = 카테고리_추가_요청(new Category("간편식사", CategoryType.FOOD, "siksa.jpeg")); final var product1 = new Product("불닭볶음면", 1000L, "image.png", "엄청 매운 불닭", category); final var product2 = new Product("참치 삼김", 2000L, "image.png", "담백한 참치마요 삼김", category); final var product3 = new Product("스트링 치즈", 1500L, "image.png", "고소한 치즈", category); diff --git a/frontend/src/components/Common/Carousel/Carousel.tsx b/frontend/src/components/Common/Carousel/Carousel.tsx index 4fa1839f4..5f7c3ad23 100644 --- a/frontend/src/components/Common/Carousel/Carousel.tsx +++ b/frontend/src/components/Common/Carousel/Carousel.tsx @@ -7,12 +7,12 @@ interface CarouselProps { carouselList: CarouselChildren[]; } -const CAROUSEL_WIDTH = window.innerWidth; - const Carousel = ({ carouselList }: CarouselProps) => { const extendedCarouselList = [...carouselList, carouselList[0]]; const [currentIndex, setCurrentIndex] = useState(0); + const CAROUSEL_WIDTH = window.innerWidth; + const showNextSlide = () => { setCurrentIndex((prev) => (prev === carouselList.length ? 0 : prev + 1)); }; @@ -25,9 +25,16 @@ const Carousel = ({ carouselList }: CarouselProps) => { return ( - + {extendedCarouselList.map(({ id, children }) => ( - {children} + + {children} + ))} @@ -44,13 +51,10 @@ const CarouselContainer = styled.div` overflow: hidden; `; -const CarouselWrapper = styled.ul<{ currentIndex: number }>` +const CarouselWrapper = styled.ul` display: flex; - transform: ${({ currentIndex }) => 'translateX(-' + currentIndex * CAROUSEL_WIDTH + 'px)'}; - transition: ${({ currentIndex }) => (currentIndex === length - 1 ? '' : 'all 0.5s ease-in-out')}; `; const CarouselItem = styled.li` - width: ${CAROUSEL_WIDTH}px; height: fit-content; `; diff --git a/frontend/src/components/Common/CategoryItem/CategoryItem.tsx b/frontend/src/components/Common/CategoryItem/CategoryItem.tsx index a16889758..4ce54ce72 100644 --- a/frontend/src/components/Common/CategoryItem/CategoryItem.tsx +++ b/frontend/src/components/Common/CategoryItem/CategoryItem.tsx @@ -34,7 +34,7 @@ const ImageWrapper = styled.div` border-radius: 10px; background: ${({ theme }) => theme.colors.white}; - img { + & > img { width: 100%; height: auto; object-fit: cover; @@ -44,5 +44,5 @@ const ImageWrapper = styled.div` const CategoryName = styled.p` margin-top: 10px; font-weight: 600; - font-size: 0.8rem; + font-size: ${({ theme }) => theme.fontSizes.xs}; `; diff --git a/frontend/src/components/Common/CategoryList/CategoryList.tsx b/frontend/src/components/Common/CategoryList/CategoryList.tsx index cb239db02..675ab02fe 100644 --- a/frontend/src/components/Common/CategoryList/CategoryList.tsx +++ b/frontend/src/components/Common/CategoryList/CategoryList.tsx @@ -4,38 +4,25 @@ import styled from 'styled-components'; import CategoryItem from '../CategoryItem/CategoryItem'; -import { MENU_IMAGES, MENU_NAME, STORE_IMAGES, STORE_NAMES } from '@/constants'; +import { CATEGORY_TYPE } from '@/constants'; +import { useCategoryQuery } from '@/hooks/queries/product'; -const MENU_LENGTH = 5; -const STORE_LENGTH = 4; +interface CategoryListProps { + menuVariant: keyof typeof CATEGORY_TYPE; +} -const menuList = Array.from({ length: MENU_LENGTH }, (_, index) => ({ - name: MENU_NAME[index], - image: MENU_IMAGES[index], -})); +const CategoryList = ({ menuVariant }: CategoryListProps) => { + const { data: categories } = useCategoryQuery(CATEGORY_TYPE[menuVariant]); -const storeList = Array.from({ length: STORE_LENGTH }, (_, index) => ({ - name: STORE_NAMES[index], - image: STORE_IMAGES[index], -})); - -const CategoryList = () => { return ( - - {menuList.map((menu, index) => ( - + + {categories.map((menu) => ( + ))} - - - {storeList.map((menu, index) => ( - - - - ))} - + ); }; @@ -57,12 +44,7 @@ const CategoryListContainer = styled.div` } `; -const MenuListWrapper = styled.div` - display: flex; - gap: 20px; -`; - -const StoreListWrapper = styled.div` +const CategoryListWrapper = styled.div` display: flex; gap: 20px; `; diff --git a/frontend/src/components/Layout/DefaultLayout.tsx b/frontend/src/components/Layout/DefaultLayout.tsx index 4daf634ee..39f1f8cff 100644 --- a/frontend/src/components/Layout/DefaultLayout.tsx +++ b/frontend/src/components/Layout/DefaultLayout.tsx @@ -25,7 +25,6 @@ const DefaultLayoutContainer = styled.div` const MainWrapper = styled.main` position: relative; height: calc(100% - 120px); - padding: 0 20px; overflow-x: hidden; overflow-y: auto; `; diff --git a/frontend/src/components/Layout/SimpleHeaderLayout.tsx b/frontend/src/components/Layout/SimpleHeaderLayout.tsx index c6c469169..4f17b93a2 100644 --- a/frontend/src/components/Layout/SimpleHeaderLayout.tsx +++ b/frontend/src/components/Layout/SimpleHeaderLayout.tsx @@ -25,7 +25,7 @@ const SimpleHeaderLayoutContainer = styled.div` const MainWrapper = styled.main` position: relative; height: calc(100% - 120px); - padding: 20px; + padding: 20px 20px 0; overflow-x: hidden; overflow-y: auto; `; diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index 2cd47c742..81b964c6c 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -67,22 +67,3 @@ export const ENVIRONMENT = window.location.href.includes('dev') ? 'dev' : 'prod' export const IMAGE_URL = ENVIRONMENT === 'dev' ? process.env.S3_DEV_CLOUDFRONT_PATH : process.env.S3_PROD_CLOUDFRONT_PATH; - -export const MENU_NAME = ['간편식사', '과자류', '아이스크림', '식품', '음료'] as const; - -export const STORE_NAMES = ['CU', 'GS25', '이마트24', '세븐일레븐'] as const; - -export const MENU_IMAGES = [ - `${IMAGE_URL}food.jpeg`, - `${IMAGE_URL}snack.jpeg`, - `${IMAGE_URL}icecream.jpeg`, - `${IMAGE_URL}ramen.jpeg`, - `${IMAGE_URL}tea.jpeg`, -]; - -export const STORE_IMAGES = [ - `${IMAGE_URL}cu.jpg`, - `${IMAGE_URL}gs.png`, - `${IMAGE_URL}emart24.png`, - `${IMAGE_URL}seven.png`, -]; diff --git a/frontend/src/mocks/data/foodCategory.json b/frontend/src/mocks/data/foodCategory.json index 81d4adc8a..daff82ce0 100644 --- a/frontend/src/mocks/data/foodCategory.json +++ b/frontend/src/mocks/data/foodCategory.json @@ -1,7 +1,7 @@ [ - { "id": 1, "name": "간편식사" }, - { "id": 2, "name": "과자류" }, - { "id": 3, "name": "아이스크림" }, - { "id": 4, "name": "식품" }, - { "id": 5, "name": "음료" } + { "id": 1, "name": "간편식사", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 2, "name": "과자류", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 3, "name": "아이스크림", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 4, "name": "식품", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 5, "name": "음료", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" } ] diff --git a/frontend/src/mocks/data/storeCategory.json b/frontend/src/mocks/data/storeCategory.json index e2242d83e..b9c7c9445 100644 --- a/frontend/src/mocks/data/storeCategory.json +++ b/frontend/src/mocks/data/storeCategory.json @@ -1,6 +1,6 @@ [ - { "id": 6, "name": "CU" }, - { "id": 7, "name": "GS25" }, - { "id": 8, "name": "이마트24" }, - { "id": 9, "name": "세븐일레븐" } + { "id": 6, "name": "CU", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 7, "name": "GS25", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 8, "name": "이마트24", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" }, + { "id": 9, "name": "세븐일레븐", "image": "https://tqklhszfkvzk6518638.cdn.ntruss.com/product/8801771029052.jpg" } ] diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 0e56c8fe1..306285fb1 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -27,18 +27,19 @@ const HomePage = () => { -
+ 카테고리 - + + -
+ -
+ 🍯 꿀조합 랭킹 @@ -48,9 +49,9 @@ const HomePage = () => { -
+ -
+ 👑 상품 랭킹 @@ -60,9 +61,9 @@ const HomePage = () => { -
+ -
+ 📝 리뷰 랭킹 @@ -72,7 +73,7 @@ const HomePage = () => { -
+ ); @@ -83,3 +84,7 @@ export default HomePage; const Banner = styled.img` width: 100%; `; + +const SectionWrapper = styled.section` + padding: 0 20px; +`; diff --git a/frontend/src/pages/MemberPage.tsx b/frontend/src/pages/MemberPage.tsx index 129e65f53..bb5fca6bb 100644 --- a/frontend/src/pages/MemberPage.tsx +++ b/frontend/src/pages/MemberPage.tsx @@ -1,6 +1,7 @@ import { Spacing } from '@fun-eat/design-system'; import { useQueryErrorResetBoundary } from '@tanstack/react-query'; import { Suspense } from 'react'; +import styled from 'styled-components'; import { ErrorBoundary, ErrorComponent, Loading, NavigableSectionTitle } from '@/components/Common'; import { MembersInfo, MemberReviewList, MemberRecipeList } from '@/components/Members'; @@ -10,7 +11,7 @@ const MemberPage = () => { const { reset } = useQueryErrorResetBoundary(); return ( - <> + }> @@ -30,8 +31,12 @@ const MemberPage = () => { - + ); }; export default MemberPage; + +const MemberPageContainer = styled.div` + padding: 20px 20px 0; +`; diff --git a/frontend/src/pages/MemberRecipePage.tsx b/frontend/src/pages/MemberRecipePage.tsx index c4dafb772..90bddc44b 100644 --- a/frontend/src/pages/MemberRecipePage.tsx +++ b/frontend/src/pages/MemberRecipePage.tsx @@ -11,7 +11,7 @@ const MemberRecipePage = () => { const memberRecipeRef = useRef(null); return ( - + @@ -20,13 +20,15 @@ const MemberRecipePage = () => { - + + ); }; export default MemberRecipePage; -const MebmberRecipePageContainer = styled.div` +const MemberRecipePageContainer = styled.div` height: 100%; + padding: 20px 20px 0; overflow-y: auto; `; diff --git a/frontend/src/pages/MemberReviewPage.tsx b/frontend/src/pages/MemberReviewPage.tsx index aa4473679..9f15905fd 100644 --- a/frontend/src/pages/MemberReviewPage.tsx +++ b/frontend/src/pages/MemberReviewPage.tsx @@ -11,7 +11,7 @@ const MemberReviewPage = () => { const memberReviewRef = useRef(null); return ( - + @@ -20,13 +20,15 @@ const MemberReviewPage = () => { - + + ); }; export default MemberReviewPage; -const MebmberReviewPageContainer = styled.div` +const MemberReviewPageContainer = styled.div` height: 100%; + padding: 20px 20px 0; overflow-y: auto; `; diff --git a/frontend/src/pages/RecipeDetailPage.tsx b/frontend/src/pages/RecipeDetailPage.tsx index 5ad75b4a5..cc77efb77 100644 --- a/frontend/src/pages/RecipeDetailPage.tsx +++ b/frontend/src/pages/RecipeDetailPage.tsx @@ -15,7 +15,7 @@ const RecipeDetailPage = () => { const { id, images, title, content, author, products, totalPrice, favoriteCount, favorite, createdAt } = recipeDetail; return ( - <> + {images.length > 0 ? ( @@ -65,12 +65,17 @@ const RecipeDetailPage = () => { {content} - + + ); }; export default RecipeDetailPage; +const RecipeDetailPageContainer = styled.div` + padding: 20px 20px 0; +`; + const RecipeImageContainer = styled.ul` display: flex; flex-direction: column; diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 1d2dbb1f7..3bfcc8576 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -12,6 +12,7 @@ export const isCategoryVariant = (value: string): value is CategoryVariant => { export interface Category { id: number; name: string; + image: string; } export interface Tag {