ryuyoonju 07-29
1차커밋
@8f3ab1079c33d86d5853c52c05abbc5259c5096f
 
App.js (added)
+++ App.js
@@ -0,0 +1,28 @@
+
+import React from 'react';
+import { NavigationContainer } from '@react-navigation/native';
+import { createStackNavigator } from '@react-navigation/stack';
+
+import LoginScreen from './src/screen/LoginScreen';
+import SelectionScreen from './src/screen/SelectionScreen';
+import CameraScreen from './src/screen/CameraScreen';
+import GpsScreen from './src/screen/GpsScreen';
+import AnalysisScreen from './src/screen/AnalysisScreen';
+import AgreementScreen from './src/screen/AgreementScreen';
+
+const Stack = createStackNavigator();
+
+export default function App() {
+  return (
+    <NavigationContainer>
+      <Stack.Navigator initialRouteName="Login">
+        <Stack.Screen name="Login" component={LoginScreen} />
+        <Stack.Screen name="Selection" component={SelectionScreen} />
+        <Stack.Screen name="Camera" component={CameraScreen} />
+        <Stack.Screen name="Gps" component={GpsScreen} />
+        <Stack.Screen name="Analysis" component={AnalysisScreen} />
+        <Stack.Screen name="Agreement" component={AgreementScreen} />
+      </Stack.Navigator>
+    </NavigationContainer>
+  );
+}(파일 끝에 줄바꿈 문자 없음)
 
App.tsx (deleted)
--- App.tsx
@@ -1,84 +0,0 @@
-import React from 'react';
-import {NavigationContainer} from '@react-navigation/native';
-import {
-  createNativeStackNavigator,
-  NativeStackNavigationProp,
-} from '@react-navigation/native-stack';
-import {useNavigation} from '@react-navigation/native';
-
-import SendLocation from './src/screens/SendLocation.tsx';
-import CameraScreen from './src/screens/CameraScreen.tsx';
-import LoginScreen from './src/screens/LoginScreen.tsx';
-import AgreementScreen from './src/screens/AgreementScreen.tsx';
-
-import AnalysisScreen from './src/screens/AnalysisScreen.tsx';
-
-import {verifyTokens} from './src/utils/loginUtils.ts';
-import {RootStackParam} from './src/utils/RootStackParam.ts';
-
-const Stack = createNativeStackNavigator<RootStackParam>();
-
-const RootNavigator = () => {
-  const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
-
-  React.useEffect(() => {
-    const checkTokens = async () => {
-      await verifyTokens(navigation);
-    };
-    checkTokens();
-  }, [navigation]);
-
-  return (
-    <Stack.Navigator
-      initialRouteName="LoginScreen"
-      screenOptions={{
-        headerShown: false,
-      }}>
-      <Stack.Screen name="SendLocation" component={SendLocation} />
-      <Stack.Screen
-        name="CameraScreen"
-        component={CameraScreen}
-        options={{
-          headerShown: true,
-          headerTransparent: true,
-          headerTintColor: '#ffffff',
-          title: '',
-          headerStyle: {
-            backgroundColor: 'rgba(255, 255, 255, 0.3)',
-          },
-        }}
-      />
-      <Stack.Screen name="LoginScreen" component={LoginScreen} />
-      <Stack.Screen
-        name="AgreementScreen"
-        component={AgreementScreen}
-        options={{
-          headerShown: true,
-          headerTransparent: true,
-          headerTintColor: '#cccccc',
-          title: '',
-          headerStyle: {
-            backgroundColor: 'rgba(255, 255, 255, 0.3)',
-          },
-        }}
-      />
-      <Stack.Screen
-        name="AnalysisScreen"
-        component={AnalysisScreen}
-        options={{
-          headerShown: true,
-          headerTintColor: '#424242',
-          title: '운행 히스토리',
-        }}
-      />
-    </Stack.Navigator>
-  );
-};
-
-export default function App() {
-  return (
-    <NavigationContainer>
-      <RootNavigator />
-    </NavigationContainer>
-  );
-}
android/app/src/main/AndroidManifest.xml
--- android/app/src/main/AndroidManifest.xml
+++ android/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
android/app/src/main/assets/index.android.bundle
--- android/app/src/main/assets/index.android.bundle
+++ android/app/src/main/assets/index.android.bundle
This diff is too big to display.
android/build.gradle
--- android/build.gradle
+++ android/build.gradle
@@ -6,6 +6,7 @@
         targetSdkVersion = 34
         ndkVersion = "26.1.10909125"
         kotlinVersion = "1.9.22"
+        playServicesLocationVersion = "21.0.1"
     }
     repositories {
         google()
android/gradlew
--- android/gradlew
+++ android/gradlew
File mode has changed
@@ -1,249 +1,249 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-##############################################################################
-#
-#   Gradle start up script for POSIX generated by Gradle.
-#
-#   Important for running:
-#
-#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-#       noncompliant, but you have some other compliant shell such as ksh or
-#       bash, then to run this script, type that shell name before the whole
-#       command line, like:
-#
-#           ksh Gradle
-#
-#       Busybox and similar reduced shells will NOT work, because this script
-#       requires all of these POSIX shell features:
-#         * functions;
-#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-#         * compound commands having a testable exit status, especially «case»;
-#         * various built-in commands including «command», «set», and «ulimit».
-#
-#   Important for patching:
-#
-#   (2) This script targets any POSIX shell, so it avoids extensions provided
-#       by Bash, Ksh, etc; in particular arrays are avoided.
-#
-#       The "traditional" practice of packing multiple parameters into a
-#       space-separated string is a well documented source of bugs and security
-#       problems, so this is (mostly) avoided, by progressively accumulating
-#       options in "$@", and eventually passing that to Java.
-#
-#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-#       see the in-line comments for details.
-#
-#       There are tweaks for specific operating systems such as AIX, CygWin,
-#       Darwin, MinGW, and NonStop.
-#
-#   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-#       within the Gradle project.
-#
-#       You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
-    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
-    [ -h "$app_path" ]
-do
-    ls=$( ls -ld "$app_path" )
-    link=${ls#*' -> '}
-    case $link in             #(
-      /*)   app_path=$link ;; #(
-      *)    app_path=$APP_HOME$link ;;
-    esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
-    echo "$*"
-} >&2
-
-die () {
-    echo
-    echo "$*"
-    echo
-    exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in                #(
-  CYGWIN* )         cygwin=true  ;; #(
-  Darwin* )         darwin=true  ;; #(
-  MSYS* | MINGW* )  msys=true    ;; #(
-  NONSTOP* )        nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD=$JAVA_HOME/jre/sh/java
-    else
-        JAVACMD=$JAVA_HOME/bin/java
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD=java
-    if ! command -v java >/dev/null 2>&1
-    then
-        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
-    case $MAX_FD in #(
-      max*)
-        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC2039,SC3045
-        MAX_FD=$( ulimit -H -n ) ||
-            warn "Could not query maximum file descriptor limit"
-    esac
-    case $MAX_FD in  #(
-      '' | soft) :;; #(
-      *)
-        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
-        # shellcheck disable=SC2039,SC3045
-        ulimit -n "$MAX_FD" ||
-            warn "Could not set maximum file descriptor limit to $MAX_FD"
-    esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-#   * args from the command line
-#   * the main class name
-#   * -classpath
-#   * -D...appname settings
-#   * --module-path (only if needed)
-#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
-    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
-    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
-    JAVACMD=$( cygpath --unix "$JAVACMD" )
-
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    for arg do
-        if
-            case $arg in                                #(
-              -*)   false ;;                            # don't mess with options #(
-              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
-                    [ -e "$t" ] ;;                      #(
-              *)    false ;;
-            esac
-        then
-            arg=$( cygpath --path --ignore --mixed "$arg" )
-        fi
-        # Roll the args list around exactly as many times as the number of
-        # args, so each arg winds up back in the position where it started, but
-        # possibly modified.
-        #
-        # NB: a `for` loop captures its iteration list before it begins, so
-        # changing the positional parameters here affects neither the number of
-        # iterations, nor the values presented in `arg`.
-        shift                   # remove old arg
-        set -- "$@" "$arg"      # push replacement arg
-    done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-#     and any embedded shellness will be escaped.
-#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-#     treated as '${Hostname}' itself on the command line.
-
-set -- \
-        "-Dorg.gradle.appname=$APP_BASE_NAME" \
-        -classpath "$CLASSPATH" \
-        org.gradle.wrapper.GradleWrapperMain \
-        "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
-    die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-#   set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
-        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
-        xargs -n1 |
-        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
-        tr '\n' ' '
-    )" '"$@"'
-
-exec "$JAVACMD" "$@"
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
jest.config.js
--- jest.config.js
+++ jest.config.js
@@ -1,3 +1,4 @@
 module.exports = {
   preset: 'react-native',
+  setupFiles: ["../../node_modules/react-native-gesture-handler/jestSetup.js"],
 };
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -9,10 +9,8 @@
       "version": "0.0.1",
       "dependencies": {
         "@react-native-async-storage/async-storage": "^1.23.1",
-        "@react-native-camera-roll/camera-roll": "^7.8.1",
-        "@react-native-community/geolocation": "^3.2.1",
         "@react-navigation/native": "^6.1.17",
-        "@react-navigation/native-stack": "^6.9.26",
+        "@react-navigation/stack": "^6.4.1",
         "axios": "^1.7.2",
         "base64-js": "^1.5.1",
         "crypto-js": "^4.2.0",
@@ -22,6 +20,8 @@
         "react-native-background-actions": "^4.0.0",
         "react-native-chart-kit": "^6.12.0",
         "react-native-fs": "^2.20.0",
+        "react-native-geolocation-service": "^5.3.1",
+        "react-native-gesture-handler": "^2.17.1",
         "react-native-gifted-charts": "^1.4.10",
         "react-native-linear-gradient": "^2.8.3",
         "react-native-reanimated": "^3.11.0",
@@ -30,7 +30,7 @@
         "react-native-shadow-2": "^7.1.0",
         "react-native-svg": "^15.3.0",
         "react-native-vector-icons": "^10.1.0",
-        "react-native-vision-camera": "4.0.5"
+        "react-native-vision-camera": "^4.0.5"
       },
       "devDependencies": {
         "@babel/core": "^7.20.0",
@@ -3528,6 +3528,17 @@
       "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
       "dev": true
     },
+    "node_modules/@egjs/hammerjs": {
+      "version": "2.0.17",
+      "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
+      "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
+      "dependencies": {
+        "@types/hammerjs": "^2.0.36"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/@eslint-community/eslint-utils": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -4518,17 +4529,6 @@
         "react-native": "^0.0.0-0 || >=0.60 <1.0"
       }
     },
-    "node_modules/@react-native-camera-roll/camera-roll": {
-      "version": "7.8.1",
-      "resolved": "https://registry.npmjs.org/@react-native-camera-roll/camera-roll/-/camera-roll-7.8.1.tgz",
-      "integrity": "sha512-voVmDlzBS1MVADFk/Vb9GzWzhpoF4ESSjU9qwDNxf/ZA9tirAzX/M0TKcz2V+iFHeRR68cHtZ9tHUhQVG7kJcA==",
-      "engines": {
-        "node": ">= 18.17.0"
-      },
-      "peerDependencies": {
-        "react-native": ">=0.59"
-      }
-    },
     "node_modules/@react-native-community/cli-clean": {
       "version": "12.3.2",
       "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-12.3.2.tgz",
@@ -5278,18 +5278,6 @@
         "joi": "^17.2.1"
       }
     },
-    "node_modules/@react-native-community/geolocation": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/@react-native-community/geolocation/-/geolocation-3.2.1.tgz",
-      "integrity": "sha512-/+HNzuRl4UCMma7KK+KYL8k2nxAGuW+DGxqmqfpiqKBlCkCUbuFHaZZdqVD6jpsn9r/ghe583ECLmd9SV9I4Bw==",
-      "engines": {
-        "node": ">=18.0.0"
-      },
-      "peerDependencies": {
-        "react": "*",
-        "react-native": "*"
-      }
-    },
     "node_modules/@react-native/assets-registry": {
       "version": "0.73.1",
       "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.73.1.tgz",
@@ -5802,9 +5790,9 @@
       "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
     "node_modules/@react-navigation/elements": {
-      "version": "1.3.30",
-      "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.30.tgz",
-      "integrity": "sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ==",
+      "version": "1.3.31",
+      "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz",
+      "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==",
       "peerDependencies": {
         "@react-navigation/native": "^6.0.0",
         "react": "*",
@@ -5827,22 +5815,6 @@
         "react-native": "*"
       }
     },
-    "node_modules/@react-navigation/native-stack": {
-      "version": "6.9.26",
-      "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-6.9.26.tgz",
-      "integrity": "sha512-++dueQ+FDj2XkZ902DVrK79ub1vp19nSdAZWxKRgd6+Bc0Niiesua6rMCqymYOVaYh+dagwkA9r00bpt/U5WLw==",
-      "dependencies": {
-        "@react-navigation/elements": "^1.3.30",
-        "warn-once": "^0.1.0"
-      },
-      "peerDependencies": {
-        "@react-navigation/native": "^6.0.0",
-        "react": "*",
-        "react-native": "*",
-        "react-native-safe-area-context": ">= 3.0.0",
-        "react-native-screens": ">= 3.0.0"
-      }
-    },
     "node_modules/@react-navigation/native/node_modules/escape-string-regexp": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -5861,6 +5833,52 @@
       "dependencies": {
         "nanoid": "^3.1.23"
       }
+    },
+    "node_modules/@react-navigation/stack": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-6.4.1.tgz",
+      "integrity": "sha512-upMEHOKMtuMu4c9gmoPlO/JqI6mDlSqwXg1aXKOTQLXAF8H5koOLRfrmi7AkdiE9A7lDXWUAZoGuD9O88cYvDQ==",
+      "dependencies": {
+        "@react-navigation/elements": "^1.3.31",
+        "color": "^4.2.3",
+        "warn-once": "^0.1.0"
+      },
+      "peerDependencies": {
+        "@react-navigation/native": "^6.0.0",
+        "react": "*",
+        "react-native": "*",
+        "react-native-gesture-handler": ">= 1.0.0",
+        "react-native-safe-area-context": ">= 3.0.0",
+        "react-native-screens": ">= 3.0.0"
+      }
+    },
+    "node_modules/@react-navigation/stack/node_modules/color": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+      "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+      "dependencies": {
+        "color-convert": "^2.0.1",
+        "color-string": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=12.5.0"
+      }
+    },
+    "node_modules/@react-navigation/stack/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/@react-navigation/stack/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
     },
     "node_modules/@sideway/address": {
       "version": "4.1.5",
@@ -5980,6 +5998,11 @@
       "dependencies": {
         "@types/node": "*"
       }
+    },
+    "node_modules/@types/hammerjs": {
+      "version": "2.0.45",
+      "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.45.tgz",
+      "integrity": "sha512-qkcUlZmX6c4J8q45taBKTL3p+LbITgyx7qhlPYOdOHZB7B31K0mXbP5YA7i7SgDeEGuI9MnumiKPEMrxg8j3KQ=="
     },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.6",
@@ -7398,7 +7421,6 @@
       "version": "1.9.1",
       "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
       "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
-      "dev": true,
       "dependencies": {
         "color-name": "^1.0.0",
         "simple-swizzle": "^0.2.2"
@@ -9817,6 +9839,19 @@
       "engines": {
         "node": ">= 8"
       }
+    },
+    "node_modules/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+      "dependencies": {
+        "react-is": "^16.7.0"
+      }
+    },
+    "node_modules/hoist-non-react-statics/node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
     },
     "node_modules/html-escaper": {
       "version": "2.0.2",
@@ -14045,6 +14080,26 @@
         }
       }
     },
+    "node_modules/react-native-geolocation-service": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/react-native-geolocation-service/-/react-native-geolocation-service-5.3.1.tgz",
+      "integrity": "sha512-LTXPtPNmrdhx+yeWG47sAaCgQc3nG1z+HLLHlhK/5YfOgfLcAb9HAkhREPjQKPZOUx8pKZMIpdGFUGfJYtimXQ=="
+    },
+    "node_modules/react-native-gesture-handler": {
+      "version": "2.17.1",
+      "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.17.1.tgz",
+      "integrity": "sha512-pWfniN6NuVKUq40KACuD3NCMe+bWNQCpD3cmxL6aLSCTwPKYmf7l4Lp0/E/almpjvxhybJZtFLU0w4tmxnIKaA==",
+      "dependencies": {
+        "@egjs/hammerjs": "^2.0.17",
+        "hoist-non-react-statics": "^3.3.0",
+        "invariant": "^2.2.4",
+        "prop-types": "^15.7.2"
+      },
+      "peerDependencies": {
+        "react": "*",
+        "react-native": "*"
+      }
+    },
     "node_modules/react-native-gifted-charts": {
       "version": "1.4.10",
       "resolved": "https://registry.npmjs.org/react-native-gifted-charts/-/react-native-gifted-charts-1.4.10.tgz",
@@ -15013,7 +15068,6 @@
       "version": "0.2.2",
       "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
       "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
-      "dev": true,
       "dependencies": {
         "is-arrayish": "^0.3.1"
       }
@@ -15021,8 +15075,7 @@
     "node_modules/simple-swizzle/node_modules/is-arrayish": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
-      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
-      "dev": true
+      "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
     },
     "node_modules/sisteransi": {
       "version": "1.0.5",
package.json
--- package.json
+++ package.json
@@ -11,10 +11,8 @@
   },
   "dependencies": {
     "@react-native-async-storage/async-storage": "^1.23.1",
-    "@react-native-camera-roll/camera-roll": "^7.8.1",
-    "@react-native-community/geolocation": "^3.2.1",
     "@react-navigation/native": "^6.1.17",
-    "@react-navigation/native-stack": "^6.9.26",
+    "@react-navigation/stack": "^6.4.1",
     "axios": "^1.7.2",
     "base64-js": "^1.5.1",
     "crypto-js": "^4.2.0",
@@ -24,6 +22,8 @@
     "react-native-background-actions": "^4.0.0",
     "react-native-chart-kit": "^6.12.0",
     "react-native-fs": "^2.20.0",
+    "react-native-geolocation-service": "^5.3.1",
+    "react-native-gesture-handler": "^2.17.1",
     "react-native-gifted-charts": "^1.4.10",
     "react-native-linear-gradient": "^2.8.3",
     "react-native-reanimated": "^3.11.0",
@@ -32,7 +32,7 @@
     "react-native-shadow-2": "^7.1.0",
     "react-native-svg": "^15.3.0",
     "react-native-vector-icons": "^10.1.0",
-    "react-native-vision-camera": "4.0.5"
+    "react-native-vision-camera": "^4.0.5"
   },
   "devDependencies": {
     "@babel/core": "^7.20.0",
 
src/api/ApiUtils.js (added)
+++ src/api/ApiUtils.js
@@ -0,0 +1,96 @@
+import axios from 'axios';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+
+// API 인스턴스 생성
+const apiInstance = axios.create({
+  baseURL: 'http://takensoftai.iptime.org:15857',
+  headers: {
+    'Content-Type': 'application/json',
+  }
+});
+
+const ApiUtils = {
+  register: async (userData) => {
+    try {
+      const response = await apiInstance.post('/auth/register', userData);
+      return response.data;
+    } catch (error) {
+      console.error('Register error:', error.response || error);
+      throw error.response?.data || error;
+    }
+  },
+
+  login: async (credentials) => {
+    try {
+      const response = await apiInstance.post('/auth/login', credentials);
+      return response.data;
+    } catch (error) {
+      console.error('Login error:', error.response || error);
+      throw error.response?.data || error;
+    }
+  },
+
+  uploadPhoto: async (uri) => {
+    const formData = new FormData();
+    formData.append('photo', {
+      uri,
+      type: 'image/jpeg',
+      name: 'photo.jpg',
+    });
+
+    try {
+      const response = await apiInstance.post('/upload', formData, {
+        headers: {
+          'Content-Type': 'multipart/form-data',
+        },
+      });
+      return response.data;
+    } catch (error) {
+      console.error('Upload photo error:', error.response || error);
+      throw error.response?.data || error;
+    }
+  },
+
+  sendTripLog: async (data, navigation) => {
+    try {
+      // AsyncStorage에서 토큰 가져오기
+      const tokenString = await AsyncStorage.getItem('token');
+      if (tokenString) {
+        const tokenObject = JSON.parse(tokenString);
+        const accessToken = tokenObject.accessToken;
+  
+        if (!accessToken) {
+          await AsyncStorage.removeItem('token');
+          // 로그인 페이지로 리디렉션
+          navigation.navigate('Login');
+          return;
+        }
+  
+        // 데이터 전송
+        try {
+          const response = await apiInstance.post('/action/gps_update', data, {
+            headers: {
+              'Content-Type': 'application/json',
+              Authorization: `Bearer ${accessToken}`, // Bearer 스키마를 사용하도록 수정
+            },
+          });
+          return response.data;
+        } catch (error) {
+          console.error('Send trip log error:', error.response?.status, error.response?.data || error.message);
+          throw error.response?.data || error;
+        }
+      } else {
+        console.log("토큰이 스토리지에 없습니다.");
+        // 로그인 페이지로 리디렉션
+        navigation.navigate('Login');
+      }
+    } catch (error) {
+      console.error('Token retrieval error:', error.message);
+      // 로그인 페이지로 리디렉션
+      navigation.navigate('Login');
+    }
+  }
+  
+};
+
+export default ApiUtils;
src/component/AnalysisComponent.js (Renamed from src/screens/AnalysisComponent.tsx)
--- src/screens/AnalysisComponent.tsx
+++ src/component/AnalysisComponent.js
@@ -1,13 +1,9 @@
-import React, {useState} from 'react';
-import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
-import {BarChart, LineChart} from 'react-native-gifted-charts';
-
-// 타입 정의
-type ScoreProps = {};
+import React, { useState } from 'react';
+import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
+import { BarChart, LineChart } from 'react-native-gifted-charts';
 
 export default function AnalysisComponent() {
-  const [selectedComponent, setSelectedComponent] =
-    useState<string>('MainMark');
+  const [selectedComponent, setSelectedComponent] = useState('MainMark');
 
   const renderComponent = () => {
     switch (selectedComponent) {
@@ -75,15 +71,15 @@
 
 // 주요 감점 요인 차트
 const mainData = [
-  {value: 60, label: '급가속'},
-  {value: 90, label: '급감속'},
-  {value: 88, label: '급출발'},
-  {value: 75, label: '급정지'},
-  {value: 97, label: '안전모착용'},
-  {value: 80, label: '충돌위험'},
+  { value: 60, label: '급가속' },
+  { value: 90, label: '급감속' },
+  { value: 88, label: '급출발' },
+  { value: 75, label: '급정지' },
+  { value: 97, label: '안전모착용' },
+  { value: 80, label: '충돌위험' },
 ];
 
-const MainMark: React.FC<ScoreProps> = () => {
+function MainMark() {
   return (
     <BarChart
       horizontal
@@ -96,7 +92,7 @@
       barBorderTopRightRadius={10}
       barBorderTopLeftRadius={10}
       labelsExtraHeight={40}
-      xAxisLabelTextStyle={{fontSize: 12, marginLeft: -35, textAlign: 'left'}}
+      xAxisLabelTextStyle={{ fontSize: 12, marginLeft: -35, textAlign: 'left' }}
       stepHeight={25}
       spacing={15}
       showGradient
@@ -106,19 +102,19 @@
       height={230}
     />
   );
-};
+}
 
 // 거리별 감점 현황 차트
 const distanceData = [
-  {value: 15, label: '10km'},
-  {value: 30, label: '20km'},
-  {value: 23, label: '30km'},
-  {value: 40, label: '40km'},
-  {value: 16, label: '50km'},
-  {value: 50, label: '60km'},
+  { value: 15, label: '10km' },
+  { value: 30, label: '20km' },
+  { value: 23, label: '30km' },
+  { value: 40, label: '40km' },
+  { value: 16, label: '50km' },
+  { value: 50, label: '60km' },
 ];
 
-const DistanceMark: React.FC<ScoreProps> = () => {
+function DistanceMark() {
   return (
     <LineChart
       areaChart
@@ -134,7 +130,7 @@
       noOfSections={5}
     />
   );
-};
+}
 
 // 운행 점수 비교 차트
 const driveData = [
@@ -143,7 +139,7 @@
     label: '이전 운행 점수',
     frontColor: '#FFA311',
     topLabelComponent: () => (
-      <Text style={{color: '#bdbdbd', fontSize: 15}}>65</Text>
+      <Text style={{ color: '#bdbdbd', fontSize: 15 }}>65</Text>
     ),
   },
   {
@@ -151,11 +147,12 @@
     label: '현재 운행 점수',
     frontColor: '#0F4BD3',
     topLabelComponent: () => (
-      <Text style={{color: '#bdbdbd', fontSize: 15}}>94</Text>
+      <Text style={{ color: '#bdbdbd', fontSize: 15 }}>94</Text>
     ),
   },
 ];
-const DriveScore: React.FC<ScoreProps> = () => {
+
+function DriveScore() {
   return (
     <BarChart
       data={driveData}
@@ -168,7 +165,7 @@
       yAxisColor="#959595"
     />
   );
-};
+}
 
 const styles = StyleSheet.create({
   buttonContainer: {
 
src/component/RadioButton.js (added)
+++ src/component/RadioButton.js
@@ -0,0 +1,61 @@
+import React from 'react';
+import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
+
+const RadioButton = ({
+  isSelected,
+  buttonColor,
+  selectedButtonColor,
+  labelColor,
+  style,
+  wrapStyle,
+  children,
+  onPress,
+}) => {
+  const color = isSelected
+    ? selectedButtonColor || '#3872ef'
+    : buttonColor || '#ccc';
+
+  return (
+    <TouchableOpacity onPress={onPress} style={[styles.container, wrapStyle]}>
+      <View style={[styles.circle, { borderColor: color }]}>
+        {isSelected && (
+          <View style={[styles.checkedCircle, { backgroundColor: color }]} />
+        )}
+      </View>
+      {children && (
+        <Text style={[styles.label, { color: labelColor || '#000' }, style]}>
+          {children}
+        </Text>
+      )}
+    </TouchableOpacity>
+  );
+};
+
+const styles = StyleSheet.create({
+  container: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    marginBottom: 10,
+  },
+  circle: {
+    height: 20,
+    width: 20,
+    borderRadius: 12,
+    borderWidth: 2,
+    borderColor: '#ccc',
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginRight: 10,
+  },
+  checkedCircle: {
+    width: 10,
+    height: 10,
+    borderRadius: 6,
+    backgroundColor: '#2196f3',
+  },
+  label: {
+    fontSize: 16,
+  },
+});
+
+export default RadioButton;
 
src/component/RadioButton.tsx (deleted)
--- src/component/RadioButton.tsx
@@ -1,77 +0,0 @@
-import * as React from 'react';
-import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
-
-export interface RadioButtonProps {
-  children?: React.ReactNode;
-  isSelected?: boolean | undefined;
-  labelHorizontal?: boolean | undefined;
-  buttonColor?: string | undefined;
-  selectedButtonColor?: string | undefined;
-  labelColor?: string | undefined;
-  style?: any;
-  wrapStyle?: any;
-  onPress?: () => void;
-}
-
-export class RadioButton extends React.Component<RadioButtonProps> {
-  render() {
-    const {
-      isSelected,
-      buttonColor,
-      selectedButtonColor,
-      labelColor,
-      style,
-      wrapStyle,
-      children,
-      onPress,
-    } = this.props;
-
-    const color = isSelected
-      ? selectedButtonColor || '#3872ef'
-      : buttonColor || '#ccc';
-
-    return (
-      <TouchableOpacity onPress={onPress} style={[styles.container, wrapStyle]}>
-        <View style={[styles.circle, {borderColor: color}]}>
-          {isSelected && (
-            <View style={[styles.checkedCircle, {backgroundColor: color}]} />
-          )}
-        </View>
-        {children && (
-          <Text style={[styles.label, {color: labelColor || '#000'}, style]}>
-            {children}
-          </Text>
-        )}
-      </TouchableOpacity>
-    );
-  }
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    marginBottom: 10,
-  },
-  circle: {
-    height: 20,
-    width: 20,
-    borderRadius: 12,
-    borderWidth: 2,
-    borderColor: '#ccc',
-    alignItems: 'center',
-    justifyContent: 'center',
-    marginRight: 10,
-  },
-  checkedCircle: {
-    width: 10,
-    height: 10,
-    borderRadius: 6,
-    backgroundColor: '#2196f3',
-  },
-  label: {
-    fontSize: 16,
-  },
-});
-
-export default RadioButton;
 
src/screen/AgreementScreen.js (added)
+++ src/screen/AgreementScreen.js
@@ -0,0 +1,184 @@
+import {
+  StyleSheet,
+  ScrollView,
+  View,
+  Text,
+  TextInput,
+  TouchableOpacity,
+  Alert,
+} from 'react-native';
+import React, { useState } from 'react';
+import RadioButton from '../component/RadioButton';
+import Api from '../api/ApiUtils';
+import { useNavigation } from '@react-navigation/native';
+
+export default function AgreementScreen() {
+  const navigation = useNavigation();
+  const [agreementData, setAgreement] = useState({
+    id: '',
+    password: '',
+    email: '',
+    sex: null,
+    phone: '',
+  });
+
+  const radio_props = [
+    { label: '여성', value: '여성' },
+    { label: '남성', value: '남성' },
+  ];
+
+  const handleInputChange = (field, value) => {
+    setAgreement(prevAgreement => ({
+      ...prevAgreement,
+      [field]: value,
+    }));
+  };
+
+  const handleSubmit = async () => {
+    try {
+      const response = await Api.register(agreementData);
+      console.log(response);
+      navigation.navigate('Login');
+    } catch (error) {
+      Alert.alert('가입 실패', error.message);
+    }
+  };
+
+  // 전화번호 포맷팅
+  const formatPhoneNumber = (text) => {
+    const cleaned = text.replace(/\D/g, ''); // Remove all non-numeric characters
+    if (cleaned.length === 11) {
+      const match = cleaned.match(/^(\d{3})(\d{4})(\d{4})$/);
+      if (match) {
+        return `${match[1]}-${match[2]}-${match[3]}`;
+      }
+    }
+    return text;
+  };
+
+  const handlePhoneChange = (text) => {
+    const formatted = formatPhoneNumber(text);
+    handleInputChange('phone', formatted);
+  };
+
+  return (
+    <View style={styles.container}>
+      <Text style={styles.logoPoint}>회원 가입</Text>
+      <ScrollView contentContainerStyle={styles.scrollContainer}>
+        <View style={styles.formGroup}>
+          <Text style={styles.inputLabel}>아이디</Text>
+          <TextInput
+            style={styles.input}
+            placeholder="아이디를 입력하세요"
+            placeholderTextColor={'#aaa'}
+            onChangeText={text => handleInputChange('id', text)}
+          />
+        </View>
+        <View style={styles.formGroup}>
+          <Text style={styles.inputLabel}>비밀번호</Text>
+          <TextInput
+            style={styles.input}
+            placeholder="비밀번호를 입력하세요"
+            secureTextEntry
+            placeholderTextColor={'#aaa'}
+            onChangeText={text => handleInputChange('password', text)}
+          />
+        </View>
+        <View style={styles.formGroup}>
+          <Text style={styles.inputLabel}>이메일</Text>
+          <TextInput
+            style={styles.input}
+            placeholder="이메일을 입력하세요"
+            placeholderTextColor={'#aaa'}
+            onChangeText={text => handleInputChange('email', text)}
+          />
+        </View>
+        <View style={styles.formGroup}>
+          <Text style={styles.inputLabel}>성별</Text>
+          <View style={styles.radioForm}>
+            {radio_props.map((radio, index) => (
+              <RadioButton
+                key={index}
+                isSelected={agreementData.sex === radio.value}
+                onPress={() => handleInputChange('sex', radio.value)}
+                buttonColor={'#B0C4DE'}
+                selectedButtonColor={'#007AFF'}
+                labelColor={'#333'}
+                style={styles.radioButton}>
+                {radio.label}
+              </RadioButton>
+            ))}
+          </View>
+        </View>
+        <View style={styles.formGroup}>
+          <Text style={styles.inputLabel}>전화번호</Text>
+          <TextInput
+            style={styles.input}
+            placeholder="전화번호를 입력하세요"
+            placeholderTextColor={'#aaa'}
+            onChangeText={handlePhoneChange}
+            value={agreementData.phone}
+            keyboardType="phone-pad"
+          />
+        </View>
+        <TouchableOpacity style={styles.button} onPress={handleSubmit}>
+          <Text style={styles.buttonText}>등록하기</Text>
+        </TouchableOpacity>
+      </ScrollView>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: '#f8f8f8',
+  },
+  scrollContainer: {
+    padding: 16,
+    alignItems: 'center',
+  },
+  logoPoint: {
+    fontSize: 28,
+    color: '#007AFF',
+    fontWeight: 'bold',
+    marginVertical: 20,
+  },
+  formGroup: {
+    width: '100%',
+    marginBottom: 20,
+  },
+  inputLabel: {
+    color: '#666',
+    marginBottom: 8,
+    fontSize: 16,
+  },
+  input: {
+    borderColor: '#ddd',
+    borderRadius: 8,
+    borderWidth: 1,
+    padding: 12,
+    backgroundColor: '#fff',
+    fontSize: 16,
+  },
+  radioForm: {
+    flexDirection: 'row',
+    justifyContent: 'flex-start',
+    marginVertical: 12,
+  },
+  radioButton: {
+    marginRight: 20,
+  },
+  button: {
+    backgroundColor: '#007AFF',
+    padding: 15,
+    borderRadius: 8,
+    width: '100%',
+    alignItems: 'center',
+  },
+  buttonText: {
+    color: '#fff',
+    fontSize: 16,
+    fontWeight: 'bold',
+  },
+});
src/screen/AnalysisScreen.js (Renamed from src/screens/AnalysisScreen.tsx)
--- src/screens/AnalysisScreen.tsx
+++ src/screen/AnalysisScreen.js
@@ -1,18 +1,6 @@
-import {StyleSheet, View, Text, Image} from 'react-native';
+import { StyleSheet, View, Text, Image } from 'react-native';
 import React from 'react';
-import AnalysisComponent from './AnalysisComponent';
-
-// 타입 정의
-type ScoreProps = {
-  score: number;
-  distance: number;
-  time: number;
-};
-
-type TotalBoxProps = {
-  distance?: number;
-  time?: number;
-};
+import AnalysisComponent from '../component/AnalysisComponent';
 
 // 더미데이터
 const data = {
@@ -37,7 +25,7 @@
 }
 
 // 운행 점수 컴포넌트
-const Score: React.FC<ScoreProps> = ({score}) => {
+function Score({ score }) {
   return (
     <View style={styles.scoreContainer}>
       <View style={styles.scoreTextBox}>
@@ -49,10 +37,10 @@
       <Image source={require('../asset/bicycle.png')} style={styles.image} />
     </View>
   );
-};
+}
 
 // 총 이동거리, 주행시간 컴포넌트
-const TotalBox: React.FC<TotalBoxProps> = ({distance, time}) => {
+function TotalBox({ distance, time }) {
   return (
     <View style={styles.totalBoxContainer}>
       <View style={styles.totalBoxLabelContainer}>
@@ -74,7 +62,7 @@
       </View>
     </View>
   );
-};
+}
 
 const styles = StyleSheet.create({
   container: {
 
src/screen/CameraScreen.js (added)
+++ src/screen/CameraScreen.js
@@ -0,0 +1,125 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { View, Button, StyleSheet, Alert, Text, Linking } from 'react-native';
+import { Camera, useCameraDevices } from 'react-native-vision-camera';
+import { useNavigation } from '@react-navigation/native';
+
+const CameraScreen = () => {
+    const [hasPermission, setHasPermission] = useState(false);
+    const [isDeviceReady, setIsDeviceReady] = useState(false);
+    const devices = useCameraDevices();
+    const device = devices.front;
+    const camera = useRef(null);
+    const navigation = useNavigation();
+
+    // Function to check and request camera permission
+    const checkPermission = async () => {
+        const cameraPermission = await Camera.getCameraPermissionStatus();
+        console.log(cameraPermission)
+        if (cameraPermission === 'granted') {
+            setHasPermission(true);
+            setIsDeviceReady(true);
+        } else if (cameraPermission === 'not-determined') {
+            const newCameraPermission = await Camera.requestCameraPermission();
+            if (newCameraPermission === 'granted') {
+                setHasPermission(true);
+                setIsDeviceReady(true);
+            } else {
+                setHasPermission(false);
+                Alert.alert('카메라 권한 필요', '카메라 권한이 필요합니다.', [
+                    { text: '설정으로 이동', onPress: () => Linking.openSettings() },
+                    { text: '취소', style: 'cancel' },
+                ]);
+            }
+        } else {
+            setHasPermission(false);
+            Alert.alert('카메라 권한 필요', '카메라 권한이 필요합니다.', [
+                { text: '설정으로 이동', onPress: () => Linking.openSettings() },
+                { text: '취소', style: 'cancel' },
+            ]); 
+        }
+    };
+
+    useEffect(() => {
+        // Check permission once on component mount
+        checkPermission();
+    }, []);
+
+    useEffect(() => {
+        // Update the device readiness when the camera device changes
+        if (device) {
+            setIsDeviceReady(true);
+        } else {
+            setIsDeviceReady(false);
+        }
+    }, [device]);
+
+    const handleCapture = async () => {
+        if (camera.current) {
+            try {
+                const photo = await camera.current.takePhoto({
+                    flash: 'off',
+                    qualityPrioritization: 'speed',
+                });
+                console.log('Photo taken:', photo);
+                navigation.navigate('Gps'); // Move to the next screen after capturing photo
+            } catch (error) {
+                console.error('Photo capture error:', error.message);
+                Alert.alert('오류', '사진 캡처 중 오류가 발생했습니다.');
+            }
+        }
+    };
+
+    if (!hasPermission) {
+        return (
+            <View style={styles.permissionContainer}>
+                <Text>카메라 권한을 요청 중입니다...</Text>
+            </View>
+        );
+    }
+
+    return (
+        <View style={styles.container}>
+            {isDeviceReady && device ? (
+                <Camera
+                    ref={camera}
+                    style={StyleSheet.absoluteFill}
+                    device={device}
+                    photo={true}
+                    video={false}
+                    isActive={true}
+                />
+            ) : (
+                <View style={styles.buttonContainer}>
+                    <Text>카메라 장치 준비 중...</Text>
+                    <Button title="다음 페이지로 이동" onPress={() => navigation.navigate('Gps')} />
+                </View>
+            )}
+            <Button title="Capture" onPress={handleCapture} style={styles.captureButton} />
+        </View>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        justifyContent: 'center',
+        alignItems: 'center',
+    },
+    buttonContainer: {
+        justifyContent: 'center',
+        alignItems: 'center',
+        flex: 1,
+    },
+    captureButton: {
+        position: 'absolute',
+        bottom: 20,
+        alignSelf: 'center',
+    },
+    permissionContainer: {
+        flex: 1,
+        justifyContent: 'center',
+        alignItems: 'center',
+    },
+});
+
+export default CameraScreen;
 
src/screen/GpsScreen.js (added)
+++ src/screen/GpsScreen.js
@@ -0,0 +1,236 @@
+import React, { useState, useEffect } from 'react';
+import { View, Text, StyleSheet, Alert, TouchableOpacity } from 'react-native';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import crypto from 'crypto-js';
+import { startBackgroundTask, stopBackgroundTask, locations } from '../services/LocationService';
+import Api from '../api/ApiUtils';
+import { useNavigation } from '@react-navigation/native';
+import Icon from 'react-native-vector-icons/FontAwesome'; // FontAwesome 아이콘 사용
+
+const GpsScreen = () => {
+    const [isMeasuring, setIsMeasuring] = useState(false);
+    const [elapsedTime, setElapsedTime] = useState(0); // 초 단위의 경과 시간
+    const [tripId, setTripId] = useState(''); // trip_id 상태 추가
+    const [userId, setUserId] = useState(''); // user_id 상태 추가
+    const navigation = useNavigation(); // useNavigation 훅으로 navigation 객체 가져오기
+
+    useEffect(() => {
+        let timer;
+
+        if (isMeasuring) {
+            timer = setInterval(() => {
+                setElapsedTime(prevElapsedTime => prevElapsedTime + 1);
+            }, 1000); // 1초마다 실행
+        } else {
+            clearInterval(timer);
+        }
+
+        return () => {
+            clearInterval(timer);
+        };
+    }, [isMeasuring]);
+
+    useEffect(() => {
+        // 앱이 처음 실행될 때 user_id를 AsyncStorage에서 가져옴
+        const fetchUserId = async () => {
+            try {
+                const storedUserId = await AsyncStorage.getItem('user_id');
+                if (storedUserId !== null) {
+                    setUserId(storedUserId);
+                }
+            } catch (error) {
+                console.error('Error fetching user_id from AsyncStorage:', error);
+            }
+        };
+
+        fetchUserId();
+    }, []);
+
+    const generateTripId = () => {
+        // 해시 값을 생성
+        const rand = Math.random().toString();
+        const date = new Date();
+        const sha256Hash = crypto.SHA256(rand + ',' + date.toString()).toString(crypto.enc.Hex);
+        return sha256Hash;
+    };
+
+    const handleStart = async () => {
+        try {
+            const newTripId = generateTripId();
+            setTripId(newTripId); // trip_id 상태 업데이트
+            await startBackgroundTask();
+            setIsMeasuring(true);
+        } catch (error) {
+            console.error('Error starting background task:', error);
+            Alert.alert('백그라운드 작업 오류', '백그라운드 작업을 시작할 수 없습니다.');
+            setIsMeasuring(false);
+        }
+    };
+
+    const handleStop = async () => {
+        setIsMeasuring(false);
+        setElapsedTime(0); // 시간 초기화
+        console.log('Measuring stopped.');
+
+        try {
+            await stopBackgroundTask();
+
+            const dataToSend = {
+                user_id: userId, // AsyncStorage에서 가져온 user_id 사용
+                trip_id: tripId, // 상태에서 trip_id 가져오기
+                trip_log: locations,
+            };
+
+            console.log('Data to send:', dataToSend);
+
+            // 서버로 전송
+            try {
+                const response = await Api.sendTripLog(dataToSend, navigation);
+                console.log(":::::", response);
+            } catch (error) {
+                Alert.alert('로그인 실패', error.message);
+            }
+
+        } catch (error) {
+            console.error('Error stopping background task:', error);
+        }
+    };
+
+    const handleLogout = () => {
+        AsyncStorage.removeItem('token');
+        navigation.navigate('Login');
+        Alert.alert('로그아웃', '로그아웃되었습니다.');
+    };
+
+    const handleHistory = () => {
+        navigation.navigate('Analysis');
+        Alert.alert('히스토리', '히스토리 화면으로 이동합니다.');
+    };
+
+    // 시간 형식으로 변환
+    const formatTime = (seconds) => {
+        const hours = Math.floor(seconds / 3600);
+        const minutes = Math.floor((seconds % 3600) / 60);
+        const secs = seconds % 60;
+        return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
+    };
+
+    return (
+        <View style={styles.container}>
+            <View style={styles.timerContainer}>
+                <Text style={styles.time}>{formatTime(elapsedTime)}</Text>
+            </View>
+            <View style={styles.buttonRowContainer}>
+                <TouchableOpacity
+                    style={[styles.button, styles.startButton, isMeasuring && styles.buttonDisabled]}
+                    onPress={handleStart}
+                    disabled={isMeasuring}
+                >
+                    <Icon name="play" size={20} color="#FFFFFF"  />
+                    <Text style={styles.buttonText}>측정 시작</Text>
+                </TouchableOpacity>
+                <TouchableOpacity
+                    style={[styles.button, styles.stopButton, !isMeasuring && styles.buttonDisabled]}
+                    onPress={handleStop}
+                    disabled={!isMeasuring}
+                >
+                    <Icon name="stop" size={20} color="#FFFFFF" />
+                    <Text style={styles.buttonText}>측정 종료</Text>
+                </TouchableOpacity>
+            </View>
+            <View style={styles.fullWidthButtonContainer}>
+                <TouchableOpacity
+                    style={[styles.fullWidthButton, styles.historyButton]}
+                    onPress={handleHistory}
+                >
+                    <Text style={styles.fullWidthButtonText}>분석결과</Text>
+                </TouchableOpacity>
+                <TouchableOpacity
+                    style={[styles.fullWidthButton, styles.logoutButton]}
+                    onPress={handleLogout}
+                >
+                    <Text style={styles.blackbuttonText}>로그아웃</Text>
+                </TouchableOpacity>
+            </View>
+        </View>
+    );
+};
+
+const styles = StyleSheet.create({
+    container: {
+        flex: 1,
+        justifyContent: 'center',
+        alignItems: 'center',
+        padding: 20,
+        backgroundColor: '#F7F7F7', // 배경색
+    },
+    timerContainer: {
+        marginVertical: 30,
+        padding: 20,
+        borderRadius: 10,
+        width: '100%',
+        alignItems: 'center',
+    },
+    time: {
+        fontSize: 68,
+        fontWeight: 'bold',
+        color: '#007AFF', // 타이머 색상
+    },
+    buttonRowContainer: {
+        flexDirection: 'row',
+        width: '100%',
+        justifyContent: 'space-between',
+        paddingHorizontal: 20,
+        marginBottom: 20,
+    },
+    button: {
+        flex: 1,
+        paddingVertical: 15,
+        borderRadius: 10,
+        marginHorizontal: 5,
+        alignItems: 'center',
+        justifyContent: 'center', // 중앙 정렬
+    },
+    startButton: {
+        backgroundColor: '#007AFF', // 시작 버튼 색상
+    },
+    stopButton: {
+        backgroundColor: '#FF3B30', // 종료 버튼 색상
+    },
+    fullWidthButtonContainer: {
+        width: '100%',
+        paddingHorizontal: 20,
+    },
+    fullWidthButton: {
+        paddingVertical: 15,
+        borderRadius: 10,
+        marginVertical: 5,
+        alignItems: 'center',
+    },
+    logoutButton: {
+        backgroundColor: '#eeeeee', // 로그아웃 버튼 색상 (연한 무채색)
+    },
+    historyButton: {
+        backgroundColor: '#CAF4FF', // 분석결과 버튼 색상 (연한 파란색)
+    },
+    buttonText: {
+        color: '#FFFFFF',
+        fontWeight: 'bold',
+        fontSize: 16,
+    },
+    blackbuttonText: {
+        fontWeight: 'bold',
+        fontSize: 16,
+
+    },
+    fullWidthButtonText: {
+        color:'#007AFF',
+        fontWeight: 'bold',
+        fontSize: 16,
+    },
+    buttonDisabled: {
+        opacity: 0.5,
+    },
+});
+
+export default GpsScreen;
 
src/screen/LoginScreen.js (added)
+++ src/screen/LoginScreen.js
@@ -0,0 +1,112 @@
+import React, { useState } from 'react';
+import { View, TextInput, Text, TouchableOpacity, Alert, StyleSheet } from 'react-native';
+import Api from '../api/ApiUtils';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+import { useNavigation } from '@react-navigation/native';
+
+const LoginScreen = () => {
+  const [userId, setUserId] = useState('');
+  const [password, setPassword] = useState('');
+  const navigation = useNavigation();
+
+  const handleLogin = async () => {
+    const credentials = {
+      id: userId,
+      password: password,
+    };
+    try {
+      const response = await Api.login(credentials);
+      console.log(response);
+     if(response.result === 'success'){
+      await AsyncStorage.setItem('token', JSON.stringify({accessToken:response.token}));
+      await AsyncStorage.setItem('user_id', credentials.id);
+      navigation.navigate('Selection');
+     }else{
+      Alert.alert('로그인 정보를 다시 한번 확인해주세요.');
+     }
+      
+      
+    } catch (error) {
+      Alert.alert('로그인 실패', error.message);
+    }
+  };
+
+  return (
+    <View style={styles.container}>
+      <Text style={styles.logo}>로그인</Text>
+      <TextInput
+        style={styles.input}
+        placeholder="아이디"
+        value={userId}
+        onChangeText={setUserId}
+      />
+      <TextInput
+        style={styles.input}
+        placeholder="비밀번호"
+        secureTextEntry
+        value={password}
+        onChangeText={setPassword}
+      />
+      <TouchableOpacity style={styles.button} onPress={handleLogin}>
+        <Text style={styles.buttonText}>로그인</Text>
+      </TouchableOpacity>
+      <View style={styles.footer}>
+        <TouchableOpacity
+          onPress={() => navigation.navigate('Agreement')}
+        >
+          <Text style={styles.footerText}>회원가입</Text>
+        </TouchableOpacity>
+      </View>
+    </View>
+  );
+};
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    justifyContent: 'center',
+    padding: 20,
+    backgroundColor: '#F7F7F7',
+  },
+  logo: {
+    fontSize: 28,
+    color: '#007AFF',
+    fontWeight: 'bold',
+    marginBottom: 30,
+    textAlign: 'center',
+  },
+  input: {
+    height: 50,
+    borderColor: '#CED4DA',
+    borderWidth: 1,
+    borderRadius: 10,
+    paddingHorizontal: 15,
+    marginBottom: 15,
+    backgroundColor: '#FFFFFF',
+    fontSize: 16,
+  },
+  button: {
+    backgroundColor: '#007AFF',
+    paddingVertical: 15,
+    borderRadius: 10,
+    alignItems: 'center',
+    marginBottom: 20,
+  },
+  buttonText: {
+    color: '#FFFFFF',
+    fontSize: 16,
+    fontWeight: 'bold',
+  },
+  footer: {
+    alignItems: 'center',
+    marginTop: 20,
+  },
+  footerText: {
+    fontSize: 16,
+    color: '#666',
+    marginBottom: 10,
+  },
+
+});
+
+export default LoginScreen;
 
src/screen/SelectionScreen.js (added)
+++ src/screen/SelectionScreen.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import { View, Text, TouchableOpacity, StyleSheet, Platform } from 'react-native';
+
+const SelectionScreen = ({ navigation }) => {
+  return (
+    <View style={styles.container}>
+      <TouchableOpacity style={styles.startButton} onPress={() => navigation.navigate('Camera')}>
+        <Text style={styles.buttonText}>운행시작</Text>
+      </TouchableOpacity>
+      <TouchableOpacity style={styles.historyButton} onPress={() => navigation.navigate('Analysis')}>
+        <Text style={styles.blueButtonText}>히스토리</Text>
+      </TouchableOpacity>
+    </View>
+  );
+};
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    justifyContent: 'center', // 중앙 정렬
+    alignItems: 'center',
+    backgroundColor: '#fff', // 배경색 (흰색)
+  },
+  startButton: {
+    backgroundColor: '#007AFF', // iOS 스타일의 버튼 색상
+    borderRadius: 10,
+    padding: 15,
+    width: '80%', // 버튼 너비
+    alignItems: 'center',
+    marginVertical: 10, // 버튼 간격
+  },
+  historyButton: {
+    backgroundColor: '#CAF4FF', 
+    borderRadius: 10,
+    padding: 15,
+    width: '80%', // 버튼 너비
+    alignItems: 'center',
+    marginVertical: 10, // 버튼 간격
+  },
+  buttonText: {
+    color: '#FFFFFF', // 버튼 텍스트 색상
+    fontSize: 18,
+    fontWeight: '600', // 텍스트 두께
+  },
+
+  blueButtonText: {
+    color: '#007AFF', // 버튼 텍스트 색상
+    fontSize: 18,
+    fontWeight: '600', // 텍스트 두께
+  },
+});
+
+export default SelectionScreen;
 
src/screens/AgreementScreen.tsx (deleted)
--- src/screens/AgreementScreen.tsx
@@ -1,176 +0,0 @@
-import {
-  StyleSheet,
-  ScrollView,
-  View,
-  Text,
-  TextInput,
-  TouchableOpacity,
-} from 'react-native';
-import React from 'react';
-import RadioButton from '../component/RadioButton';
-import {agreementAPI} from '../utils/loginUtils';
-import {useNavigation} from '@react-navigation/native';
-import {NativeStackNavigationProp} from '@react-navigation/native-stack';
-import {RootStackParam} from '../utils/RootStackParam';
-
-export default function AgreementScreen() {
-  const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
-  const [agreementData, setAgreement] = React.useState({
-    id: '',
-    password: '',
-    email: '',
-    sex: null,
-    phone: '',
-  });
-
-  const radio_props = [
-    {label: '여성', value: '여성'},
-    {label: '남성', value: '남성'},
-  ];
-
-  const handleInputChange = (field: string, value: string | number | null) => {
-    setAgreement(prevAgreement => ({
-      ...prevAgreement,
-      [field]: value,
-    }));
-  };
-
-  const handleSubmit = async () => {
-    console.log(agreementData); //
-    try {
-      const response = await agreementAPI(agreementData);
-      console.log('Response from server:', response);
-      // if (response.Authorization) {
-      //   navigation.navigate('LoginScreen');
-      // }
-      navigation.navigate('LoginScreen');
-    } catch (error) {
-      console.error('Error submitting agreement:', error);
-    }
-  };
-
-  // 전화번호 포맷팅
-  const formatPhoneNumber = (text: string) => {
-    const cleaned = text.replace(/\D/g, ''); // Remove all non-numeric characters
-    if (cleaned.length === 11) {
-      const match = cleaned.match(/^(\d{3})(\d{4})(\d{4})$/);
-      if (match) {
-        return `${match[1]}-${match[2]}-${match[3]}`;
-      }
-    }
-    return text;
-  };
-
-  const handlePhoneChange = (text: string) => {
-    const formatted = formatPhoneNumber(text);
-    handleInputChange('phone', formatted);
-  };
-
-  return (
-    <View style={styles.container}>
-      <Text style={styles.logoPoint}>회원 가입</Text>
-      <ScrollView>
-        <Text style={styles.inputText}> 아이디</Text>
-        <TextInput
-          style={styles.input}
-          placeholder="아이디를 입력하세요"
-          placeholderTextColor={'#cccccc'}
-          onChangeText={text => handleInputChange('id', text)}
-        />
-        <Text style={styles.inputText}> 비밀번호 </Text>
-        <TextInput
-          style={styles.input}
-          placeholder="비밀번호를 입력하세요"
-          secureTextEntry={true}
-          placeholderTextColor={'#cccccc'}
-          onChangeText={text => handleInputChange('password', text)}
-        />
-        <Text style={styles.inputText}> 이메일 </Text>
-        <TextInput
-          style={styles.input}
-          placeholder="이메일을 입력하세요"
-          placeholderTextColor={'#cccccc'}
-          onChangeText={text => handleInputChange('email', text)}
-        />
-        <Text style={styles.inputText}> 성별 </Text>
-        <View style={styles.radioForm}>
-          {radio_props.map((radio, index) => (
-            <RadioButton
-              key={index}
-              isSelected={agreementData.sex === radio.value}
-              onPress={() => handleInputChange('sex', radio.value)}
-              buttonColor={'#2196f3'}
-              selectedButtonColor={'#2196f3'}
-              labelColor={'#000'}
-              style={styles.radioButton}>
-              {radio.label}
-            </RadioButton>
-          ))}
-        </View>
-        <Text style={styles.inputText}> 전화번호 </Text>
-        <TextInput
-          style={styles.input}
-          placeholder="전화번호를 입력하세요"
-          placeholderTextColor={'#cccccc'}
-          onChangeText={handlePhoneChange}
-          value={agreementData.phone}
-          keyboardType="phone-pad"
-        />
-        <TouchableOpacity style={styles.button} onPress={handleSubmit}>
-          <Text style={styles.buttonText}> 등록하기 </Text>
-        </TouchableOpacity>
-      </ScrollView>
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    paddingHorizontal: 16,
-    paddingVertical: 50,
-    justifyContent: 'center',
-    backgroundColor: '#ffffff',
-  },
-  logoPoint: {
-    fontSize: 25,
-    color: '#3872ef',
-    fontWeight: 'bold',
-    textAlign: 'center',
-    marginTop: 20,
-    marginBottom: 30,
-  },
-  inputText: {
-    color: '#959595',
-    marginVertical: 10,
-  },
-  input: {
-    borderColor: '#959595',
-    borderRadius: 10,
-    borderWidth: 1,
-    marginBottom: 10,
-    padding: 10,
-    backgroundColor: '#ffffff',
-  },
-  radioForm: {
-    flexDirection: 'row',
-    justifyContent: 'flex-start',
-    marginBottom: 16,
-    marginLeft: 10,
-    marginTop: 12,
-  },
-  radioButton: {
-    marginRight: 20,
-  },
-  button: {
-    backgroundColor: '#3872ef',
-    padding: 10,
-    borderRadius: 10,
-    marginTop: 30,
-    marginBottom: 10,
-    alignItems: 'center',
-  },
-  buttonText: {
-    color: '#fff',
-  },
-});
 
src/screens/CameraScreen.tsx (deleted)
--- src/screens/CameraScreen.tsx
@@ -1,130 +0,0 @@
-import {
-  StyleSheet,
-  View,
-  Text,
-  TouchableOpacity,
-  Alert,
-  Linking,
-} from 'react-native';
-import React from 'react';
-import useCameraApi from '../utils/useCameraAPI.ts';
-import {Camera, useCameraDevice} from 'react-native-vision-camera';
-import Icon from 'react-native-vector-icons/Entypo.js';
-// import {CameraRoll} from '@react-native-camera-roll/camera-roll';
-
-export default function CameraScreen() {
-  const {
-    requestStoragePermission,
-    isPermission,
-    requestGPSPermission,
-    isGpsPermission,
-    sendPhotoFile,
-  } = useCameraApi();
-  const device = useCameraDevice('back');
-  const camera = React.useRef<Camera>(null);
-
-  // const [photoPath, setPhotoPath] = React.useState<string | null>(null);
-
-  React.useEffect(() => {
-    // 카메라 및 위치 권한 확인
-    const checkPermissions = async () => {
-      await requestStoragePermission(permissionGranted => {
-        if (!permissionGranted) {
-          Alert.alert(
-            '카메라 권한이 없습니다.',
-            '카메라를 사용하려면 권한을 허용해야 합니다.',
-            [{text: '설정으로 이동', onPress: () => Linking.openSettings()}],
-          );
-          console.log('카메라 권한이 거부되었습니다.');
-        }
-      });
-
-      await requestGPSPermission(permissionGranted => {
-        if (!permissionGranted) {
-          Alert.alert(
-            '위치 권한이 없습니다.',
-            '위치를 사용하려면 권한을 허용해야 합니다.',
-            [{text: '설정으로 이동', onPress: () => Linking.openSettings()}],
-          );
-          console.log('위치 권한이 거부되었습니다.');
-        }
-      });
-    };
-
-    // 디바이스 확인
-    if (!device) {
-      Alert.alert(
-        '카메라 디바이스가 없습니다.',
-        '카메라 디바이스를 찾을 수 없습니다.',
-      );
-    }
-    checkPermissions();
-  }, [requestStoragePermission, requestGPSPermission, device]);
-
-  // 사진 찍기
-  const takePhotoAsync = async () => {
-    if (camera.current) {
-      try {
-        const photo = await camera.current.takePhoto();
-        // console.log('Photo:', photo); // photo 확인
-        // setPhotoPath(photo.path);
-        // await CameraRoll.save(`file://${photo.path}`, {
-        //   type: 'photo',
-        // });
-
-        // 사진 전송
-        await sendPhotoFile(photo.path);
-      } catch (error) {
-        console.error('Error taking photo:', error);
-      }
-    }
-  };
-
-  const handleTakePhoto = async () => {
-    await takePhotoAsync();
-  };
-
-  return (
-    <View style={{flex: 1}}>
-      {isPermission && isGpsPermission ? (
-        device ? (
-          <>
-            <Camera
-              style={{flex: 1}}
-              device={device}
-              isActive={isPermission}
-              ref={camera}
-              photo={true}
-              enableLocation={true}
-            />
-            {/* 찍은 사진 렌더링 */}
-            {/* {photoPath ? (
-              <Image source={{uri: `file://${photoPath}`}} style={{flex: 1}} />
-            ) : (
-              <></>
-            )} */}
-            <TouchableOpacity
-              style={styles.takePicture}
-              onPress={handleTakePhoto}>
-              <Icon name="circular-graph" size={60} color={'white'} />
-            </TouchableOpacity>
-          </>
-        ) : (
-          <Text>디바이스 없다</Text>
-        )
-      ) : (
-        <></>
-      )}
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  takePicture: {
-    position: 'absolute',
-    bottom: '5%',
-    width: '100%',
-    alignItems: 'center',
-    justifyContent: 'center',
-  },
-});
 
src/screens/LoginScreen.tsx (deleted)
--- src/screens/LoginScreen.tsx
@@ -1,130 +0,0 @@
-import {
-  StyleSheet,
-  ScrollView,
-  View,
-  Text,
-  TextInput,
-  TouchableOpacity,
-} from 'react-native';
-import React from 'react';
-import {useNavigation} from '@react-navigation/native';
-import {NativeStackNavigationProp} from '@react-navigation/native-stack';
-import {useLoginAPI} from '../utils/loginUtils';
-import {RootStackParam} from '../utils/RootStackParam';
-import {useFocusEffect} from '@react-navigation/native';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-
-export default function LoginScreen() {
-  const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
-  const {loginAPI} = useLoginAPI(navigation);
-
-  const [loginData, setLogin] = React.useState({
-    id: '',
-    password: '',
-  });
-
-  const handleInputChange = (field: string, value: string | number | null) => {
-    setLogin(prevLogin => ({
-      ...prevLogin,
-      [field]: value,
-    }));
-  };
-
-  useFocusEffect(
-    React.useCallback(() => {
-      setLogin({
-        id: '',
-        password: '',
-      });
-    }, []),
-  );
-
-  // 로그인
-  const handleSubmit = async () => {
-    console.log(loginData); //
-    try {
-      const response = await loginAPI(loginData);
-      await AsyncStorage.setItem('user_id', loginData.id);
-      console.log('Response from server:', response);
-    } catch (error) {
-      console.error('Error submitting agreement:', error);
-    }
-  };
-
-  return (
-    <View style={styles.container}>
-      <Text style={styles.logoPoint}>로그인</Text>
-      <ScrollView>
-        <Text style={styles.inputText}> 아이디</Text>
-        <TextInput
-          style={styles.input}
-          placeholder="아이디를 입력하세요"
-          placeholderTextColor={'#cccccc'}
-          onChangeText={text => handleInputChange('id', text)}
-        />
-        <Text style={styles.inputText}> 비밀번호 </Text>
-        <TextInput
-          style={styles.input}
-          placeholder="비밀번호를 입력하세요"
-          secureTextEntry={true}
-          placeholderTextColor={'#cccccc'}
-          onChangeText={text => handleInputChange('password', text)}
-        />
-        <TouchableOpacity style={styles.button} onPress={handleSubmit}>
-          <Text style={styles.buttonText}> 로그인 </Text>
-        </TouchableOpacity>
-        <TouchableOpacity
-          style={styles.toAgreement}
-          onPress={() => navigation.navigate('AgreementScreen')}>
-          <Text> 회원가입 하러 가기 </Text>
-        </TouchableOpacity>
-      </ScrollView>
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    paddingHorizontal: 16,
-    paddingVertical: 50,
-    justifyContent: 'center',
-    backgroundColor: '#ffffff',
-  },
-  logoPoint: {
-    fontSize: 25,
-    color: '#3872ef',
-    fontWeight: 'bold',
-    textAlign: 'center',
-    marginTop: 20,
-    marginBottom: 30,
-  },
-  inputText: {
-    color: '#959595',
-    marginVertical: 10,
-  },
-  input: {
-    borderColor: '#959595',
-    borderRadius: 10,
-    borderWidth: 1,
-    marginBottom: 10,
-    padding: 10,
-    backgroundColor: '#ffffff',
-  },
-  button: {
-    backgroundColor: '#3872ef',
-    padding: 10,
-    borderRadius: 10,
-    marginTop: 30,
-    marginBottom: 10,
-  },
-  buttonText: {
-    color: '#ffffff',
-    textAlign: 'center',
-  },
-  toAgreement: {
-    marginTop: 30,
-    alignItems: 'center',
-    color: '#ccc',
-  },
-});
 
src/screens/SendLocation.tsx (deleted)
--- src/screens/SendLocation.tsx
@@ -1,403 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react';
-import {
-  StyleSheet,
-  View,
-  Text,
-  TouchableOpacity,
-  Dimensions,
-} from 'react-native';
-import { Shadow } from 'react-native-shadow-2';
-import Icon from 'react-native-vector-icons/FontAwesome5';
-import Geolocation from '@react-native-community/geolocation';
-import { sendLocationData } from '../utils/useGeolocationAPI';
-import { useNavigation } from '@react-navigation/native';
-import { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import { RootStackParam } from '../utils/RootStackParam';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import BackgroundService from 'react-native-background-actions';
-import crypto from 'crypto-js';
-
-let intervalId: NodeJS.Timeout | null = null;
-
-export default function SendLocation() {
-  const navigation = useNavigation<NativeStackNavigationProp<RootStackParam>>();
-  const [time, setTime] = useState<number>(0);
-  const [time2, setTime2] = useState<number>(0);
-  const [isRunning, setIsRunning] = useState<boolean>(false);
-  const [locations, setLocations] = useState<{
-    longitudes: string[],
-    latitudes: string[],
-    timestamps: string[]
-  }>({ longitudes: [], latitudes: [], timestamps: [] });
-  const locationsRef = useRef(locations);
-  const [hash, setHash] = useState("");
-  const tripIdRef = useRef<string>(hash);
-  const userIdRef = useRef<string>('');
-  const [watchId, setWatchId] = useState<number | null>(null);
-
-  // `hash`가 업데이트될 때마다 `tripIdRef`도 업데이트
-  useEffect(() => {
-    tripIdRef.current = hash;
-  }, [hash]);
-
-  // `locations`가 업데이트될 때마다 `locationsRef`도 업데이트
-  useEffect(() => {
-    locationsRef.current = locations;
-  }, [locations]);
-
-  // 컴포넌트 초기화 및 `isRunning` 상태 변경 시 초기화
-  useEffect(() => {
-    const initialize = async () => {
-      // `user_id`를 AsyncStorage에서 가져옴
-      const userId = await AsyncStorage.getItem('user_id');
-      if (userId) {
-        userIdRef.current = userId;
-      }
-
-      // 해시 값을 생성
-      const rand = Math.random().toString();
-      const date = new Date();
-      const sha256Hash = crypto.SHA256(rand + ',' + date.toString()).toString(crypto.enc.Hex);
-      setHash(sha256Hash);
-
-      // `isRunning` 상태에 따라 백그라운드 서비스 시작 또는 중지
-      if (isRunning) {
-        await startBackgroundService();
-      } else {
-        await stopBackgroundService();
-      }
-    };
-
-    initialize();
-  }, [isRunning]);
-
-  // `time2` 밀리초 동안 대기하는 함수
-  const sleep = (time2: number) => new Promise(resolve => setTimeout(() => resolve(true), time2));
-
-  // 위치 데이터를 서버로 전송하는 함수
-  const sendData = async () => {
-    const currentLocations = { ...locationsRef.current };
-    // 전송할 위치 데이터가 있는지 확인
-    if (currentLocations.longitudes.length === 0 || currentLocations.latitudes.length === 0 || currentLocations.timestamps.length === 0) {
-      console.log("No data to send.");
-      return;
-    }
-    await sendLocationData(userIdRef.current, tripIdRef.current, currentLocations, navigation);
-    setLocations({ longitudes: [], latitudes: [], timestamps: [] });
-  };
-
-  // 백그라운드 서비스에서 실행될 함수
-  const veryIntensiveTask = async (taskDataArguments: any) => {
-    const { delay } = taskDataArguments;
-
-    while (BackgroundService.isRunning()) {
-      await sleep(delay);
-      await sendData();
-    }
-  };
-
-  // 백그라운드 서비스 옵션
-  const options = {
-    taskName: '측정 서비스',
-    taskTitle: '측정 중',
-    taskDesc: '측정 진행 중... ',
-    taskIcon: {
-      name: 'ic_launcher',
-      type: 'mipmap',
-    },
-    color: '#ff00ff',
-    linkingURI: 'sendLocation',
-    parameters: {
-      delay: 15000,
-    },
-  };
-
-  // 백그라운드 서비스를 시작하는 함수
-  const startBackgroundService = async () => {
-    return new Promise<void>((resolve, reject) => {
-      let latestPosition: GeolocationPosition | null = null; // 마지막 위치를 저장할 변수
-      let locationCheckInterval: NodeJS.Timeout | null = null; // 위치 체크 인터벌을 저장할 변수
-      let watchId: number | null = null; // 위치 감시 ID를 저장할 변수
-      let lastUpdateTime = Date.now(); // 마지막 업데이트 시간을 저장할 변수
-  
-      // 위치 업데이트를 처리하는 함수
-      const handleLocationUpdate = (position: GeolocationPosition) => {
-        latestPosition = position; // 최신 위치를 업데이트
-        lastUpdateTime = Date.now(); // 마지막 업데이트 시간 갱신
-  
-        const { longitude, latitude } = position.coords;
-        const time = new Date().toISOString();
-  
-        // 한국 시간으로 변환하는 함수
-        const formatDate = (time) => {
-          const date = new Date(time);
-          const year = date.getFullYear();
-          const month = `0${date.getMonth() + 1}`.slice(-2);
-          const day = `0${date.getDate()}`.slice(-2);
-          const hours = `0${date.getHours()}`.slice(-2);
-          const minutes = `0${date.getMinutes()}`.slice(-2);
-          const seconds = `0${date.getSeconds()}`.slice(-2);
-          const milliseconds = `00${date.getMilliseconds()}`.slice(-3);
-  
-          return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
-        };
-  
-        const timestamp = formatDate(time);
-  
-        // 위치 데이터를 상태에 추가
-        setLocations(prevData => ({
-          longitudes: [...prevData.longitudes, longitude.toString()],
-          latitudes: [...prevData.latitudes, latitude.toString()],
-          timestamps: [...prevData.timestamps, timestamp]
-        }));
-      };
-  
-      // 1초마다 위치 업데이트를 확인하고 3초 동안 업데이트가 없을 경우 마지막 위치를 추가
-      locationCheckInterval = setInterval(() => {
-        if (latestPosition && (Date.now() - lastUpdateTime > 3000)) {
-          const { longitude, latitude } = latestPosition.coords;
-          const time = new Date().toISOString();
-          const timestamp = formatDate(time);
-  
-          setLocations(prevData => ({
-            longitudes: [...prevData.longitudes, longitude.toString()],
-            latitudes: [...prevData.latitudes, latitude.toString()],
-            timestamps: [...prevData.timestamps, timestamp]
-          }));
-          lastUpdateTime = Date.now();
-        }
-      }, 1000);
-  
-      // 위치 업데이트를 시작하는 함수
-      const startWatchingPosition = () => {
-        watchId = Geolocation.watchPosition(
-          handleLocationUpdate,
-          error => {
-            console.log('watchPosition Error:', error);
-            reject(error);
-          },
-          {
-            enableHighAccuracy: false, // 높은 정확도를 사용할지 여부
-            distanceFilter: 0, // 최소 거리 필터
-            interval: 1000, // 업데이트 간격 (밀리초)
-          }
-        );
-      };
-  
-      // watchId가 유효한 경우 백그라운드 서비스 시작
-      if (watchId === null) {
-        (async () => {
-          try {
-            await BackgroundService.start(veryIntensiveTask, options);
-            startWatchingPosition(); // 위치 업데이트 시작
-            resolve();
-          } catch (error) {
-            reject(error);
-          }
-        })();
-      }
-  
-      // 백그라운드 서비스를 중지할 때의 정리 함수
-      return () => {
-        if (locationCheckInterval !== null) {
-          clearInterval(locationCheckInterval);
-          locationCheckInterval = null;
-        }
-        if (watchId !== null) {
-          Geolocation.clearWatch(watchId);
-          watchId = null;
-        }
-        BackgroundService.stop();
-      };
-    });
-  };
-
-  // 백그라운드 서비스를 중지하는 함수
-  const stopBackgroundService = async () => {
-    if (watchId !== null) {
-      Geolocation.clearWatch(watchId);
-      setWatchId(null);
-    }
-    if (intervalId) {
-      clearInterval(intervalId);
-      intervalId = null;
-    }
-    await BackgroundService.stop();
-  };
-
-  // 측정을 시작하는 함수
-  const handleStart = () => {
-    setIsRunning(true);
-    intervalId = setInterval(() => {
-      setTime(prevTime => prevTime + 1);
-    }, 1000);
-  };
-
-  // 측정을 중지하는 함수
-  const handleStop = () => {
-    // `watchPosition` 제거
-    if (watchId !== null) {
-      Geolocation.clearWatch(watchId); // watchId 제거
-      setWatchId(null); // watchId 초기화
-    }
-
-    // `intervalId` 제거
-    if (intervalId) {
-      clearInterval(intervalId);
-      intervalId = null;
-    }
-
-    // 상태 초기화
-    setTime(0);
-    setIsRunning(false);
-
-    // 백그라운드 서비스가 실행 중인지 확인
-    if (BackgroundService.isRunning()) {
-      // 백그라운드 서비스 중지
-      BackgroundService.stop().then(() => {
-        console.log("Background service stopped successfully.");
-        // 중지 후 즉시 데이터 전송
-        sendData().then(() => {
-          console.log("Data sent successfully after stopping background service.");
-        }).catch(error => {
-          console.log("Error sending data after stopping background service:", error);
-        });
-      }).catch(error => {
-        console.log("Error stopping background service:", error);
-      });
-    } else {
-      // 백그라운드 서비스가 실행 중이 아니면 데이터만 전송
-      sendData().then(() => {
-        console.log("Data sent successfully when background service is not running.");
-      }).catch(error => {
-        console.log("Error sending data when background service is not running:", error);
-      });
-    }
-  };
-
-  // 시간을 형식화하는 함수
-  const formatTime = (time: number): string => {
-    const hours = Math.floor(time / 3600);
-    const minutes = Math.floor((time % 3600) / 60);
-    const seconds = time % 60;
-    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
-  };
-
-  return (
-    <View style={styles.container}>
-      <View style={{ alignItems: 'center' }}>
-        <Shadow
-          distance={8}
-          startColor={'#3872ef33'}
-          endColor={'#ffffff'}
-          offset={[5, 6]}
-          containerStyle={styles.watchContainer}>
-          <View style={styles.watchContainer}>
-            <Text style={{ fontWeight: 'bold', fontSize: 20 }}>
-              측정 경과 시간
-            </Text>
-            <Text style={styles.time}> {formatTime(time)} </Text>
-          </View>
-        </Shadow>
-        <View style={styles.buttonContainer}>
-          <View style={styles.row}>
-            <TouchableOpacity style={styles.button} onPress={handleStart}>
-              <Icon name="play" size={25} color={'#717171'} />
-            </TouchableOpacity>
-            <Text> 측정 시작 </Text>
-          </View>
-          <View style={styles.row}>
-            <TouchableOpacity style={styles.button} onPress={handleStop}>
-              <Icon name="power-off" size={25} color={'#717171'} />
-            </TouchableOpacity>
-            <Text> 측정 종료 </Text>
-          </View>
-        </View>
-        <TouchableOpacity
-          onPress={() => navigation.navigate('CameraScreen')}
-          style={styles.navButton}>
-          <Text> 카메라 촬영 </Text>
-        </TouchableOpacity>
-        <TouchableOpacity
-          style={styles.navButton}
-          onPress={() => navigation.navigate('AnalysisScreen')}>
-          <Text> 분석 결과 </Text>
-        </TouchableOpacity>
-        <TouchableOpacity
-          style={styles.logout}
-          onPress={() => {
-            AsyncStorage.removeItem('token');
-            navigation.navigate('LoginScreen');
-          }}>
-          <Text> 로그아웃 </Text>
-        </TouchableOpacity>
-      </View>
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  container: {
-    flex: 1,
-    paddingHorizontal: 16,
-    paddingVertical: 50,
-    justifyContent: 'center',
-    backgroundColor: '#ffffff',
-  },
-  watchContainer: {
-    alignItems: 'center',
-    justifyContent: 'center',
-    borderRadius:
-      Math.round(
-        Dimensions.get('window').width + Dimensions.get('window').height,
-      ) * 0.35,
-    width: Dimensions.get('window').width * 0.7,
-    height: Dimensions.get('window').width * 0.7,
-    backgroundColor: '#ffffff',
-  },
-  time: {
-    marginTop: 30,
-    color: '#3872ef',
-    fontWeight: 'bold',
-    fontSize: 50,
-  },
-  buttonContainer: {
-    flexDirection: 'row',
-    justifyContent: 'space-between',
-    paddingHorizontal: 10,
-    marginTop: 65,
-    marginBottom: 70,
-    width: '55%',
-  },
-  row: {
-    alignItems: 'center',
-  },
-  button: {
-    backgroundColor: '#F0F0F0',
-    justifyContent: 'center',
-    alignItems: 'center',
-    borderRadius: 100,
-    width: 60,
-    height: 60,
-    marginBottom: 10,
-  },
-  navButton: {
-    backgroundColor: '#f0f0f0',
-    alignItems: 'center',
-    justifyContent: 'center',
-    height: Dimensions.get('screen').height * 0.05,
-    width: Dimensions.get('screen').width * 0.7,
-    marginTop: 15,
-    borderRadius: 10,
-  },
-  logout: {
-    borderWidth: 3,
-    borderColor: '#f0f0f0',
-    alignItems: 'center',
-    justifyContent: 'center',
-    height: Dimensions.get('screen').height * 0.05,
-    width: Dimensions.get('screen').width * 0.7,
-    marginTop: 15,
-    borderRadius: 10,
-  },
-});
 
src/services/LocationService.js (added)
+++ src/services/LocationService.js
@@ -0,0 +1,160 @@
+import React, { useEffect } from 'react';
+import { Platform, PermissionsAndroid, AppState } from 'react-native';
+import Geolocation from 'react-native-geolocation-service';
+import BackgroundActions from 'react-native-background-actions';
+
+let watchId = null; // 구독 ID를 저장할 변수
+const locations = {
+  latitude: [],
+  longitude: [],
+  timestamp: []
+};
+let intervalId = null; // Interval ID를 저장할 변수
+// 위치 추적 옵션 설정
+const locationOptions = {
+  accuracy: {
+    ios: 'best',
+    android: 'mediumAccuracy',
+  }[Platform.OS],
+  distanceFilter: 0, // meters
+  interval: 1000, // 1 seconds
+  fastestInterval: 1000, // 5 seconds
+  forceRequestLocation: true
+
+};
+
+// 위치 권한 요청 함수
+const requestLocationPermission = async () => {
+  if (Platform.OS === 'ios') {
+    const permission = await new Promise((resolve) => {
+      Geolocation.requestAuthorization((status) => {
+        resolve(status);
+      });
+    });
+    return permission;
+  } else {
+    const granted = await PermissionsAndroid.request(
+      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
+    );
+    return granted === PermissionsAndroid.RESULTS.GRANTED ? 'granted' : 'denied';
+  }
+};
+
+// 위치 서비스 시작 함수
+const startLocationService = async () => {
+  console.log('그다음 위치서비스가 시작되는거야');
+  try {
+    const permission = await requestLocationPermission();
+    if (permission !== 'granted') {
+      console.error('Location permission not granted');
+      return; // 권한이 없으면 함수 종료
+    }
+
+    if (watchId === null) {
+      watchId = Geolocation.watchPosition(
+        (position) => {
+          const { latitude, longitude } = position.coords;
+          const time = new Date().toISOString();
+          const timestamp = formatDate(time);
+          locations.latitude.push(latitude);
+          locations.longitude.push(longitude);
+          // locations.latitude.push(String(latitude));
+          // locations.longitude.push(String(longitude));
+          locations.timestamp.push(timestamp);
+          console.log('Location updated:', { latitude, longitude, timestamp });
+        },
+        (error) => {
+          console.error('Error watching position:', error);
+        },
+        locationOptions
+      );
+
+      console.log('Location service started.');
+    }
+  } catch (error) {
+    console.error('Error starting location service:', error);
+  }
+};
+
+// 위치 서비스 중지 함수
+const stopLocationService = async () => {
+  try {
+    if (watchId !== null) {
+      Geolocation.clearWatch(watchId);
+      watchId = null;
+    }
+    console.log('Location service stopped.');
+  } catch (error) {
+    console.error('Error stopping location service:', error);
+  }
+};
+
+// 백그라운드에서 실행할 작업
+const backgroundTask = async (taskData) => {
+  console.log('백그라운드에서 이게 시작되는거고');
+  await new Promise(async (resolve) => {
+    await startLocationService();
+
+    intervalId = setInterval(() => {
+      console.log('Background task running');
+    }, 10000); // 10초마다 로그 출력ㄴ
+
+  });
+};
+
+// 백그라운드 작업 시작 함수
+const startBackgroundTask = async () => {
+  try {
+    await BackgroundActions.start(backgroundTask, options);
+    console.log('Background task started.');
+  } catch (error) {
+    console.error('Error starting background task:', error);
+  }
+};
+
+// 백그라운드 작업 중지 함수
+const stopBackgroundTask = async () => {
+  try {
+    await BackgroundActions.stop();
+    await stopLocationService(); // 위치 서비스 중지
+    clearInterval(intervalId); // Interval 중지
+    console.log('Background task stopped and location service stopped.');
+  } catch (error) {
+    console.error('Error stopping background task:', error);
+  }
+};
+
+// 백그라운드 서비스 옵션
+const options = {
+  taskName: '측정 서비스',
+  taskTitle: '측정 중',
+  taskDesc: '측정 진행 중... ',
+  taskIcon: {
+    name: 'ic_launcher',
+    type: 'mipmap',
+  },
+  color: '#ff00ff',
+  linkingURI: 'sendLocation',
+  parameters: {
+    delay: 1000,
+  },
+};
+
+// 한국 시간으로 변환하는 함수
+const formatDate = (time) => {
+  const date = new Date(time);
+  const year = date.getFullYear();
+  const month = `0${date.getMonth() + 1}`.slice(-2);
+  const day = `0${date.getDate()}`.slice(-2);
+  const hours = `0${date.getHours()}`.slice(-2);
+  const minutes = `0${date.getMinutes()}`.slice(-2);
+  const seconds = `0${date.getSeconds()}`.slice(-2);
+  const milliseconds = `00${date.getMilliseconds()}`.slice(-3);
+
+  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
+};
+
+
+
+export { startBackgroundTask, stopBackgroundTask, locations };
+
 
src/styles/GlobalStyles.tsx (deleted)
--- src/styles/GlobalStyles.tsx
@@ -1,55 +0,0 @@
-export const container = {
-  flex: 1,
-  paddingHorizontal: 16,
-  paddingVertical: 50,
-  justifyContent: 'center',
-  backgroundColor: '#ffffff',
-};
-
-export const ItemFlex = {
-  flex: 1,
-};
-export const ItemFlex2 = {
-  flex: 2,
-};
-
-export const direction = {
-  flexDirection: 'row',
-  justifyContent: 'space-between',
-};
-
-export const directionAlign = {
-  flexDirection: 'row',
-  justifyContent: 'space-between',
-  alignItems: 'center',
-  borderBottomWidth: 1,
-  borderBottomColor: '#eeeeee',
-  paddingVertical: 20,
-};
-
-export const pageTitleBox = {
-  paddingVertical: 20,
-  borderBottomWidth: 1,
-  borderBottomColor: '#cccccc',
-  backgroundColor: '#ffffff',
-};
-export const pageTitle = {
-  fontSize: 15,
-  fontWeight: 'bold',
-  textAlign: 'center',
-  color: '#333333',
-};
-export const marginBottom = {
-  marginBottom: 20,
-};
-export const marginRight = {
-  marginRight: 10,
-};
-
-export const textColor = {
-  color: '#333333',
-};
-
-export const padding = {
-  paddingHorizontal: 16,
-};
 
src/utils/RootStackParam.ts (deleted)
--- src/utils/RootStackParam.ts
@@ -1,7 +0,0 @@
-export type RootStackParam = {
-  SendLocation: undefined;
-  CameraScreen: undefined;
-  AgreementScreen: undefined;
-  LoginScreen: undefined;
-  AnalysisScreen: undefined;
-};
 
src/utils/loginUtils.ts (deleted)
--- src/utils/loginUtils.ts
@@ -1,107 +0,0 @@
-import {Alert} from 'react-native';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import {NativeStackNavigationProp} from '@react-navigation/native-stack';
-import {RootStackParam} from './RootStackParam';
-
-// API 주소
-const url = 'http://takensoftai.iptime.org:15857/auth';
-
-// 회원가입
-export const agreementAPI = async (agreementData: any) => {
-  try {
-    const response = await fetch(`${url}/register`, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-      },
-      body: JSON.stringify(agreementData),
-    });
-
-    if (!response.ok) {
-      throw new Error('Network response was not ok');
-    }
-
-    const data = await response.json();
-
-    return data;
-  } catch (error) {
-    console.error('Register Error:', error);
-  }
-};
-
-// 로그인
-export const useLoginAPI = (
-  navigation: NativeStackNavigationProp<RootStackParam>,
-) => {
-  const loginAPI = async (loginData: any) => {
-    try {
-      // console.log('data | ', loginData);
-      const response = await fetch(`${url}/login`, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        body: JSON.stringify(loginData),
-      });
-
-      const data = await response.json();
-      console.log(`data : ${data}`);
-
-      if (data.result === 'success') {
-        console.log('login 성공');
-        // console.log('userInfo', userInfoData);
-        // 로그인 성공 시 토큰 스토리지에 저장
-        AsyncStorage.setItem(
-          'token',
-          JSON.stringify({
-            accessToken: data.token,
-          }),
-        );
-        navigation.reset({routes: [{name: 'SendLocation'}]});
-      } else {
-        // 로그인 실패 시 처리
-        Alert.alert('로그인 정보를 다시 한 번 확인해주세요.');
-      }
-
-      return data;
-    } catch (error) {
-      console.error('로그인 오류:', error);
-      throw error;
-    }
-  };
-
-  return {loginAPI};
-};
-
-// 로컬 저장소에서 가져오기
-export const getTokenFromStorage = async () => {
-  try {
-    const tokenString = await AsyncStorage.getItem('token');
-    if (tokenString) {
-      const tokenObject = JSON.parse(tokenString);
-      const accessToken = tokenObject.accessToken;
-      // console.log(`accessToken: ${accessToken}`);
-      return accessToken;
-    } else {
-      console.log('토큰이 AsyncStorage에 없습니다.');
-      return null;
-    }
-  } catch (error) {
-    console.error('액세스 토큰 가져오는 중 오류 발생:', error);
-    return null;
-  }
-};
-
-export const verifyTokens = async (
-  navigation: NativeStackNavigationProp<RootStackParam>,
-) => {
-  const token = await getTokenFromStorage();
-
-  // 토큰이 없는 경우
-  if (!token) {
-    navigation.reset({routes: [{name: 'LoginScreen'}]});
-  } else {
-    // 토큰이 있는 경우
-    navigation.reset({routes: [{name: 'SendLocation'}]});
-  }
-};
 
src/utils/useCameraAPI.ts (deleted)
--- src/utils/useCameraAPI.ts
@@ -1,139 +0,0 @@
-import React from 'react';
-import {Linking} from 'react-native';
-import {Camera} from 'react-native-vision-camera';
-// import RNFS from 'react-native-fs';
-import {getTokenFromStorage} from './loginUtils';
-
-// API 주소
-const url = 'http://takensoftai.iptime.org:15857/action/image_summit';
-
-type PermissionCallback = (result: boolean) => void;
-type GpsPermissionCallback = (result: boolean) => void;
-
-interface UseCameraApi {
-  requestStoragePermission: (callback: PermissionCallback) => Promise<void>;
-  isPermission: boolean;
-  requestGPSPermission: (callback: GpsPermissionCallback) => Promise<void>;
-  isGpsPermission: boolean;
-  sendPhotoFile: (photoPath: string) => Promise<void>;
-}
-
-// 권한 요청
-export default function useCameraApi(): UseCameraApi {
-  const [isPermission, setIsPermission] = React.useState<boolean>(false);
-  const [isGpsPermission, setIsGpsPermission] = React.useState<boolean>(false);
-
-  // 카메라 권한 요청
-  const requestStoragePermission = async (callback: PermissionCallback) => {
-    try {
-      const cameraPermission = await Camera.getCameraPermissionStatus();
-
-      let newCameraPermission = cameraPermission;
-
-      if (
-        cameraPermission === 'not-determined' ||
-        cameraPermission === 'denied'
-      ) {
-        newCameraPermission = await Camera.requestCameraPermission();
-      }
-
-      const permissionGranted = newCameraPermission === 'granted';
-      setIsPermission(permissionGranted);
-      callback(permissionGranted);
-
-      if (!permissionGranted) {
-        await Linking.openSettings();
-      }
-    } catch (err) {
-      console.log(`카메라 권한 요청 에러 : ${err}`);
-    }
-  };
-
-  // 위치 권한 요청
-  const requestGPSPermission = async (callback: GpsPermissionCallback) => {
-    try {
-      const gpsPermissionStatus = await Camera.getLocationPermissionStatus();
-
-      let newGpsPermission = gpsPermissionStatus;
-
-      if (
-        newGpsPermission === 'not-determined' ||
-        newGpsPermission === 'denied' ||
-        newGpsPermission === 'restricted'
-      ) {
-        newGpsPermission = await Camera.requestLocationPermission();
-      }
-
-      const gpsPermissionGranted = newGpsPermission === 'granted';
-      setIsGpsPermission(gpsPermissionGranted);
-      callback(gpsPermissionGranted);
-
-      if (!gpsPermissionGranted) {
-        await Linking.openSettings();
-      }
-    } catch (err) {
-      console.log(`위치 권한 요청 에러 : ${err}`);
-    }
-  };
-
-  // 사진 전송
-  const sendPhotoFile = async (photoPath: string) => {
-    try {
-      const token = await getTokenFromStorage();
-      if (!token) {
-        console.log('토큰 없음');
-        return;
-      }
-
-      if (!photoPath || typeof photoPath !== 'string') {
-        console.log('사진 경로 없음');
-        return;
-      }
-
-      const pathSplit = photoPath.split('/').pop();
-      if (!pathSplit) {
-        console.log('사진 경로 split 불가');
-        return;
-      }
-      const fileName = pathSplit.split('.')[0];
-
-      const formData = new FormData();
-      formData.append('file', {
-        uri: `file://${photoPath}`,
-        name: `${fileName}.jpg`,
-        type: 'image/jpg',
-      });
-
-      try {
-        const response = await fetch(url, {
-          method: 'POST',
-          body: formData,
-          headers: {
-            'Content-Type': 'multipart/form-data',
-            Authorization: `${token}`,
-          },
-        });
-        const responseJson = await response.json();
-        if (response.status === 200) {
-          console.log(
-            'Upload Successful',
-            `(${response.status})`,
-            responseJson,
-          );
-        } else {
-          console.log('Upload failed', responseJson);
-        }
-      } catch (error) {
-        console.log('Upload error', error);
-      }
-    } catch (error) {}
-  };
-
-  return {
-    requestStoragePermission,
-    isPermission,
-    requestGPSPermission,
-    isGpsPermission,
-    sendPhotoFile,
-  };
-}
 
src/utils/useGeolocationAPI.ts (deleted)
--- src/utils/useGeolocationAPI.ts
@@ -1,62 +0,0 @@
-import React from 'react';
-import { getTokenFromStorage, verifyTokens } from './loginUtils';
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import { NativeStackNavigationProp } from '@react-navigation/native-stack';
-import { RootStackParam } from '../utils/RootStackParam';
-
-// API 주소
-const url = 'http://takensoftai.iptime.org:15857/action/gps_update';
-
-export const sendLocationData = async (
-  userId: string,
-  tripId: string,
-  locations: { longitudes: string[], latitudes: string[], timestamps: string[] },
-  navigation: NativeStackNavigationProp<RootStackParam>,
-) => {
-  try {
-    const token = await getTokenFromStorage();
-    if (!token) {
-      await AsyncStorage.removeItem('token');
-      verifyTokens(navigation);
-      return;
-    }
-
-    const requestBody = {
-      user_id: userId,
-      trip_id: tripId,
-      trip_log: {
-        latitude: locations.latitudes,
-        longitude: locations.longitudes,
-        timestamp: locations.timestamps,
-      },
-    };
-
-    console.log('Request body:', requestBody);
-
-    try {
-      const response = await fetch(url, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-          Authorization: `${token}`,
-        },
-        body: JSON.stringify(requestBody),
-      });
-
-      const data = await response.json();
-      console.log('Response:', data);
-
-      if (!response.ok) {
-        console.log('Server response error:', data.msg || response.statusText);
-      }
-      if (data.result === 'fail') {
-        await AsyncStorage.removeItem('token');
-      }
-    } catch (error) {
-      console.log('Server request error:', error);
-    }
-  } catch (error) {
-    console.log('Token retrieval error:', error);
-  }
-};
-
Add a comment
List