Programing/React, React Native

[React Native] 커스텀 모달 팝업 만들기(Custom Alert Modal)

hye3193 2024. 7. 18. 19:17

진행 중인 프로젝트에서 모달 창이 필요했는데, react native에서 제공하는 기본 Alert 스타일은 스타일 편집이 불가해서 그냥 새로 컴포넌트 형태로 만들었다

export default class AlertModal extends Component {
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <Modal
                animationType="fade"
                transparent={true}
                visible={this.props.modalVisible}
                onRequestClose={() => {
                    this.props.setModalVisible(false);
                }}
            >
                <View style={{ flex: 1, backgroundColor: "rgba(217, 217, 217, 0.4)", justifyContent: 'center', alignItems: 'center' }}>
         
                </View>
            </Modal>
        )
    }
}

우선 위와 같이 컴포넌트를 만들어 준다

Modal의 props에서 여러 옵션들을 설정할 수가 있는데

 

animationType은 모달이 보이는 방식

(fade: 서서히 투명도가 변경되며 보이고 사라진다,

slide: 바닥에서 슬라이드 된다,

none: 애니메이션 효과 없음)

 

transparent는 뒷배경 투명 여부인데, true면 배경이 아예 투명하고, false면 배경이 아예 불투명해진다

중간 정도의 투명도 값을 지정해 줄 수가 없어서, 보이는 바와 같이 View 컴포넌트에 알파값을 지정해서 반투명 상태로 설정해 주었다

 

visible은 말 그대로 화면에 보일 것인가 여부

 

onRequestClose는 앱 화면 외부에서 뒤로가기나 닫는 동작을 취할 경우 호출되는 함수이다

쉽게 말해 그냥 모달을 닫는 동작을 넣어 주면 된다

 

<View style={{
    backgroundColor: '#ffffff',
    width: '70%',
    alignItems: 'center',
    borderRadius: 11,
    minHeight: '25%',
    ...Platform.select({
        ios: {
            shadowColor: "#000000",
            shadowOpacity: 0.45,
        },
        android: { elevation: 5 }
    })
}}>

아까 위에서 만든 View 내부에 이런 식으로 모달 창을 표현할 View를 만들어 준다

width나 height를 설정할 때 '70%'와 같은 식으로 표시하면, 화면의 70%를 차지하는 길이를 자동으로 계산해서 적용해 준다

그림자 효과는 ios와 안드로이드에서 넣는 방법이 각각 달라서 Platform.select를 이용해 나누어 적용시켜 주어야 한다

안드로이드는 옵션이 elevation 하나 뿐이고, ios는 저거 외에도 많다

그리고 이제 저 View 안에 차례차례 요소들을 넣어 주면 된다

 

{
    (this.props.showCloseButton) ?
        <TouchableOpacity
            style={{ position: 'absolute', top: 5, right: 5 }}
            onPress={() => this.props.setModalVisible(false)}>
            <MaterialIcons name="cancel" size={20} color="#FF8989" />
        </TouchableOpacity>
        : null
}

closeButton이 있는 모달창은 아래와 같다

디자인을 보면 모달 창에 x 버튼이 있는 경우도 있고 없는 경우도 있어, 옵션으로 선택 가능하게 만들었다

expo vector icon에 마침 딱 맞는 아이콘이 있길래 MaterialIcons에서 가져다가 썼다(https://icons.expo.fyi/Index 사이트에서 검색 가능)

 

<Text style={{
    fontSize: 15,
    marginTop: 25,
    marginBottom: 10
}}>{this.props.title}</Text>
<View style={{
    width: '90%',
    height: 0.7,
    backgroundColor: '#DADADA',
}} />
<Text style={{
    fontSize: 12,
    marginTop: 30,
    marginBottom: 30,
    minHeight: '10%'
}}>{this.props.message}</Text>

 

이어서 제목이랑 중간 구분선이랑 본문 부분 재량껏 설정해 준다

message 부분의 경우 디자인 상에서 한 줄이어도 어느 정도 공간을 차지하고 있길래 minHeight를 주었다

 

{
    (this.props.alertButtons == null) ? null
        : <View style={{
            marginBottom: 30,
            width: '100%',
            alignSelf: 'flex-end',
            flexDirection: 'row',
            justifyContent: 'space-evenly'
        }}>
            {
                this.props.alertButtons.map((buttons, index) => {
                    return (
                        <TouchableOpacity style={{
                            borderRadius: 6,
                            paddingVertical: 3,
                            width: 70,
                            height: 25,
                            justifyContent: 'center',
                            alignItems: 'center',
                            ...shadowStyle,
                            ...buttons.style
                        }} onPress={buttons.onPress}>
                            <Text>{buttons.text}</Text>
                        </TouchableOpacity>
                    )
                })
            }
        </View>
}

 

그 아래 쪽엔 이렇게 버튼들을 띄우는 부분인데, 버튼 없이 사용할 수도 있으니 null 체크를 한번 해 주었다

그리고 map으로 각 버튼들을 설정해 주었다

 

const [modalVisible, setModalVisible] = useState(false);
return(
    <AlertModal
        modalVisible={modalVisible}
        setModalVisible={setModalVisible}
        title='게시글 삭제'
        message='정말 게시글을 삭제하시겠습니까?'
        showCloseButton={false}
        alertButtons={[
            {
                text: '취소',
                style: alertButtonStyle.default,
                onPress: () => { setModalVisible(false) }
            },
            {
                text: '삭제',
                style: alertButtonStyle.destructive,
                onPress: () => { deletePost({ navigation }) }
            }
        ]}
    /> 
)

사용할 때는 이런 식으로 쓰면 된다

 

이 경우에는 디자인이 이미 다 정해진 상태에서 공용으로 쓸 컴포넌트를 만드느라 디자인을 직접 미리 다 지정해 주었는데, 범용으로 쓸 거면 ContainerStyle, ButtonStyle, TextStyle 등등 스타일을 다 빼서 구성하면 된다

 

 

 

참고: https://reactnative.dev/docs/modal

https://blog.logrocket.com/create-custom-alert-dialog-react-native/