diff --git a/ios/zipbap/Info.plist b/ios/zipbap/Info.plist
index 0949aac..a23ea89 100644
--- a/ios/zipbap/Info.plist
+++ b/ios/zipbap/Info.plist
@@ -38,7 +38,7 @@
CFBundleVersion
- 19
+ 22
ITSAppUsesNonExemptEncryption
KAKAO_APP_KEY
diff --git a/src/features/feed/api/useToggleBookmarkMutation.ts b/src/features/feed/api/useToggleBookmarkMutation.ts
index f93acce..f877e90 100644
--- a/src/features/feed/api/useToggleBookmarkMutation.ts
+++ b/src/features/feed/api/useToggleBookmarkMutation.ts
@@ -6,7 +6,6 @@ import { useUserStore } from '@shared/store';
export const useToggleBookmarkMutation = () => {
const { user } = useUserStore();
const queryClient = useQueryClient();
- console.log(user?.id);
return useMutation({
mutationFn: async ({ recipeId, isBookmarked }: { recipeId: string; isBookmarked: boolean }) => {
diff --git a/src/features/feed/ui/FeedModalImageViewer.tsx b/src/features/feed/ui/FeedModalImageViewer.tsx
index c8fa193..f6c20e9 100644
--- a/src/features/feed/ui/FeedModalImageViewer.tsx
+++ b/src/features/feed/ui/FeedModalImageViewer.tsx
@@ -37,7 +37,6 @@ const FeedModalImageViewer = ({ visible, onClose, item }: Props) => {
step {item.turn.toString().padStart(2, '0')}
- {item.description}
{item.description}
diff --git a/src/features/recipe/model/useRecipeCreateForm.ts b/src/features/recipe/model/useRecipeCreateForm.ts
index 80680ac..c28b1ef 100644
--- a/src/features/recipe/model/useRecipeCreateForm.ts
+++ b/src/features/recipe/model/useRecipeCreateForm.ts
@@ -96,7 +96,6 @@ export const useRecipeCreateForm = () => {
// 임시 저장
const tempSaveMutation = useMutation({
mutationFn: async (recipe: RecipeDetail) => {
- console.log(recipe.id);
const res = await apiInstance.put(`/recipes/${recipe.id}/temp`, recipe);
return res.data;
},
diff --git a/src/features/user/ui/FollowItem.tsx b/src/features/user/ui/FollowItem.tsx
index 6ad7147..bb0f835 100644
--- a/src/features/user/ui/FollowItem.tsx
+++ b/src/features/user/ui/FollowItem.tsx
@@ -33,7 +33,6 @@ const FollowItem = ({ user, navigation }: Props) => {
);
};
- console.log(isFollowing);
return (
{
}
}, [feedDetail]);
- // prefetch recipe orders image
- useEffect(() => {
- if (!feedDetail) return;
-
- const prefetchStepImages = async () => {
- for (const order of feedDetail.cookingOrders) {
- if (order.image) {
- const cachePath = await Image.getCachePathAsync(order.image);
+ // prefetch image
+ const imagesToPrefetch = useMemo(() => {
+ if (!feedDetail) return [];
- if (!cachePath) await Image.prefetch(order.image);
- }
- }
- };
-
- prefetchStepImages();
+ return [
+ feedDetail.thumbnail,
+ feedDetail.profileImage,
+ ...feedDetail.cookingOrders.map(order => order.image),
+ ];
}, [feedDetail]);
+ usePrefetchImages(imagesToPrefetch);
+
// Skeleton ui
if (!feedDetail || !feedId || isLoading) return ;
diff --git a/src/pages/recipe/ui/MyRecipe.tsx b/src/pages/recipe/ui/MyRecipe.tsx
index 7088c3e..3a4dbcd 100644
--- a/src/pages/recipe/ui/MyRecipe.tsx
+++ b/src/pages/recipe/ui/MyRecipe.tsx
@@ -1,9 +1,9 @@
import { useQuery } from '@tanstack/react-query';
-import { Image } from 'expo-image';
-import React, { useEffect, useMemo } from 'react';
+import React, { useMemo } from 'react';
import { View, FlatList } from 'react-native';
import { Portal } from 'react-native-portalize';
import loginVideo from '@/assets/video/emptyScreenVideo.mp4';
+import { usePrefetchImages } from '@/src/shared/lib/usePrefetchImages';
import { RecipeItemSkeleton } from '@features/recipe';
import { EmptyStateUsingVideo } from '@features/user';
import { useCategories } from '@entities/category';
@@ -37,24 +37,9 @@ const MyRecipe: React.FC = ({ navigation }) => {
// recipes
const recipeList: Recipe[] = useMemo(() => recipes?.result || [], [recipes]);
- // prefetch image
- useEffect(() => {
- if (!recipeList) return;
- const prefetchMyrecipeImage = async () => {
- for (const recipe of recipeList) {
- if (!recipe.thumbnail) return;
-
- const cachePath = await Image.getCachePathAsync(recipe.thumbnail);
-
- // cache miss
- if (!cachePath) {
- await Image.prefetch(recipe.thumbnail);
- }
- }
- };
-
- prefetchMyrecipeImage();
- }, [recipeList]);
+ // prefetch recipe thumbnail
+ const thumbnailUrls = useMemo(() => recipeList.map(recipe => recipe.thumbnail), [recipeList]);
+ usePrefetchImages(thumbnailUrls);
// filtered recipes
const filteredRecipes = recipeList.filter(recipe => {
diff --git a/src/pages/recipe/ui/RecipeCreate.tsx b/src/pages/recipe/ui/RecipeCreate.tsx
index fbdf333..62f207e 100644
--- a/src/pages/recipe/ui/RecipeCreate.tsx
+++ b/src/pages/recipe/ui/RecipeCreate.tsx
@@ -1,9 +1,9 @@
-import { Image } from 'expo-image';
-import React, { useEffect, useMemo } from 'react';
+import { useMemo } from 'react';
import { View, FlatList, TouchableOpacity } from 'react-native';
import Swipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import PlusIcon from '@/assets/img/recipe/plus-float.svg';
import loginVideo from '@/assets/video/emptyScreenVideo.mp4';
+import { usePrefetchImages } from '@/src/shared/lib/usePrefetchImages';
import { EmptyStateUsingVideo } from '@features/user';
import { useRecipeListQuery, ArticleView, DetailDeleteComponent, Recipe } from '@entities/recipe';
import { useRecipeTypeStore } from '@shared/store';
@@ -13,7 +13,7 @@ interface MainPageProps {
navigation: RootNavigationProp<'Main'>;
}
-const RecipeCreate: React.FC = ({ navigation }) => {
+const RecipeCreate = ({ navigation }: MainPageProps) => {
// recipe type
const { recipeType } = useRecipeTypeStore();
@@ -22,22 +22,9 @@ const RecipeCreate: React.FC = ({ navigation }) => {
const recipeList = useMemo(() => (data || []) as Recipe[], [data]);
const isRecipeListEmpty = recipeList.length === 0;
- // prefetch image
- useEffect(() => {
- if (isRecipeListEmpty) return;
- const perfetchImage = async () => {
- for (const recipe of recipeList) {
- if (!recipe.thumbnail) return;
-
- const cachePath = await Image.getCachePathAsync(recipe.thumbnail);
-
- if (!cachePath) {
- await Image.prefetch(recipe.thumbnail);
- }
- }
- };
- perfetchImage();
- }, [isRecipeListEmpty, recipeList]);
+ // prefetch thumbnail
+ const thumbnailUrls = useMemo(() => recipeList.map(recipe => recipe.thumbnail), [recipeList]);
+ usePrefetchImages(thumbnailUrls);
// navigate
const navigateToRecipeCreateForm = () => {
diff --git a/src/pages/recipe/ui/RecipeDetail.tsx b/src/pages/recipe/ui/RecipeDetail.tsx
index 0859d4c..8230e4e 100644
--- a/src/pages/recipe/ui/RecipeDetail.tsx
+++ b/src/pages/recipe/ui/RecipeDetail.tsx
@@ -1,7 +1,8 @@
import { Image } from 'expo-image';
-import React, { useEffect } from 'react';
+import React, { useMemo } from 'react';
import { Text, View, ScrollView } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { usePrefetchImages } from '@/src/shared/lib/usePrefetchImages';
import {
RecipeDetailSection,
RecipeStepsArticleViewType,
@@ -43,22 +44,11 @@ const RecipeDetail = ({ navigation, route }: RecipeDetailProps) => {
// recipe list
const { data: detailRecipe, isLoading } = useRecipeDetailQuery(recipeId);
- // prefetch: recipe orders image
- useEffect(() => {
- if (!detailRecipe) return;
-
- const prefetchStepImages = async () => {
- for (const order of detailRecipe.cookingOrders) {
- if (order.image) {
- const cachePath = await Image.getCachePathAsync(order.image);
-
- if (!cachePath) await Image.prefetch(order.image);
- }
- }
- };
-
- prefetchStepImages();
- }, [detailRecipe]);
+ const thumbnailUrls = useMemo(
+ () => detailRecipe?.cookingOrders.map(order => order.image) || [],
+ [detailRecipe],
+ );
+ usePrefetchImages(thumbnailUrls);
// category
const { categoryValue } = useCategories();
@@ -142,7 +132,7 @@ const RecipeDetail = ({ navigation, route }: RecipeDetailProps) => {
{detailRecipe?.ingredientInfo}
}
- subTitle="레시피 소개"
+ subTitle="재료 소개"
/>
{/* 레시피 영상 */}
{detailRecipe?.video && (
diff --git a/src/pages/user/ui/ProfileEdit.tsx b/src/pages/user/ui/ProfileEdit.tsx
index eec2512..9beab8d 100644
--- a/src/pages/user/ui/ProfileEdit.tsx
+++ b/src/pages/user/ui/ProfileEdit.tsx
@@ -16,7 +16,6 @@ import {
const ProfileEdit = ({ navigation, route }: ProfileEditProps) => {
const { userId } = route.params;
- console.log(userId);
const { user } = useUserStore();
diff --git a/src/pages/user/ui/Secession.tsx b/src/pages/user/ui/Secession.tsx
index 3341de7..cf48c70 100644
--- a/src/pages/user/ui/Secession.tsx
+++ b/src/pages/user/ui/Secession.tsx
@@ -9,7 +9,6 @@ import { defaultShadow, ModalHeader } from '@shared/ui';
const Secession = ({ navigation, route }: SecessionProps) => {
const { userId } = route.params;
- console.log(userId);
const insets = useSafeAreaInsets();
const [confirmText, setConfirmText] = useState('');
const [isChecked, setIsChecked] = useState(false);
diff --git a/src/pages/user/ui/UserSettingBottomSheet.tsx b/src/pages/user/ui/UserSettingBottomSheet.tsx
index de6b3a2..c9aa0b2 100644
--- a/src/pages/user/ui/UserSettingBottomSheet.tsx
+++ b/src/pages/user/ui/UserSettingBottomSheet.tsx
@@ -31,7 +31,6 @@ const UserSettingBottomSheet = ({
const handleCatagorySave = () => {
// 카테고리 저장 로직
bottomSheetClose();
- console.log(2);
};
console.log(pushAll);
diff --git a/src/shared/lib/usePrefetchImages.ts b/src/shared/lib/usePrefetchImages.ts
new file mode 100644
index 0000000..0fc2c98
--- /dev/null
+++ b/src/shared/lib/usePrefetchImages.ts
@@ -0,0 +1,34 @@
+import { Image } from 'expo-image';
+import { useEffect, useMemo } from 'react';
+
+type PrefetchableImages = string | undefined | null;
+
+const CHUNK_SIZE = 10;
+
+export const usePrefetchImages = (imageUrls: readonly PrefetchableImages[]) => {
+ const stringifiedUrls = useMemo(() => JSON.stringify(imageUrls), [imageUrls]);
+
+ useEffect(() => {
+ const urls: PrefetchableImages[] = JSON.parse(stringifiedUrls);
+ const prefetchUrls = urls.filter((url): url is string => !!url);
+
+ if (prefetchUrls.length === 0) return;
+
+ const executePrefetchInChunks = async () => {
+ for (let i = 0; i < prefetchUrls.length; i += CHUNK_SIZE) {
+ const chunk = prefetchUrls.slice(i, i + CHUNK_SIZE);
+
+ const prefetchTasks = chunk.map(async url => {
+ const cachePath = await Image.getCachePathAsync(url);
+ if (!cachePath) {
+ return Image.prefetch(url);
+ }
+ });
+
+ await Promise.allSettled(prefetchTasks);
+ }
+ };
+
+ executePrefetchInChunks();
+ }, [stringifiedUrls]);
+};