솜이의 데브로그

[React Native] 채팅 애플리케이션(2) 본문

dev/React native

[React Native] 채팅 애플리케이션(2)

somsoming 2022. 1. 6. 19:07

로그인 화면

헤더 수정하기

options={{ headerBackTitleVisible: false }}

Screen 컴포넌트에서 위의 옵션을 추가하면 화면의 헤더에서 뒤로가기 버튼의 타이틀을 감출 수 있다.

 

 

노치 디자인 대응

  • 내비게이션의 헤더를 감추면 노치 디자인에 대한 문제가 발생할 수 있으므로 대응해야한다.
  • SafeAreaView 컴포넌트 이용
  • 스타일에 설정해야 하는 padding 값을 얻는 받법
  • useSafeAreaInsets 함수의 장점은 iOS 뿐만 아니라 안드로이드에서도 적용 가능한 padding 값을 전달한다는 점이다.

 

 

회원가입 화면

  • 로그인 화면 제작 과정에서 만든 컴포넌트를 재사용하면 쉽고 빠르게 만들 수 있다.

src/screens/Signup.js

import React, { useState, useRef, useEffect } from "react";
import styled from "styled-components/native";
import { Image, Input, Button } from "../components";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import { validateEmail, removeWhitespace } from "../utils/common";
// import { Text } from 'react-native';

const Container = styled.View`
    flex: 1;
    justify-content: center;
    align-items: center;
    background-color: ${({ theme }) => theme.background};
    padding: 0 20px;
`;
const ErrorText = styled.Text`
    align-items: flex-start;
    width: 100%;
    height: 20px;
    margin-bottom: 10px;
    line-height: 20px;
    color: ${({ theme }) => theme.ErrorText };
`;

const Signup = () => {
    const [name, setName] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [passwordConfirm, setPasswordConfirm] = useState('');
    const [errorMessage, setErrorMessage] = useState('');
    const [disabled, setDisabled] = useState('');

    const emailRef = useRef();
    const passwordRef = useRef();
    const passwordConfirmRef = useRef();

    useEffect(() => {
        let _errorMesage = '';
        if (!name) {
            _errorMesage = 'Please enter your name.';
        } else if (!validateEmail(email)) {
            _errorMesage = 'Please verify your email.';
        } else if (password.length < 6) {
            _errorMesage = 'The password must contain 6 characters at least.';
        } else if (password !== passwordConfirm) {
            _errorMesage = 'Passwords need to match.';
        } else {
            _errorMesage = '';
        }
        setErrorMessage(_errorMesage);
    }, [name, email, password, passwordConfirm]);

    useEffect(() => {
        setDisabled(
            !(name && email && password && passwordConfirm && !errorMessage)
        );
    }, [name, email, password, passwordConfirm, errorMessage]);

    const _handleSignupButtonPress = () => {};
    
    return(
        <KeyboardAwareScrollView
            contentContainerStyle={{ flex:1 }}
            extraScrollHeight={20}
        >
            <Container>
                <Image rounded />
                <Input
                    label="Name"
                    value={name}
                    onChangeText={text => setName(text)}
                    onSubmitEditing={() => {
                        setName(name.trim());
                        emailRef.current.focus();
                    }}
                    onBlur={() => setName(name.trim())}
                    placeholder="Name"
                    returnKeyType="next"
                />
                <Input
                    ref={emailRef}
                    label="Email"
                    value={email}
                    onChangeText={text => setEmail(removeWhitespace(text))}
                    onSubmitEditing={() => passwordRef.current.focus()}
                    placeholder="Email"
                    returnKeyType="next"
                />
                <Input
                    ref={passwordRef}
                    label="Password"
                    value={password}
                    onChangeText={text => setPassword(removeWhitespace(text))}
                    onSubmitEditing={() => passwordConfirmRef.current.focus()}
                    placeholder="Password"
                    returnKeyType="done"
                    isPassword
                />
                <Input
                    ref={passwordConfirmRef}
                    label="Password Confirm"
                    value={passwordConfirm}
                    onChangeText={text => setPasswordConfirm(removeWhitespace(text))}
                    onSubmitEditing={_handleSignupButtonPress}
                    placeholder="Password"
                    returnKeyType="done"
                    isPassword
                />
                <ErrorText>{errorMessage}</ErrorText>
                <Button
                    title="Signup"
                    onPress={_handleSignupButtonPress}
                    disabled={disabled}
                />
            </Container>
        </KeyboardAwareScrollView>
    );
};

export default Signup;
  • 입력받아야하는 값이 많아졌으므로 유효성 검사와 오류 메시지의 종류가 많아졌다.
  • useEffect를 이용해 관련된 값이 변할 때마다 적절한 오류 메시지가 렌더링되도록 한다.

 

화면 스크롤

  • flex: 1 스타일을 설정하면 컴포넌트가 차지하는 영역이 부모 컴포넌트 영역만큼으로 한정되므로, 컴포넌트의 크기에 따라 화면을 넘어가서 스크롤이 생성되도록 flex:1 을 삭제한다.
  • contentContainerStyle을 삭제하고 화면의 위아래에 약간의 여유 공간을 두기 위해 Container 컴포넌트의 padding 값을 수정한다.

src/components/Image.js

const ButtonContainer = styled.TouchableOpacity`
    background-color: ${({ theme }) => theme.imageButtonBackground};
    position: absolute;
    bottom: 0;
    right: 0;
    width: 30px;
    height: 30px;
    border-radius: 15px;
    justify-content: center;
    align-items: center;
`;

const ButtonIcon = styled(MaterialIcons).attrs({
    name: 'photo-camera',
    size: 22,
})`
    color: ${({ theme }) => theme.imageButtonIcon};
`;

const PhotoButton = ({ onPress }) => {
    return(
        <ButtonContainer onPress={onPress}>
            <ButtonIcon />
        </ButtonContainer>
    );
};
  • Image 컴포넌트에서 사진 변경하기 버튼으로 사용할 PhotoButton 컴포넌트 생성
  • 추가된 버튼은 Iamge 컴포넌트의 props로 전달되는 showButton의 값에 따라 렌더링 여부가 결정되도록 작성
  • 버튼을 클릭하면 기기의 사진첨에 접근해서 선택된 사진의 정보를 가져오는 기능 추가
expo install expo-image-picker

 

src/components/Image.js

const Image = ({ url, imageStyle, rounded, showButton, onChangeImage }) => {
    useEffect(() => {
        (async () => {
            try{
                if (Platform.OS === 'ios'){
                    const { status } = await Permissions.askAsync(
                        Permissions.CAMERA_ROLL
                    );
                    if (status !== 'granted'){
                        Alert.alert(
                            'Photo Permission',
                            'Please turn on the camera roll permissions.'
                        );
                    }
                }
            } catch (e) {
                Alert.alert('Photo Permission Error', e.message);
            }
        })();
    }, []);

    const _handleEditButton = async () => {
        try{
            const result = await ImagePicker.launchImageLibraryAsync({
                mediaTypes: ImagePicker.MediaTypeOptions.Images,
                allowsEditing: true,
                aspect: [1, 1],
                quality: 1,
            });

            if (!result.cancelled){
                onChangeImage(result.uri);
            }
        } catch(e) {
            Alert.alert('Photo Error', e.message);
        }
    };
  • 사진첩에 접근하기 위해 사용자에게 권한을 요청하는 과정이 필요하므로 권한을 요청하는 부분을 추가한다.
  • 사진 변경 버튼을 클릭하면 호출되는 함수에서 기기의 사진에 접근하기 위해 호출되는 라이브러리 함수는 다음 값들을 포함한 객체를 파라미터로 전달받는다.
    • mediaTypes : 조회하는 자료의 타입
    • allowsEditing : 이미지 선택 후 편집 단계 진행 여부
    • aspect : 안드로이드 전용 옵션으로 이미지 편집 시 사각형의 비율 ([x,y])
    • quality : 0~1 사이의 값을 받으며 압축 품질을 의미 (1: 최대 품질)

 

src/screens/Signup.js

return(
        <KeyboardAwareScrollView extraScrollHeight={20}>
            <Container>
                <Image
                    rounded url={photoUrl}
                    showButton
                    onChangeImage={url => setPhotoUrl(url)}
                />

기기에서 권한 요청을 수락하면 기기의 사진에 접근할 수 있고, 사진을 선택하면 Image 컴포넌트에서 선택한 사진이 렌더링 되는 것을 확인할 수 있다.

 

귀욥

 

로그인과 회원가입

파이어베이스의 인증을 이용해 로그인과 회원가입 기능을 만들어보자.

 

로그인

  • 생성된 사용자가 없으므로 파이어베이스 콘솔에서 사용자를 추가하고 로그인 기능을 만든다.

'dev > React native' 카테고리의 다른 글

[React Native] 채팅 애플리케이션 만들기  (0) 2021.12.26
[React Native] Navigation  (0) 2021.11.21
[React Native] Context API  (0) 2021.11.14
[React Native] Hooks  (0) 2021.11.12
[React Native] 할 일 관리 애플리케이션(2)  (0) 2021.11.06