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

5.React Native 레이아웃 디자인 - 4부 이미지 컴포넌트와 UI 마무리 본문

React/React Native

5.React Native 레이아웃 디자인 - 4부 이미지 컴포넌트와 UI 마무리

오지고지리고알파고포켓몬고 2018. 7. 30. 18:53


이 글은

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 마무리(현재글)로 구성되어있습니다.


바로 이어서 시작하겠습니다.



1.이미지 컴포넌트


이미지 컴포넌트에 이미지를 지정해주기 위해서 source라는 속성을 명시해줘야합니다.

source는 {uri : '이미지 주소'}로 외부 주소를 통해 가져오거나 require('로컬경로')를 통해 내부의 이미지를 사용할 수 있습니다.


uri와 require를 사용하는 경우를 예로 들어보겠습니다.

먼저 uri입니다.

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

type Props = {};
export default class App extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
          <Image
            style={{height:'100%',width:'100%'}}
            source={{uri:'https://t1.daumcdn.net/cfile/tistory/9942214E5B5E76930B'}}/>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
    backgroundColor: 'white',
  },
});


대상 서버의 상태에 따라서 외부 이미지로 사용할 수 없는 경우도 있으니 참고하세요.

사용된 이미지는 제 블로그의 리액트 네이티브 글 도입부에서 사용하는 이미지로 uri 링크를 타시면 볼 수 있습니다.

source={{uri : '주소'}} 형태로 사용되는데, 주의해야 할 점은 source={uri : '주소'}가 아닌 source={{uri : '주소'}} 입니다.


이전에 언급한 적 있지만 {}는 JSX에서 자바스크립트 변수(객체)를 사용할 때 사용하는 문법입니다.

const img_uri = {uri : '주소'};

<Image source={  img_uri  } /> 이렇게 떼어본다면 의미를 파악하기 쉬울 것 같습니다.



다음은 require입니다.

내부 경로에서 이미지를 가져오기 위해 아무 이미지(jpeg, jpg, png 등)를 다음과 같이 위치하게 합니다.

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


그리고 이미지 컴포넌트의 source 부분을 아래와 같이 변경하고 앱을 새로고침 해봅니다.


render() {
    return (
      <View style={styles.container}>
          <Image
            style={{height:'100%',width:'100%'}}
            source={require('./img.jpeg')}/>
      </View>
    );
  }

이미지가 바뀐것을 확인하셨나요?

만약 빨간창과 함께 에러가 난다면 이미지의 경로와 파일명을 잘 확인하셔야합니다.

파일 이름의 대소문자에 주의하세요! 특히 확장자 대소문자도 맞춰주셔야 합니다.



2.resizeMode


혹시 이미지가 극단적으로 확대되는 모습을 보셨나요? 직접 이미지를 입혀보셨다면 체감 하셨으리라 생각되는데,

원래 이미지에 비해 Image 컴포넌트에 적용된 이미지는 기괴하게 확대된 것을 볼 수 있습니다.

이것은 Image 컴포넌트가 컴포넌트 크기에 맞게 자동으로 확대시키기 때문입니다.


컴포넌트 크기에 맞게 이미지를 조절하는 스타일을 resizeMode라고 하며 여러가지 속성을 가지고 있습니다.

이 글에서는 대표적인 속성인 covercontain을 알아보겠습니다.


- cover


cover는 Image 컴포넌트의 기본 속성입니다.

이미지의 가로 세로 중 좁은 부분이 부모 컴포넌트의 100%를 차지할 때까지 이미지를 늘리는 것 입니다.

예를들어, 가로가 길고 세로가 짧은 이미지는 세로 영역이 부모 컴포넌트를 차지할 때 까지 늘어나게 됩니다.


부모 컴포넌트 크기를 꽉 채워야 할 때 사용됩니다.


- contain


contain은 제가(아마 대부분의 사람들이) 자주 사용하는 속성입니다.

cover와 비슷하지만 가로 세로 중 넓은 부분이 100%를 차지할 때 까지만 이미지를 늘리는 것 입니다.

예를들어, 가로가 길고 세로가 짧은 이미지는 가로 영역이 부모 컴포넌트를 차지할 때 까지만 늘어나게 됩니다.


본 이미지의 전체를 보여줘야 할 때 사용됩니다.



쉬운 이해를 위해 둘을 비교해보겠습니다.


render() {
    return (
      <View style={styles.container}>
          <Image
            style={{height:'100%',width:'100%',resizeMode:'cover'}}
            source={require('./img.jpeg')}/>
      </View>
    );
  }
render() {
    return (
      <View style={styles.container}>
          <Image
            style={{height:'100%',width:'100%',resizeMode:'contain'}}
            source={require('./img.jpeg')}/>
      </View>
    );
  }

cover(좌) contain(우)


차이가 느껴지시나요?


cover는 말 그대로 부모 컴포넌트의 전체를 cover 하도록 늘어납니다.

contain은 부모 컴포넌트와 맞닥뜨리면 늘어나지 않습니다.


이렇게보니 cover는 가로, 세로 두 면이 닿을때까지 늘어나고 contain은 가로, 세로 중 한 면이 닿을때까지 늘어난다 라고 정리할 수 있겠네요.


보통 배경, 혹은 작게 나타내는 프로필 사진 같은 경우는 cover를 많이 사용합니다.

다른 크기의 이미지를 같은 영역에 표시할 수 있기 때문입니다.

contain은 사진첩에서 사진을 띄운다던가 고정된 화면의 이미지 영역으로 사용될 수 있습니다.



3.레이아웃 구현


이제 저희가 같이 공부한 것들을 사용하여 화면을 구현하도록 하겠습니다.

import React, {Component} from 'react';
import {StyleSheet, Text, View, Image} 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} />
        <View style={styles.title}>
          <Text style={{fontSize:35,color:'white'}}>어서와,{'\n'}개발은 처음이지?</Text>
        </View>
        <View style={styles.content}>
          <Image
            style={{height:'100%',width:'100%',resizeMode:'contain'}}
            source={require('./img.jpg')}/>
        </View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#444'}
            title={'회원가입'}
            onPress={() => alert('회원가입 버튼')}/>
          <CustomButton
          buttonColor={'#023e73'}
          title={'로그인'}
          onPress={() => alert('로그인 버튼')}/>
        </View>
      </View>
    );
  }
}

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


처음부터 검은색 테마를 생각하고 이미지를 따왔더니 약간 부자연스러운 부분이 있었습니다.

헤이카카오에 비해 이미지에 여백이 없다보니 빡빡한 부분이 있었는데, content에 paddingBottom을 설정하여 여유를 줬습니다.


그리고 헤더의 영역도 5%로 줄였습니다.

회원가입과 로그인을 구별하기 위해 버튼에 약간의 색감을 넣어줬는데, 얼추 구색을 갖췄으니 이제 레이아웃 영역의 배경색을 통일해보겠습니다.



디테일은 약간 차이가 있겠지만 어느정도 구색이 갖춰졌네요.

무엇보다 좋은 것은 우리의 앱은 디바이스 크기에 관계없이 거의 동일한 UI를 이룰 것 입니다.


그리고 또 한번 느끼게 됩니다.


흐음~ 덕후냄새~


그래도 윾을 포기하는건 쉽지않군요.

아무튼 이 레이아웃을 재사용하면 아래와 같은 화면을 구성할 수 있습니다.


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

type Props = {};
export default class Login extends Component<Props> {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.header} />
        <View style={styles.title}>
          <Text style={{fontSize:35,paddingBottom:20}}>로그인</Text>
          <View style={{width:"100%",borderBottomWidth:0.5,borderColor:'#444'}} />
        </View>
        <View style={styles.content}>
          <View style={{flexDirection:'row',justifyContent:'space-between',alignItems:'center',paddingBottom:10}}>
            <Text style={{fontSize:15}}>아이디</Text>
            <TextInput style={{borderColor: '#aaa', width:'70%', height:35, borderWidth: 1, borderRadius: 5, padding:5}}/>
          </View>
          <View style={{flexDirection:'row',justifyContent:'space-between',alignItems:'center',paddingBottom:10}}>
            <Text style={{fontSize:15}}>비밀번호</Text>
            <TextInput style={{borderColor: '#aaa', width:'70%', height:35, borderWidth: 1, borderRadius: 5, padding:5}}/>
          </View>
        </View>
        <View style={styles.footer}>
          <CustomButton
            buttonColor={'#444'}
            title={'취소'}
            onPress={() => alert('취소 버튼')}/>
          <CustomButton
          buttonColor={'#023e73'}
          title={'확인'}
          onPress={() => alert('확인 버튼')}/>
        </View>
      </View>
    );
  }
}

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

간단한 구성으로 이루어진 화면이지만 디자인 컨셉을 잘 잡으면 재사용이 용이해진다는 부분을 보여드리고 싶었습니다.

실제로는 더 많은 구성요소(사용자 인증, 중복검사, 개인정보 수집 동의 등)들이 필요할테고, 

이제 여러분은 저보다 훨씬 화려하고 멋진 화면을 구성하실 수 있습니다.


잠시 시간을 내어서 본인만의 페이지를 상상을 해보고(아주 간단한 것이라도 좋습니다) 직접 구현해보는 시간을 가지시길 바랍니다.



4.요약


* Image 컴포넌트는 source 속성에 {uri : '주소'}를 사용하여 외부 이미지를 끌어올 수 있고 require('로컬경로')를 사용하여 프로젝트 내에 있는 이미지를 사용할 수 있습니다.

* resizeMode로 cover과 contain이 자주 사용되며, cover는 부모 가로/세로 전체를, contain은 부모의 한 면을 차지하도록 이미지를 맞춥니다.

* 개인적으로 이미지의 실제 크기를 %로 조절하기 보다는 부모 View로 한번 감싸고 resizeMode 속성을 이용하는 것이 반응형으로 사용하기 용이합니다.



5. 마무리


이로써 4부에 해당하는 기초 레이아웃 디자인의 대장정을 마무리하게 되었습니다.

다음 글 부터는 이 세가지 페이지를 가지고 Navigator를 사용하여 화면을 이동하는 방법을 알아보겠습니다.


Navigator는 react-native-navigation v1.1을 사용하겠습니다. (v2에서 다양한 버그가 고쳐지는 중이지만 아직 stable하지 않다고 판단하여 v1.1을 사용하도록 하겠습니다.)


Comming Soon!

0 Comments
댓글쓰기 폼