스파르타코딩클럽 내일배움캠프 TIL53

한재창·2023년 1월 13일
0

프로젝트

오늘 한 일 1

  • 설정페이지에서 닉네임 변경 및 로그아웃 기능 구현
    • 닉네임 변경
      • 파이어베이스에서 제공하는 updateProfile 메서드를 사용해 처음에 저장한 displayName(닉네임) 값을 Input에 입력한 값으로 변경해줌
      • 하지만 새로고침을 하지 않으면 바로 화면에 렌더링 되지 않는 현상 발생
      • 위와 같은 문제가 생기는 이유는 리액트의 동작 원리때문이다. 리액트는 변화된 값만을 감지해서 상태를 변화시키는데에 최적화 되어있다. 그러나 authService.currentUser 내에는 너무 많은 데이터가 담겨있고, 이를 리액트가 모두 하나하나 비교할 수는 없기 때문이다.
      • 따라서 userObj에 currentUser객체를 모두 넣지않고 필요한것만 넣어서 리액트가 비교하기 편하도록 설계해야한다.
      • 현재 우리가 필요한 currentUser의 속성과 메서드는 displayName, uid 와 updateProfile 메서드 뿐이다.
      • 따라서 처음 useEffect로 UserObj를 변경할때부터 위 값만을 넣도록 만들어었다.
    • 로그아웃
      • 로그아웃은 간단하게 파이어베이스에서 제공하는 singOut 메서드를 이용해서 기능을 구현하였다.
      • Alert를 사용해서 바로 로그아웃 되지 않게 UI,UX 면에 신경썼다.
// Settings.jsx

import styled from '@emotion/native';
import { View, Text, Alert, useColorScheme } from 'react-native';
import { SCREEN_HEIGHT, SCREEN_WIDTH } from '../utils';
import { signOut } from 'firebase/auth';
import { authService, dbService } from '../firebase';
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import { useCallback, useEffect, useState } from 'react';
import { getAuth, updateProfile } from 'firebase/auth';
import profileImg from '../assets/profileImg.png';
import { ScrollView, TouchableOpacity } from 'react-native-gesture-handler';
import { AntDesign } from '@expo/vector-icons';
import { ORANGE_COLOR, BLUE_COLOR, DARK_COLOR } from '../colors';

export default function Settings() {
  const isDark = useColorScheme() === 'dark';

  const { navigate, setOptions, goBack } = useNavigation();
  const [textValue, setTextValue] = useState('');
  const [init, setInit] = useState(false);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [userObj, setUserObj] = useState(null);

  const auth = getAuth();
  const user = auth.currentUser;
  const displayName = user?.displayName;

  const editNickName = async () => {
    await updateProfile(auth.currentUser, {
      displayName: textValue,
    })
      .then(() => {
        setTextValue('');
      })
      .catch((error) => {
        console.log(error);
      });
    refreshUser();
  };

  useEffect(() => {
    authService.onAuthStateChanged((user) => {
      if (user) {
        setIsLoggedIn(true);
        setUserObj({
          displayName: user.displayName,
          uid: user.uid,
          updateProfile: (args) => user.updateProfile(args),
        });
      } else {
        setIsLoggedIn(false);
      }
      setInit(true);
    });
  }, []);

  const refreshUser = () => {
    const user = authService.currentUser;
    setUserObj({
      displayName: user.displayName,
      uid: user.uid,
      updateProfile: (args) => user.updateProfile(args),
    });
  };

  const logout = () => {
    Alert.alert('로그아웃', '로그아웃 하시겠습니까?', [
      {
        text: '취소',
        style: 'cancel',
        onPress: () => console.log('취소 클릭!'),
      },
      {
        text: '로그아웃',
        style: 'destructive',
        onPress: () => {
          signOut(authService);
          navigate('NotTabs', { screen: 'Login' });
        },
      },
    ]);
  };

  // useNavigation의 setOptions를 사용하려면 useFocusEffect에서 사용해줘야 한다.
  // goBack으로 이전 페이지로 가지 않는 것을 처리해주기 위해 뒤로가기 버튼이 필요한 컴포넌트에서 각각 지정해줘야 한다.!!
  useFocusEffect(
    useCallback(() => {
      setOptions({
        headerLeft: () => (
          <TouchableOpacity onPress={() => goBack()}>
            <AntDesign
              name='left'
              size={24}
              style={{ marginLeft: 16 }}
              color={isDark ? ORANGE_COLOR : BLUE_COLOR}
            />
          </TouchableOpacity>
        ),
      });
    }, [])
  );

  return (
    <>
      {!!user ? (
        <SettingWrap style={{ backgroundColor: isDark ? '#1B1D21' : 'white' }}>
          <View style={{ marginTop: -22 }}>
            <SettingImage source={profileImg} />
          </View>
          <ProfileView
            style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
          >
            <ProfileTextWrap>
              <ProfileTitle>{displayName}</ProfileTitle>
              <ProfileTextInput
                placeholder='닉네임을 입력해주세요 ...'
                placeholderTextColor='#A8A8A8'
                value={textValue}
                onChangeText={setTextValue}
              />
            </ProfileTextWrap>
            <ProfileButton onPress={editNickName}>
              <ProfileButtonText
                style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
              >
                수정하기
              </ProfileButtonText>
            </ProfileButton>
          </ProfileView>
          <LogoutButton
            onPress={logout}
            style={{ backgroundColor: isDark ? '#3320B3' : BLUE_COLOR }}
          >
            <LogoutButtonText>로그아웃</LogoutButtonText>
          </LogoutButton>
        </SettingWrap>
      ) : (
        <VisitorView>
          <Text>로그인이 필요한 서비스입니다.</Text>
        </VisitorView>
      )}
    </>
  );
}

const SettingWrap = styled.View`
  flex: 1;
  padding: 10% 15%;
`;

const SettingImage = styled.Image`
  height: ${SCREEN_HEIGHT / 3.5 + 'px'};
  width: ${SCREEN_WIDTH / 2 + 'px'};
  margin-bottom: 10%;
  margin-left: 15%;
`;

const ProfileView = styled.View`
  justify-content: space-around;
  align-items: center;
  height: ${SCREEN_HEIGHT / 3.5 + 'px'};
  background-color: #0c68f2;
  border-radius: 20%;
`;

const ProfileTextWrap = styled.View`
  align-items: center;
  width: ${SCREEN_WIDTH / 2 + 'px'};
  border-bottom-width: 1px;
  border-bottom-color: white;
`;

const ProfileTitle = styled.Text`
  font-size: 20px;
  font-weight: bold;
  color: white;
  margin-bottom: 30px;
`;

const ProfileTextInput = styled.TextInput`
  color: white;
  /* &::placeholder {
    color: #A8A8A8;
  } */
  margin-bottom: 3%;
`;

const ProfileButton = styled.TouchableOpacity`
  width: 150px;
  height: 30px;
  border-width: 1px;
  border-color: white;
  border-radius: 20%;
  justify-content: center;
  align-items: center;
`;

const ProfileButtonText = styled.Text`
  color: white;
`;

const LogoutButton = styled.TouchableOpacity`
  margin-top: 10%;
  border-radius: 30%;
  width: ${SCREEN_WIDTH};
  height: 50px;
  background-color: #0c68f2;
  justify-content: center;
  align-items: center;
`;

const LogInButton = styled.TouchableOpacity`
  margin-top: 10%;
  border-radius: 30%;
  width: ${SCREEN_WIDTH};
  height: 50px;
  background-color: #0c68f2;
  justify-content: center;
  align-items: center;
`;

const LogoutButtonText = styled.Text`
  color: white;
`;

const VisitorView = styled.View`
  height: ${SCREEN_HEIGHT / 1.5 + 'px'};
  justify-content: center;
  align-items: center;
`;

오늘 한 일 2

  • 관심목록에 추가하기 기능 ( 미완성 )
    • 관심목록에 추가하기 기능을 구현하기 위해서 일단 가져온 데이터들을 파이어베이스에 저장한다.
    • 한 번에 모두 저장하면 데이터가 많아지기 때문에 메인페이지에서 디테일 페이지로 클릭할 때 데이터가 추가되도록 한다.
    • 생각해보니 클릭했던 것을 또 클릭하면 계속 추가되기 때문에 추가되었던 데이터가 가져온 데이터와 일치하면 추가되지 않도록 막아주었다.
    • 추가할 때 api데이터 키-값에 isLike: false 를 추가하여 하트 버튼을 누르면 true : false 가 되도록 해주었다.
    • onSnapshot 메서드를 이용하고 상위컴포넌트에서 useState로 데이터를 저장해주었는데, 메인 페이지를 벗어나는 순간 코드들이 죽어 업데이트 된 데이터가 넘어오지 않는 현상이 발생하였다.
profile
취준 개발자

0개의 댓글