리액트 컴포넌트, 속성, 상태

리액트 기초지식

  1. 컴포넌트(Component) : 정해진 요소를 사용하여 만든 화면의 일부분
  1. 상태(State) : 컴포넌트에서 데이터를 유지하고 관리하기 위한 방법 = 데이터
  1. 속성(Props) : 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방식 = 데이터 전달
  1. useEffect : 화면에 컴포넌트가 그려지면 처음 실행해야 하는 함수들을 모아두는 곳
  • Component에 있는 Props가 데이터를 전달 → Component 안의 State가 데이터를 관리

 

컴포넌트(Component)

  • UI 요소, 화면의 모든 부분

 

  • Card.js
//비구조 할당 방식으로 넘긴 속성 데이터를 꺼내 사용함
export default function Card({content}) {
    return (<View style={styles.card}>
        <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>)
}
  • 비구조 할당 방식 이용

 

  • MainPage.js
import Card from '../components/Card';

...

<View style={styles.cardContainer}>
	{/* 하나의 카드 영역을 나타내는 View */}
  {
		 tip.map((content,i)=>{
	     return (<Card content={content} key={i}/>)
     })
  }    
</View>
  • MainPage.js에서 Card.js로 content라는 데이터를 넘겼다.

 

속성(Props)

  • 컴포넌트에 데이터를 전달하는 것
  • key와 value의 형태 (ex. content={content})
  • 컴포넌트에 대해 map으로 반복문 → 반드시 인덱스(i)를 key={i}로 속성 전달

 

상태(useState)와 useEffect

  • 컴포넌트마다 보유, 관리하는 데이터를 상태라고 한다.
  • 리액트에서 상태(state) → useState로 생성, setState로 변경 가능

useState

  • 리액트에서 화면은 데이터에 따라 변경되고, 상태(state)로 관리되는 데이터가 변경되면 화면이 바뀐다.
  • UI = component(state)

useEffect

  • 화면이 로딩되고 가장 먼저 실행되는 함수
useEffect(()=>{
	화면이 그려지고 나서 가장 먼저 실행돼야 할 코드
},[])
  • 데이터를 준비(데이터를 받고 상태(state)에 반영)할 때 사용
  • 화면이 그려진다 → useEffect가 데이터를 준비 → 상태 데이터가 업데이트 되었으니 다시 화면이 그려진다

 

const [state, setState] = useState([])

useEffect(()=>{
	setState(data)
},[])
  • state : 컴포넌트에서 관리될 상태 데이터를 담고 있는 변수
  • setState : state를 변경시킬 때 사용해야하는 함수
  • useState() 안에 전달되는 것 → state 초기값
  • useEffect 함수로 data.json에서 가져온 데이터를 state에 담음

 

로딩 화면

  • useEffect는 화면이 그려지고 나서 실행되는데, 현재는 화면을 그릴 때 이용할 state에 값이 설정되어 있지 않은 상태임
  • 따라서 로딩 화면을 먼저 그린 후 → useEffect를 통해 state를 설정하고 나서 → 메인 화면을 그려야 오류가 나지 않음

 

  • Loading.js 파일 생성 후 MainPage.js를 다음과 같이 수정
import Loading from '../components/Loading';

const [state,setState] = useState([])
const [ready,setReady] = useState(true) //초기 ready : True

useEffect(()=>{
    setTimeout(()=>{
        setState(data)
        setReady(false)
    },1000) //1000 = 1초
 },[])
  • 상태는 여러개 만들어도 된다.
  • setTimeout으로 1초 뒤에 상태 관리에 들어가게 함

 

return ready ? <Loading/> :  (
    <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}>
         {
          tip.map((content,i)=>{
            return (<Card content={content} key={i}/>)
          })
        }
      </View>
    </ScrollView>
  );
  • 삼항연산자를 이용해서 ready가 true이면 로딩 화면을, false이면 메인 화면을 그린다.
  • 실행 순서
    1. 초기 ready 상태는 true여서 로딩 화면을 먼저 그림
    1. useEffect가 실행돼서 1초 뒤에 state 값을 설정, ready를 false로 변경
    1. ready 상태가 변경되었으니 자동으로 retrun ready ? ... 가 실행됨
    1. false이므로 메인 화면이 그려짐

 

카테고리 기능

MainPage.js

const [state,setState] = useState([]) //전체 리스트
const [cateState,setCateState] = useState([]) //카테고리 선택에 따른 리스트
const [ready,setReady] = useState(true)

useEffect(()=>{
	setTimeout(()=>{
		let tip = data.tip;
		setState(tip)
		setCateState(tip) //처음 로딩 시 전체 리스트로 초기화
		setReady(false)
	},1000)
},[])
  • 선택한 카테고리에 따라 보여줄 리스트를 저장하는 cateState
  • useEffect에서 cateState를 tip (전체 데이터) 으로 초기화

 

<ScrollView style={styles.middleContainer} horizontal indicatorStyle={"white"}>
	<TouchableOpacity style={styles.middleButtonAll} onPress={()=>{category('전체보기')}}><Text style={styles.middleButtonTextAll}>전체보기</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton01} onPress={()=>{category('생활')}}><Text style={styles.middleButtonText}>생활</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton02} onPress={()=>{category('재테크')}}><Text style={styles.middleButtonText}>재테크</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton03} onPress={()=>{category('반려견')}}><Text style={styles.middleButtonText}>반려견</Text></TouchableOpacity>
	<TouchableOpacity style={styles.middleButton04} onPress={()=>{category('꿀팁 찜')}}><Text style={styles.middleButtonText}>꿀팁 찜</Text></TouchableOpacity>
</ScrollView>
  • 카테고리 버튼마다 onPress 함수를 설정함
  • category 함수에 카테고리 이름을 넘겨주도록 함

 

const category = (cate) => {
	if(cate == "전체보기"){
		//전체보기면 원래 꿀팁 데이터를 담고 있는 상태값으로 다시 초기화
		setCateState(state)
		}else{ //전체(state)에서 선택한 카테고리 데이터만 추출
			setCateState(state.filter((d)=>{
			return d.category == cate //true일때 cateSate에 넣는다
		}))
	}
}
  • category 함수에는 cate(카테고리 이름) 파라미터가 있음
  • 전체보기를 클릭했다면 cateState를 전체 데이터(state)로 변경
  • 그 외 카테고리 버튼을 클릭했다면 전체 데이터(state) 중 해당 카테고리에 속하는 데이터만 cateState에 저장 (filter 함수 이용)

 


 

스택네비게이터

네비게이션

  • 컴포넌트들을 페이지화 시켜서 해당 페이지끼리 이동을 가능하게 하는 라이브러리

 

도구 설치

yarn add @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

 

스택네비게이션

  • 컴포넌트에 페이지 기능 부여, 컴포넌트에서 컴포넌트로 이동할 수 있도록 해준다.
  • Stack.Screen : 페이지
  • Stack.Navigator : 책갈피 (모든 페이지를 등록시킬 곳)

 

설치

yarn add @react-navigation/stack

 

적용하기

  • StackNavigator.js
import { createStackNavigator } from '@react-navigation/stack';
//페이지로 만들 컴포넌트 불러오기
import DetailPage from '../pages/DetailPage';
import MainPage from '../pages/MainPage';

const Stack = createStackNavigator();
const StackNavigator = () =>{
	return (
		<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;

 

  • App.js
import React from 'react';
import { StatusBar } from 'expo-status-bar';
import {NavigationContainer} from '@react-navigation/native';
import StackNavigator from './navigation/StackNavigator'

export default function App() {
	console.disableYellowBox = true;

  return ( 
  <NavigationContainer>
    <StatusBar style="black" />
    <StackNavigator/>
  </NavigationContainer>);
}
  • 앱의 가장 최상위 컴포넌트인 App.js에 네비게이션 기능을 달아준 것
  • NavigationContainer 태그로 StatusBar(상태바)과 StackNavigator(직접 만든 스택네비게이션)을 감싼다.

 

💡
Stack.screen에 등록된 모든 페이지 컴포넌트들은 navigationroute라는 딕셔너리(객체)를 속성으로 넘겨받아 사용할 수 있다!
navigation.setOptions({
   title:'나만의 꿀팁'
})
navigation.navigate("DetailPage")
navigation.navigate("DetailPage",{
  title: title
})

const { title} = route.params; //비구조 할당방식
  • navigation
    1. setOptions : 제목, 헤더 옵션 스타일 등 바꿀 수 있음
    1. navigate : 해당 페이지로 이동하는 함수 (StackNavigator.js의 Stack.screen name 속성), 두번째 매개변수로 딕셔너리 데이터 전달
  • route : navigate로 인해 전달받은 데이터를 받는 딕셔너리

 


 

공유하기 기능

  • React Native 자체에서 제공하는 기능이다.
import { Share } from "react-native";

export default function DetailPage({navigation,route}) {
		...
    const share = () => {
        Share.share({
            message:`${tip.title} \n\n ${tip.desc} \n\n ${tip.image}`,
        });
    }
    return (
        <ScrollView style={styles.container}>
            <Image style={styles.image} source={{uri:tip.image}}/>
            <View style={styles.textContainer}>
                <Text style={styles.title}>{tip.title}</Text>
                <Text style={styles.desc}>{tip.desc}</Text>
                <View style={styles.buttonGroup}>
                    <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}>팁 찜하기</Text></TouchableOpacity>
                    <TouchableOpacity style={styles.button} onPress={()=>share()}><Text style={styles.buttonText}>팁 공유하기</Text></TouchableOpacity>
                </View>
            </View>
        </ScrollView>
    )
}
  • share 함수를 이용해 공유할 때 전달할 제목, 설명, 이미지 등을 설정할 수 있음

 


 

외부 링크 클릭 이벤트

도구 설치

expo install expo-linking

 

적용하기

import * as Linking from 'expo-linking';

const link = () => {
	Linking.openURL("https://spartacodingclub.kr")
}

<TouchableOpacity style={styles.button} onPress={()=>link()}><Text style={styles.buttonText}>외부 링크</Text></TouchableOpacity>

 


리액트를 기반으로 하는 리액트 네이티브를 공부하고 있기 때문에

리액트의 기초 지식인 컴포넌트/속성/상태에 대해 공부했다.

상태의 개념이 처음 접했을 때 헷갈렸는데, 상태의 변화가 일어날 때 화면을 새로 그린다는 것에 유의하자.

스택네비게이터를 이용해 여러 페이지간의 이동을 구현하는 방법을 알게 되어 좀더 앱다운 앱을 만들 수 있게 되었다.

그 외 공유하기 및 외부 링크 클릭 이벤트를 구현하는 방법을 익혀서 추후에 앱을 출시하게 된다면 유용하게 사용할 수 있을 것 같다!

스택네비게이션의 이용을 여러 번 복습해서 익숙해질때까지 공부해야겠다.

 

카드를 클릭하여 해당 내용의 상세 페이지로 이동한 모습

 

이번 주 숙제 : 어바웃 페이지에서 외부 링크 열기, 꿀팁찜 페이지 만들어서 네비게이터에 연결하기

리액트 네이티브 & Expo

리액트 네이티브

리액트(React) + 네이티브(Native)

리액트는 JS 기반인데 개발하다보면 카카오톡 로그인 등 안드로이드, iOS 각각의 코드를 알아야하는 상황이 생김 → Expo를 이용함

 

Expo

  • 리액트 네이티브 코어 앱 개발 코어 기술을 Expo로 감싸서 JS로만 개발할 수 있는 환경을 구축하도록 도와주는 역할
  • Expo 클라이언트 앱-Expo 서버를 킨다 → QR 코드를 찍어서 개발중인 앱을 확인한다.

개발 환경 준비

  1. Node & NPM-Node.js로 JS 개발 환경 구축-Node.js 설치 시 NPM 자동 설치
  2. -유용한 JS 도구를 가지고 올 때 NPM(Node Package Manager) 사용
  1. Yarn-NPM보다 더 효율적으로 JS 도구를 가져올 수 있는 도구
    💡
    npm install -g yarn * -g : 컴퓨터에 전역적으로 설치한다는 옵션
  2. -NPM을 이용해서 설치
  1. Expo 명령어 도구 설치-Expo 도구 : NPM을 이용해서 설치
  2. 💡
    npm install -g expo-cli
  1. Expo 가입 및 로컬에 Expo 계정 세팅
    • cmd창에서 Expo 서비스 로그인
    💡
    expo login —username 사용자이름
  1. Expo 실행하기
    • 바탕화면에 sparta study 생성
    • VS code에서 Open Folder - sparta study 폴더 선택
    • View - Terminal 클릭해서 터미널 창 열기
    • expo init으로 프로젝트 생성
    💡
    expo init sparta-honeytip-yewon
    *이때 오류나는 경우 터미널을 Powershell이 아닌 Command Prompt로 변경
    • 개발하기 위해 폴더 이동
    💡
    cd sparta-honeytip-yewon
    • expo 시작
    💡
    expo start
    *Expo 클라이언트 앱으로 개발을 진행할 것(같은 와이파이 상에서 연결이 안되는 문제 : LAN → Tunnel로 변경 후 새 QR 코드 인식해서 해결)
  2. → 구글 플레이스토어에서 앱 다운 후 QR 인식 (동일한 WIFI 연결)
  1. Expo 파일 간단 설명
    • assets 폴더 : 이미지 폴더
    • node_modules : 도구 폴더
    • App.js : 화면이 되는 파일
    • app.json : 앱 배포 시 설명서

React Native 태그, Styles, Flex

App.js

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}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

//화면을 꾸밈
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});
  • <View>, <Text> 등 꺽쇠를 이용한 부분 → JSX라고 함 (화면을 그리는 문법)
  • <View> </View> 처럼 태그로 화면의 한 영역을 구성할 때 → 엘리먼트라고 함

 

  • 사용할 태그는 import를 이용해서 가져온다.
import { StyleSheet, Text, View } from 'react-native';
  • 모든 엘리먼트는 감싸는 최상위 엘리먼트가 있어야 한다.
  • return을 이용해 렌더링할 때 항상 소괄호로 감싸져있어야 한다.
export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

 

View 태그

  • <View> : 화면의 영역을 잡아주는 엘리먼트

 

Text 태그

  • <Text> : 앱에 글을 작성하기 위해 사용하는 엘리먼트
  • 문자는 반드시 Text 태그 안에 써야함!

 

ScrollView 태그

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

export default function App() {
  return (
    <ScrollView 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 style={styles.textContainer}>
        <Text style={styles.textStyle}>영역을 충분히 갖는 텍스트 입니다!</Text>
      </View>
    </ScrollView>
  );
}
  • <ScrollView> : 감싼 엘리먼트 스크롤이 가능해진다.

 

Button 태그

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>
        <Button 
          style={styles.buttonStyle} 
          title="버튼입니다 "
          color="#f194ff" 
          onPress={function(){
            Alert.alert('팝업 알람입니다!!')
          }}
        />
        <Button 
            style={styles.buttonStyle} 
            title="버튼입니다 "
            color="#FF0000" 
            onPress={()=>{
              Alert.alert('팝업 알람입니다!!')
            }}
          />
          </View>
    </View>
  );
}
  • <Button> : 버튼을 만들 수 있는 엘리먼트
  • title 속성 : 버튼 안에 넣을 문자
  • color 속성 : 버튼 색상
  • onPress 속성 : 버튼 클릭 시 실행할 함수 연결

 

TouchableOpacity

  • <TouchableOpacity> : 버튼과 비슷한 역할을 함, 대신 화면에 영향을 주지 않음
  • 임의의 영역과 디자인에 버튼 기능을 추가할 때 주로 사용할 태그

 

Image 태그

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}>
      <Image 
        source={favicon}
				// 사용설명서에 나와 있는 resizeMode 속성 값을 그대로 넣어 적용합니다
        resizeMode={"repeat"}
        style={styles.imageStyle}
      />
    </View>
  );
}
  • <Image> : 이미지를 넣는 태그
  • import를 통해 assets 폴더의 이미지 파일을 가져온 다음 사용
  • source : import한 이미지
  • resizeMode : 이미지를 보여주는 방식-repeat : 이미지가 반복-그 외 contain, stretch, center
  •  
  • -cover : 이미지가 화면 전체에 꽉 차게 보여줌
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>
  );
}
  • 외부 이미지를 가져와서 사용하는 방법
  • source에 uri 지정

*코드가 너무 길면 alt+z를 눌러서 한 화면에 코드가 다 들어오도록 설정

 

Styles

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,
    backgroundColor: '#fff',
    justifyContent:"center",
    alignContent:"center"
  },
  textContainer: {
    margin:10,
    padding: 10,
    borderRadius:10,
    borderWidth:2,
    borderColor:"#000",
    borderStyle:"dotted",

  },
  textStyle: {
    color:"red",
    fontSize:20,
    fontWeight:"700",
    textAlign:"center"
  }
});
  • const style : 값의 재할당이 불가능한 style 변수 생성
  • StyleSheet에 딕셔너리를 넘겨줌
  • margin, padding, borderRadius, borderWidth, borderColor, borderStyle
  • color, fontSize, fontWeight, textAlign

 

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 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,
    flexDirection:"row",
    justifyContent:"flex-end",
    backgroundColor:"orange"
  }
});
  • flex : 레이아웃 비율 (상대적)
  • flexDirection : flex 방향 지정
  • 상하 : column (기본) / 좌우 : row
  • justifyContent : flexDirection에 따라 정렬
  • flex-start, flex-end, center를 가장 자주 씀, 그 외 space-around, space-between, space-evenly
  • alignItems : flexDirection 방향과 반대로 정렬 (flex 영역 내에서 사용 가능)
  • alignSelf : flex 없어도 가능
  • numberOfLines={3} : 텍스트 잘리는 부분 ... 으로 대체

 

앱&자바스크립트 모듈, 반복문

  • App.js : 모듈 시스템에 의해서 내보내짐
  • 앞으로 여러 파일들을 만들 때 export default function을 이용해서 내보낼 것
export default function App() {...}

 

  • data.json 파일 : 딕셔너리 + 리스트 구조
  • App.js에 data.json 파일 import
import data from './data.json'

 

  • data.tip으로 json 파일의 tip 가져오기
let tip = data.tip;
let todayWeather = 10 + 17;
let todayCondition = "흐림"

 

  • App.js JSX 문법 안에서 map을 이용한 반복문
  • 반복문을 돌릴 때 가장 최상위 태그에는 key값이 주어져야 한다. (key는 유니크함)
<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>

 

  • JSX 안에서 중괄호 { }를 쓰면 그 안에서 자바스크립트 문법을 쓸 수 있다!
<Text style={styles.weather}>오늘의 날씨: {todayWeather + '°C ' + todayCondition} </Text>

 

삼항연산자

let result = 조건 ? 참일때 : 거짓일때
let result = 10>2 : '참' : '거짓'

tip.map((content,i)={
	return i%2==0 ? (<View>...</View>) : (<View>...</View>)
})

 

숙제

어바웃 페이지 만들기

Uploaded by Notion2Tistory v1.1.0


지난 주 1주차 수업에서는 자바스크립트의 기초를 배웠고

이번 주에는 본격적으로 리액트를 이용하여 앱을 만들어보는 시간을 가졌다.

Node.js, NPM, Expo 등 미리 세팅해야할 사항이 많아보여서 처음에는 약간 겁을 먹었으나

세팅을 무사히 마친 후 VS Code에서 JSX의 다양한 태그들을 활용하며 레이아웃을 구성하니 흥미로웠다.

 

태그를 사용하여 레이아웃을 구성하는 것은 안드로이드 스튜디오와 비슷해서 낯설지 않았고

이미 css를 공부했었기 때문에 StyleSheet를 활용하여 꾸미는 것도 익숙했다.

아직 flex 개념이 헷갈리는데 추가적으로 공부를 해서 익혀야겠다. (안드로이드 스튜디오 layout_gravity가 생각나는 속성이었다.)

 

Expo 클라이언트 앱을 이용하여 실시간으로 내가 수정한 코드가 반영된 모습을 볼 수 있는 점이 정말 편리했다.

안드로이드 스튜디오 같은 경우에는 미리보기 창으로 레이아웃을 얼추 볼 수 있긴 하나

제대로 실행시키려면 빌드를 해야하기 때문에 시간이 걸려서 은근 번거로운데

Expo는 ctrl+s 할 때마다 바로 핸드폰 화면에 수정 사항이 반영되기 때문에 앞으로 개발하면서 시간 단축이 많이 될 것 같다는 생각이 들었다.

또한 오류 메세지도 자세하게 나오는 편이라 어느 부분에서 오류가 났는지 확인하기 쉬웠다.

 

이번 주 강의를 열심히 수강한 결과, 마지막에 첨부한 이미지처럼 숙제로 어바웃 페이지를 구현할 수 있었다.

오늘 막 리액트에 입문한 것 치고 많은 것을 배우고 그것을 응용할 수 있는 능력을 가지게 된 것 같아 좋았다!

앱개발이란?

앱 서비스를 만드는 것 → 클라이언트, 서버를 모두 만들어야함

  • 클라이언트 : 사용자가 보는 화면
  • 서버 : 데이터가 있는 곳, 데이터 요청 시 응답해주는 곳

 

앱 개발 : 안드로이드, iOS(아이폰)

  • 안드로이드 → Java & Kotlin
  • iOS → Swift

운영체제가 다르기 때문에 개발 기술 언어가 다르며, 둘 다 러닝 커브 (허들) 존재

 

앱개발 종류

  1. 네이티브 앱 : 안드로이드, iOS를 각각 개발
  1. 하이브리드 앱 : 웹 사이트를 만들고 간단히 배포하는 앱
  1. 크로스 플랫폼 앱 : JS 하나의 언어로 두 개의 플랫폼의 앱을 제작 (강의에서 다룰 것)

 

리액트 네이티브 (React Native)

  • 크로스 플랫폼 앱 개발 언어 중 하나
  • 페이스북에서 만들었고 지원함
  • 리액트(React.js) 라이브럴/프레임워크를 기반으로 앱 제작하는 기술
  • 자바스크립트(JS)로 개발 가능

 


 

Javascript 기초

js는 웹 개발에서만 사용되는 기술이 아닌가? → 아니다. (리액트 네이티브가 js 기반)

js를 학습하면 웹/앱 개발 가능

 

크롬 F12 콘솔창에서 실습 진행

console.log("hello world")

console.log → 화면에 값을 출력하는 함수

 

  • F12 콘솔창 점 세개 버튼 클릭 → Dock side에서 팝업으로 띄울 수 있음
  • 상단의 clear console 버튼을 클릭해서 콘솔창 정리 가능 (변수는 그대로 유지)
  • Shift+Enter로 여러 줄 입력 가능

 

변수

변수 선언

  • let 이용
let num = 20
console.log(num) //20

 

숫자형 및 문자형 변수

let a = 1
let b = 2
let c = a + b
console.log(c) //3

let first = 'yewon'
let last = 'jeong' //큰따옴표 or 작은따옴표로 문자열
console.log(first+" "+last) //yewon jeong

 

변수 이름 규칙

let firstName //카멜 케이스 (camel case)
let first_name // 스네이크 케이스 (snake case)

 

const 변수

  • 값을 재할당할 수 없는 변수
let num = 100
const num2 = 1000
num = 200
num2 = 2000
console.log(num)
console.log(num2)

→ 위의 코드 실행 시 오류 발생

 

💡
Uncaught TypeError: Assignment to constant variable. at <anonymous>:4:6

const로 선언한 변수는 값을 바꿀 수 없기 때문

 

리스트

리스트란?

  • 여러 데이터들을 담을 수 있는 자료형
  • 인덱스는 0부터 시작
let a_list = [1,2,3,4,'yewon',6,'jeong']

//두 번째 값을 꺼낸다 (인덱스 1)
console.log(a_list[1]) //2

 

값 추가하기

  • push 함수 이용
a_list.push('sparta')
console.log(a_list) //[1, 2, 3, 4, "yewon", 6, "jeong", "sparta"]

 

길이

  • length
a_list.length //8

 

딕셔너리

딕셔너리란?

  • 사전 형식
  • key : value
let a_dict = {"name":"yewon", "age":23}
console.log(a_dict["name"])
a_dict["height"] = 200 //값 추가
console.log(a_dict) //{name: "yewon", age: 23, height: 200}

 

리스트, 딕셔너리 복합 구조

let names = [{"name":"yewon", "age":23}, {"name":"yewon2","age":23}]
console.log(names)

실행 결과

💡
(2) [{…}, {…}] 0: {name: "yewon", age: 23} 1: {name: "yewon2", age: 23} length: 2 __proto__: Array(0)

 

names.push({"name":"yewon3","age":23}) //3
console.log(names)
names[1]["age"] //23

 

JSON 데이터 구조

*크롬 확장 프로그램 JSON view 설치

 

함수

기본 제공 함수

  • 나머지 구하기 : %
  • 대문자로 바꾸기 : toUpperCase()
  • 특정 문자로 문자열을 나누기 : split() (ex. split('.') , split('@') ...)
  • 문자열 합치기 : join()
let myname = "jeongyewon"
console.log(myname.toUpperCase()) //JEONGYEWON

let email = "yewon@gmail.com"
console.log(email.split('@')) //["yewon", "gmail.com"] 리스트 형식
console.log(email.split('@')[1]) //gmail.com
console.log(email.split('@')[1].split('.')) //["gmail", "com"]
console.log(email.split('@')[1].split('.')[1]) //com

let txt = "서울시-마포구-망원동"
let names = txt.split("-")
console.log(names) //["서울시", "마포구", "망원동"]
console.log(names.join('>')) //서울시>마포구>망원동

 

함수 만들기

  • 함수 이름 지정
  • 파라미터 이용
  • return을 이용하여 리턴값 반환
function funcName(){
    console.log("함수")
}
funcName() //함수

function sum(num1,num2){
    console.log(num1+num2)
}
sum(10,20) //30

function minus(num1,num2){
    return num1-num2
}
let result = minus(100,10) //변수에 리턴값 저장
console.log(result) //90

 

  • 리터럴 함수 선언 방식 : 변수에 함수를 저장
let a = function(){
    console.log("리터럴 함수 방식")
}
a()

 

조건문

if - else if - else

function is_adult(age){
    if(age>20){
        console.log("성인")
    }else if(age>10){
        console.log("청소년")
    }else{
        console.log("어린이")
    }
}
is_adult(30) //성인

 

AND 연산자

  • 모든 조건이 참이여야 참
function is_adult(age, sex){
	if(age > 20 && sex == '여'){
		alert('성인 여성')
	} else if (age > 20 && sex == '남') {
		alert('성인 남성')
	} else {
		alert('청소년이에요')
	}
}

 

OR 연산자

  • 하나라도 참이면 참
function is_adult(age, sex){
	if (age > 65 || age < 10) {
		alert('탑승하실 수 없습니다')
	} else if(age > 20 && sex == '여'){
		alert('성인 여성')
	} else if (age > 20 && sex == '남') {
		alert('성인 남성')
	} else {
		alert('청소년이에요')
	}
}
is_adult(25,'남') //성인 남성

 

반복문

for문

  • 시작 조건, 반복 조건, 더하기
for(let i=0;i<10;i++){
    console.log(i)
}
// 0~9까지 출력

 

  • 리스트와 같이 이용
let people=['철수','영희','민수','형준','기남','동희']
for(let i=0;i<people.length;i++){
    console.log(people[i])
}

 

  • 리스트, 딕셔너리 복합 구조
let scores = [
	{'name':'철수', 'score':90},
	{'name':'영희', 'score':85},
	{'name':'민수', 'score':70},
  {'name':'형준', 'score':50},
  {'name':'기남', 'score':68},
  {'name':'동희', 'score':30},
]

for (let i=0;i<scores.length;i++){
    if(scores[i]['score']<70){
        console.log(scores[i]); //70점 미만의 점수인 경우에만 출력
    }
}

 


 

앱개발에 자주 쓰이는 Javascript

화살표 함수

  • 함수를 짧게 선언할 수 있음
  • function 대신 () ⇒ 이용
let a = () => {
		console.log("arrow function")
}
a();

 

비구조 할당 방식

  • key에 따옴표를 붙이지 않아도 됨
  • . 을 이용해서 딕셔너리 안의 값 접근 가능
  • 딕셔너리 키와 값을 빠르게 꺼낼 수 있음
let blog = {
		owner : "noah",
		url : "noahlogs.tistory.com",
		getPost() {
				console.log("ES6 문법 정리");
		}
};

//기존 할당 방식
let owner = blog['owner']
let owner = blog.owner //동일한 결과
let getPost = blog.getPost()

//비구조 할당 방식
let { owner, getPost } = blog;
console.log(owner)

 

💡
리액트 네이티브에서 가장 많이 사용할 방식!
//비구조 할당 방식
let blogFunction = ({owner,url,getPost}) => {
		console.log(owner)
		console.log(url)
		console.log(getPost())
}
blogFunction(blog)

 

백틱 (`) 사용

  • 줄바꿈을 자유롭게
const message = `줄바꿈
자유롭게 가능`
message //"줄바꿈\n자유롭게 가능"
  • 문자열 안에 변수 넣기
let name="정예원"
let str=`내 이름은 ${name}`
console.log(str) //내 이름은 정예원

 

딕셔너리 구성을 간단하게

  • 변수 이름과 key가 같은 경우
  • 기존에는 딕셔너리 내에서 name:name, job:job으로 썼음
let name="정예원"
let job = "개발자"
let dic={
    name,
    job
}
console.log(dic) //{name: "정예원", job: "개발자"}

 

map

  • 기존 코드
//기존 코드
let numbers = [1,2,3,4,5,6,7]
for(let i=0; i<numbers.length; i++){
		console.log(numbers[i])
}

→ 리스트 길이 값을 알아야 for문 사용 가능

 

  • map 활용
let numbers = [1,2,3,4,5,6,7]
numbers.map((value,i) => {
		console.log(value,i)
})

//위와 동일한 코드
numbers.map(function(value,i){
		console.log(value,i)
}
💡
value : 값 i : 인덱스 (순서)

 

JS 파일 모듈화

  • export : 자바스크립트 값, 함수, 딕셔너리, JS 파일 자체를 외부로 내보내기
  • import : JS 파일로 불러오기
//util.js 파일

export function times(x){
		return x*x
}
export function plusTwo(number){
		return number + 2
}
//index.js 파일

import {times, plusTwo} from './util.js'
console.log(times(2))
console.log(plusTwo(3))

 

  • export default
💡
export default는 하나의 파일 안에서 유일해야함
//util.js 파일
export default function times(x){
		return x*x
}
//app.js 파일
import k from './util.js'
console.log(k(4)) //16

 

Uploaded by Notion2Tistory v1.1.0


지금까지 "리액트"라는 것을 들어보긴 했으나 내가 직접 다루고 공부한 적은 없었다.

오늘부터 수강하게 된 앱개발 종합반에서는 리액트 네이티브 앱 개발을 배울 것이다.

 

리액트 네이티브란, 안드로이드와 iOS 두 개의 운영체제에서 모두 실행될 수 있는 크로스 플랫폼 앱 개발 언어 중 하나이며,
이번 주에는 리액트를 본격적으로 다루기 전에
리액트의 기반 언어인 자바스크립트의 기초를 배우는 시간을 가졌다.

자바스크립트의 리스트, 딕셔너리 구조와 이 둘의 복합 구조를 사용하는 방법을 알게 되었고
JSON 데이터 구조가 리스트와 딕셔너리의 복합 구조라는 사실을 알게 되어
JSON이라는 내가 알지 못하던 것에 대한 거부감(?)이 사라진 듯 하다.

튜터님이 차근차근 잘 알려주셔서 그런듯!👍

그리고 앱 개발 시 자주 쓰인다고 하는 화살표 함수, 비구조 할당 방식, map 등을 배워서 좋았다~

+ Recent posts