어서와, 개발은 처음이지?

5.React Native 레이아웃 디자인 - 3부 커스텀 버튼 본문

React/React Native

5.React Native 레이아웃 디자인 - 3부 커스텀 버튼

오지고지리고알파고포켓몬고 2018. 7. 30. 13:50
반응형


이 글은

5.React Native 레이아웃 디자인 - 1부 flex와 width, height

5.React Native 레이아웃 디자인 - 2부 배치(Flex Direction)와 정렬(justify content, align items)

5.React Native 레이아웃 디자인 - 3부 커스텀 버튼(현재글)

5.React Native 레이아웃 디자인 - 4부 이미지 컴포넌트와 UI 마무리로 구성되어있습니다.



정신없이 휘몰아치다보면 목적지가 어딘지 깜빡할 때가 있죠. :)

우리가 모방하려고 했던 화면의 모습이 기억 나시나요?


혹시 기억이 안나실까봐 다시 가져왔습니다.


자, 우리는 지난시간동안 레이아웃 잡는데 필요한 여러가지 속성을 알아봤습니다.

이번에는 5-1번 글에서 작성한 레이아웃을 토대로 화면을 구성할텐데요.


그 전에 한가지 문제가 있습니다.

안드로이드(좌) 아이폰(우)


기본 Button 컴포넌트를 사용하면 각 플랫폼에 따라 네이티브 버튼이 다르기 때문에 UI 조절이 제한적이고, 일관성이 없게 됩니다.

우리는 이를 해결하기 위해 TouchableOpacity라는 컴포넌트를 사용하여 커스텀 버튼을 제작하겠습니다.



1.TouchableOpacity


TouchableOpacity 컴포넌트는 '터치 이벤트(onPress 등)를 사용할 수 있는 VIew'라고 생각하시면 됩니다.

간단한 예를 들어보겠습니다.

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

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity style={{backgroundColor:'gray'}}>
          <Text>회원가입</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'white',
  },
});

회원가입 누르기 전(좌) 누르는 중(우)


위 화면은 TouchableOpacity 컴포넌트에 Text 컴포넌트를 넣어서 버튼처럼 쓸 수 있도록 만든 것 입니다.

Opacity는 투명도를 뜻하는데, 버튼을 눌렀을 때 오른쪽 사진처럼 투명도가 들어가서 이 버튼에 이벤트가 일어나고 있다는 것을 볼 수 있습니다.


이제 스타일 속성을 사용하여 더욱 그럴듯한 버튼을 만들어보겠습니다.

우선 CustomButton.js라는 파일을 생성해줍니다.

├── App.js
├── android/
├── app.json
├── CustomButton.js
├── index.js
├── ios/
├── node_modules/
├── package.json
└── yarn.lock

그리고 아래와 같이 입력합니다.

// CustomButton.js
import React, { Component } from 'react';
import {
  TouchableOpacity,
  Text,
  StyleSheet,
} from 'react-native';

export default class CustomButton extends Component{
  constructor(props){
    super(props);
  }

  render(){
    return (
      <TouchableOpacity style={styles.button}>
        <Text style={styles.title}>타이틀</Text>
      </TouchableOpacity>
    )
  }
}

const styles = StyleSheet.create({
  button: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'red',
  },
  title: {
    fontSize: 15,
  },
});


그리고 App.js로 가서 CustomButton을 사용해봅니다.

import CustomButton from './CustomButton'; 으로 컴포넌트를 가져올 수 있습니다.


// App.js
import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';
import CustomButton from './CustomButton';

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.header}><Text>header</Text></View>
        <View style={styles.title}><Text>title</Text></View>
        <View style={styles.content}><Text>content</Text></View>
        <View style={styles.footer}>
          <CustomButton />
          <CustomButton />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
    backgroundColor: 'white',
  },
  header: {
    width:'100%',
    height:'9%',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ff9a9a',
  },
  title: {
    width:'100%',
    height:'18%',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#9aa9ff',
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#d6ca1a',
  },
  footer: {
    width:'100%',
    height:'20%',
    backgroundColor: '#1ad657',
  },
});

현재까지 진행한 내용을 실행하면 위와 같습니다.


저는 반응형 레이아웃을 좋아하기 때문에 Flex나 %를 주로 사용하는데, 일관된 공백을 주기 위해 App.js의 Container 속성에 padding을 줬습니다.

또한 부모 컴포넌트에 가로 정렬(align items) 설정이 되어있다면 양 끝으로 늘어나지 않기 때문에 footer의 정렬 속성을 삭제했습니다.


위의 header가 신경쓰이신다면 position:absolute를 사용하셔도 됩니다.

하지만 Navigator 모듈을 사용하면 Navbar 같은 경우는 모듈이 처리해주기 때문에 넘어가도록 하겠습니다.


그럼 이제 다음 단계로 넘어가서 CustomButton에서 props를 사용하여 커스터마이징이 가능하도록 하겠습니다.



2.props를 통한 배경색, 버튼이름 설정


이전에 stateprops를 통해 컴포넌트에서 값을 전달받는 내용을 배운 적 있습니다.

상위 컴포넌트에서 버튼의 이름과 색깔을 설정할 수 있도록 CustomButton.js를 아래와 같이 수정합니다.

// CustomButton
import React, { Component } from 'react';
import {
  TouchableOpacity,
  Text,
  StyleSheet,
} from 'react-native';

export default class CustomButton extends Component{
  static defaultProps = {
    title: 'untitled',
    buttonColor: '#000',
    titleColor: '#fff',
    onPress: () => null,
  }

  constructor(props){
    super(props);
  }

  render(){
    return (
      <TouchableOpacity style={[
        styles.button,
        {backgroundColor: this.props.buttonColor}
      ]}
      onPress={this.props.onPress}>
        <Text style={[
          styles.title,
          {color: this.props.titleColor}
        ]}>{this.props.title}</Text>
      </TouchableOpacity>
    )
  }
}

const styles = StyleSheet.create({
  button: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    marginBottom: 10,
    borderRadius: 5,
  },
  title: {
    fontSize: 15,
  },
});


CustomButton에서 버튼 사이의 여백을 위해 marginBottm을 넣었고, borderRadius를 사용하여 둥근 모서리를 설정했습니다.

다음으로 상위 컴포넌트인 App.js에서 CustomButton 컴포넌트에 속성을 넣어줍니다.


// ... 생략
render() {
    return (
      <View style={styles.container}>
        <View style={styles.header}><Text>header</Text></View>
        <View style={styles.title}><Text>title</Text></View>
        <View style={styles.content}><Text>content</Text></View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#023e71'}
            title={'회원가입'}
            onPress={() => alert('회원가입 버튼')}/>
          <CustomButton />
        </View>
      </View>
    );
  }
// ... 생략


위의 CustomButton에는 buttonColor와 title, onPress를 넣어줬고 아래 CustomButton에는 아무것도 넣지 않았습니다.

화면에는 어떻게 나왔을까요?


수정된 App, CustomButton(좌)과 회원가입 눌렀을때 함수 실행(우)


3.defaultProps


아무 속성도 입력하지 않은 CustomButton은 검은색 버튼, 흰색 글씨에 untitled라는 버튼 이름이 새겨졌습니다.

어떻게된 일 일까요?


수정된 CustomButton.js 코드를 유심히 보시면 static defaultProps = {}라는 것을 볼 수 있습니다.

defaultProps는 부모 컴포넌트에서 속성을 입력하지 않았을 때 기본 값으로 동작하여, 컴포넌트의 초기값 등을 담아서 사용합니다.


때문에 부모 컴포넌트에서 아무것도 넘겨주지 않아도 {backgroundColor: this.props.buttonColor}, {color: this.props.titleColor}, {this.props.title}

이런 값들을 사용하는데 문제가 없습니다.


여기에 부가적으로 설명하자면 style은 배열을 사용해서 여러가지 속성을 동시에 넣을 수 있으며, 중복되는 속성이 있을 경우 가장 마지막에 들어온 속성이 발현됩니다.



4.요약


* TouchableOpacity 컴포넌트를 이용하여 이벤트 핸들링이 가능한 커스텀 컴포넌트를 만들 수 있다.

* 컴포넌트에서 props를 참조하는 값이 있다면 defaultProps를 정의하여 예외 상황이 발생하지 않도록 하자



5.마무리


이미지와 세부 레이아웃을 수정까지 작성하면 내용이 너무 길어질 것 같아서 한번 끊고 가겠습니다.

다음 글에서는 세부 레이아웃을 구현하는 것으로 레이아웃 디자인에 대한 내용을 마치도록 하겠습니다.


Comming Soon!

Comments