인프런 커뮤니티 질문&답변

Veronica님의 프로필 이미지
Veronica

작성한 질문수

핸즈온 리액트 네이티브

8.18 :: Modal 컴포넌트와 Button 컴포넌트 준비

8.17 프로필 사진 캐싱 관련 질문

작성

·

123

0

안녕하세요,

8.17 코드를 그대로 따라갔는데 마지막에 앱을 새로고침하면 이미지가 아예 안 뜹니다. (첨부 이미지 참고)

에러 메세지도 따로 없고, 그냥 이미지가 뜨지 않습니다.

source.uri에는 firebase 스토리지에 올린 기본 프로필 이미지 링크가 들어있는 것을 확인했습니다.

 

코드는 github 까지 비교해가며 봤는데 동일합니다.

 

혹시 몰라 FastImage.js 및 ProfileScreen.js 코드 전문 첨부합니다.

 

무엇이 문제일까요..?ㅠ 그동안 라이브러리가 업데이트 된걸까요?

 

FastImage.js

import { Image } from 'react-native';
import PropTypes from 'prop-types';
import { useEffect, useState } from 'react';
import * as Crypto from 'expo-crypto';
import * as FileSystem from 'expo-file-system';

const FastImage = ({ source, ...props }) => {
  const [uri, setUri] = useState(source.uri);

  useEffect(() => {
    (async () => {
      try {
        console.log('source.uri', source.uri);
        const hashed = await Crypto.digestStringAsync(
          Crypto.CryptoDigestAlgorithm.SHA256,
          source.uri
        ); // Hash the URL
        const fileSystemUri = `${FileSystem.cacheDirectory}${hashed}`; // Create a file path

        const metadata = await FileSystem.getInfoAsync(fileSystemUri); // Check if the file exists
        if (!metadata.exists) {
          await FileSystem.downloadAsync(source.uri, fileSystemUri); // Download the file
        }

        setUri(fileSystemUri); // Set the file path
      } catch (error) {
        setUri(source.uri);
      }
    })();
  }, [source.uri]);

  return <Image source={{ uri }} {...props} />;
};

FastImage.propTypes = {
  source: PropTypes.object.isRequired,
};

export default FastImage;

 

ProfileScreen.js

import { StyleSheet, Text, View, Pressable } from 'react-native';
import PropTypes from 'prop-types';
import { useUserState } from '../context/UserContext';
import { signOut } from '../api/auth';
import { GRAY, WHITE } from '../colors';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { MaterialIcons } from '@expo/vector-icons';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import FastImage from '../components/FastImage';

const ProfileScreen = () => {
  const [user, setUser] = useUserState(); // [user, setUser]
  const { top } = useSafeAreaInsets();

  return (
    <View style={[styles.container, { paddingTop: top }]}>
      <View style={styles.settingButton}>
        <Pressable
          onPress={async () => {
            await signOut();
            setUser({});
          }}
          hitSlop={10}
        >
          <MaterialIcons name="logout" size={24} color={GRAY.DEFAULT} />
        </Pressable>
      </View>

      <View style={styles.profile}>
        <View
          style={[
            styles.photo,
            user.photoURL || { backgroundColor: GRAY.DEFAULT },
          ]}
        >
          <FastImage source={{ uri: user.photoURL }} style={styles.photo} />
          <Pressable style={styles.editButton} onPress={() => {}}>
            <MaterialCommunityIcons name="pencil" size={20} color={WHITE} />
          </Pressable>
        </View>

        <Text style={styles.nickname}>{user.displayName || '닉네임'}</Text>
        <Text style={styles.email}>{user.email}</Text>
      </View>

      <View style={styles.listContainer}>
        <Text style={styles.listText}>설정 리스트?</Text>
      </View>
    </View>
  );
};

ProfileScreen.propTypes = {};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: WHITE,
  },
  settingButton: {
    alignItems: 'flex-end',
    paddingHorizontal: 20,
  },
  profile: {
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomWidth: 0.5,
    borderBottomColor: GRAY.DEFAULT,
    paddingVertical: 20,
  },
  photo: {
    width: 100,
    height: 100,
    borderRadius: 50,
  },
  editButton: {
    position: 'absolute',
    bottom: 0,
    right: 0,
    width: 30,
    height: 30,
    borderRadius: 15,
    backgroundColor: GRAY.DARK,
    justifyContent: 'center',
    alignItems: 'center',
  },
  nickname: {
    fontSize: 24,
    fontWeight: '600',
    marginTop: 20,
  },
  email: {
    fontSize: 15,
    // fontWeight: '600',
    color: GRAY.DARK,
    marginTop: 5,
  },
  listContainer: {
    flex: 1,
    alignItems: 'center',
  },
  listText: {
    fontSize: 20,
    fontWeight: '600',
    marginVertical: 20,
    marginHorizontal: 20,
  },
});

export default ProfileScreen;

 

 

IMG_5734.jpg

답변 1

0

김범준님의 프로필 이미지
김범준
지식공유자

안녕하세요,

 

질문을 할 때, 본인의 코드를 깃헙에 올리고 깃헙 링크를 남겨주세요. 그래야 조금 더 정확한 확인이 가능합니다.

 

올려준 코드에 큰 문제는 없어보입니다.

먼저, Firebase에서 받아온 이미지 url을 브라우저에 복사-붙여넣기를 해서 확인해보세요. 브라우저에서 잘 나타나나요?

만약 이미지가 나타나지 않고 권한 에러 화면이 나오면, Firebase의 Storage에서 '규칙' 부분을 수정해야 합니다. (강의 8.15 Storage와 보안 규칙)

 

추가로, 현재는 Expo 에서 제공하는 Image 컴포넌트가 강의에서 만든 FastImage 컴포넌트를 대체할 수 있습니다. Expo에서 제공하는 Image 컴포넌트를 사용하는 것을 권장합니다.

https://docs.expo.dev/versions/latest/sdk/image/

 

Veronica님의 프로필 이미지
Veronica
질문자

보안규칙은 잘 설정되어있었습니다!

레파지토리가 private이라서 공유 못드렸던 점 죄송합니다 ㅠㅠ

말씀하신 Expo Image 컴포넌트 사용해서 캐싱 간단하게 설정 완료했습니다! 감사합니다!!

Veronica님의 프로필 이미지
Veronica

작성한 질문수

질문하기