[앱개발 종합반] 3주 1일차 개발일지(3주 1강)


📱TIL App.301

복습: 나만의 꿀팁 앱 상세화면 만들기

1. 예시 이미지 / 나의 결과 이미지


2. 예시 이미지 보고 스스로 해보기

1) 사전 준비

// DetailPage.js
import React from 'react';
import { StyleSheet, Text, View, Image, TouchableOpacity, ScrollView } from 'react-native';

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

const styles = StyleSheet.create({

})


// App.js
import React from 'react'
import MainPage from './pages/MainPage'
import AboutPage from './pages/AboutPage'
import DetailPage from './pages/DetailPage'

export default function App() {
  // return (<MainPage/>)
  // return (<AboutPage/>)
  return (<DetailPage/>)
}

새로운 페이지를 만들기 위해서, 가장 먼저 pages 폴더에 DetailPage.js 파일을 만들고, 위와 같은 기본 구조를 세팅한다. 파일 이름과 함수 이름이 같아야 하므로 함수 이름을 DetailPage로 설정한 후, 기기 화면에서 노란색 권고사항 창을 숨기기 위해 console.disableYellowBox = true; 코드를 추가한다. 이 코드는 각 페이지 파일마다 추가해도 되고, App.js 파일에만 추가해도 된다. App.js 파일에서는, 이전에 불러온 <Mainpage/><AboutPage.js/> 태그를 주석 처리하고, <DetailPage/> 태그를 추가한다.

⚠️ console.disableYellowBox = true는 deprecated라는 메세지가 떴다. 공식 문서에 따르면 LogBox.ignoreAllLogs(value)로 대체될 예정이라고 한다. console.disableYellowBox = true; 대신 LogBox.ignoreAllLogs(true)를 사용하라는 경고 메세지이지만, 해당 코드를 제거해도 동작에는 문제가 없다.


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

export default function DetailPage() {
  console.disableYellowBox = true;
  const tip = {
    "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"
}
  return ()
}

const styles = StyleSheet.create({

})

다음으로, 사용할 JSON 데이터를 DetailPage() 함수 안에 입력한다. 입력한 데이터는 하단의 JSX 문법에서 딕셔너리(Object) 형태로 key값에 접근한 후 값을 꺼내 사용할 수 있다.


2) 나의 코드

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

export default function DetailPage() {
  console.disableYellowBox = true;
  const tip = {
    "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"
}
  return (
  <ScrollView style={styles.container}>
    <View style={styles.imageContainer}>
      <Image
        source={{uri:tip.image}}
        style={styles.detailImage}
      />
    </View>
    <View style={styles.textContainer}>
      <Text style={styles.titleText}>{tip.title}</Text>
      <Text style={styles.descText}>{tip.desc}</Text>
      <TouchableOpacity style={styles.button}><Text style={styles.buttonText}> 찜하기</Text></TouchableOpacity>
    </View>
  </ScrollView>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "black"
  },
  imageContainer: {
    alignItems: "center"
  },
  detailImage: {
    width: "92%",
    height: 415,
    marginTop: 50,
    borderRadius: 20
  },
  textContainer: {
    alignItems: "center",
    marginHorizontal: 20
  },
  titleText: {
    color: "white",
    fontSize: 22,
    fontWeight: "bold",
    marginTop: 30,
    marginBottom: 15
  },
  descText: {
    color: "white"
  },
  button: {
    width: 100,
    height: 45,
    marginTop: 25,
    justifyContent: "center",
    alignItems: "center",
    borderWidth: 2,
    borderRadius: 5,
    borderColor: "orange"
  },
  buttonText: {
    color: "white"
  }
})


3) 🤔 QnA

✅ 이미지를 assets 폴더에서 가져올 때는 코드 최상단에 import 키워드를 사용해서 이미지를 가져온다. 외부 이미지 주소를 사용할 때에는 <Image> 태그의 source 속성의 uri 키 값에 주소를 그대로 입력하거나, 주소를 담은 변수명을 입력한다. 이미지는 영역이 설정되어야 화면에 나온다는 점 잊지 말기!



☑️ 이미지의 width를 설정한 후 가운데 정렬을 하기 위해서, 웹 개발반에서 HTML을 다룰 때처럼 이미지 태그 스타일의 marginHorizontal 속성에 auto 값을 주었는데 아무런 변화가 없다.

✅ CSS에서 margin이나 padding 속성은 숫자 외에도 문자열 값을 가질 수 있다. 문자열 값으로는 퍼센트 또는 "auto"가 올 수 있다. "auto"는 브라우저가 자동으로 계산하는 값으로, 컨텐츠를 자동으로 가운데 정렬하는 데 사용된다. 다만 React Native에서는 margin이나 padding 속성값으로 auto를 지원하지 않는다. 2주차 실습 QnA에서도 언급했지만, React Native는 margin이라는 하나의 속성에 시계 방향으로 모든 값을 줄 수 있는 축약형 형태도 지원하지 않는다. CSS와 React Native가 헷갈린다면, React Native와 CSS를 비교한 글을 읽어보자.



✅ 앞으로 전체를 감싸는 최상위 태그로 <ScrollView>를 자주 사용하게 될 것이다. 이때 전체 화면을 가운데 정렬하기 위해 태그에 alignItems 속성을 주게 되면 ‘ScrollView child layout([“alignItems”])must be applied through the contentContainerStyle prop.’이라는 에러 메세지가 뜬다. 전체를 <ScrollView>로 감쌌다면, 자식 태그로 <View> 태그를 새로 설정해 그 태그에 정렬 속성을 주자.



☑️ 최상위 태그인 <View><ScrollView> 태그로 바꾸면, 퍼센트 값으로 지정한 이미지의 height 값이 무효 처리된다.

<ScrollView> 태그 자체가 상하 길이에 제한을 두지 않는다는 뜻이기 때문에, 이미지의 높이를 퍼센트로 설정하는 것은 의미가 없다. height 값을 숫자 값으로 주어야 한다.


3. 해설 코드와 배울 점

1) 기본 구조부터!

return (
  <ScrollView>
    <Image source={}/>
    <View>
      <Text></Text>
      <Text></Text>
      <TouchableOpacity><Text></Text></TouchableOpacity>
    </View>
  </ScrollView>
)

튜터분께선 기본적인 구조를 작성하고, 각 태그의 style key 이름을 먼저 설정하신 뒤 코드를 짜셨다. 처음부터 너무 복잡하게 생각할 필요 없이, 태그만이라도 기본적인 구조를 잡고 들어가는 게 효율적일 것 같다.


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

export default function DetailPage() {

  const tip = {
      "idx":9,
      "category":"재테크",
      "title":"렌탈 서비스 금액 비교해보기",
      "image": "https://storage.googleapis.com/sparta-image.appspot.com/lecture/money1.png",
      "desc":"요즘은 정수기, 공기 청정기, 자동차나 장난감 등 다양한 대여서비스가 활발합니다. 사는 것보다 경제적이라고 생각해 렌탈 서비스를 이용하는 분들이 늘어나고 있는데요. 다만, 이런 렌탈 서비스 이용이 하나둘 늘어나다 보면 그 금액은 겉잡을 수 없이 불어나게 됩니다. 특히, 렌탈 서비스는 빌려주는 물건의 관리비용까지 포함된 것이기에 생각만큼 저렴하지 않습니다. 직접 관리하며 사용할 수 있는 물건이 있는지 살펴보고, 렌탈 서비스 항목에서 제외해보세요. 렌탈 비용과 구매 비용, 관리 비용을 여러모로 비교해보고 고민해보는 것이 좋습니다. ",
      "date":"2020.09.09"
  }

  const popup = () => {
      Alert.alert("팝업!!")
  }
  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>
              <TouchableOpacity style={styles.button} onPress={()=>popup()}><Text style={styles.buttonText}> 찜하기</Text></TouchableOpacity>
          </View>
      </ScrollView>
    )
}

const styles = StyleSheet.create({
  container:{
    backgroundColor:"#000"
  },
  image:{
    height:400,
    margin:10,
    marginTop:40,
    borderRadius:20
  },
  textContainer:{
    padding:20,
    justifyContent:'center',
    alignItems:'center'
  },
  title: {
    fontSize:20,
    fontWeight:'700',
    color:"#eee"
  },
  desc:{
    marginTop:10,
    color:"#eee"
  },
  button:{
    width:100,
    marginTop:20,
    padding:10,
    borderWidth:1,
    borderColor:'deeppink',
    borderRadius:7
  },
  buttonText:{
    color:'#fff',
    textAlign:'center'
  }
})


2) ScrollView에서 flex?

튜터분의 설명에 따르면, <ScrollView>에서의 flex 값은 의미가 없다. 이는 스크롤 기능이 정확히 보여지는 화면을 몇 등분하는 것이 아니라, 화면에 넣은 컨텐츠를 모두 보여주기 위해 존재하기 때문이다. DetailPage 화면에서는 내부 컨텐츠들의 영역을 결정짓기 위해 height 값과 margin, padding 값을 적절히 잘 이용해야 한다. 코드를 짤 때 습관적으로 flex:1을 줬는데, <ScrollView> 태그에는 필요 없다!


3) flex 없이 justifyContentalignItems를?

textContainer:{
  padding:20,
  justifyContent:'center',
  alignItems:'center'
}

예전에 justifyContentalignItems에 대해 언급하면서, 이 두 속성은 flex로 지정한 영역 내에서만 이용이 가능하다고 했었다. 그런데 해설 코드 중 일부를 보면, flex를 사용하지 않고도 내부 컨텐츠가 중앙 정렬되는 모습을 볼 수 있다.

이는 두 속성이 적용되는 textContainer 태그가 하나의 자체 영역을 차지하고 있기 때문에, 즉 해당 태그가 텍스트 태그들의 부모 태그 역할을 하고 있기 때문이다. 따라서 이 태그에느 flex가 적용되고 있다고 해도 무방하다. 실제로 위 코드에 flex: 1을 입력해도 보이는 결과값은 같다. 내부 컨텐츠들을 포함한, 영역을 가진 태그이기 때문에 영역 안의 컨텐츠들의 위치를 정렬시킬 수 있는 것이다.

자체적인 영역을 갖고 있는 태그에서는 flex 속성이 적용되고 있다!

위의 코드에 굳이 flex: 1을 명시적으로 적는다면, 그것은 내부 컨텐츠들의 고유 영역을 비율로 지정하고 싶은 경우다. 이러한 경우가 아니라면, <View> 태그나 <Text> 태그들은 각자 고유한 영역을 갖고 있으므로, 내부 컨텐츠들의 영역 구분을 위해서가 아니라면 flex를 반드시 쓸 필요는 없다.



4) padding으로 버튼 크기 조절하기

나는 widthheight 속성을 사용해서 버튼의 크기를 지정했지만, 버튼 안에 텍스트 컨텐츠가 있으므로 padding을 주면 버튼의 내부 크기를 키울 수 있다. padding 속성을 사용해 상하좌우 간격을 모두 같은 정도로 띄웠기 때문에, 텍스트는 자동으로 버튼의 가운데에 위치한다. width 값을 더 길게 지정한 경우는 <Text> 태그에 textAlign 속성을 center로 주면 텍스트가 중앙 정렬된다.


Categories:

Updated:

Leave a comment