본문 바로가기
프로그래밍/REACT-NATIVE

EXPO INSTAGRAM CLONE 만들기 (1)

by Lihano 2021. 8. 3.
반응형

서론

이번 프로젝트는 간단하게 인스타그램 클론을 만들어볼 생각이다.

코드에 대한 소스는 유튜브에서 찾았다.

 

인스타그램 클론에 대해 한국어로 써있는 포스트도 있었지만 아무래도 3년 전 자료다보니 좀 더 최신의 내용을 참조하고 싶었다.

그래서 영어지만 유튜브의 강의를 찾아서 한번 똑같이 따라해보기로 했다.

기술 스택은 내 전 프로젝트와 완전히 동일하다.

  • FIREBASE
  • EXPO
  • REDUX

그러다보니 아무래도 내 전 프로젝트와 겹치는 부분이 많았다.

그래서 여기에는 클론 강의를 따라하면서 새롭게 알게된 사실만 적으려고 한다.

 

내 블로그의 모든 포스팅은 이런 지식과 개념도 있다는 걸 잊지 않기 위해 나를 위한 참조용으로 만든 것이다.

혹시라도 이 글을 클릭한 사람들에겐 불친절한 글의 내용에 대해 사과하고 싶다.

 

expo start -c

expo를 다시 한 번 설명하자면, REACT NATIVE를 크로스 플랫폼으로 개발하기 위한 빌드 도구로써 NATIVE 모듈을 보다 쉽고 편하게 사용하고 빠르게 테스트해볼 수 있도록  해주는 XDE라고 설명되어 있다.

 

이건 말이 너무 어렵다.

크로스 플랫폼이란 다양한 플랫폼에서 사용할 수 있는 기능을 의미한다.

그러니까 여기서는 안드로이드와 iOS에서 돌아가게 한다는 의미일 것이다.

 

빌드 도구란 소스 코드에서 어플리케이션 생성을 자동화하기 위한 프로그램이다.

즉, 우리가 짠 코드를 실제로 실행가능한 형태로 변환시키는 것이다.

 

XDE란 어플리케이션의 모델링 프로세스와 개발 프로세스를 결합함으로써 프로젝트를 수월하게 하는 도구라고 한다.

솔직히 내겐 어려운 개념이다.

그냥 어플리케이션 개발 프로세스를 간략화하는 도구 정도로 이해해두자.

 

아무튼 expo start는 리액트에서 사용하던 npm start와 마찬가지로 프로젝트를 실행하는 기능을 한다.

expo start는 -c 옵션을 붙일 수 있는 데 이 옵션을 사용하면 캐시를 지우고 프로젝트를 실행한다.

 

즉, 잔여 캐시로 인한 오류가 없어지는 것이다.

유튜버가 이걸로 프로젝트를 시작하길래 신기해서 적어봤다.

 

Run in web browser

expo 프로젝트를 시작하면 어느 환경에서 앱을 실행시킬지를 결정할 수 있다.

전에는 갤럭시 폰과 연결하여 앱을 테스트해봤지만 이번에는 그러지 않을거다.

 

관심이 없어 거들떠도 안봤지만 Run in web browser도 상당히 유용한 테스트 방법이다.

브로우저의 개발자 툴을 이용하면 브라우저의 작동방식을 모바일처럼 변경시킬 수 있다.

심지어 화면의 크기도 자유자재로 바꿀 수 있다.

 

클릭도 할 수 있으니 굳이 갤럭시로 할 이유가 없다.

물론 안드로이드, iOS, 웹 브라우저 이 셋은 패키지에 따라 지원 여부가 갈릴 수도 있다.그러니 실제 개발을 할 때는 이런 마음가짐이 아니라 모든 환경을 고려하고 직접 테스트해봄이 바람직할 것이다.

 

ES7 REACT/REDUX/GRAPHQL/REACT-NATIVE Snippets

이건 VSC에서 사용되는 확장 플러그인이다.

상당히 유용한 기능인 것 같아서 언급하기로 했다.

 

이게 뭘하는거냐 하면 줄임말을 제공한다.

내가 적고 싶은 코드를 줄임말 몇개로 대체할 수 있다.

 

이건 import 할 때도 유용하고 export 할 때나 Proptypes를 작성할 때도 유용하다.

범용으로 사용되는 작업들의 부담을 줄여준다.

 

유튜브에선 이걸 리액트 컴포넌트 세팅에 사용한다.

리액트 컴포넌트라면 함수형 컴포넌트냐 클래스 컴포넌트냐에 따라 지정된 양식이 있다.

그 양식 또한 줄임말로 대체할 수 있다.

 

rce를 적으면 클래스형 컴포넌트를 위한 초기 세팅이 완료되고,

rcf를 적으면 함수형 컴포넌트를 위한 초기 세팅이 완료된다.

 

줄임말의 종류는 굉장히 많다.

그 정보는 공식 사이트를 직접 참조하도록 하자.

https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets 

 

ES7 React/Redux/GraphQL/React-Native snippets - Visual Studio Marketplace

Extension for Visual Studio Code - Simple extensions for React, Redux and Graphql in JS/TS with ES7 syntax

marketplace.visualstudio.com

 

stack navigation options

이번 클론 프로젝트에서도 기본 네비게이션으로 Stack을 사용한다.

영상에서 처음 안 사실은 options 중에 headerShown 속성은 상단 네비게이션 바를 가려준다는 것이다.

Stack 네비게이션은 리액트 네이티브 개발 중에 굉장히 많이 사용될 것 같으니까 이 기회에 options의 옵션들을 알아두기로 하자.

 

  • title -> 말 그대로 타이틀이다.
  • animationEnabled : 화면에서 전환 애니메이션을 사용하도록 설정해야 하는지 여부
  • gestureEnabled : 제스처를사용하여 이 화면을 해제할 수 있는지 여부
  • gestureResponseDistance : 터치 시작 거리를 화면 가장자리에서 수정하여 제스처를 인식하는 숫자 (웹 지원 X)
  • gestureVelocityImpact : 제스처의 속도 관련성을 결정하는 숫자 (웹 지원 X)

 

이동

  • push : 새 화면을 스택의 맨위로 밀고 스택으로 이동. (name : 스택에 push할 경로 이름 / params : 목적지 경로로 전달되는 props 이름)
  • pop : 스택에서 현재 화면을 팝업하고 이전 화면으로 다시 이동. count를 사용하여 팝업할 화면 수를 지정 가능.
  • popToTop : 첫번째 화면을 제외한 스택의 모든 화면을 팝업하고 해당 화면으로 이동

 

이벤트

  • transitionStart : 이 이벤트는 현재 화면에 대한 전환 애니메이션이 시작될 때 실행
  • transitionEnd : 이 이벤트는 현재 화면에 대한 전환 애니메이션이 종료될 때 실행
  • gestureStart : 이 이벤트는 현재 화면에 대해 스와이프 제스처가 시작되면 실행
  • gestureEnd : 이 이벤트는 현재 화면에 대한 문지름이 종료될 때 실행
  • gestureCancel 이 이벤트는 현재 화면의 스와이프 제스처가 취소될 때 실행, 예를 들어 제스처로 화면이 해제되지 않은 경우

 

Class Component state bind 이유

이건 알고가면 나쁠게 없을 것 같아서 기술하기로 했다.

 

만일 클래스 컴포넌트에서 버튼을 누르면 setState를 실행하는 코드를 짜려면 어떻게 해야하는가?

 

onClick() {
this.setState(()=>{{ done : true }})
}
...
<Button title="Button" onPress={onClick} />

최소한 이런 코드가 필요할 것이다.

하지만 위의 코드가 이대로만 실행시킨다면 오류를 일으킨다면 믿겠는가?

 

함수형 컴포넌트부터 시작한 나에겐 생소한 개념이지만,

button을 통해서 function이 실행되면,

그 안의 this가 뭘 의미하는지 javascript가 이해를 못한다고 한다.

 

그래서 그냥 최상위 window와 연결시켜버리니 setState도 실행되지 않는다고... 한다.

state는 해당 컴포넌트 내부에만 존재하니.

 

그래서 state를 bind 해주는 작업이 필요하다고 한다.

constructor(props) {
	super(props);
	this.state = {
		done : false,
	}
    this.onClick = this.onClick.bind(this);
}

이런 식으로 말이다.

물론 함수형 컴포넌트에는 알 필요 없는 개념이다.

 

combineReducers(reducers)

리덕스는 자바스크립트에서 변수를 관리하는 일반적인 방법이다.

하지만 앱이 점점 거대해지고 복잡해지면 이런 리덕스를 다루는 방법 또한 복잡해진다.

 

특히 리듀서들을 하나로만 관리하기 곤란해질 수가 있다.

그래서 리듀싱 함수들을 State의 독립된 부분들을 관리하는 함수들로 분리하고 싶어질 거다.

 

combineReducers 함수는 서로 다른 리듀싱 함수들을 값으로 가지는 객체를 받아서 createStore에 넘길 수 있는 하나의 리듀싱 함수로 바꿔준다.

 

생성된 리듀서는 내부의 모든 리듀서들을 호출하고 그 결과를 모아 하나의 State 객체로 바꿔준다.

State 객체의 형태는 reducers로 전달된 키들을 따른다.

 

import {combineReducers} from "redux";
import {user} from "../user";

const Reducers = combineReducers({userState:user})

export default Reducers;

user라는 리듀싱 함수를 combineReduces로 묶었다.

지금은 하나의 리듀싱 함수만 입력해서 의미는 없지만 단지 사용방법을 알려주기 위함이다.

 

const mapStateToProps = (store) => ({
	currentUser : store.userState.currentUser;
})

Main 컴포넌트에서 store의 값을 참조하고 싶다면 combineReducers를 통해 넘긴 key값을 따라야한다.

이런 식으로 store의 값에 접근할 수 있다.

 

react-navigation/bottom-tabs 사용

화면 하단에 있는 간단한 탭으로 여러 경로에 접근할 수 있는 패키지다.

메뉴 네비게이션의 기능을 하기 때문에 굉장히 편리하다.

 

특징은 느리게 초기화되며,

버튼이 클릭되기 전까지는 그 경로의 화면이 마운트 되지 않는다.

 

npm install @react-navigation/native
npm install @react-navigation/bottom-tabs

 

위의 과정을 통해 다운 받을 수 있다.

이 외의 의존성이 존재할 수 있지만 오류가 뜨면 설치해주면 그만이다.

 

const Tab = createBottomTabNavigator();

 

Tab을 생성해주는 게 우선이다.

createBottomTabNavigator을 통해 Tab을 생성해준다.

 

return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={HomeScreen} />
      <Tab.Screen name="Settings" component={SettingsScreen} />
    </Tab.Navigator>
  );

 

사용방법은 여타 Navigation과 거의 유사하다.

Stack Navigation을 사용해봤다면 사용법을 몰라 헤멜일은 없을 것이다.

 

initialRouteName 속성은 처음 렌더링되는 라우터를 설정한다.

backBehavior는 뒤로가기 버튼을 눌렸을 때 동작을 제어한다.

   - firstRoute : 첫번째 탭으로 돌아가기

   - initialRoute : 초기 탭으로 돌아가기

   - order : 이전 탭으로 돌아가기

   - history : 마지막으로 방문한 탭으로 돌아가기

   - none : 뒤로가기 버튼 허용 X

detachInactiveScreens 속성은 지금 화면에 비활성화된 스크린들을 메모리에서 분리해서 메모리를 절약해야하는지 여부를 결정한다.

scenContainerStyle 속성은 스크린 컨텐트를 둘러싼 컴포넌트의 스타일 객체다.

tabBar는 탭 바로 표시할 리액션 요소를 반환하는 함수다.

 

options에 관한 내용은 너무 많아서 일일이 설명하기 힘들다.

우선 내가 사용한 속성에 관해서만 설명하겠다.

tabBarIcon은 주어진 props를 적용한 컴포넌트를 반환한다.

<Tab.Screen
	name="Feed"
    component={FeedScreen}
    options={{
    	tabBarIcon: ({color, size}) => (
        	<MaterialCommunityIcons name="home" color={color} size={26} />
        )
    }}
/>

 

그리고 이벤트가 있다.

네비게이터는 특정 동작에 대한 이벤트를 내보낼 수 있다.

여기서 지원되는 이벤트는 다음과 같다.

 

tabPress - 이 이벤트는 사용자가 탭 모음에서 탭 버튼을 누를 때 실행된다.

다음과 같은 몇가지 작업을 한다.

  1. 탭이 포커스가 되어 있지 않은 경우 탭 프레스는 해당 탭의 포커스를 맞춘다.
  2. 탭이 이미 포커스를 맞춘 경우, 탭의 화면이 스크롤 보기를 렌더링하는 경우, 탭의 화면을 위쪽으로 스크롤하는데 사용할 수 있다.
  3. 탭이 이미 포커스를 맞춘 경우, 탭의 화면이 스택 탐색기를 렌더링하는 경우 popToTop 작업을 수행할 수 있다.

이 기본 동작을 방지하고 싶다면 event.preventDefault()를 호출하면 된다.

 

<Tab.Screen
	name="Addcontainer"
    component={EmptyScreen}
    listeners={(navigation) => {
    	tabPress : (event) => {
            event.preventDefault();
            navigation.nabvigate("Add");
        }
    }}
/>

 

tabLongPress - 이 이벤트는 사용자가 탭 모음에서 탭 버튼을 길게 누르면 실행된다.

 

 

마무리

사실 기능적으로 새롭게 배운 내용은 얼마 안된다.

클래스 컴포넌트도 잘 사용하지 않기에 위에 적은 내용도 별로 쓸모는 없다.

 

그래도 네비게이션의 새로운 종류를 배운다거나 하는 건 좋은 것 같다.

그리고 아직 초기 단계이니 새롭게 배우는 내용이 적을 수 밖에 없다.

 

완성된 클론 결과물은 내 전 프로젝트보단 기능적으로 우수한 모양이니 분명 새로 배울 것들도 많을 것이다.

그걸 전부 배운 뒤 내 결과물에 녹이고 싶다.

 

그럼 오늘은 이만🖐🖐🖐🖐🖐

반응형

댓글