스파르타코딩클럽 내일배움단 앱개발 종합반 2주차 개발일지

Bluewiz_YSH·2022년 5월 13일
0

1. 리액트 네이티브 & expo 준비,실행하기

강사님은 저번주에 말했던, 페이스북이 개발한 react native 기반으로 ios/android 모두 작업 가능한 expo를 이번 2주차때 본격적으로 다룬다고 말씀하셨다. 그리고 문법은 자바스크립트보다 더 간결한 문법을 쓰게 될것이며, JSX 문법을 주로 이용한다고 말씀하셨다. (다음 코드가 예시였다.)

//잠시 살펴보는 JSX  !!!

//이렇게 상단에서 사용할 엘리먼트를 react-native 라이브러리로부터 꺼내 사용합니다.
import { Text, View } from 'react-native';

<View>
  <Text>Hello, I am {props.name}!</Text>
</View>

화면의 구역을 잡을때는 < View > 태그를, 텍스트를 넣을때는 < Text > 태그를 이용한다고 설명하셨다.

일단, 그런 expo를 사용하기에 먼저 자바스크립트 개발 환경을 구축하는 node.js와 필요한 자바스크립트 앱 개발 도구들을 다음 npm을 설치해야한다고 하셨다.(Window 설치 링크,mac 설치 링크)

그런뒤 yarn도 설치 해야하는데 yarn은 npm보다 더 빠른 자바스크립트 매니저 패키지 툴이라고 하시면서 다음 코드를 cmd창에 입력후 엔터를 누르면 된다고 하셨다.

//도구를 가져와 설치하는 npm 의 설치 명령어 install과
//컴퓨터 어디서든 설치하고 있는 도구를 사용할 수 있게 해주는 -g 옵션 명령어
npm install -g yarn
//설치가 완료된다음
yarn -v

이제 기반 프로그램이 다 설치되었기에 본격적으로 expo 설치를 해야하는데 다음과 같은 코드를 또 cmd창에 입력후 엔터를 치면 된다고 하셨다.

npm install -g expo-cli
// 
이 명령어 한 줄은 이런 의미가 담겨 있습니다.

npm: 노드 패키지 매니저 명령을 실행하겠다
install: 설치하겠다
-g: 컴퓨터 전역적으로 설치하겠다 == 어디서든지 -g 다음에 오는 명령어를 사용할 수 있게끔!
expo-cli: 설치 할 패키지 이름

따라서 우린 이 명령어 한 줄로, 여러분 컴퓨터 어디서든지 Expo를 사용할 수 있게끔 패키지를 전역적으로 설치했습니다.
//

그 후, expo에 가입후 가입한 해당 계정을 로컬 컴퓨터에 세팅해줘야 한다고 하셔서 다음과 같이 진행했다.
(가입 주소 링크 들어가서 가입, 다음 cmd 창 열어서 밑 코드문 입력후 엔터)

expo login --username "Expo 사이트 가입당시 입력한 name"
...
expo 패스워드 입력란이 차례로 나오고, 차례대로 입력하면 로그인 성공!

그런뒤, 1주차에 설치했던 visual studio code 프로그램을 실행(설치 주소 링크)한 다음 앞으로 프로젝트를 저장할 폴더를 만들고 보기-터미널을 눌러서 터미널을 연 다음 다음 코드문을 작성한뒤 엔터를 눌렀다.

expo init sparta-myhoneytip-영어이름

//
expo는 Expo 명령어를 사용하겠다.
init은 Expo 앱을 생성하는 Expo 명령어!
sparta-myhoneytip-영어이름은 앱 이름!
(앱 이름은 자유롭게 가능! 영어만 가능!)
//

//
윈도우 노트북 사용자분들 중에 vscode 에디터 상에서 expo init 또는 expo start를 하니 권한 문제가 발생한다!

vscode를 다음 순서대로 실행시켜보세요!
• 1) VSCode를 실행하고 Ctrl + Shift + P 조합키를 입력합니다. (아마 모든 설정 검색 창)
• 2) "shell"이라고 입력합니다.
• 3) "Treminal: Select Default Shell"을 클릭합니다.
• 4) "Comand Prompt C:\Windows\System32\cmd.exe"를 클릭합니다.
• 5) VScode를 재실행 하면 제일 아래 "PS"로 시작하던 것이 없어졌을 것이고 이는 CMD로 바뀌었다는 의미가 됩니다.
//

그 뒤 터미널에서 어떤 앱을 만들어줄까? 라고 물어보는 내용이 나올텐데 "blank"라고 입력한 뒤 엔터를 치면 가장 기본 틀을 가진 expo 앱이 만들어진다. 그런 다음 밑의 코드를 터미널에 입력해 앱이 위치한 해당 폴더 안으로 이동해야한다고 하셨다.

cd sparta-myhoneytip-gun
// 
cd <폴더명> : change directory의 약자로 입력한 폴더명으로 이동하는 명령어입니다.
//

그럼 경로 마지막 부분이 sparta-myhoneytip-gun로 바뀐걸 확인할수 있고 이 상태에서 또 터미널에 "expo start"를 입력하면 expo 앱이 실행된다고 하셨다. (종료할때는 ctrl+c)

그 뒤 터미널에 d 키를 입력하면 다음 사진과 같이 웹 화면이 뜬다.

왼쪽편 영어 그대로 내용처럼 각각 버튼을 누르면 android 디바이스에서/에뮬레이터에서/ios 시뮬레이터에서 해당 앱 실행이 가능하고 이메일로 앱 실행 링크를 보낼수도 있으며 이 앱 프로젝트를 배포도 할수 있다.

그리고 마지막 왼편 아래 QR코드를 핸드폰 카메라로 인식하면 expo 클라이언트 앱(안드로이드용,ios용)이 켜지면서 해당 앱 화면을 볼 수 있다.

일단 expo 기본 설치 세팅은 여기까지가 끝이었고, 지금 설치된 expo 앱 내부 파일 구조는 다음사진을 보면서 함께 설명해주셨다.

  • assets : 앱이 동작되고 기본적으로 서비스 되는데 있어서 필요한 이미지 및 아이콘 파일들 모음

  • node_modules : 리액트 네이티브 및 expo로 앱 만들시 설치하게 되는 라이브러리 모음들

  • app.js : 앱이 시작되는 출발점. 웹의 index.html, main.html과 같음. 앱의 시작/준비화면이라고 보면 됨.

  • app.json : 앱의 이름, 앱의 출시 버전, 앱이 휴대폰에 설치될때 보여질 아이콘, 앱이 켜질때 보여지는 스플래시 스크린 화면, 안드로이드 또는 IOS 각각의 광고 설정 등 앱이 가지는 기본 정보들을 설정하는 파일 (나중에 가서 광고 및 배포 외엔 다룰 일이 없음)

여튼 지금까지 한 설치 세팅 순서를 정리하자면 다음과 같다.

1) Expo 명령어 설치
2) 로컬에 Expo 계정 세팅
3) expo init 명령어로 기본 앱 생성
4) expo start로 Expo 앱 실행
5) 휴대폰에 설치한 Expo 클라이언트 앱으로 Expo 앱 실행


2. 기본 앱 화면 설명 및 JSX 문법

강사님에게 큰 제목 1번에서 배운대로 저번에 만든 app.js를 실행했더니 흰 바탕화면에 "Open up App.js to start working on your app!" 글귀가 떴다. 이것은 다음 코드로 이뤄져 있는데 같이 붙어있는 주석대로 강사님이 하나 하나 설명을 해주셨다.

//우리가 리액트, 리액트 네이티브, 엑스포 라이브러리에서 꺼내 사용할 기능들을
//이렇게 앞으로도 상단에 선언한다음 가져다 사용합니다.
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

//App.js는 결국 App 함수를 내보내기 하고 있는 자바스크립트 파일입니다.
//이 함수는 무언가?를 반환하고 있는데 결국 화면을 반환하고 있습니다.
export default function App() {
	//화면을 반환합니다.
	//HTML 태그 같이 생긴 이 문법은 JSX라 불리우며 실제 화면을 그리는 문법입니다,
	//이번 강의에서 자세히 다룹니다
	console.disableYellowBox = true;
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

// styles 변수 이름 답게 화면을 그려주는, 
//더 자세히는 JSX문법을 꾸며주는 내용을 담고 있습니다.
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

// +) 노란 경고창 안보이게 하기 : console.disableYellowBox = true; 코드 추가

이렇게 app.js가 본격적인 앱 화면을 그리는데 이때 쓰는 문법이 바로 JSX 문법이고 return 부분은 JSX 문법으로 적은 것을 그대로 앱 화면에 보여주는 역할을 한다고 강사님이 말씀하셨다. 그러면서 JSX 문법으로 앱 화면을 그리는 동작 행위를 렌더링(rendering)이라고 부른다고 하셨다.

- JSX 문법 기본 원칙 -

(1) < View > ~ < /View > 처럼 꺽쇠(태그)로 구성되며 마지막 /태그로 닫는 것이 중요!(이를 엘리먼트라고 부른다.)

(2) 윗 상단 import 부분에서 보이다시피 기본적으로 모든 태그들은 리액트 네이티브에서 끌어오는 기능이다.

(3) (1)의 < View > 태그처럼 마지막에 따로 닫는 태그도 있지만 < StatusBar ~ / > 태그처럼 스스로 닫는 태그도 있다. (구별하는 방법은 리액트 네이티브 공식 문서, EXPO 공식 문서를 참고 할것!)

(4) 모든 엘리먼트는 자신을 감싸는 최상위 엘리멘트(가장 바깥에 위치한 엘리멘트)가 있어야 한다. (웹 개발 1주차 개발일지 中 "< div > 부모-자식 태그"를 떠올리면 이해가 쉬울것)

(5) return으로 렌더링할땐 꼭 소괄호()로 감싸야 한다.

(6) JSX 밖(return 밖)의 주석은 "//", JSX 안에서의 주석은 "/* ~ */" 로 쳐야 한다.


3. 많이 쓰게 될 대표 엘리멘트 유형 소개

강사님이 알려주신 엘리멘트 유형은 9가지인데 천천히 다시 복습해보자면,

1) < View >

화면의 영역(레이아웃)을 잡아주는 엘리먼트입니다. 현재 App.js 상에서 View는 화면 전체 영역을 가집니다. 우리는 이 View 엘리먼트로 다음과 같이 화면을 원하는 대로 분할 할 수도 있습니다. (view 태그는 꼭 StyleSheet 태그와 함께 사용해야함!)

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.subContainerOne}></View>
      <View style={styles.subContainerTwo}></View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  subContainerOne: {
    flex:1,
    backgroundColor:"yellow"
  },
  subContainerTwo: {
    flex:1,
    backgroundColor:"green"
  }
});

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

2) < Text >

앱에 글을 작성하려면 반드시 사용해야하는 엘리먼트입니다.
방금전 위에서 배운 View 엘리먼트 안에서 문자를 작성하면 오류가 발생합니다.

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

export default function App() {
  return (
    <View style={styles.container}>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
      <Text>문자는 Text 태그 사이에 작성!!</Text>
    </View>
  );
}

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

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

3) < ScrollView >

앱 화면을 벗어나는 영역의 경우 ScrollView 엘리먼트로 감싸면 스크롤이 가능해지면서 모든 컨텐츠를 볼 수 있습니다. 다음 코드를 복사 붙여넣기 해보세요!
좀 길어보이지만 같은 코드 복붙을 여러번 한 코드입니다 😉

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

export default function App() {
  return (
		//각 태그들에는 style이라는 속성을 갖습니다.
		//이 속성은 파일 최하단에 작성한 스타일 코드 객체의 키 값을 부여해
    // 엘리먼트들에 스타일을 줄 수 있습니다.
    //이는 JSX문법을 배우고 난 다음 더 자세히 다룹니다.
    <View style={styles.container}>
			{/* //보인 영역을 갖는 엘리먼트 일곱개가 반복적으로 쓰였습니다. */}
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
    </View>
  );
}

//텍스트가 영역을 갖고, 가운데 정렬도 하며, 테두리 스타일을 갖게 끔 하는 코드입니다.
//JSX를 마저 배우고 스타일에 대해 자세히 다루니
//걱정 안해도 좋습니다!
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  textContainer: {
    height:100,
    borderColor:'#000',
    borderWidth:1,
    borderRadius:10,
    margin:10,
  },
  textStyle: {
    textAlign:"center"
  }
});

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

4) < button >과 함수

대부분 앱엔, 버튼이 당연히 있죠? 그리고 그 버튼을 누르면 팝업이 뜨기도하고, 다른 페이지로 넘어가기도 하며 이 밖의 다양한 기능들이 실행됩니다. (title = 버튼 안 문자, color = 버튼 색상, onpress = 버튼을 눌렀을 시 연결된 함수 실행 가능 그 외 자세한 공식 내용은 버튼 설명서 링크팝업 알람 설명서 링크참조 할것.)

import React from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>아래 버튼을 눌러주세요</Text>
        {/* 버튼 onPress 속성에 일반 함수를 연결 할 수 있습니다. */}
        <Button 
          style={styles.buttonStyle} 
          title="버튼입니다 "
          color="#f194ff" 
          onPress={function(){
            Alert.alert('팝업 알람입니다!!')
          }}
        />
        {/* ES6 문법으로 배웠던 화살표 함수로 연결 할 수도 있습니다. */}
        <Button 
            style={styles.buttonStyle} 
            title="버튼입니다 "
            color="#FF0000" 
            onPress={()=>{
              Alert.alert('팝업 알람입니다!!')
            }}
          />
          </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  textContainer: {
    height:100,
    margin:10,
  },
  textStyle: {
    textAlign:"center"
  },
});

또한, 같은 의미지만 아래와 같이 코드를 작성할 수 있다.

import React from 'react';
import { StyleSheet, Text, View, Button, Alert } from 'react-native';

export default function App() {
  //화살표 함수 형식으로 함수를 정의하고
  //jSX문법 안에서 사용할 수 있습니다
  const customAlert = () => {
    Alert.alert("JSX 밖에서 함수 구현 가능!")
  }
  return (
    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>아래 버튼을 눌러주세요</Text>
        {/* onPress에 밖에서 구현한 함수 이름을 고대로 넣을 수도 있고*/}
        <Button 
          style={styles.buttonStyle} 
          title="버튼입니다 "
          color="#f194ff" 
          onPress={customAlert}
        />
        {/* onPress를 누르면 속성에 바로 구현해 놓은 함수 안에 customALert함수를 두고 실행할 수 있게도 가능합니다 */}
        <Button 
            style={styles.buttonStyle} 
            title="버튼입니다 "
            color="#FF0000" 
            onPress={() => {customAlert()}}
          />
          </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  textContainer: {
    height:100,
    margin:10,
  },
  textStyle: {
    textAlign:"center"
  },
});

이렇게 구현한 버튼 화면은 다음 그림과 같다.

5) < TouchableOpacity >

버튼 기능을 사용한 콘텐츠를 쭉 일렬로 넣어서 카드뷰로 만들고 싶은데 앞서 봤던 button 태그 특성상 내부에 코드로 일일이 스타일 입력하기는 힘들때, 일괄로 다수의 버튼 겸 영역을 가진, 텍스트를 포함한 콘텐츠 태그를 넣은 다음 편하게 아래 < style > 태그에서 한꺼번에 디자인을 관리하고 싶을때 사용하는 태그가 바로 이 TochableOpacity 이다.

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

export default function App() {
  const customAlert = () => {
    Alert.alert("TouchableOpacity에도 onPress 속성이 있습니다")
  }
  return (
    <ScrollView style={styles.container}>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
      <TouchableOpacity style={styles.textContainer} onPress={customAlert}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </TouchableOpacity>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  textContainer: {
    height:100,
    borderColor:'#000',
    borderWidth:1,
    borderRadius:10,
    margin:10,
  },
  textStyle: {
    textAlign:"center"
  }
});

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

6) < Image > (2가지 방식)

- assets 폴더에 있는 이미지를 가져오는 방식 (import 기능 이용)

import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
//이렇게 상단에 가져와 사용할 이미지를 불러옵니다
import favicon from "./assets/favicon.png"

export default function App() {
  return (
    <View style={styles.container}>
			{/*이미지 태그 soruce 부분에 가져온 미지 이름을 넣습니다 */}
      <Image 
        source={favicon}
				// 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다
                // "repeat"는 이미지 반복 채움
        resizeMode={"repeat"}
        style={styles.imageStyle}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    //혹시 미리 궁금하신 분들을 위해 언급하자면,
    //justifyContent와 alignContent는 영역 안에 있는 콘텐츠들을 정렬합니다
    justifyContent:"center",
    alignContent:"center"
  },
  imageStyle: {
    width:"100%",
    height:"100%",
    alignItems:"center",
    justifyContent:"center"
  }
});

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

(< Image > 태그 공식 사용 설명서 링크에 들어가서 보고 resizeMode에 다른 속성 값을 주면 화면이 달라진다.)

- 외부 이미지 활용하기
앞서 했던 source 속성값에 uri로 시작하는 외부 링크 값을 주면 된다. 자세한 코드는 다음과 같다.

import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
//이렇게 상단에 가져와 사용할 이미지를 불러옵니다
import favicon from "./assets/favicon.png"

export default function App() {
  return (
    <View style={styles.container}>
			{/*이미지 태그 soruce 부분에 가져온 이미지 이름을 넣습니다 */}
      <Image 
        source={{uri:'https://images.unsplash.com/photo-1424819827928-55f0c8497861?fit=crop&w=600&h=600%27'}}
				// 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다
        resizeMode={"cover"}
        style={styles.imageStyle}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    //혹시 미리 궁금하신 분들을 위해 언급하자면,
    //justifyContent와 alignContent는 영역 안에 있는 콘텐츠들을 정렬합니다
    justifyContent:"center",
    alignContent:"center"
  },
  imageStyle: {
    width:"100%",
    height:"100%",
    alignItems:"center",
    justifyContent:"center"
  }
});

위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다.

(아까처럼 반복이 안된 이유는 resizeMode 속성값에 "cover" 값을 주었기 때문이다. -> 이미지하나로 앱 화면 전체 채움)

7) 모든 태그에 적용되는 < style >

리액트 네이티브 기반에서는 웹과 조금 다르게 StyleSheet가 만드는 styles 객체로 태그에 디자인 스타일을 적용하는데,모든 태그에 style에 속하며, styles 객체 안에 있는, 각각의 키 값을 부여해 태그 태그 마다 전용 스타일 키 값이 존재한다.

ex) <View style={styles.container}> // <- View 태그 - style 속성 - container 키 값

8) StyleSheet 문법 중 자주 사용하는 style 속성들

일단, 다음 코드를 보면서 주석대로 하나 하나 이해하면 편하다.

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

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.textContainer}>
        <Text style={styles.textStyle}>스파르타 코딩클럽!!</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    //영역을 잡는 속성입니다. 따로 자세히 다룹니다.
    //flex: 1은 전체 화면을 가져간다는 뜻입니다
    flex: 1,
    //영역의 배경 색을 결정합니다
    backgroundColor: '#fff',
    //아래 두 속성은 영역 안의 컨텐츠들의 배치를 결정합니다. 
    //flex를 자세히 다룰때 같이 자세히 다룹니다
    justifyContent:"center",
    alignContent:"center"
  },
  textContainer: {
    //영역의 바깥 공간 이격을 뜻합니다(하단 이미지 참조)
    margin:10,
    //영역 안의 컨텐츠 이격 공간을 뜻합니다(하단 이미지 참조)
    padding: 10,
    //테두리의 구부러짐을 결정합니다. 지금 보면 조금 둥글죠?
    borderRadius:10,
    //테두리의 두께를 결정합니다
    borderWidth:2,
    //테두리 색을 결정합니다
    borderColor:"#000",
    //테구리 스타일을 결정합니다. 실선은 solid 입니다
    borderStyle:"dotted",

  },
  textStyle: {
    //글자 색을 결정합니다. rgb, 값 이름, 색상코드 모두 가능합니다
    color:"red",
    //글자의 크기를 결정합니다
    fontSize:20,
    //글자의 두께를 결정합니다
    fontWeight:"700",
    //가로기준으로 글자의 위치를 결정합니다
    textAlign:"center"
  }
});

margin과 패딩의 차이는 다음 이미지와 같다.

(margin - 컨텐츠 외부 경계선과 나머지 앱 화면 사이 외부 여백, padding - 컨텐츠 외부 경계선과 실질 컨텐츠 사이의 내부 여백, border - 컨텐츠 외부 경계선 (영역선))

(스타일 공식 문서는 다음 링크 1번,2번으로)

9) flex (영역 구분하기)

영역의 레이아웃을 결정하는 기능이 flex인데, 다음 코드들을 천천히 보면서 이해하면 된다.

  • flex의 개념
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.containerOne}>

      </View>
      <View style={styles.containerTwo}>

      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex:1
  },
  containerOne: {
    flex:1,
    backgroundColor:"red"
  },
  containerTwo:{
    flex:2,
    backgroundColor:"yellow"
  }
});

앱 화면이 구현된 그림으로 설명하면 아래 그림과 같다.

그러니까, 첫 전체 영역은 container로 flex : 1을 준 뒤에 두번째에서는 containerOne에게 flex : 1을, 마지막 세번째에서는 containerTwo에게 flex : 2를 주면 1 (containerOne) / 3 (containerOne + containerTwo) 영역은 containerOne에게, 나머지 2/3은 containerTwo에게 할당된다.

  • flexDirection
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.containerOne}>

      </View>
      <View style={styles.containerTwo}>
        <View style={styles.innerOne}>

        </View>
        <View style={styles.innerTwo}>

        </View>

      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex:1
  },
  containerOne: {
    flex:1,
    backgroundColor:"red"
  },
  containerTwo:{
    flex:2,
    flexDirection:"row",
    backgroundColor:"yellow"
  },
  innerOne: {
    flex:1,
    backgroundColor:"blue"
  },
  innerTwo: {
    flex:4,
    backgroundColor:"orange"
  }
});

앱 화면이 구현된 그림으로 설명하면 아래 그림과 같다.

즉, 영역을 나눌때 기본 설정은 column(세로 방향)인데 가로로 영역을 나누고 싶을때는 flexDirection: "row"를 이용하면 된다.

  • justifyContent

flexDirection에서 설정한 방향과 동일한 방향으로 정렬하는 속성이다.
속성값으로 flex-start, center, flex-end, space-between, space-around가 있다.
아래 그림을 보면 이해하기가 편할 것이다.


그리고 다음 코드로 실습해보자.

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

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.containerOne}>

      </View>
      <View style={styles.containerTwo}>
        <View style={styles.innerOne}>
         
        </View>
        <View style={styles.innerTwo}>
          <Text>!!컨텐츠!!</Text>
        </View>

      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex:1
  },
  containerOne: {
    flex:1,
    backgroundColor:"red"
  },
  containerTwo:{
    flex:2,
    flexDirection:"row",
    backgroundColor:"yellow"
  },
  innerOne: {
    flex:1,
    backgroundColor:"blue"
  },
  innerTwo: {
    flex:4,
    justifyContent:"flex-start", // <- flexDirection 기본 설정 column 세로방향
    backgroundColor:"orange"
  }
});

그러면 위와 같이 app.js에 코드를 치면 아래 그림과 같이 앱 화면이 구현된다. (똑같이 flexDirection 세로 정렬)

  • alignItems
    flexDirection이 설정한 방향과 수직한 방향 (반대 방향)으로 정렬하는 속성이다.
    속성값으로 flex-start, center, flex-end, stretch가 있다.
    아래 그림을 보면 이해하기가 편할 것이다.

    그리고 다음 코드로 실습해보자.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.containerOne}>

      </View>
      <View style={styles.containerTwo}>
        <View style={styles.innerOne}>
         
        </View>
        <View style={styles.innerTwo}>
          <View style={styles.content}></View>
        </View>

      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex:1
  },
  containerOne: {
    flex:1,
    backgroundColor:"red"
  },
  containerTwo:{
    flex:2,
    flexDirection:"row",
    backgroundColor:"yellow"
  },
  innerOne: {
    flex:1,
    backgroundColor:"blue"
  },
  innerTwo: {
    flex:4,
    backgroundColor:"orange",
    alignItems:"flex-end" // <- flexDirection 기본 설정 column 세로방향
  },
  content: {
    width:50,
    height:50,
    backgroundColor:"#000"
  }
});

그러면 아래 그림과 같은 앱 화면이 실행된다.(flexDirection과 수직 반대 방향으로 가로 정렬)


4. 본격적인 앱 화면 만들기 (실습)

JSX 문법도 배웠으니 이제 본격적으로 앱 화면 만드는 법을 강사님과 함께 실습 하였다.
아래 화면을 만들어야 했다.

강사님은 너무 당황하지말고 아래 순서대로 천천히 구조를 뜯어나가면서 뭘 구현해야할지 정리해보라고 하셨다.

1) 상단에 '나만의 꿀팁' 제목 텍스트를 넣어야 겠다
2) 제목 바로 밑에 이미지를 해당 이미지를 넣어야 겠다(이미지 제공 해드립니다 🙂)
3) 가운데 버튼은 횡(수평) 스크롤(ScrollView) 기능을 써서 좌우로 스크롤 가능하게 만들어야 겠다
4) ScrollVoew 수직 스크롤은 그냥 쓰면 된다는 것을 알았지만 횡은 어떻게 해야할까? 공식문서를 찾아봐야지, 수평은 영어로 Horizontal
5) 주어진 데이터는 문자이므로 Text 태그에 넣어야 겠다.
6) 설명 글은 긴데 3줄이 넘어가면, 말 줄임표가 되네! Text 태그 속성에 이런 기능이 있는지 공식문서에서 찾아봐야겠다.
7) 피자 이미지와 글자 부분의 영역을 대~충 1 : 3정도로 분할하면 되겠네

정리하자면 다음과 같은 순서로 생각하는걸 먼저 하라고 하셨다.

1) 어떤 태그가 필요할지 생각한 후!
2) 해당 태그에 어떤 속성 기능이 존재하는 지 공식문서를 찾아본 후!
3) 디자인을 고려하며 옷을 입히기!

앞서 알려주셨던 스타일 공식 문서 1번,2번도 참고하면서 하라고 하셨다.

(정확히는 ScrollView의 공식 문서 링크에서 horizontal 부분을, text의 공식 문서 링크에서 말줄임표 링크를 찾아보라고 지시하셨다.)

그리하여 따로 제공된 이미지와 텍스트를 조합해 완성하면 다음과 같은 코드문이 나왔다.

import React from 'react';
import main from './assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';

export default function App() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석
  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
        {/* 하나의 카드 영역을 나타내는 View */}
        <View style={styles.card}>
          <Image style={styles.cardImage} source={{uri:"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpizza.png?alt=media&token=1a099927-d818-45d4-b48a-7906fd0d2ad3"}}/>
          <View style={styles.cardText}>
            <Text style={styles.cardTitle}>먹다 남은 피자를 촉촉하게!</Text>
            <Text style={styles.cardDesc} numberOfLines={3}>먹다 남은 피자는 수분이 날라가기 때문에 처음처럼 맛있게 먹을 수 없는데요. 이럴 경우 그릇에 물을 받아 전자레인지 안에서 1분 30초에서 2분 정도 함께 돌려주면 촉촉하게 먹을 수 있습니다. 물이 전자레인지 안에서 수증기를 일으키고, 피자에 촉촉함을 더해줍니다.</Text>
            <Text style={styles.cardDate}>2020.09.09</Text>
          </View>
        </View>
        
      </View>
   
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    //앱의 배경 색
    backgroundColor: '#fff', //흰색
  },
  title: {
    //폰트 사이즈
    fontSize: 20,
    //폰트 두께
    fontWeight: '700',
    //위 공간으로 부터 이격
    marginTop:50,
	    //왼쪽 공간으로 부터 이격'
    marginLeft:20
  },
  mainImage: {
    //컨텐츠의 넓이 값
    width:'90%',
    //컨텐츠의 높이 값
    height:200,
    //컨텐츠의 모서리 구부리기
    borderRadius:10,
    marginTop:20,
    //컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
    //각 속성의 값들은 공식문서에 고대로~ 나와 있음
    alignSelf:"center"
  },
  middleContainer:{
    marginTop:20,
    marginLeft:10,
    height:60
  },
  middleButton01: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fdc453",
    borderColor:"deeppink",
    borderRadius:15,
    margin:7
  },
  middleButton02: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fe8d6f",
    borderRadius:15,
    margin:7
  },
  middleButton03: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#9adbc5",
    borderRadius:15,
    margin:7
  },
  middleButton04: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#f886a8",
    borderRadius:15,
    margin:7
  },
  middleButtonText: {
    color:"#fff",
    fontWeight:"700",
    //텍스트의 현재 위치에서의 정렬 
    textAlign:"center"
  },
  cardContainer: {
    marginTop:10,
    marginLeft:10
  },
  card:{
    flex:1,
    //컨텐츠들을 가로로 나열
    //세로로 나열은 column <- 디폴트 값임 
    flexDirection:"row",
    margin:10,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10

  },
  cardImage: {
    flex:1,
    width:100,
    height:100,
    borderRadius:10,
  },
  cardText: {
    flex:2,
    flexDirection:"column",
    marginLeft:10,
  },
  cardTitle: {
    fontSize:20,
    fontWeight:"700"
  },
  cardDesc: {
    fontSize:15
  },
  cardDate: {
    fontSize:10,
    color:"#A6A6A6",
  }


});

실제로 강사님의 지시에 맞춰서 해봤을땐 단번에 되지 않았다. 영역을 확인하기 위해 borderWidth 속성으로 일부러 테두리 표시를 해주면서 margin, padding, width, height 값을 일일이 시험해보면서 최적의 값을 찾았고, 폰트 사이즈와 정렬도 마찬가지였다. 강사님도 처음에 단번에 착착 맞게 할수는 없다면서 여러 시행착오를 거치면서 제시한 화면과 비슷하게만 가도 성공이라고 하셨었다.


5. 자바스크립트를 응용해 모듈,반복문,{}표현식,조건문 사용

저번 1주차때 마지막에 import와 export에 대해 살짝 알려주셨었는데 이걸 이용한 모듈 시스템에 대해 강사님은 언급하시면서 app.js 또한 "export default function App() { ... }" 부분에서 보다시피 리액트 네이티브를 통해 export로 외부로 전달되어 앱 화면을 띄우는 모듈 시스템 구조를 가진다는것을 역설하셨다. 그러면서 4번 실습에 이어하는것으로 다음의 data.json 코드를 복붙하기를 지시하셨다.

{
    "tip":[
        {
            "idx":0,
            "category":"생활",
            "title":"먹다 남은 피자를 촉촉하게!",
            "image":"https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpizza.png?alt=media&token=1a099927-d818-45d4-b48a-7906fd0d2ad3",
            "desc":"먹다 남은 피자는 수분이 날라가기 때문에 처음처럼 맛있게 먹을 수 없는데요. 이럴 경우 그릇에 물을 받아 전자레인지 안에서 1분 30초에서 2분 정도 함께 돌려주면 촉촉하게 먹을 수 있습니다. 물이 전자레인지 안에서 수증기를 일으키고, 피자에 촉촉함을 더해줍니다.",
            "date":"2020.09.09"
        },
        {
            "idx":1,
            "category":"생활",
            "title":"바나나를 싱싱하게 보관하기",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fbanana.png?alt=media&token=886aeb87-7ff8-4498-8674-7e4f878b8845",
            "desc":"바나나에 날파리가 꼬이거나 금방 익어버리는 것을 예방하기 위한 방법인데요. 바나나 양쪽 끝을 자른 후, 보관용 케이스나 비닐봉지에 묶어 밀봉합니다. 그리고 냉장고에 넣어주면 되는데요. 하루에 1~2개씩 꺼내서 싱싱하게 먹을 수 있습니다.",
            "date":"2020.09.09"
        },
        {
            "idx":2,
            "category":"생활",
            "title":"셔츠에 묻은 볼펜 자국 없애기",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fshirt.png?alt=media&token=6388d9fb-f87f-4234-b580-46a2f918aab1",
            "desc":"셔츠를 자주 입는 사람의 경우, 종종 볼펜 자국이 묻기 마련인데요. 이럴 경우에는 집에 있는 물파스로 가볍게 지울 수 있습니다. 옷 뒷부분을 키친타올로 받쳐 번지는 것을 방지한 후, 볼펜 자국 있는 부분을 물파스로 눌러주고, 키친타올로 닦아냅니다.",
            "date":"2020.09.09"
        },
        {
            "idx":3,
            "category":"재테크",
            "title":"잠자는 내 돈을 찾아라",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmoney1.png?alt=media&token=491096e7-0b57-40a3-991b-b984193f8018",
            "desc":"‘새는 돈’에는 미처 몰랐던 카드 포인트, 휴면예금이나 환급금도 포함됩니다. 확실히 파악하지 못한 잠자는 돈을 찾아보고 자투리 돈들을 모으는 것도 중요합니다. 케이블방송, 위성방송 서비스를 이용하면서 중복 납부한 요금, 셋톱박스 보증금 등 돌려받지 않은 돈이 있는지 확인 해보세요. 또, 카드 포인트 통합 조회 서비스를 이용해 여러 개의 카드 포인트가 모두 얼마인지 체크해두는 것이 좋습니다. 보험해약 환급금, 휴면 보험금이나 휴면 예금을 찾아보고 돌려받는 일도 요즘에는 어렵지 않습니다.",
            "date":"2020.09.09"
        },
        {
            "idx":4,
            "category":"재테크",
            "title":"할인행사, 한정할인판매 문구의 함정 탈출!",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fmoney2.png?alt=media&token=9c9df304-16e8-4a6f-8ae4-1d3f9ad58134",
            "desc":"‘안 사면 100% 할인’이라는 말 들어보셨나요? 견물생심, 좋은 물건을 보면 사고 싶기 마련입니다. 특히 대대적인 ‘할인 행사’ 중인 대형 마트에 갔을 때는 말할 것도 없겠죠. 따라서 생필품을 살 때, 한꺼번에 사서 사용하는 것보다 필요할 때 조금씩 구매하는 편이 좋습니다. 장을 보면서 대형마트에 자주 가다 보면 지금 필요한 것뿐 아니라 앞으로 필요할 것까지 사게 되어 지출이 커지기 때문입니다. 특히 할인 품목을 보면 뜻하지 않은 소비를 하는 경우도 많아진다. 홈쇼핑, 대형마트 등의 ‘할인행사’, ‘한정할인판매’ 등의 문구를 조심하세요. ",
            "date":"2020.09.09"
        },
        {
            "idx":5,
            "category":"생활",
            "title":"방전된 건전지 살리기",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fbattery.png?alt=media&token=7b2f37a2-537d-4b98-a139-da6aa2f420ec",
            "desc":"건전지를 다 사용하지 않아도 방전되면, 버리는 경우가 종종 있는데요. 건전지의 무게감이 느껴진다면, 드라이기를 활용해 방전된 건전지를 깨울 수 있습니다. 드라이기 열기를 10초~30초 정도 골고루 가해주면 되는데요. 건전지가 불필요하게 낭비되는 것을 막을 수 있습니다.",
            "date":"2020.09.09"
        },
        {
            "idx":6,
            "category":"반려견",
            "title":"반려견에게 배변 교육 시킬 때",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpuppy.png?alt=media&token=85ba2c18-d263-4857-8e09-8922cc14ad29",
            "desc":"우선, 배변패드를 순서대로 돌며 간식을 조금씩 떨어뜨려 놓는다. 2단계는 배변패드 앞에서 기다렸다 반려견이 스스로 올라오면 간식을 주어서 보상하고, 3단계는 “화장실 가자”나 “매트” 같은 명령어를 붙여 말한 뒤 배변패드에 올라오면 간식을 주는 것이다. 마지막 단계는 배변패드에 올라간 반려견이 대소변을 본 다음 간식을 줌으로써 이 장소가 즐거운 곳이라는 인식을 심어주는 것이다. 그리고 무엇보다 1, 2회 사용한 배변패드는 바로 갈아줘야 한다.",
            "date":"2020.09.09"
        },
        {
            "idx":7,
            "category":"반려견",
            "title":"반려견이 주인과 떨어지는 것을 무서워 할 때",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpuppy2.png?alt=media&token=3f07ca26-6791-4d9a-8638-264f80c01038",
            "desc":"분리불안교육은 반려견에게 혼자 남는 법을 알려주기 위한 것이 아니라, 보호자가 ‘언제나 너에게 돌아올 거야’라고 알려주는 교육이다. 반려견과 5초 동안 떨어져 있다가 다시 문을 열고 들어가 손 냄새를 맡게 해주는 훈련을 하루 10번씩 7일 동안 반복하는 ‘5,10,7 법칙’을 통해 반려견의 마음을 편안하게 해줄 수 있다.",
            "date":"2020.09.09"
        },
        {
            "idx":8,
            "category":"반려견",
            "title":"반려견을 아이와 함께 키울 때",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Fpuppy3.png?alt=media&token=b59fa6d7-7d3d-4147-8401-88bba030ec25",
            "desc":"‘인간의 행복’을 위해 반려동물을 키우는 것에 대해 꾸준히 비판과 우려를 제기해온 그는 특히 ‘아이들의 정서’를 위해 반려견을 키우려 한다는 부모들에게 당부한다. “반려동물을 통해 아이들의 정서가 좋아진다면, 그것은 부모가 나와 생김새와 느낌, 말과 행동이 다른 동물을 아끼는 모습을 보기 때문입니다.” 인간의 뜻에 의해 인간과 함께 살게 된 생명을 좀 더 이해하고 행복하게 살 수 있도록 하는 것은 역시 인간의 노력에 달려 있다.",
            "date":"2020.09.09"
        },
        {
            "idx":9,
            "category":"재테크",
            "title":"렌탈 서비스 금액 비교해보기",
            "image": "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2Frental.png?alt=media&token=97a55844-f077-4aeb-8402-e0a27221570b",
            "desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
            "date":"2020.09.09"
        }

    ]
}

강사님은 이 json 데이터를 app.js에 사용하는 방법을 알려준다고 하셨다.
그러면서 임포트를 위해 다음 코드를 app.js 상단에 입력하라고 하셨다.

import data from './data.json';
// 
./   ⇒ 현재 파일과 동일한 위치에서 불러올 파일을 찾는 코드
../  ⇒ 현재 파일이 위치한 폴더보다 상위 위치에서 불러올 파일을 찾는 코드 (뒤로 가기)
//

그리고 4번 실습 app.js를 다음과 같은 코드로 수정하면 json 데이터 적용이 완료 된다.

import React from 'react';
import main from './assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from './data.json';
export default function App() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  let tip = data.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"

  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
      <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <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>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    //앱의 배경 색
    backgroundColor: '#fff',
  },
  title: {
    //폰트 사이즈
    fontSize: 20,
    //폰트 두께
    fontWeight: '700',
    //위 공간으로 부터 이격
    marginTop:50,
    //왼쪽 공간으로 부터 이격
    marginLeft:20
  },
  weather:{
    alignSelf:"flex-end",
    paddingRight:20
  },
  mainImage: {
    //컨텐츠의 넓이 값
    width:'90%',
    //컨텐츠의 높이 값
    height:200,
    //컨텐츠의 모서리 구부리기
    borderRadius:10,
    marginTop:20,
    //컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
    //각 속성의 값들은 공식문서에 고대로~ 나와 있음
    alignSelf:"center"
  },
  middleContainer:{
    marginTop:20,
    marginLeft:10,
    height:60
  },
  middleButton01: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fdc453",
    borderColor:"deeppink",
    borderRadius:15,
    margin:7
  },
  middleButton02: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fe8d6f",
    borderRadius:15,
    margin:7
  },
  middleButton03: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#9adbc5",
    borderRadius:15,
    margin:7
  },
  middleButtonText: {
    color:"#fff",
    fontWeight:"700",
    //텍스트의 현재 위치에서의 정렬 
    textAlign:"center"
  },
  middleButton04: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#f886a8",
    borderRadius:15,
    margin:7
  },
  cardContainer: {
    marginTop:10,
    marginLeft:10
  },
  card:{
    flex:1,
    //컨텐츠들을 가로로 나열
    //세로로 나열은 column <- 디폴트 값임 
    flexDirection:"row",
    margin:10,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10

  },
  cardImage: {
    flex:1,
    width:100,
    height:100,
    borderRadius:10,
  },
  cardText: {
    flex:2,
    flexDirection:"column",
    marginLeft:10,
  },
  cardTitle: {
    fontSize:20,
    fontWeight:"700"
  },
  cardDesc: {
    fontSize:15
  },
  cardDate: {
    fontSize:10,
    color:"#A6A6A6",
  },


});

tip 변수에 먼저 들어가야할 json 데이터를 대입하고 그 다음 < View style={styles.cardContainer} > 부분을 주목해서 보면 1주차에 향상된, 간소화된 자바스크립트 문법JSX에서 map 기능을 이용한 반복문을 사용한것을 알 수 있다. {}으로 전체를 괄호치고 i값 순서대로 content 객체의 image,title,desc,date 값을 반복 대입해 마지막 return으로 출력하면 자연스럽게 json을 이용한 카드 컨텐츠 추가가 되는것이다, 그것도 모듈 시스템을 이용해 코드 편의성을 가져가면서 말이다. 구현되는 화면은 아래 그림과 같다.

또한, 다음과 같이 코드를 또 수정하면 {}을 이용해 연산/변수 사용도 가능해진다.

import { StatusBar } from 'expo-status-bar';
import React from 'react';
import main from './assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from './data.json';
export default function App() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  let tip = data.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"
  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
      <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <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>
   
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    //앱의 배경 색
    backgroundColor: '#fff',
  },
  title: {
    //폰트 사이즈
    fontSize: 20,
    //폰트 두께
    fontWeight: '700',
    //위 공간으로 부터 이격
    marginTop:50,
    //왼쪽 공간으로 부터 이격
    marginLeft:20
  },
  weather:{
    alignSelf:"flex-end",
    paddingRight:20
  },
  mainImage: {
    //컨텐츠의 넓이 값
    width:'90%',
    //컨텐츠의 높이 값
    height:200,
    //컨텐츠의 모서리 구부리기
    borderRadius:10,
    marginTop:20,
    //컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
    //각 속성의 값들은 공식문서에 고대로~ 나와 있음
    alignSelf:"center"
  },
  middleContainer:{
    marginTop:20,
    marginLeft:10,
    height:60
  },
  middleButton01: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fdc453",
    borderColor:"deeppink",
    borderRadius:15,
    margin:7
  },
  middleButton02: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fe8d6f",
    borderRadius:15,
    margin:7
  },
  middleButton03: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#9adbc5",
    borderRadius:15,
    margin:7
  },
  middleButtonText: {
    color:"#fff",
    fontWeight:"700",
    //텍스트의 현재 위치에서의 정렬 
    textAlign:"center"
  },
  middleButton04: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#f886a8",
    borderRadius:15,
    margin:7
  },
  cardContainer: {
    marginTop:10,
    marginLeft:10
  },
  card:{
    flex:1,
    //컨텐츠들을 가로로 나열
    //세로로 나열은 column <- 디폴트 값임 
    flexDirection:"row",
    margin:10,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10

  },
  cardImage: {
    flex:1,
    width:100,
    height:100,
    borderRadius:10,
  },
  cardText: {
    flex:2,
    flexDirection:"column",
    marginLeft:10,
  },
  cardTitle: {
    fontSize:20,
    fontWeight:"700"
  },
  cardDesc: {
    fontSize:15
  },
  cardDate: {
    fontSize:10,
    color:"#A6A6A6",
  }


});

todayWeather와 todayCondition 부분을 {}과 JSX 문법을 이용해 새로 추가한 결과 아래 그림과 같이 날씨와 기온 컨텐츠가 늘었다.

마지막으로, 때로는 조건문을 통해 다른 모습을 보이거나 연산을 진행하고 싶을때가 있을텐데 그때는 번거로운 if문을 이용하는게 아니라 아래 설명과 같은 삼항 연산자를 {}안에 표현해 구현할수 있다.

let result = 10 > 2 ? "참" : "거짓"

(기본 모습)
let result = 조건 ? 참일 때 : 거짓 일때

(예제)
let result = 10 == 9 ? true : false  // result <-- false 값 할당  
let result = 10 !== 9 ? true : false // result <-- true  할당  
let reuslt = 99 > 10 ? true : false // result <-- true 값 할당  

최종적으로 삼항 연산자까지 적용시키면 아래와 같은 코드문이 나온다.

import React from 'react';
import main from './assets/main.png';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView} from 'react-native';
import data from './data.json';
export default function App() {
  console.disableYellowBox = true;
  //return 구문 밖에서는 슬래시 두개 방식으로 주석

  let tip = data.tip;
  let todayWeather = 10 + 17;
  let todayCondition = "흐림"

  return (
    /*
      return 구문 안에서는 {슬래시 + * 방식으로 주석
    */
    <ScrollView style={styles.container}>
      <Text style={styles.title}>나만의 꿀팁</Text>
      <Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>
      <Image style={styles.mainImage} source={main}/>
      <ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
        <TouchableOpacity style={styles.middleButton01}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton02}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton03}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
        <TouchableOpacity style={styles.middleButton04}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
      </ScrollView>
      <View style={styles.cardContainer}>
         {/* 하나의 카드 영역을 나타내는 View */}
         { 
          tip.map((content,i)=>{
            return i % 2 == 0 ? (<View style={styles.cardEven} 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 style={styles.cardOdd} 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>
   
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    //앱의 배경 색
    backgroundColor: '#fff',
  },
  title: {
    //폰트 사이즈
    fontSize: 20,
    //폰트 두께
    fontWeight: '700',
    //위 공간으로 부터 이격
    marginTop:50,
    //왼쪽 공간으로 부터 이격
    marginLeft:20
  },
  weather:{
    alignSelf:"flex-end",
    paddingRight:20
  },
  mainImage: {
    //컨텐츠의 넓이 값
    width:'90%',
    //컨텐츠의 높이 값
    height:200,
    //컨텐츠의 모서리 구부리기
    borderRadius:10,
    marginTop:20,
    //컨텐츠 자체가 앱에서 어떤 곳에 위치시킬지 결정(정렬기능)
    //각 속성의 값들은 공식문서에 고대로~ 나와 있음
    alignSelf:"center"
  },
  middleContainer:{
    marginTop:20,
    marginLeft:10,
    height:60
  },
  middleButton01: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fdc453",
    borderColor:"deeppink",
    borderRadius:15,
    margin:7
  },
  middleButton02: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#fe8d6f",
    borderRadius:15,
    margin:7
  },
  middleButton03: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#9adbc5",
    borderRadius:15,
    margin:7
  },
  middleButtonText: {
    color:"#fff",
    fontWeight:"700",
    //텍스트의 현재 위치에서의 정렬 
    textAlign:"center"
  },
  middleButton04: {
    width:100,
    height:50,
    padding:15,
    backgroundColor:"#f886a8",
    borderRadius:15,
    margin:7
  },
  cardContainer: {
    marginTop:10,
    marginLeft:10
  },
  card:{
    flex:1,
    //컨텐츠들을 가로로 나열
    //세로로 나열은 column <- 디폴트 값임 
    flexDirection:"row",
    margin:10,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10

  },
  cardImage: {
    flex:1,
    width:100,
    height:100,
    borderRadius:10,
  },
  cardText: {
    flex:2,
    flexDirection:"column",
    marginLeft:10,
  },
  cardTitle: {
    fontSize:20,
    fontWeight:"700"
  },
  cardDesc: {
    fontSize:15
  },
  cardDate: {
    fontSize:10,
    color:"#A6A6A6",
  },
  //카드 짝수 홀수에 따른 스타일 적용
  cardEven:{
    flex:1,
    flexDirection:"row",
    margin:10,
    backgroundColor:"#FFFED7",
    borderRadius:20,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10
  },
  cardOdd:{
    flex:1,
    flexDirection:"row",
    margin:10,
    borderBottomWidth:0.5,
    borderBottomColor:"#eee",
    paddingBottom:10
  },


});

아까 봤던 map 반복문 부분에 삼항 연산자를 추가하여 짝수 순서/홀수 순서에 해당하는 컨텐츠 각각 따로 지정된 스타일 디자인을 줘 앱 화면을 다르게 표현할수 있다. 아래 그림과 같이 구현된다.


6. 2주차 숙제

2주차 숙제는 아래 그림과 같이 어바웃 페이지 앱화면을 만드는것이었다.

다만, 그냥 만드는게 아니라, 복습 겸 따로 폴더를 만들고 MainPage.js와 AboutPage.js를 만들어 원래 app.js 내용을 MainPage.js에 복붙한뒤 함수 맞춰주고 상단 임포트 경로 ../ 맞춰주고 app.js는 원래 담았던 모든 내용을 지운뒤 AboutPage.js를 임포트해서 표현하는 모듈 방식으로 하라고 하셨다. 또 이것이 3주차때 배울 컴포넌트 & 페이지화의 예습이라고도 하셨다. 그래서 시행착오 끝에 완성하여 다음과 같은 코드문이 나왔다.

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


export default function AboutPage(){
    const aboutImage = "https://firebasestorage.googleapis.com/v0/b/sparta-image.appspot.com/o/lecture%2FaboutImage.png?alt=media&token=13e1c4f6-b802-4975-9773-e305fc7475c4"
  return (
    <View style={styles.container}>
        <Text style={styles.title}>HI! 스파르타코딩 앱개발 반에 오신것을 환영합니다</Text>
       
        
        <View style={styles.textContainer}>
            <Image style={styles.aboutImage} source={{uri:aboutImage}} resizeMode={"cover"}/>
            <Text style={styles.desc01}>많은 내용을 간결하게 담아내려 노력했습니다!</Text>
            <Text style={styles.desc02}>꼭 완주 하셔서 꼭 여러분것으로 만들어가시길 바랍니다</Text>
            <TouchableOpacity style={styles.button}>
                <Text style={styles.buttonText}>여러분의 인스타계정</Text>
            </TouchableOpacity>
        </View>
    </View>)
}

const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:"#1F266A",
        alignItems:"center"
      
    },
    title: {
        fontSize:30,
        fontWeight:"700",
        color:"#fff",
        paddingLeft:30,
        paddingTop:100,
        paddingRight:30
    },
    textContainer: {
        width:300,
        height:500,
        backgroundColor:"#fff",
        marginTop:50,
        borderRadius:30,
        justifyContent:"center",
        alignItems:"center"
    },
    aboutImage:{
        width:150,
        height:150,
        borderRadius:30
    },
    desc01: {
        textAlign:"center",
        fontSize:20,
        fontWeight:"700",
        paddingLeft:22,
        paddingRight:22

    },
    desc02: {
        textAlign:"center",
        fontSize:15,
        fontWeight:"700",
        padding:22
    },
    button:{
        backgroundColor:"orange",
        padding:20,
        borderRadius:15
    },
    buttonText: {
        color:"#fff",
        fontSize:15,
        fontWeight:"700"
    }
})

0개의 댓글