본문 바로가기
앱 개발

[react-native expo] 페이지 이동 기능 구현 (navigation, stackNavagator)

by evekang 2022. 6. 15.

 

 

 

 

 

 

지역별로 해당 검색어에 맞는 지역의 언어치료센터 리스트를 불러오는 것 까지 성공했다.

코드가 좀 지저분해진 것 같긴 하지만 일단은 구현 먼저! 코드 정리는 나중에!ㅋㅋㅋ

 

 

 

[카테고리 기능이 구현된 모습]

 

 

 

 

 

 

 

 

 

오늘은 예전에 만들어 놓은 상세보기 페이지를 이용해서 

메인에서 리스트를 클릭하면 해당 상세보기 페이지로 이동하는 것을 구현할 예정이다.

 

 

1. 네비게이션을 이용하기 위한 기본 라이브러리 설치하기

react-navigation 공식문서 : https://reactnavigation.org 

 

React Navigation | React Navigation

Routing and navigation for your React Native apps

reactnavigation.org

 

@첫번째 설치
yarn add @react-navigation/native

@두번째 설치
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

 

@createStackNavigator 설치
yarn add @react-navigation/stack

 

 

 

2. navigation 폴더를 만들고 StackNavigator.js 파일 만들기

[ StackNavigation.js ]

import React from "react";
//설치한 스택 네비게이션 라이브러리를 가져옴
import { createStackNavigator } from "@react-navigation/stack";

//페이지로 만든 컴포넌트들을 불러옴
import Detail from "../pages/Detail";
import Main from "../pages/Main";

//스택 네비게이션 라이브러리가 제공해주는 여러 기능이 담겨있는 객체를 사용
//그래서 이렇게 항상 상단에 선언하고 시작하는게 규칙!
const Stack = createStackNavigator();

const StackNavigator = () => {
  return (
    //컴포넌트들을 페이지로 만들어주는 네비게이터(Navigator) 태그 선언
    //위에서 선언한 Stack 변수에 들어있는 태그를 꺼내 사용
    //Stack.Navigator 태그 내부엔 페이지(화면)를 스타일링 할 수 있는 다양한 옵션들이 있음 (여기선 사용안함)
    <Stack.Navigator
    //   screenOptions={{
    //     headerStyle: {
    //       backgroundColor: "black",
    //       borderBottomColor: "black",
    //       shadowColor: "black",
    //       height: 100,
    //     },
    //     headerTintColor: "#FFFFFF",
    //     headerBackTitleVisible: false,
    //   }}
    >
      {/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣기 = 이 자체로 페이지 기능 가능*/}
      <Stack.Screen name="Main" component={Main} />
      <Stack.Screen name="Detail" component={Detail} />
    </Stack.Navigator>
  );
};

export default StackNavigator;

아래쪽의  <Stack.Screen~~~>에 name으로 정해둔 이름을 가지고 나중에 페이지 이동을 한다. 

※ name은 반드시 있어야 함!

 

 

 

 

3. 최상단 컴포넌트(App.js)에다 네비게이션 기능 달아주기

[ App.js ]

import React from "react";
// 네비게이션 달아줄거니까 이제 페이지 컴포넌트들 안 불러와도 됨.
// import Main from "./pages/Main";
// import Detail from "./pages/Detail";

// 네비게이션 도구들 불러옴.
import { NavigationContainer } from "@react-navigation/native";
import StackNavigator from "./navigation/StackNavigator";

export default function App() {
  return (
    // <Main />
    // <Detail />
    <NavigationContainer>
      <StackNavigator />
    </NavigationContainer>
  );
}

App.js에다가 네비게이션을 달아줘야 앱 어디서든지 원하는 페이지 이동이 가능하다.

 

 

 

 

 

4. 스택네비게이션으로 페이지 이동하기

Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigation과 route라는 딕셔너리(혹은 객체)를 속성으로 넘겨받아서 사용할 수 있다.

 

@ navigation

//해당 페이지의 제목을 설정할 수 있음
navigation.setOptions({
   title:'메인입니다.'
})

//Stack.screen에서 name 속성으로 정해준 이름을 지정해주면 해당 페이지로 이동하는 함수
navigation.navigate("Detail")

//name 속성을 전달해주고, 두 번째 인자로 딕셔너리 데이터를 전달해주면, Detail 페이지에서 
//두번째 인자로 전달된 딕셔너리 데이터를 route 딕셔너리로 로 받을 수 있음
navigation.navigate("Detail",{
  title: title
})

 

@ route

//전달받은 데이터를 받는 route 딕셔너리
//비구조 할당 방식으로 route에 params 객체 키로 연결되어 전달되는 데이터를 꺼내 사용
//navigate 함수로 전달되는 route 데이터는 아래와 같은 모습
  {
		route : {
			params :{
				title:title
			}
		}
	}
const { title} = route.params;

 

 

 

 

Card에서 Detail로 이동할 때 Main으로부터 넘겨받은 content도 같이 넘겨준다.

 

[ Card.js ]

import React from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";

export default function Card({ content, navigation }) {    // 보내야 되니까 content 추가
  return (
    <TouchableOpacity
      style={styles.cardContainer}
      onPress={() => {
        navigation.navigate("Detail", content);           // navigation과 함께 content도 넘겨준다.
      }}
    >
      <View style={styles.card1}>
        <Text style={styles.cardName}>{content.name}</Text>
        <Text style={styles.cardRating}>평점: 5점</Text>
      </View>
      <Text style={styles.cardAddress} numberOfLines={1}>
        {content.address}
      </Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  cardContainer: {
    marginTop: 10,
    width: "95%",
    alignSelf: "center",
    borderBottomWidth: 1,
    borderBottomColor: "#eee",
    paddingBottom: 10,
  },
  card1: {
    flexDirection: "row",
    justifyContent: "space-between",
  },
  cardName: {
    fontSize: 20,
    fontWeight: "bold",
    marginTop: 10,
    marginBottom: 10,
  },
  cardRating: {
    fontSize: 15,
    marginTop: 10,
    marginBottom: 10,
  },
  cardAddress: {
    fontSize: 15,
  },
});

 

 

 

[ Detail.js ]

import React, { useState, useEffect } from "react";
import MapView from "react-native-maps";
import {
  StyleSheet,
  Text,
  View,
  ScrollView,
  TouchableOpacity,
} from "react-native";

export default function Detail({ navigation, route }) {    // navigation하고 route를 받는다.
  const [center, setCenter] = useState({});

  useEffect(() => {
    navigation.setOptions({                               // 받은 navigation의 setOptions로
      title: route.params.name,                           // route.params.name을 꺼낸다.
    });
    setCenter(route.params);               // setCenter함수를 사용해서 center의 값을 route.params로 지정한다.
  }, []);

  return (
    <View style={styles.container}>
      <View style={styles.localView}>
        <Text style={styles.localSi}>대전광역시</Text>
        <Text style={styles.localGu}>유성구</Text>
      </View>
      <View style={styles.textView}>
        <View style={styles.nameRateView}>
          <Text style={styles.name}>{center.name}</Text>    // 위의 center에서 name을 꺼내 보여준다.
          <Text style={styles.rating}>별점: 5점</Text>
        </View>
        <View style={styles.addressView}>
          <Text style={styles.address} numberOfLines={1}>
            {center.address}
          </Text>
        </View>
      </View>
      <View style={styles.mapView}>
        <Text>지도</Text>
        <MapView style={styles.map} />
      </View>
      <View style={styles.buttonView}>
        <TouchableOpacity style={styles.button}>
          <Text style={styles.buttonText} onPress={() => popup()}>
            찜하기
          </Text>
        </TouchableOpacity>
        <TouchableOpacity style={styles.button}>
          <Text style={styles.buttonText}>공유하기</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginLeft: 10,
    marginRight: 10,
    backgroundColor: "#CBE7F5",
  },
  localView: {
    width: "100%",
    flexDirection: "row",
    paddingBottom: 10,
    marginTop: 50,
    backgroundColor: "#CBE7F5",
  },
  localSi: {
    fontWeight: "bold",
    fontSize: 20,
    alignSelf: "flex-start",
  },
  localGu: {
    fontWeight: "bold",
    fontSize: 20,
    alignSelf: "flex-start",
    marginLeft: 10,
  },
  textView: {
    flex: 1,
    paddingTop: 20,
    backgroundColor: "orange",
  },
  nameRateView: {
    flexDirection: "row",
    justifyContent: "space-between",
  },
  name: {
    fontSize: 20,
    marginTop: 10,
    fontWeight: "bold",
    backgroundColor: "red",
  },
  rating: {
    fontSize: 20,
    marginTop: 10,
    fontWeight: "bold",
    backgroundColor: "lightgray",
  },
  addressView: {},
  address: {
    fontSize: 20,
  },
  mapView: {
    flex: 3,
    backgroundColor: "green",
  },
  buttonView: {
    flex: 1,
    flexDirection: "row",
    justifyContent: "space-between",
    marginLeft: 50,
    marginRight: 50,
    backgroundColor: "pink",
  },
  button: {
    width: 120,
    height: 50,
    marginTop: 20,
    padding: 10,
    borderWidth: 1,
    borderColor: "deeppink",
    borderRadius: 7,
  },
  buttonText: {
    color: "#fff",
    textAlign: "center",
    fontSize: 18,
  },
});

 

 

 

 

 

 

 

 

[결과]

SOS 아동복지센터명과 주소가 잘 나오는 것을 확인할 수 있다.

 

 

 

 

 

디테일까지 연결해놨으니 이제 인트로 페이지를 구현해 볼 생각이다. 

지금은 대한민국 전체 시도를 카테고리로 보여주고 있어서 제주도의 언어치료센터를 찾으려면 끝까지 스크롤을 해야하는 불편함이 있다.

그래서 앱을 실행하자마자 인트로 페이지를 보여주고, 본인이 사는 곳의 지역을 선택하도록 하면 조금 더 깔끔한 UI를 만들 수 있을 것 같다.

 

생각하고 있는 건 유저의 현재 위치를 파악해서 근처에 있는 언어치료센터를 보여주는 것인데

아직 쌩초보라 갈길이 멀기 때문에...ㅠㅠㅠㅠ 

 

내가 할 수 있는 범위보다 조금 더 발전된 기능을 추가하다 보면 목표까지 갈 수 있으리라 생각한다.

화이팅!

 

 

 

반응형

댓글