<스파르타 App개발> 3주차 개발일지

드디어 3주차 개발일지를 작성한다. 

 

이번주에는..

React Native의 모체가 되는 React가 어떤 원리로 돌아가는지

앱 내부의 페이지의 이동을 어떻게 하는지

이동할 때 페이지에서 페이지로 어떻게 데이터 전달을 하는지

 

등 굉장히 Essential한 내용이 많았고 강의만 한번 들어서는 다 익혀지지 않았다. 

 

저번 글에서 헷갈리던 문법들에 대해 정리했으니 개발일지를 작성하면서 다시 한 번 정리해보도록 하자.

 

 

 

1. React의 구조

React건 React native 건 결국 뭔가 웹이나 앱을 만드는 도구일 뿐이다. 

이러한 도구가 어떠한 방법으로 웹/앱을 구성하는지를 아는 것은 중요할 수밖에 없다. 

 

React의 특징으로 강의에서 강조하는 네 가지는 다음과 같다. 

  • Component
  • State
  • Props
  • useEffect

여기에 추가로 useState에 대해서도 정리해보자.

 

강의에서 만들고 있는 꿀팁 어플리케이션의 첫 페이지를 떠올려보자.

Component란 앱을 구성하는 모든 요소들을 뜻한다. 

이 페이지 또한 하나의 컴포넌트이며, 페이지를 구성하는 버튼들, Card들도 각각 모두 component이다. 

 

하나의 컴포넌트를 들여다 볼까?

자 이 하나의 Card component에는 여러 내용들이 적혀 있다. 

이 내용들은 이 컴포넌트가 가진 data이며, 이를 React에서는 State라고 한다. 

 

이러한 컴포넌트의 data들은 항상 변하지 않을까? 그렇지 않다. 

실시간으로 데이터가 바뀌거나, 페이지를 이동하거나 하면 새로운 데이터로 컴포넌트가 그려져야 하는데

이렇게 전달되는 data Props (properties) 라고 한다. 

 

React 공식문서에 나와 있는 문장이 이해를 도와주기에 남겨본다. 

 

props는 (함수 매개변수처럼) 컴포넌트에 전달되는 반면 state는 (함수 내에 선언된 변수처럼) 컴포넌트 안에서 관리됩니다.

 

 

 

useState와 useEfffect

1) useState

useState의 사용은 다음과 같다. 

해당 컴포넌트에서 사용할 state와

state를 변경할 때 사용하는 setState 함수를 정의하게 되며

이 때 초기값을 함께 설정해주는 형태이다. 

 

왜 let으로 변수를 설정하고, 조건문에 따라 변수를 변경하는 게 아니라

굳이 useState를 이용할까??

 

만약 let 과 같은 방법을 이용해서 state를 선언하고

setState가 아닌 단순히 변수 값을 바꿔준다고 생각해보면

개발자는 이 변수가 바뀌어 있음을 알 수 있지만,

사용자는 화면을 새로 고치기 전까지는 이를 알 수 없다.  

 

리액트가 이러한 상태 변화를 감지하여 화면 렌더링을 새로 해주기 위해 useState, useEffect를 사용한다고 이해하고 넘어가자. 

 

 

2) useEffect

useEffect는 화면을 감시하고 있다가 뭔가 변화가 나타나면 실행되는 것이라고 생각하자.

다음과 같이 사용한다.

useEffect(()=>{}, []);

// 예를 들면
useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

 

함수 자체의 파라미터는 없으나 

함수 실행부에는 화면 렌더링이 끝난 후 실행할 함수의 내용이,

뒤의 [  ] array 부분에는 의존성 배열 (dependent array)가 들어가게 되는데,

  • 의존성 배열에 값을 입력하면, 배열의 값이 변경되는 경우에만 실행이 된다.
  • 빈 배열을 주는경우, 첫 렌더링에만 실행하겠다는 의미
  • 의존성 배열을 주지 않는 경우, 컴포넌트가 렌더링 할 때마다 실행이 된다.

 

 

2. Component 화

지난 주차에서 다음과 같은 MainPage를 만들 때

하단부 카드에 들어가는 여러 데이터들을 다음과 같이 map을 이용하여 구성하였다.

<View style={styles.cardContainer}>
{/* 하나의 카드 영역을 나타내는 View */}
{ 
  tip.map((content,i)=>{
    return (<View style={styles.card} key={i}>
      <Image style={styles.cardImage} source={{uri:content.image}}/>
      <View style={styles.cardText}>
        <Text style={styles.cardTitle} numberOfLines={1}>{content.title}</Text>
        <Text style={styles.cardDesc} numberOfLines={3}>{content.desc}</Text>
        <Text style={styles.cardDate}>{content.date}</Text>
      </View>
    </View>)
  })
 }

</View>

이러한 카드 영역을 MainPage 내부에 둬도 나쁠건 없지만,

해당 카드를 다른 프로젝트에도 쓰기 편하도록 분리시킬 때 필요한 것이 바로 컴포넌트화 이다. 

 

프로젝트에 components라는 폴더를 생성하고, 그 안에 다음과 같은 Card.js 라는 파일을 생성한다. 

이 Card component가 잘 동작하려면 어떻게 해야 할까?

 

상식적인 순서로 생각해보자.

 

  1. MainPage.js에서 Card.js 파일을 import한다.
  2. import한 component를 렌더링하려면 return( ) 내에 들어가야하며,
  3. return( ) 내부에 들어간다는 것은 JSX 문법을 따른다는 것이며, import된 것을 JSX로 사용하려면 <Card>와 같은 tag로 사용해야 한다. 
  4. Card.js는 카드 모양을 만드는 일종의 form이기 때문에 MainPage에서 Card로 data를 넘겨줘야 한다. 

이를 코드로 표현한다면 어떻게 될까?

 

먼저 MainPage.js를 보면

이렇게 표현할 수 있다.

 

Card tag에 "데이터를 넘겨주는 부분"을 보면 속성값인 content에 { content } 변수를 넣어준 것을 볼 수 있다.

(부모 컴포넌트에서 자식 컴포넌트로 데이터를 넘겨주는 것을 props라고 했다는 것을 기억하자!!!!)

 

 

Card.js로 넘어가보자.

 

Card.js 는 호출을 받은 부모 컴포넌트로부터 content라는 key를 갇는 props를 전달 받아야 한다. 

즉 function Card는 { content } 라는 변수를 구조 분해 할당 받아 가지고 있음을 볼 수 있다. 

 

*** 왜 function Card(content) {   } 가 아니라  function Card({content}) {   } 인지 잘 생각해 보아야 한다.

전자content라는 parameter를 변수로 받는 것이고

후자는 마치 파이썬의 **kwarg처럼, 딕셔너리를 받는데 그 딕셔너리의 content라는 key의 value를 변수로 받는 것이다. 

 

정리하면, 

MainPage.js에서 Card를 import하여 content props을 넘겨주어 호출하면

Card.js에서 content key 값에 해당하는 value를 취하여 function Card({conent}) 를 실행하고 이 결과를 return하여

export하는 순서이다. 

 

 

이렇게 여러 component들을 component화 하여 따로 보관하면 편리하게 프로젝트를 관리할 수 있다. 

 

 

 

3. 초기 렌더링의 문제

앱을 실행하면 다음과 같은 순서로 진행된다. 

 

  1. 화면이 그려진다. 
  2. useEffect가 실행되며 컴포넌트들의 변화를 감지하여 변화가 있으면 setState로 state를 변경한다.
  3. 바뀐 state로 화면을 다시 그린다. 

1번에서 문제가 생기는데, 

'컴포넌트화'를 해둔 컴포넌트들은 쉽게 말해 비어있는 Form과 같다. 

 

useEffect가 실행되어 state에 값이 담겨야 그릴 재료가 있는데 맨 처음에는 이게 없다. 

 

이를 보통 두가지를 통해 해결할 수 있는데

임의로 초기 데이터를 넣어두거나, Loading 화면을 이용하는 방법이 있다. 

 

초기 데이터를 넣어두는 방법은 상황에 맞지 않는 데이터를 보여줄 수 있으므로 보통 Loading 화면을 많이 이용한다. 

 

 

Logic은 다음과 같다. 

ready 상태변수를 정의하고 초기값을 true로 설정한다.

삼항 연산자를 이용하여 초기 실행시 : ready = true이므로 <Loading/>으로 loading page가 호출된다. 

 

이후 useEffect 함수가 실행되며 1초 후 setReady를 이용하여 ready를 false로 바꾸고 화면을 다시 렌더링한다. 

 

ready = false로 변경되었으므로 위의 삼항 연산자에서 뒤의 원래 MainPage 컴포넌트들이 그려진다. 

 

 

 

4. 카테고리 기능

위의 내용들의 응용에 불과하여 패스한다. 

 

 

5. 상태바

react-native에서도 StatusBar를 제공하기는 하나, 강의에서 제공하는 방법을 소개한다. 

expo에서 제공하는 expo-status-bar를 설치하여 사용한다. 

 

expo install expo-status-bar

 

이후 이를 사용할 컴포넌트에서 다음과 같이 임포트하여 tag로 사용한다. 크게 어려울 것 없다. 

자세한 사용 방법들은 공식 문서를 참고하면 되고, expo에서도 여러 도구들을 제공한다는 것이 중요하다. 

 

 

 

6. 네비게이터

이번 주의 가장 중요한 내용이라고 생각된다. 

화면 하나로만 이뤄진 앱은 거의 없고, 여러 페이지들을 왔다 갔다 하면서 실행되므로 네비게이터를 이해하는 것은 매우 중요하며

 

react-navigation 이라는 library를 사용한다. 

 

react-navigation의 여러 형태와 타입들은 공식문서를 참조하고, 본 강의에서는 Stack navigation이라는 형태의 navigator를 공부해 본다. 

 

다음과 같이 설치한다. 

expo install react-native-screens react-native-safe-area-context react-native-gesture-handler

 

1) Stack Navigator

 

이 중 강의에서 사용할 Stack navigation은 다음과 같은 형태를 갖는다.

설치 코드는 다음과 같다. 

 

사용 방법을 살펴보자. 

프로젝트에 navigation 폴더를 만들고 StackNavigation.js 파일을 생성한다. 

StackNavigatior.js의 코드는 다음과 같다. 

 

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

//페이지로 만든 컴포넌트들을 불러옵니다
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';

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

 

필요한 준비 사항은 

  1. 설치한 stacknavigation library에서 createStackNavigator를 임포트 한다.
  2. 페이지화 할 컴포넌트 들을 가져 온다.
  3. createStackNavigator의 객체인 Stack을 선언한다.
const StackNavigator = () =>{
    return (
        //컴포넌트들을 페이지처럼 여기게끔 해주는 기능을 하는 네비게이터 태그를 선언합니다.
        //위에서 선언한 const Stack = createStackNavigator(); Stack 변수에 들어있는 태그를 꺼내 사용합니다.
        //Stack.Navigator 태그 내부엔 페이지(화면)를 스타일링 할 수 있는 다양한 옵션들이 담겨 있습니다.
        <Stack.Navigator
            screenOptions={{
                headerStyle: {
                    backgroundColor: "black",
                    borderBottomColor: "black",
                    shadowColor: "black",
                    height:100
                },
                headerTintColor: "#FFFFFF",
                headerBackTitleVisible: false
            }}
        >
            {/* 컴포넌트를 페이지로 만들어주는 엘리먼트에 끼워 넣습니다. 이 자체로 이제 페이지 기능을 합니다*/}
            <Stack.Screen name="MainPage" component={MainPage}/>
            <Stack.Screen name="DetailPage" component={DetailPage}/>
        </Stack.Navigator>
    )
}

export default StackNavigator;

 

그 다음으로는 StackNavigator 함수 파트가 나온다. 

해당 함수에는 아까 만든 Stack 객체를 이용하여 태그를 생성하는데, 

위의 형식을 가져다 쓰면 되겠다. 

 

참고로 <Stack.Screen>을 이용하여 정의해 주어야만 네비게이터를 이용하여 이동할 수 있으며

 

앱의 최상위 컴포넌트인 App.js에 네비게이션 기능을 달아야 한다. 

이때 기존에 하나씩 불러오던 MainPage, DetailPage 등은 더이상 임포트할 필요가 없어진다. 

 

대신 NavigationContainer와 StackNavigator를 return해주면

Navigator를 이용하여 페이지 간 이동을 할 수 있게 된다. 

 

 

2) 페이지 간 이동

Stack.Screen으로 페이지화 된 컴포넌트 들은 모두 

  • navigation
  • route

라는 두개의 속성(dictionary)을 사용할 수 있게 된다. 

페이지들과 이 속성들의 관계는 다음과 같다.

 

 

7. Share, Linking

우리가 평소에 보는 앱들에서 많이 제공하는 기능 또한 react-native에서 제공한다.

 

1) Share - 공유 기능

이렇게 특정 데이터를 message에 담는 함수를 만들 수 있고

 

공유 기능을 갖는 버튼이나 TouchableOpacity 등에 onPress로 함수를 연결해주면 된다. 

 

해당 버튼을 누른 모습이다. 

 

 

2) Linking - 외부 링크 기능

간단하게 사용할 수 있다. 

 

 


여기까지 3주차 강의 내용의 정리를 마쳤다. 

 

지난주까지 앱의 모양을 잡는데에 집중했다면,

이번주에는 앱이 정말 앱처럼 기능할 수 있도록 react, react-native, expo 등에서 제공하는 도구들을 어떻게 사용하는지 알아보았고,

가장 중요한 react의 작동 방식에 대해 맛을 보았다.

 

 

아직 DB, 서버와의 연결은 모르지만 그래도 간단한 앱을 만들 수 있게 되어 기쁘다. 

댓글()