일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백엔드
- 풀스택
- 리엑트
- 리엑트 네이티브
- 상태관리
- Java
- react
- Spring
- 개발
- expo
- 국비 지원
- 개발자
- 스프링
- react native
- spring boot
- 비전공
- 스프링 부트
- 스타트업
- 자바
- 프론트엔드
- 프론트 엔드
- Redux
- ffmpeg
- 풀스택 개발자
- 코딩
- 인스타그램
- react-native
- 클론코딩
- 국비지원
- 비전공자
- Today
- Total
오티스의개발일기
[REACT NATIVE] 리엑트 네이티브 인스타그램 클론 코딩 (3) 구글 로그인구현 및 급 react-native-cli 로 급 우회.... google-signin 2022-12-26 기준 동작함 본문
[REACT NATIVE] 리엑트 네이티브 인스타그램 클론 코딩 (3) 구글 로그인구현 및 급 react-native-cli 로 급 우회.... google-signin 2022-12-26 기준 동작함
안되면 될때까지.. 2022. 12. 26. 21:48
< 이전글
다음글 >
2022.12.27 - [개발/spring boot] - [REACT NATIVE] 인스타그램 클론 만들기 (4) 풀스택 spring boot로 api 만들기 프로잭트 시작
오늘은 구글api 를 활용해 구글 로그인을 시도해볼것이다.
그렇게 2일동안 열심히 삽질을하고
expo로는 도저히 안되겠다는 생각이 문득 들게되었다.
어찌됬던간에 구글 로그인을 구현하려면 네이티브 gradle 설정을 바꿔줘야하고
eppo로 진행했을시 npx expo run:android 이 코드를 사용해
프로잭트에 native 파일들을 받고 수정을 하는것이다.
그리고 또한 이작업을 했을시 그전처럼 윈도우 안에서 아이폰으로 개발이 안된다.
qr코드를 스캔했을시 이상한 url로 넘어가버린다.
이래저래 react-native로 하는것과 뭐가다른가라는 생각이들어이번기회에 프로잭트를 새로다시만들고 그전에 만들었던 파일들을 옮기고 작업을 시작했다.
사용한 모듈은
yarn add @react-native-google-signin/google-signin
or
npm install @react-native-google-signin/google-signin
https://github.com/react-native-google-signin/google-signin/blob/master/docs/android-guide.md
google signin 이다.
참고로 firebase는 필수인것같다.
firebase없이 google api로만 시도하려고 프로잭트만 10개는 만든거 같다
안된다.
새로바뀐 정책 .. 코드버그 등등 여러가지 변수로 그냥 안된다 생각하면 몸과 마음이 편해진다.
본론으로 돌아가 오늘은 구글로 넘어갈수있는 버튼을 만들고
네이티브의 코드를 수정하고 연동하는것을 보여줄계획이다.
아 맞다...
참고로 이번일로인해 맥북의 필요성이 더욱 증가하였다..
이번주에 지를계획이다..
m1 pro 16인치 램32기가 512gb
4백만원 가까이 엄청난 가격이다.
모르겠다 지르고 열심히 코딩하면 본전은 뽑겠지...
ㅋㅋ
일단 코드를 옯겼다
이건그냥 복사붙여넣기이기때문에
따로 설명은 안하겠다.
다행히 src폴더에 묶어놔서 폴더만 옮기는 작업을하였다. 정말다행이다.. ㅎㅎ
시작하기전에 본인처럼 expo -> react-native-cli 로 옮기는 사람은
분명 에러가 뜰텐데
navigation 이부분 일것이다
이 부분 참고하면 도움이 될것이다.
이제 구글 버튼을 만들어보자
원래 인스타그램은 페이스북이지만 요즘 누가 페이스북을 사용하는가..?
어차피 미래에 다른 어플 개발할때도 구글은 필수여서 구글로 선택하고 작업하였다.
아 정말로 안되서 여러가지 생각을 다했던거같다.
spring boot oauth2 사용해서 인증받고 redirect 하면되지않을까?
이런 하찮은 생각을 실행으로 옮기고 눈물을 흘렸다 ㅋㅋ
이런 짓을 하지말자 우리는 앱을 만들고 redirect는 어떻게할것인가? 이바보야
뭐..다시 본론으로 다시 만들어보겠다.
지금 너무 해탈한상태라
코드 전체를 올려버리겠다.. 알아서 봐주세요..
/src/screens/auth/LoginScreen.js
import { StyleSheet } from 'react-native'
import React from 'react'
import { ROUTES } from '../../constants/routes'
import styled, { css } from 'styled-components/native';
import { Divider } from 'react-native-elements';
import * as yup from 'yup'
import { Formik } from 'formik'
import axios from 'axios'
import Ionicons from "@expo/vector-icons/Ionicons";
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from '@react-native-google-signin/google-signin';
GoogleSignin.configure({
scopes: ['https://www.googleapis.com/auth/drive.readonly'], // what API you want to access on behalf of the user, default is email and profile
webClientId: '865431737111-q0qimp0imkuaabtuopjgo0gk5jqkkgpl.apps.googleusercontent.com', // client ID of type WEB for your server (needed to verify user ID and offline access)
});
const LoginScreen = (props) => {
const googleLogin = async () => {
console.log('구글 로그인 시작');
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
console.log('유저 임포메이션');
console.log(userInfo);
} catch (error) {
console.log(error);
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
}
};
const { navigation } = props; // 네비게이션
return (
<SafeAreaView>
<Container>
<ImageBox>
<Image source={require("../../../assets/whiteLogo.png")} />
</ImageBox>
<Formik
initialValues={{ email: '', password: '' }}
validateOnMount={true}
onSubmit={values => {
navigation.navigate(ROUTES.INDEX)
}}
validationSchema={loginValidationSchema}
>
{({ handleChange, handleBlur, handleSubmit, values, touched, errors, isValid }) => (
<>
<TextInputBox>
<TextInput
placeholder="이메일을 입력해주세요"
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
keyboardType="email-address"
value={values.email}
autoFocus={true}
/>
<ValidationTextBox>
<ValidationText>
{
values.email.length > 0 ?
errors.email
:
''
}
</ValidationText>
</ValidationTextBox>
<TextInput
autoCapitalize="none"
textContentType="password"
secureTextEntry={true}
placeholder="비밀번호를 입력해주세요"
onChangeText={handleChange('password')}
onBlur={handleBlur('password')}
value={values.password}
/>
<ValidationTextBox>
<ValidationText>
{
values.password.length > 0 ?
errors.password
:
''
}
</ValidationText>
</ValidationTextBox>
</TextInputBox>
<ForgetPasswordBox>
<TouchableOpacity onPress={() => navigation.navigate(ROUTES.FORGOTPASSWORD)}>
<ForgetPasswordText>비밀번호를 잊으셨나요?</ForgetPasswordText>
</TouchableOpacity>
</ForgetPasswordBox>
<LoginButtonBox>
{
isValid ?
<LoginButton onPress={handleSubmit}>
<LoginButtonText>
로그인
</LoginButtonText>
</LoginButton>
:
<InActiveLoginButton>
<LoginButtonText>
로그인
</LoginButtonText>
</InActiveLoginButton>
}
</LoginButtonBox>
</>
)}
</Formik>
<SnsLoginBox>
<SnsLoginOrBox>
<SnsLoginDividerBox>
<Divider />
</SnsLoginDividerBox>
<Text>또는</Text>
<SnsLoginDividerBox>
<Divider />
</SnsLoginDividerBox>
</SnsLoginOrBox>
<SnsLoginTextBox>
<GoogleIcon source={require('../../../assets/googleIcon.png')}/>
<TouchableOpacity onPress={() => googleLogin()}>
<SnsLoginText>
구글로 로그인
</SnsLoginText>
</TouchableOpacity>
</SnsLoginTextBox>
</SnsLoginBox>
</Container>
<JoinBox>
<JoinBoxNormalText>
계정이 없으신가요?
</JoinBoxNormalText>
<TouchableOpacity onPress={() => navigation.navigate(ROUTES.REGISTER)}>
<JoinBoxText>
가입하기
</JoinBoxText>
</TouchableOpacity>
</JoinBox>
</SafeAreaView>
)
}
const styles = StyleSheet.create({})
const loginValidationSchema = yup.object().shape({
email: yup.string().required("이메일을 입력해주세요").email("올바른 이메일을 작성해주세요"),
password: yup.string().min(8, ({ min }) => "비밀번호는 최소 " + min + " 자리 이상입니다.").required("비밀번호를 입력해주세요")
})
export default LoginScreen
const GoogleIcon = styled.Image`
width: 20px;
height: 20px;
margin-right: 5px;
`;
const SnsLoginTextBox = styled.View`
margin-top: 20px;
align-items: center;
flex-direction: row;
justify-content: center;
`;
const SnsLoginText = styled.Text`
color: #0095F6;
align-items: center;
`;
const SnsLoginBox = styled.View`
margin-top: 20px;
`;
const SnsLoginOrBox = styled.View`
flex-direction: row;
justify-content: space-between;
align-items: center;
`;
const SnsLoginDividerBox = styled.View`
width: 40%;
`;
const ValidationTextBox = styled.View`
margin-top: 8px;
margin-bottom: 8px;
`
const ValidationText = styled.Text`
color: red
`
const Container = styled.View`
width: 100%;
padding: 0px 20px;
`
const LoginButtonBox = styled.View`
margin-top: 20px;
`
const JoinBox = styled.View`
flex-direction: row;
position: absolute;
bottom: 40px;
left: 0;
width: 100%;
justify-content: center;
align-items: center;
`
const JoinBoxNormalText = styled.Text`
color: gray;
font-size: 12px;
margin-right: 10px;
`
const JoinBoxText = styled.Text`
color: #0095F6;
`
const InActiveLoginButton = styled.View`
background-color: #014068d1;
height: 50px;
border-radius: 5px;
font-size: 12px;
`;
const LoginButton = styled.TouchableOpacity`
background-color: #0095F6;
height: 50px;
border-radius: 5px;
font-size: 12px;
`;
const LoginButtonText = styled.Text`
color: white;
text-align: center;
margin: auto;
font-weight: 600;
`
const ForgetPasswordBox = styled.View`
margin-top: 15px;
align-items: flex-end;
`
const ForgetPasswordText = styled.Text`
color: #0095F6;
font-size: 12px;
`
const SafeAreaView = styled.SafeAreaView`
justify-content: center;
flex: 1;
align-items: center;
background-color: ${props => props.theme.backgroundColor};
`
const TextInputBox = styled.View`
margin-top: 20px;
width: 100%;
`
const TextInput = styled.TextInput`
padding: 0px 10px;
color: ${props => props.theme.TextColor};
background-color: #282828a6;
border: 1px solid #7a7a7a5c;
width: 100%;
height: 40px;
border-radius: 5px;
`
const Image = styled.Image`
width: 200px;
height: 55px;
`
const ImageBox = styled.View`
width: 100%;
align-items: center;
`
const View = styled.View`
`
const Text = styled.Text`
color: ${props => props.theme.TextColor};
`
const TouchableOpacity = styled.TouchableOpacity`
`
이런식으로 만들면
이런 화면을 볼수있을것이다.
이제 저 구글로 로그인을 클릭하여 로그인을 가능하게하면된다.
+++ 추가 [2022.12.30] +++
ios 설정방식은 다시 포스팅했으니 ios 를 연결하고싶은사람은
확인하기바란다.
이제 안드로이드의 네이티브 코드를 건드릴 차례이다.
우리가 필요한건 3가지 정도이다
1. gradle 설정 파일
2. 파이어베이스에 같은 안드로이드 패키지 이름으로 등록
3. 파이어 베이스에서 google-services.json 다운로드 받고 프로잭트 안에 심기
4. SHA1 코드 얻기
자 일단 gradle 파일을 수정해보도록하겠다.
상단 google-signin npm에 더 자세한 글이 있으니 그대로 따라하면 동작할것이다.
https://github.com/react-native-google-signin/google-signin
이곳에 들어가면
Android guide 라는게 보일것이다.
저부분을 클릭하면 지금부터 하는 모든 과정이 자세히 나올것이니 참고바란다.
/android/build.gradle 을 수정해주자
주석으로 <--여기 라고 적어놨으니 보고 따라하면된다.
/android/build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 31
targetSdkVersion = 31
googlePlayServicesAuthVersion = "19.2.0" // <--- 여기
if (System.properties['os.arch'] == "aarch64") {
// For M1 Users we need to use the NDK 24 which added support for aarch64
ndkVersion = "24.0.8215888"
} else {
// Otherwise we default to the side-by-side NDK version from AGP.
ndkVersion = "21.4.7075529"
}
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.2.1")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("de.undercouch:gradle-download-task:5.0.1")
classpath 'com.android.tools.build:gradle:4.2.1' // <--- 여기
classpath 'com.google.gms:google-services:4.3.10' // <--- 여기
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
google() // <--- 여기
maven { url 'https://www.jitpack.io' }
}
}
2번째는 android/app/build.gradle 이다
build.gradle 가 2개가있으니 조심하자
이곳도 마찬가지로
<-- 여기
라고 적어놨다
그리고 또 firebase에서 프로잭트 등록후 이곳을 한번더 수정해야하니 기억하고있자.
/android/app/build.gradle
apply plugin: "com.android.application"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.reactnativeigclone"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the CMake build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
arguments "-DPROJECT_BUILD_DIR=$buildDir",
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
"-DANDROID_STL=c++_shared"
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
path "$projectDir/src/main/jni/CMakeLists.txt"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureCMakeDebug* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
configureCMakeDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" // <--- 여기
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group:'com.facebook.fbjni'
}
} else {
implementation jscFlavor
}
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
apply plugin: 'com.google.gms.google-services' // <--- 여기
자이제 파이어베이스 홈페이지에 들어가보자
https://console.firebase.google.com/?hl=ko
들어가서 로그인하면
이런 화면이 보일것이다
중간 프로잭트 만들기를 클릭하자.
그리고 프로잭트 이름을 적는 부분이 나오는데 웬만하면 자신의 프로잭트 패키지명과 통일시켜주자
패키지명은
/android/app/src/main/AndroidManifest.xml
안에 있다
저부분에서 com. 을 제외한 reactnativeigclone을 적어주자
참고로 저기있는 패키지명이 안드로이드 패키지명이다.
좀있다 또 쓸것이니 기억해두자
또한 패키지명은 그냥 소문자혹은 대문자로 통일하는게 마음이 편하다
웬만하면 소문자로하고 소문자대문자가 틀릴시 오류를 볼것이다.
안드로이드 버튼을 눌러주자
위에있는 패키지명이 아까봤던 그 패키지명이다.
그대로 복사해서 넣어주자
그리고 나머지는 현재 적어줄 필요는 없고 firebase sdk 추가부분에서 멈추고 프로잭트로 돌아가보자
어차피 다시 다운로드받아야해서 그냥 받지않고 다음을 눌러준다
이부분은 방금 우리가 그전에 작성한것이다 패스해주자
저기 밑줄친 부분을 메모장에 적어두고 다음을 누른다
콘솔로 이동을 누르고 프로잭트로돌아와
코드를 수정하자
/android/app/build.gradle 파일로돌아와 코드를 추가해준다
/android/app/build.gradle
apply plugin: "com.android.application"
import com.android.build.OutputFile
import org.apache.tools.ant.taskdefs.condition.Os
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://reactnative.dev/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and that value will be read here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
/**
* Architectures to build native code for.
*/
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}
android {
ndkVersion rootProject.ext.ndkVersion
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "com.reactnativeigclone"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) {
// We configure the CMake build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
arguments "-DPROJECT_BUILD_DIR=$buildDir",
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
"-DANDROID_STL=c++_shared"
}
}
if (!enableSeparateBuildPerCPUArchitecture) {
ndk {
abiFilters (*reactNativeArchitectures())
}
}
}
}
if (isNewArchitectureEnabled()) {
// We configure the NDK build only if you decide to opt-in for the New Architecture.
externalNativeBuild {
cmake {
path "$projectDir/src/main/jni/CMakeLists.txt"
}
}
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
into("$buildDir/react-ndk/exported")
}
afterEvaluate {
// If you wish to add a custom TurboModule or component locally,
// you should uncomment this line.
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
// Due to a bug inside AGP, we have to explicitly set a dependency
// between configureCMakeDebug* tasks and the preBuild tasks.
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
configureCMakeDebug.dependsOn(preDebugBuild)
reactNativeArchitectures().each { architecture ->
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
dependsOn("preDebugBuild")
}
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
dependsOn("preReleaseBuild")
}
}
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include (*reactNativeArchitectures())
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" // <--- 여기
// Import the Firebase BoM
implementation platform('com.google.firebase:firebase-bom:31.1.1') // <--- 파이어 베이스 추가 여기
// TODO: Add the dependencies for Firebase products you want to use
// When using the BoM, don't specify versions in Firebase dependencies
implementation 'com.google.firebase:firebase-analytics' // <--- 파이어 베이스 추가 여기
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
exclude group:'com.squareup.okhttp3', module:'okhttp'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
exclude group:'com.facebook.fbjni'
}
} else {
implementation jscFlavor
}
}
if (isNewArchitectureEnabled()) {
// If new architecture is enabled, we let you build RN from source
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
// This will be applied to all the imported transtitive dependency.
configurations.all {
resolutionStrategy.dependencySubstitution {
substitute(module("com.facebook.react:react-native"))
.using(project(":ReactAndroid"))
.because("On New Architecture we're building React Native from source")
substitute(module("com.facebook.react:hermes-engine"))
.using(project(":ReactAndroid:hermes-engine"))
.because("On New Architecture we're building Hermes from source")
}
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.implementation
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}
apply plugin: 'com.google.gms.google-services' // <--- 여기
콘솔로 돌아오자
돌아오면 방금만든 앱이 한개 보일것이다
설정버튼을 눌러주자
하단에 보면 디지털 지문 추가가 있는데 우리는 이것을 찾아서 추가해줄것이다.
프로잭트로 돌아와
터미널을 열어보자
android 폴더로 이동후
아래 커맨드를 작성해보자
실행하면 이런게 4개가 뜰것이다
우리는 가장 상단에있는 사용할것이다.
SHA1 과 SHA-256의 코드를 복사하고 파이어 베이스에 등록하자
2개다 등록해주자
이제 google-services.json을 다운받을 차례이다.
다운로드를 받고
/android/app/src 안에 파일을 넣어주자
파이어 베이스로 돌아와
authentication 버튼을 눌러주자
이것이 우리가 사용할 클라이언트 아이디이다
위에있는 웹 클라이언트ID 만 복사해서 메모장에 적어두자
이제 본격적으로 LoginScreen.js 에 구글 코드를 추가해주자
import { StyleSheet } from 'react-native'
import React from 'react'
import { ROUTES } from '../../constants/routes'
import styled, { css } from 'styled-components/native';
import { Divider } from 'react-native-elements';
import * as yup from 'yup'
import { Formik } from 'formik'
import axios from 'axios'
import Ionicons from "@expo/vector-icons/Ionicons";
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from '@react-native-google-signin/google-signin'; {/* <<<< 여기 */}
GoogleSignin.configure({
scopes: ['https://www.googleapis.com/auth/drive.readonly'], // what API you want to access on behalf of the user, default is email and profile
webClientId: '865431737111-q0qimp0imkuaabtuopjgo0gk5jqkkgpl.apps.googleusercontent.com', // client ID of type WEB for your server (needed to verify user ID and offline access)
}); {/* <<<< 여기 */}
const LoginScreen = (props) => {
const googleLogin = async () => { {/* <<<< 여기 */}
console.log('구글 로그인 시작');
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
console.log('유저 임포메이션');
console.log(userInfo);
} catch (error) {
console.log(error);
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
}
};
const { navigation } = props; // 네비게이션
return (
<SafeAreaView>
<Container>
<ImageBox>
<Image source={require("../../../assets/whiteLogo.png")} />
</ImageBox>
<Formik
initialValues={{ email: '', password: '' }}
validateOnMount={true}
onSubmit={values => {
navigation.navigate(ROUTES.INDEX)
}}
validationSchema={loginValidationSchema}
>
{({ handleChange, handleBlur, handleSubmit, values, touched, errors, isValid }) => (
<>
<TextInputBox>
<TextInput
placeholder="이메일을 입력해주세요"
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
keyboardType="email-address"
value={values.email}
autoFocus={true}
/>
<ValidationTextBox>
<ValidationText>
{
values.email.length > 0 ?
errors.email
:
''
}
</ValidationText>
</ValidationTextBox>
<TextInput
autoCapitalize="none"
textContentType="password"
secureTextEntry={true}
placeholder="비밀번호를 입력해주세요"
onChangeText={handleChange('password')}
onBlur={handleBlur('password')}
value={values.password}
/>
<ValidationTextBox>
<ValidationText>
{
values.password.length > 0 ?
errors.password
:
''
}
</ValidationText>
</ValidationTextBox>
</TextInputBox>
<ForgetPasswordBox>
<TouchableOpacity onPress={() => navigation.navigate(ROUTES.FORGOTPASSWORD)}>
<ForgetPasswordText>비밀번호를 잊으셨나요?</ForgetPasswordText>
</TouchableOpacity>
</ForgetPasswordBox>
<LoginButtonBox>
{
isValid ?
<LoginButton onPress={handleSubmit}>
<LoginButtonText>
로그인
</LoginButtonText>
</LoginButton>
:
<InActiveLoginButton>
<LoginButtonText>
로그인
</LoginButtonText>
</InActiveLoginButton>
}
</LoginButtonBox>
</>
)}
</Formik>
<SnsLoginBox>
<SnsLoginOrBox>
<SnsLoginDividerBox>
<Divider />
</SnsLoginDividerBox>
<Text>또는</Text>
<SnsLoginDividerBox>
<Divider />
</SnsLoginDividerBox>
</SnsLoginOrBox>
<SnsLoginTextBox>
<GoogleIcon source={require('../../../assets/googleIcon.png')}/>
<TouchableOpacity onPress={() => googleLogin()}> {/* <<<< 여기 */}
<SnsLoginText>
구글로 로그인
</SnsLoginText>
</TouchableOpacity>
</SnsLoginTextBox>
</SnsLoginBox>
</Container>
<JoinBox>
<JoinBoxNormalText>
계정이 없으신가요?
</JoinBoxNormalText>
<TouchableOpacity onPress={() => navigation.navigate(ROUTES.REGISTER)}>
<JoinBoxText>
가입하기
</JoinBoxText>
</TouchableOpacity>
</JoinBox>
</SafeAreaView>
)
}
const styles = StyleSheet.create({})
const loginValidationSchema = yup.object().shape({
email: yup.string().required("이메일을 입력해주세요").email("올바른 이메일을 작성해주세요"),
password: yup.string().min(8, ({ min }) => "비밀번호는 최소 " + min + " 자리 이상입니다.").required("비밀번호를 입력해주세요")
})
export default LoginScreen
const GoogleIcon = styled.Image`
width: 20px;
height: 20px;
margin-right: 5px;
`;
const SnsLoginTextBox = styled.View`
margin-top: 20px;
align-items: center;
flex-direction: row;
justify-content: center;
`;
const SnsLoginText = styled.Text`
color: #0095F6;
align-items: center;
`;
const SnsLoginBox = styled.View`
margin-top: 20px;
`;
const SnsLoginOrBox = styled.View`
flex-direction: row;
justify-content: space-between;
align-items: center;
`;
const SnsLoginDividerBox = styled.View`
width: 40%;
`;
const ValidationTextBox = styled.View`
margin-top: 8px;
margin-bottom: 8px;
`
const ValidationText = styled.Text`
color: red
`
const Container = styled.View`
width: 100%;
padding: 0px 20px;
`
const LoginButtonBox = styled.View`
margin-top: 20px;
`
const JoinBox = styled.View`
flex-direction: row;
position: absolute;
bottom: 40px;
left: 0;
width: 100%;
justify-content: center;
align-items: center;
`
const JoinBoxNormalText = styled.Text`
color: gray;
font-size: 12px;
margin-right: 10px;
`
const JoinBoxText = styled.Text`
color: #0095F6;
`
const InActiveLoginButton = styled.View`
background-color: #014068d1;
height: 50px;
border-radius: 5px;
font-size: 12px;
`;
const LoginButton = styled.TouchableOpacity`
background-color: #0095F6;
height: 50px;
border-radius: 5px;
font-size: 12px;
`;
const LoginButtonText = styled.Text`
color: white;
text-align: center;
margin: auto;
font-weight: 600;
`
const ForgetPasswordBox = styled.View`
margin-top: 15px;
align-items: flex-end;
`
const ForgetPasswordText = styled.Text`
color: #0095F6;
font-size: 12px;
`
const SafeAreaView = styled.SafeAreaView`
justify-content: center;
flex: 1;
align-items: center;
background-color: ${props => props.theme.backgroundColor};
`
const TextInputBox = styled.View`
margin-top: 20px;
width: 100%;
`
const TextInput = styled.TextInput`
padding: 0px 10px;
color: ${props => props.theme.TextColor};
background-color: #282828a6;
border: 1px solid #7a7a7a5c;
width: 100%;
height: 40px;
border-radius: 5px;
`
const Image = styled.Image`
width: 200px;
height: 55px;
`
const ImageBox = styled.View`
width: 100%;
align-items: center;
`
const View = styled.View`
`
const Text = styled.Text`
color: ${props => props.theme.TextColor};
`
const TouchableOpacity = styled.TouchableOpacity`
`
이제 터미널 창에 npm run android 를 작성하고
버튼을 클릭해보자
정상적으로
정보가 들어오는걸 확인할수있다
이것으로 구글 로그인을 마치도록하겠다.
깃 참조했으니 확인바란다.
https://github.com/1domybest/react-native-ig-clone.git
다음글 >