솜이의 데브로그

[React Native] 스타일링 본문

dev/React native

[React Native] 스타일링

somsoming 2021. 10. 16. 00:47

Reference : 처음배우는 리액트 네이티브

 

 

리액트 네이티브에서의 스타일링은 CSS와 약간의 차이가 있다.

CSS와는 달리 카멜 표기법으로 작성해야한다.

 

 

스타일링

 

인라인 스타일링

  • 인라인 스타일링은 어떤 스타일이 적용되는지 잘 보인다는 장점이 있다.
  • 그러나 비슷한 역할의 컴포넌트에 동일한 코드가 반복된다는 점과, 어떤 이유로 스타일이 적용되었는지 코드만으로 이해하기 어렵다는 단점이 있다.

 

클래스 스타일링

  • 컴포넌트의 태그에 직접 입력하는 방식이 아니라 스타일 시트에 정의된 스타일을 사용하는 방법이다.
  • 스타일 시트에 스타일을 정의, 컴포넌트에서는 정의된 스타일의 이름으로 적용. (CSS 클래스 이용법과 유사)
  • 프로젝트를 생성하면 함께 생성되는 App.js 파일에서 클래스 스타일링이 적용된다.
  • 스타일의 의도를 파악하기 쉽고(ex : 빨간글씨에는 error), 전체적인 스타일 관리에 용이하다.

 

여러개의 스타일 적용

  • 여러 개의 스타일을 적용해야 할 경우 배열을 이용하여 style 속성에 여러 개의 스타일을 적용한다.
  • 배열 사용 시, 적용하는 스타일의 순서에 주의해야한다.
    • 뒤에 오는 스타일이 앞에 있는 스타일을 덮는다.
  • 상황에 따라 인라인 스타일과 클래스 스타일 방식을 혼용해서 사용할 수 있다.
  • import React from 'react';
    import { StyleSheet, View, Text } from 'react-native';
    
    const App = () => {
        return(
           <View style ={styles.container}>
               <Text style={[styles.text, {color: 'green'}]}>Inline Styling - Text</Text>
               <Text style={[styles.text, styles.error]}>Inline Styling - Error</Text>
            </View>
        );
    };
    
    const styles = StyleSheet.create({
        container: {
            flex:1,
            backgroundColor: '#fff',
            alignItems: 'center',
            justifyContent: 'center',
        },
        text: {
            padding: 10,
            fontSize: 26,
            fontWeight: '600',
            color: 'black',
        },
        error: {
            fontWeight: '400',
            color: 'red',
        },
    });
    
    export default App;

 

외부 스타일 이용하기

  • 외부 파일에 스타일을 정의하고 여러 개의 파일에서 스타일을 공통으로 사용하는 경우
  • 외부에 StyleSheet 정의하는 파일 작성 후, App.js 에서 import 후 사용한다.

 

 

리액트 네이티브 스타일

 

flex와 범위

  • 각 컴포넌트의 width와 height을 설정
  • src>components>Layout.js
  • import React from 'react';
    import { StyleSheet, View, Text } from 'react-native';
    
    export const Header = () => {
        return(
            <View style={[styles.container, styles.header]}>
                <Text style={styles.text}>Header</Text>
            </View>
        );
    };
    
    export const Contents = () => {
        return(
            <View style={[styles.container, styles.contents]}>
                <Text style={styles.text}>Contents</Text>
            </View>
        );
    };
    
    export const Footer = () => {
        return(
            <View style={[styles.container, styles.footer]}>
                <Text style={styles.text}>Footer</Text>
            </View>
        );
    };
    
    const styles = StyleSheet.create({
        container:{
            width : '100%',
            alignItems: 'center',
            justifyContent: 'center',
            height: 80,
        },
        header: {
            backgroundColor: '#f1c40f',
        },
        contents: {
            backgroundColor: '#1abc9c',
            height: 640,
        },
        footer: {
            backgroundColor: '#3498db',
        },
        text: {
            fontSize: 26,
        },
    });
  • 위처럼 코드 컴포넌트 생성 시 크기와 스타일을 지정하고, 해당 컴포넌트들을 app 에서 생성한다.

 

  • 위의 코드는 아이폰 11기준으로 진행했고 내 폰이 11이기 때문에 잘 나오지만, 이처럼 고정값을 이용하면 기기마다 화면의 크기 차이 때문에 서로 다른 모습으로 나타난다.
  • 따라서 flex를 이용해 항상 비율 크기로 설정되도록 한다.
    • flex는 값을 숫자로 받으며, 0일때는 설정된 width와 height 값에 따라 크기 결정
    • 양수인 경우 값에 비례하여 크기 조정. ex) 1:2:1 로 나눌 경우 원하는 비율만큼 컴포넌트의 flex값을 1,2,1로 설정.

왼쪽은 height 값을 각각 지정했을 때의 결과, 오른쪽은 flex로 1:2:1 비율 조정한 결과.

 

한 컴포넌트의 크기만 고정하고 싶은 경우, 해당 컴포넌트의 높이를 고정하고 나머지 부분들을 각 차지하도록 한다.

 

 

정렬

flexDirection

컴포넌트가 쌓이는 방식을 변경할 수 있다. 자기 자신이 쌓이는 방향이 아닌 자식 컴포넌트가 쌓이는 방향.

  • column : 세로 방향으로 정렬 (기본값)
  • column-reverse : 세로 방향 역순 정렬
  • row : 가로 방향으로 정렬
  • row-reverse : 가로 방향 역순 정렬

 

justifyContent, alignItems

컴포넌트를 배치할 방향을 결정한 후 방향에 따라 정렬하는 방식을 결정하는 속성이 justifyContent와 alignItems 이다.

  • justifyContent : flexDirection에서 결정한 방향과 동일한 방향으로 정렬하는 속성
  • alignItems : flexDirection에서 결정한 방향과 수직인 방향으로 정렬하는 속성

justifyContent값 설정

  • flex-start : 시작점에서부터 정렬(기본값)
  • flex-end : 끝에서부터 정렬
  • center : 중앙정렬
  • space-between : 컴포넌트 사이의 공간을 동일하게 만들어서 정렬
  • space-around : 컴포넌트 각각의 주변 공간을 동일하게 만들어서 정렬
  • space-evenly : 컴포넌트 사이와 양 끝에 동일한 공간을 만들어서 정렬

 

alignItems 값 설정

  • flex-start : 시작점에서부터 정렬(기본값)
  • flex-end : 끝에서부터 정렬
  • center : 중앙정렬
  • stretch : alignItems의 방향으로 컴포넌트 확장
  • baseline : 컴포넌트 내부의 텍스트 베이스라인을 기준으로 정렬

 

그림자

리액트 네이티브 플랫폼마다 다르게 적용되는 스타일 속성

  • shadowColor : 그림자 색 설정
  • shadowOffset : width와 height 값을 지정하여 그림자 거리 설정
  • shadowOpacity : 그림자의 불투명도 설정
  • shadowRadius : 그림자의 흐림 반경 설정

위의 속성들은 iOS에만 적용. 안드로이드에서 표현하려면 elevation 이라는 속성을 사용해야 한다.

각 플랫폼마다 적용여부가 다른 속성은 Platform 모듈을 이용해 각 플랫폼마다 다른 코드가 적용되도록 코드를 작성할 수 있다.

https://reactnative.dev/docs/platform-specific-code#platform-module

 

Platform Specific Code · React Native

When building a cross-platform app, you'll want to re-use as much code as possible. Scenarios may arise where it makes sense for the code to be different, for example you may want to implement separate visual components for Android and iOS.

reactnative.dev

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

export default () => {
  return <View style={styles.shadow}></View>;
};

const styles = StyleSheet.create({
  shadow: {
    backgroundColor: '#fff',
    width: 200,
    height: 200,
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: {
          width: 10,
          height: 10,
        },
        shadowOpacity: 0.5,
        shadowRadius: 10,
      },
      android: {
        elevation: 20,
      },
    }),
  },
});

 

 

스타일드 컴포넌트

 

리액트 네이티브의 스타일에서 웹 프로그래밍과의 차이

  • 카멜 표기법으로 작성해야 한다.
  • CSS 속성과 이름이 같지만 타입이 다르거나 단위가 생략되는 경우가 있음
  • width, height 과 같이 입력되는 값에 맞춰 타입이 달라지는 경우가 있다.

→ 스타일드 컴포넌트로 해소할 수 있다.

스타일드 컴포넌트는 자바스크립트 파일 안에 스타일을 작성하는 CSS-in-JS 라이브러리이다. (스타일이 적용되는 컴포넌트라고 생각)

 

다음 명령어를 통해 스타일드 컴포넌트를 설치한다.

npm install styled-components

 

  • "styled.[컴포넌트 이름]" 형태 뒤에 백틱(`) 을 사용하여 만든 문자열을 붙이고 그 안에 스타일을 지정한다.
    이 문법을 태그드 템플릿 리터럴 이라고 한다.
  • styled 뒤에 작성하는 컴포넌트 이름은 반드시 존재하는 컴포넌트를 지정해야 한다.
  • 스타일드 컴포넌트에서는 css를 이용하여 재사용 가능한 코드를 관리할 수 있다.
  • styled(컴포넌트 이름) 형식으로 이미 작성된 스타일을 상속받아 새로운 스타일드 컴포넌트를 만들 수 있다.

 

import React from "react";
import styled from 'styled-components/native';

const ButtonContainer = styled.TouchableOpacity`
    background-color: #9b59b6;
    border-radius: 15px;
    padding: 15px 40px;
    margin: 10px 0px;
    justify-content: center;
`;

const Title = styled.Text`
    font-size: 20px;
    font-weight: 600;
    color: #fff;
`;

const Button = props => {
    return(
        <ButtonContainer>
            <Title>{props.title}</Title>
        </ButtonContainer>
    );
};

export default Button;

 

  • TouchableOpacity 컴포넌트에 스타일이 적용된 ButtonContainer 라는 이름의 컴포넌트 생성
  • Text 컴포넌트에 스타일이 적용된 Title 컴포넌트 생성
  • 스타일드 컴포넌트로 만들어진 컴포넌트 이용해서 Button 컴포넌트 생성

스타일드 컴포넌트를 이용하면 PaddingVertical 이나 PaddingHorizontal 처럼 익숙하지 않은 속성보다 조금 더 익숙한 이름과 적용방법으로 값을 설정할 수 있다.

 

 

props 사용하기

  • 스타일드 컴포넌트에서는 스타일을 작성하는 백틱 안에서 props 에 접근할 수 있다.
  • 따라서 스타일을 작성하는 곳에서 조건에 따라 스타일을 변경할 수 있다.
const ButtonContainer = styled.TouchableOpacity`
    background-color: ${props =>
        props.title === 'Hanbit' ? '#3498db':'#9b59b6'};
    border-radius: 15px;
    padding: 15px 40px;
    margin: 10px 0px;
    justify-content: center;
`;

 

 

attrs 사용하기

  • 스타일드 컴포넌트를 이용하면 스타일을 작성하는 곳에서 컴포넌트의 속성도 설정할 수 있다.
  • 속성 설정 시 전달된 props를 이용할 수 있으므로 props의 값에 따라 속성을 변경할 수 있다.

src/components/Input.js

import React from "react";
import styled from "styled-components/native";

const StyledInput = styled.TextInput.attrs(props => ({
    placeholder: 'Enter a text...',
    placeholderTextColor: props.borderColor
}))`
    width: 200px;
    height: 60px;
    margin: 5px;
    padding: 10px;
    border-radius: 10px;
    border: 2px;
    border-color: ${props => props.borderColor};
    font-size: 24px;
`;

const Input = props => {
    return <StyledInput borderColor={props.borderColor} />;
};

export default Input;
  • TextInput 컴포넌트를 이용해 StyledInput 컴포넌트 생성
  • placeholder와 placeholderTextColor 속성을 설정한다.
  • attrs를 이용해 스타일을 설정하는 곳에서 props의 값에 따라 컴포넌트의 속성을 다르게 적용 가능. 또는 항상 일정한 속성을 미리 정의할 수 있다.

 

 

ThemeProvider

  • ThemeProvider는 Context API를 활용해 애플리케이션 전체에서 스타일드 컴포넌트를 이용할 때 미리 정의한 값들을 사용할 수 있도록 props로 전달한다.

src/theme.js

export const theme = {
    purple: '#9b59b6',
    blue: '#3498db',
};
  • 모든 컴포넌트를 감싸는 최상위 컴포넌트로 ThemeProvider 컴포넌트를 사용한다.
  • ThemeProvider 컴포넌트의 자식 컴포넌트에서는 스타일드 컴포넌트를 이용할 때 props로 theme을 전달받아 미리 정의된 색을 이용할 수 있다.
  • 즉, 하나의 파일에서 미리 정의해둔 색을 하위 컴포넌트에서 사용할 수 있다. -> 유지보수에서 이점

 

ThemeProvider를 활용해 애플리케이션의 색 테마를 변경할 수 있다.

src/theme.js

export const lightTheme = {
    background: '#ffffff',
    text: '#ffffff',
    purple: '#9b59b6',
    blue: '#3498db',
};

export const darkTheme = {
    background: '#34495e',
    text: '#34495e',
    purple: '#9b59b6',
    blue: '#3498db',
};

 

const Container = styled.View`
    flex: 1;
    background-color: ${props => props.theme.background};
    align-items: center;
    justify-content : center;
`;

const App = () => {
    const [isDark, setIsDark] = useState(false);
    const _toggleSwitch = () => setIsDark(!isDark);

    return(
        <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
            <Container>
                <Switch value={isDark} onValueChange={_toggleSwitch} />
                <Button title="Hanbit" />
                <Button title="React Native" />
                <Input borderColor="#3498db" />
                <Input borderColor="#9b59b6" />
            </Container>
        </ThemeProvider>
    );
};
  • useState를 이용해 테마의 상태를 관리할 isDark와 상태를 변경할 setIsDark 함수 생성
  • Switch 컴포넌트를 활용해 isDark 상태 변경
  • ThemeProvider 컴포넌트의 theme 속성에는 isDark 상태에 따라 theme.js 파일에 정의된 테마 적용
  • App 컴포넌트의 Container 컴포넌트 스타일 정의하는 곳에서 props로 전달된 theme 이용해 배경색 설정

 

결과

 

 

Prettier

코드 스타일 정리 도구.

들여쓰기, 줄바꿈, 세미콜론 등을 일관성 있게 자동 변환해준다.

-> 협업에 용이

 

 

 

느낀점

뭐.. 웹 프로그래밍에 익숙한 방법으로 할 수 있도록 배웠는데 나는 아무것도 몰라서(^^) 이러나 저러나 낯설다ㅎ

아직까진 재밌다! 직관적인게 정말 큰 장점인 것 같다.