일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 백준
- 모두를 위한 딥러닝
- 문자열
- 모두를위한딥러닝
- 정리
- 백준 5525번
- 깃 터미널 연동
- 백준 4358번
- 팀플회고
- 지네릭스
- 딥러닝
- 데이터베이스
- 모두의네트워크
- 데베
- 백준 4358 자바
- 네트워크
- 깃허브 토큰 인증
- 리액트 네이티브 프로젝트 생성
- 스터디
- 깃 연동
- 모두의 네트워크
- 백준 4949번
- 자바
- SQL
- React Native
- 리액트 네이티브
- 깃허브 로그인
- 머신러닝
- HTTP
- 리액트 네이티브 시작하기
- Today
- Total
솜이의 데브로그
[React Native] 채팅 애플리케이션 만들기 본문
Reference : 처음 배우는 리액트네이티브 (김범준)
네비게이션 및 추가 라이브러리들을 먼저 설치한다.
npm install @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
npm install @react-navigation/stack @react-navigation/bottom-tabs
스타일드 컴포넌트 라이브러리와 prop-types 라이브러리도 추가적으로 설치한다.
src/theme.js
const colors = {
white: '#ffffff',
black : '#000000',
grey_0: '#d5d5d5',
grey_1: '#a6a6a6',
red: '#e82118',
blue: '#3679fe'
};
export const theme = {
background: colors.white,
text: colors.black,
};
공통으로 사용할 색을 정의하고, 배경색과 글자의 색을 미리 정의한다.
파이어베이스
- 파이버에시는 인증, 데이터베이스 등의 다양한 기능을 제공하는 개발 플랫폼이다.
- 대부분의 서비스에서 필요한 서버와 데이터베이스를 직접 구축하지 않아도 개발이 가능하다.
https://console.firebase.google.com/
파이어베이스 콘솔에서 프로젝트를 생성한다.
파이어베이스에서 앱을 추가 후, 해당 앱의 구성을 복사해서 firebase.json 파일로 생성하여 추가한다.
또 이 파일이 커밋되지 않도록 .gitignore 파일에 firebase.json 을 추가한다.
위와 같이 이메일과 비밀번호를 이용하여 인증하는 기능을 만들기 위해 활성화한다.
파이어베이스 데이터베이스를 생성하고, 본인이 위치하는 지역을 선택한다.
또 사용자의 사진을 저장하고 가져오는 기능을 만들기 위해 스토리지를 생성한다.
라이브러리 설치
expo install firebase
리액트 네이티브에서 파이어베이스를 사용하기 위해 라이브러리를 설치한다.
https://www.npmjs.com/package/firebase
라이브러리 설치 후 다음과 같이 파일을 작성한다.
src/utils/firebase.js
import * as firebase from 'firebase';
import config from '../../firebase.json';
const app = firebase.initializeApp(config);
이렇게 하면 파이어베이스 사용 준비가 완료된다.
앱 아이콘과 로딩 화면
앱의 아이콘으로 사용할 1024*1024 크기의 icon.png 파일과 로딩화면으로 사용할 1242*2436 크기의 spalsh.png 파일을 생성해서 assets 폴더에 자동으로 생성된 파일들과 교체하고 App 컴포넌트를 수정한다.
const cacheImages = images => {
return images.map(image => {
if (typeof image === 'string') {
return Image.prefetch(image);
} else{
return Asset.fromModule(image).downloadAsync();
}
});
};
const cacheFonts = fonts => {
return fonts.map(font => Font.loadAsync(font));
};
- 프로젝트에서 사용할 이미지와 폰트를 미리 불러와서 사용할 수 있도록 cacheImages와 cacheFonts 함수를 작성한다.
const App = () => {
const [isReady, setIsReady] = useState(false);
const _loadAssets = async () => {
const imageAssets = cacheImages([require('../assets/splash.png')]);
const fontAssets = cacheFonts([]);
await Promise.all([...imageAssets, ...fontAssets]);
};
return isReady ? (
<ThemeProvider theme={theme}>
<StatusBar barStyle='dark-content'/>
</ThemeProvider>
) : (
<AppLoading
startAsync={_loadAssets}
onFinish={()=> setIsReady(true)}
onError={console.warn}
/>
);
};
- 미리 불러와야하는 항목들을 모두 불러오고 화면이 렌더링되도록 Apploading 컴포넌트의 startAsync와 _loadAssets 함수를 지정한다.
- AppLoading import 해오는 항목이 expo-app-loading 으로 변경되었다.
인증화면
인증을 위해 이메일과 비밀번호를 로그인 및 회원가입화면에서 이메일과 비밀번호를 입력받도록 한다.
src/screens/Login.js
import React from "react";
import styled from 'styled-components/native';
import { Text, Button } from 'react-native';
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: center;
background-color: ${({theme}) => theme.background};
`;
const Login = ({ navigation }) => {
return(
<Container>
<Text style={{ fontsize: 20 }}>Login Screen</Text>
<Button title="Signup" onPress={() => navigation.navigate('Signup')} />
</Container>
);
};
export default Login;
- 회원가입 화면으로 이동할 수 있는 버튼을 넣은 로그인 화면 작성
src/screens/Signup.js
import React from "react";
import styled from "styled-components/native";
import { Text } from 'react-native';
const Container = styled.View`
flex: 1;
justify-content: center;
align-items: center;
background-color: ${({ theme }) => theme.background};
`;
const Signup = () => {
return(
<Container>
<Text style = {{ fontSize: 30 }}>Signup Screen</Text>
</Container>
);
};
export default Signup;
- 화면을 확인할 수 있는 텍스트가 있는 간단한 화면 구성
index.js 파일에 위의 두 파일을 import, export 하도록 작성한다.
스택 내비게이션을 이용해 작성
src/navigations/AuthStack.js
import React, { useContext } from "react";
import { ThemeContext } from "styled-components/native";
import { createStackNavigator } from '@react-navigation/stack';
import { Login, Signup } from '../screens';
const Stack = createStackNavigator();
const AuthStack = () => {
const theme = useContext(ThemeContext);
return(
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerTitleAlign: 'center',
cardStyle: { backgroundColor: theme.background },
}}
>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</Stack.Navigator>
);
};
export default AuthStack;
- 첫 화면을 로그인화면으로 설정하고, 로그인과 회원가입 화면을 가진 내비게이션 생성
- 스타일드 컴포넌트에서 제공하는 ThemeContext 컴포넌트와 useContext hook 함수를 이용해 theme을 받아온다.
- 내비게이션 화면의 배경 색 설정, 헤더 타이틀 위치를 안드로이드와 iOS에서 동일한 위치에 렌더링 하기 위해 center에 지정.
src/navigations/index.js
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import AuthStack from "./AuthStack";
const Navigation = () => {
return(
<NavigationContainer>
<AuthStack />
</NavigationContainer>
);
};
export default Navigation;
- NavgationContainer 컴포넌트 사용
- 자식 컴포넌트로 AuthStack 내비게이션을 사용
이제 작성한 파일들을 렌더링되도록 App.js 파일을 수정한다.
src/components/Image.js
import React from "react";
import styled from "styled-components/native";
import PropTypes from 'prop-types';
const Container = styled.View`
align-self: center;
margin-bottom: 30px;
`;
const StyledImage = styled.Image`
background-color: ${({ theme }) => theme.imageBackground};
width: 100px;
height: 100px;
`;
const Image = ({ url, imageStyle }) => {
return(
<Container>
<StyledImage source= {{ uri: url }} style={imageStyle} />
</Container>
);
};
Image.PropTypes = {
uri: PropTypes.string,
imageStyle: PropTypes.object,
};
export default Image;
- props로 전달되는 url을 렌더링하고 imageStyle을 전달받아 컴포넌트의 스타일을 수정할 수 있는 Image 컴포넌트를 생성.
- Image 컴포넌트 작성이 완료되면 components 폴더에 index.js 파일을 생성
- firebase 스토리지에 로고를 업로드하고, url을 받아와 src/utils/images.js 파일 밑에 입력한다. 이 때 token값은 제외하고 입력한다.
- 후 로고 이미지도 로딩과정에서 미리 불러오도록 App 컴포넌트를 수정한다.
src/components/Input.js
import React, { useState } from "react";
import styled from "styled-components/native";
import PropTypes from 'prop-types';
const Container = styled.View`
flex-direction: column;
width: 100%;
margin: 10px 0;
`;
const Label = styled.Text`
font-size: 14px;
font-weight: 600;
margin-bottom: 6px;
color: ${({ theme, isFocused }) => (isFocused ? theme.text : theme.label)};
`;
const StyledTextInput = styled.TextInput.attrs(({ theme }) => ({
placeholderTextColor: theme.inputPlaceholder,
}))`
background-color: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.text};
padding: 20px 10px;
font-size: 16px;
border: 1px solid
${({ theme, isFocused }) => (isFocused ? theme.text : theme.inputBorder)};
border-radius: 4px;
`;
const Input = ({
label,
value,
onChangeText,
onSubmitEditing,
onBlur,
placeholder,
isPassword,
returnKeyType,
maxLength,
}) => {
const [isFocused, setIsFocused] = useState(false);
return(
<Container>
<Label isFocused={isFocused}>{label}</Label>
<StyledTextInput
isFocused={isFocused}
value={value}
onChangeText={onChangeText}
onSubmitEditing={onSubmitEditing}
onFocus={() => setIsFocused(true)}
onBlur={() => {
setIsFocused(false);
onBlur();
}}
placeholder={placeholder}
secureTextEntry={isPassword}
returnKeyType={returnKeyType}
maxLength={maxLength}
autoCapitalize="none"
autoCorrect={false}
textContentType="none"
/>
</Container>
);
};
Input.defaultProps = {
onBlur: () => {},
};
Input.propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
onChangeText: PropTypes.func.isRequired,
onSubmitEditing: PropTypes.func.isRequired,
onBlur: PropTypes.func,
placeholder: PropTypes.string,
isPassword: PropTypes.bool,
returnKeyType: PropTypes.oneOf(['done', 'next']),
maxLength: PropTypes.number,
};
export default Input;
- 라벨을 TextInput 컴포넌트 위에 렌더링하고, 포커스 여부에 따라 스타일이 변경되도록 컴포넌트를 생성.
- secureTextEntry 속성은 입력되는 문자를 감추는 기능이다.
- Input 컴포넌트 작성이 완료되면 components 폴더의 index.js 파일을 수정한다.
src/screens/Login.js
const Login = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return(
<Container>
<Image url={images.logo} imageStyle={{ borderRadius: 8 }} />
<Input
label="Email"
value={email}
onChangeText={text => setEmail(text)}
onSubmitEditing={() => {}}
placeholder="Email"
returnKeyType="next"
/>
<Input
label="Password"
value={password}
onChangeText={text => setPassword(text)}
onSubmitEditing={() => {}}
placeholder="Password"
returnKeyType="done"
isPassword
/>
<Button title="Signup" onPress={() => navigation.navigate('Signup')} />
</Container>
);
};
- 입력되는 이메일과 비밀번호를 관리할 email과 password를 useState 함수로 생성
- 각각 이메일과 비밀번호를 입력받는 Input 컴포넌트의 value로 지정.
- 비밀번호를 입력받는 Input 컴포넌트는 입력되는 값이 보이지 않도록 isPassword 속성을 추가한다.
- 이메일 입력받는 returnKeyType은 next로 설정해 바로 비밀번호를 입력하도록한다.
- useRef를 이용해 이메일을 입력받는 Input 컴포넌트에서 키보드의 next 버튼을 클릭하면 비밀번호를 입력하는 Input 컴포넌트 포커스가 이동.
- passwordRef를 만들고 비밀번호를 입력하는 Input 컴포넌트의 ref로 지정
- onSubmitEditing 함수를 passwordRef를 이용해 비밀번호를 입력하는 Input 컴포넌트로 포커스가 이동되도록 수정.
키보드 감추기
- TouchableWithoutFeedback : 클릭에 대해 상호작용은 하지만 스타일 속성이 없고 반드시 하나의 자식 컴포넌트를 가져야한다ㅏ.
- Keyboard API : 리액트 네이티브에서 제공하는 키보드 관련 API로 키보드 상태에 따른 이벤트 등록에 많이 사용하며, dismiss 함수는 활성화된 키보드를 닫는 기능이다.
npm install react-native-keyboard-aware-scroll-view
react-native-keyboard-aware-scroll-view 라이브러리를 이용해 포커스가 있는 TextInput 컴포넌트의 위치로 자동 스크롤하는 기능 제공.
- 입력 도중 다른 영역 터치 시 키보드가 사라짐
- 포커스를 얻은 TextInput 컴포넌트의 위치에 맞춰 스크롤이 이동하는 것을 확인할 수 있다.
- 스크롤 되는 위치를 조정하고 싶은 경우 extraScrollHeight 의 값을 조절해서 원하는 위치로 스크롤되도록 설정할 수 있다.
오류메시지
src/screens/Login.js
const ErrorText = styled.Text`
align-items: flex-start;
width: 100%;
height: 20px;
margin-bottom: 10px;
line-height: 20px;
color: ${({ theme }) => theme.errorText};
`;
const Login = ({ navigation }) => {
const [errorMessage, setErrorMessage] = useState('');
const _handleEmailChange = email => {
const changedEmail = removeWhitespace(email);
setEmail(changedEmail);
setErrorMessage(
validateEmail(changedEmail) ? '' : 'Please verify your email.'
);
};
const _handlePasswordChange = password => {
setPassword(removeWhitespace(password));
};
}
- 이메일에는 공백이 존재하지 않으므로 email의 값이 변경될 때마다 공백을 제거하도록 수정
- validateEmail 함수를 이용해 공백이 제거된 이메일이 올바른 형식인지 검사
- 비밀번호도 공백허용하지 않는다.
src/components/Button.js
import React from "react";
import styled from "styled-components/native";
import PropTypes from 'prop-types';
const TRANSPARENT = 'transparent';
const Container = styled.TouchableOpacity`
background-color: ${({ theme, isFilled }) =>
isFilled ? theme.buttonBackground : TRANSPARENT};
align-items: center;
border-radius: 4px;
width: 100%;
padding: 10px;
`;
const Title = styled.Text`
height: 30px;
line-height: 30px;
font-size: 16px;
color: ${({ theme, isFilled }) =>
isFilled ? theme.buttonTitle : theme.buttonUnfilledTitle};
`;
const Button = ({ containerStyle, title, onPress, isFilled }) => {
return(
<Container style={containerStyle} onPress={onPress} isFilled={isFilled}>
<Title isFilled={isFilled}>{title}</Title>
</Container>
);
};
Button.defaultProps = {
isFilled: true,
};
Button.propTypes = {
containerStyle: PropTypes.object,
title: PropTypes.string,
onPress: PropTypes.func.isRequired,
isFilled: PropTypes.bool,
};
export default Button;
- isFilled의 값에 따라 버튼 내부를 채우거나 투명하게 처리하는 Button 컴포넌트 생성
- 버튼 내부가 채워지지 않았을 경우 props로 전달된 title의 색이 변경되도록 작성
- 사용되는 곳에 따라 버튼의 스타일을 수정하기 위해 containerStyle을 props로 전달받아 적용하도록 작성.
<Button title="Login" onPress={_handleLoginButtonPress} />
- Button 컴포넌트를 이용해 로그인과 회원가입 화면으로 이동하는 버튼 생성
- 로그인 버튼 클릭시와 비밀번호 입력받는 Input 컴포넌트의 onSubmitEditing 함수의 역할이 같으므로 동일한 작업 수행.
- Button 컴포넌트에서 props를 통해 전달되는 disabled의 값에 따라 버튼 스타일이 변경되도록 수정.
- TouchableOpacity 컴포넌트에 disabled 속성을 전달하면 값에 따라 클릭 등의 상호작용이 동작하지 않는다.
- disabled 값을 props로 전달하는 것으로 버튼 비활성화 기능 추가.
src/screens/Login.js
const Login = ({ navigation }) => {
const [disabled, setDisabled] = useState(true);
useEffect(() => {
setDisabled(!(email && password && !errorMessage));
}, [email, password, errorMessage]);
}
src/components/Button.js
const Button = ({ containerStyle, title, onPress, isFilled, disabled }) => {
return(
<Container
style={containerStyle}
onPress={onPress}
isFilled={isFilled}
disabled={disabled}
>
<Title isFilled={isFilled}>{title}</Title>
</Container>
);
};
- useState를 사용해 버튼의 활성화 상태를 관리하는 disabled를 생성하고 useEffect를 이용해 email, password, errorMessage의 상태가 변할 때마다 조건에 맞게 disabled 상태가 변경되도록 작성.
- 로그인 버튼은 이메일과 비밀번호가 입력되어 있고, 오류 메시지가 없는 상태에서만 활성화.
- 로그인 버튼의 Button 컴포넌트에 disabled를 전달해서 값에 따라 버튼의 활성화여부 결정
현재 상태...
'dev > React native' 카테고리의 다른 글
[React Native] 채팅 애플리케이션(2) (0) | 2022.01.06 |
---|---|
[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 |