타닥타닥 개발자의 일상

react / Axios 이용해서 스프링부트로 만든 웹페이지 안드로이드 화면과 연결시키기 (회원가입/게시판/글작성) 본문

코딩 기록/react

react / Axios 이용해서 스프링부트로 만든 웹페이지 안드로이드 화면과 연결시키기 (회원가입/게시판/글작성)

NomadHaven 2022. 3. 13. 23:30

https://developerson.tistory.com/117

 

스프링 부트로 SpringBoot 회원가입, 페이징, 글작성-수정-삭제-답변, 댓글 작성 되는 게시판 페이지

기본적 세팅은 아래글 https://developerson.tistory.com/114 페이징은 아래글을 참고해서 만든 게시판이다. https://developerson.tistory.com/116 별도의 설명 없이 우선 코드만 백업할 예정. 이제 이걸 이해하..

developerson.tistory.com

react와 연결시킬 스프링 프로젝트는 해당 링크에 있는 프로젝트다.

 

 

react와 스프링 부트를 잘 연결시키기 위해서는 sts를 통해서 해당 프로젝트의 서버를 미리 켜준다.

또한 스프링 부트 프로젝트의 BbsDto 부분에 @JsonProperty("userid")라는 어노테이션을 추가해준다.

react에서 이름이 그냥 id된 데이터를 잘 못받아서 그런다는데.. 잘 모르겠다.

 

 

 

그럼 리액트 프로젝트도 생성해보자

 

생성문
npx react-native init Samples --template react-native-template-typescript

 

여러 기능을 써야하므로 미리 입력해줄 설치문도 많다

설치문
npm install @react-navigation/native
npm install @react-navigation/native-stack
npm install react-native-safe-area-context
npm install react-native-gesture-handler
npm install react-native-screens
npm install watcher
npm i axios
npm i @react-native-async-storage/async-storage
npm i react-native-paper

 

watcher 때문에 설치가 불안정할수 있는데 그럴때는 package.json에서 dependencies에 있는 watcher 버전을 1.1.0 으로 바꿔준다.

 

 

 

그럼 이제 본격적으로 react 프로젝트를 만들어보자

파일 구성 및 폴더 구성

 

이용할 이미지는 assets 폴더를 생성해서 저장한다.

dog.png
0.02MB
logo2.png
0.00MB
welcome.jpg
0.01MB

screens 폴더를 생성하고 그 안에 bbs/member 폴더를 생성한다.

회원가입/로그인 화면을 위해 member 폴더 안에 Account.tsx / Login.tsx 파일을 생성

게시판 목록/ 글쓰기 / 글읽기 기능을 위해 bbs 폴더 안에 Bbs.tsx/Bbsdetail.tsx/Bbslist.tsx/Bbswrite.tsx 파일을 생성

 

 

각 파일별 코드는 아래와 같다.


bbs 폴더 안에 있는 파일들

Bbs.tsx
import React, { useState } from "react";
import { Button, StyleSheet, Text, View } from "react-native";
import Bbsdetail from "./Bbsdetail";
import Bbslist from "./Bbslist";
import Bbswrite from "./Bbswrite";

function Bbs(){
    const[bbslist,setBbslist]=useState("bbslist")
    const[bbs,setBbs]=useState({})


    let child:any

    if(bbslist=="bbslist"){
      //  child = (<Text>Bbslist view</Text>)
      child=(<Bbslist setBbslist={setBbslist} setBbs={setBbs}/> )
        
    }else if(bbslist=="bbswrite"){
      //  child = (<Text>BbsWrite view</Text>)
        child =(<Bbswrite setBbslist={setBbslist}/>)

    }else if(bbslist=="bbsdetail"){
      //  child = (<Text>BbsDetail view {JSON.stringify(bbs)} </Text>)
      child=(<Bbsdetail bbs={bbs}/>)
    }


    return(
        <View>
            <View style={styles.menu}>
            <View style={styles.button}>
                <Button title="글목록" onPress={bbslist=>setBbslist("bbslist")}/>
                </View>
                <View style={styles.button}>
                <Button title="글추가" onPress={bbslist=>setBbslist("bbswrite")}/>    
                </View>
            </View>

            <View>{child}</View>    


        </View>
    )
}

const styles = StyleSheet.create({
    menu:{
        flexDirection:"row",
        flexWrap:"wrap",
    },
    button:{
        flex:1,
        height:50,
        margin:1,

    },
    childView:{
        height:760,

    }
})

export default Bbs;
Bbsdetail.tsx
import React from "react";
import { StyleSheet,  ScrollView, Text } from "react-native";
import { Avatar, Card, Headline, Paragraph } from "react-native-paper";

import { SafeAreaView } from "react-native-safe-area-context";

function Bbsdetail(props:any){


    return(
        <SafeAreaView>
            <ScrollView contentContainerStyle={styles.contentContainer}>
        
            <Headline>글 내용</Headline>
            <Card style={styles.card}>

            <Card.Title
            title={props.bbs.title}
            subtitle={"작성자 :" + props.bbs.userid}
            left={props=><Avatar.Text {...props} label="AAA"/>}/>

            <Card.Content>
                <Paragraph style={styles.content}>
                    {props.bbs.content}
                </Paragraph>
                <Text style={{textAlign:"right"}}>
                    조회수:{props.bbs.readcount}
                </Text>

                <Text style={{textAlign:"right"}}>
                    작성일:{props.bbs.wdate}
                </Text>

            </Card.Content>

            </Card>
        
            </ScrollView>
        </SafeAreaView>
    )
}


const styles = StyleSheet.create({

    contentContainer:{
        padding:16

    },
    card:{
        marginTop:20
    },
    content:{
        fontSize:20
    }

})

export default Bbsdetail
Bbslist.tsx
import axios from "axios";
import React, { useEffect, useState } from "react";
import { StyleSheet, View, TouchableOpacity, Image, Text, FlatList } from "react-native";

//연습용 데이터
// const data=[
//     {
//         "userid":"aaa",
//         "title":"첫번째 제목",
//         "readcount": 3
//     },
//     {
//         "userid":"bbb",
//         "title":"두번째 제목",
//         "readcount": 2
//     },
//     {
//         "userid":"ccc",
//         "title":"세번째 제목",
//         "readcount": 1
//     }

// ]


function Item({id,title,readcount,seq, props}:any){

    function itemClick(seq:number){
        console.log('itemClick')
        console.log(seq)

        axios.get("http://192.168.35.3:3000/bbsdetail", { params:{seq:seq}})
            .then(function(resp){
                console.log(resp.data)

                //짐싸기
                props.setBbs(resp.data)
            })
            .catch(function(err){
                console.log(err)
            })



        //bbsdetail로 이동

        props.setBbslist("bbsdetail")

    }

    return(
        <View style={styles.item}>
            <TouchableOpacity onPress={()=>itemClick(seq)}>
                <View style={styles.idRow}>
                    <Image style={styles.image} source={require("../../assets/dog.png")}/>
                    <Text style={styles.title}>{title}</Text>
                </View>
                <View style={styles.idRow}>
                    <Text style={styles.id}>{id}</Text>
                    <Text style={styles.readcount}>{readcount}</Text>
                    </View>
            </TouchableOpacity>

        </View>
    )
}





function Bbslist(props:any){
    const[data,setData]=useState([])

    function renderItem({item}:any){

        function strLength(str:String){
    
            if(str.length > 20){
                return str.substring(0,17)+"..."
            }else{
                return str
            }
        }
    
        return(
            <Item id={"작성자:"+item.userid} title={strLength(item.title)} 
            readcount={"조회수:" +item.readcount} seq={item.seq} props={props}/>
    
        )

    }

    let mounted= true

    useEffect(()=>{

        axios.get("http://192.168.35.3:3000/getBbsList",{} )
            .then(function(resp){
             console.log(resp.data)   

             setData(resp.data)

            }).catch(function(err){
             console.log(err)
            })
    },[])

return(
    <View style={styles.scrView}>
        <FlatList data={data} renderItem={renderItem} />
    </View>
)
}

const styles = StyleSheet.create({

    item:{
        backgroundColor:'#d7d9f4',
        padding:10,
        marginVertical:2,
        marginHorizontal:8,
        borderColor:"#ff0000",
        borderRadius:15,
        borderStyle:'solid',
        borderWidth:2
    },
    image:{
        width:50,
        height:50,
        marginTop:15
    },
    title: {
        fontFamily: "roboto-regular",
        paddingLeft: 20,
        color: "#400040",
        height: 60,
        width: 500,
        fontSize: 24,
    },
    id: {
        fontFamily: "roboto-regular",
        //backgroundColor: "#ffff00",
        color: "#121212",
        height: 30,
        width: 166,
        fontSize: 20,
        marginLeft: 80,
        marginTop: 8
    },
    readcount: {
        fontFamily: "roboto-regular",
        //backgroundColor: "#00ff00",
        color: "#121212",
        height: 30,
        width: 211,
        fontSize: 20,
        marginLeft: 51,
        marginTop: 8,
        textAlign: "center",
    },

    idRow:{
        height:40,
        flexDirection:"row",
        marginLeft:5,
    },
    scrView:{
        height:760
    }

})

export default Bbslist;

 

Bbswrtie.tsx
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import React, { useState } from "react";
import { Alert, StyleSheet, Text, View } from "react-native";
import { Button,  TextInput } from "react-native-paper";


function Bbswrite(props:any){
    const [id,setId] = useState('')
    const [title,setTitle]= useState('')
    const [content, setContent] =useState('')
    
    const loginData = async () =>{
        try{
            let user = await AsyncStorage.getItem('login')
            if(user !=null){
            setId((JSON.parse(user)).id )
            }
        }catch(err){}
    }
    loginData()
   

    function bbsWriteBtn(){
        console.log(id)
        console.log(title)
        console.log(content)

        if(title.trim()==""){
        }

        axios.get("http://192.168.35.3:3000/bbswrite",
        {
            params:{
                id:id,
                title:title,
                content:content
            }
        }).then(function(resp){
            console.log(resp.data)

            if(resp.data=="YES"){
                props.setBbslist("bbslist")
            }

        }).catch(function(){
            Alert.alert("추가되지 않았습니다.")
        })

    }



    return(
        <View>
            <Text style={styles.text}>글추가</Text>
            <View style={{alignItems:'center'}}>


                <TextInput
                style={styles.texInput}
                mode="outlined"
                label="작성자"
                value={id}
                editable={false}/>

                <TextInput
                    style={styles.textArea}
                    mode="outlined"
                    label="제목"
                    value={title}
                    onChangeText={title=>setTitle(title)}/>

                <TextInput
                style={styles.textArea}
                placeholder="내용"
                multiline={true}
                numberOfLines={20}
                value={content}
                onChangeText={content=>setContent(content)}/>
                
                <Button 
                  mode="outlined"
                  style={styles.button}
                  onPress={bbsWriteBtn}>작성완료</Button>


            </View>

        </View>
    )

}

const styles = StyleSheet.create({
    text:{
        marginTop:10,
        fontSize:30,
        textAlign:"center"
    },
    texInput:{
        marginTop:20,
        fontSize:16,
        width:500,
        height:40,
        backgroundColor:"#e3e3e3"
    },
    textArea:{
        fontSize:16,
        borderWidth:1,
        marginTop:20,
        backgroundColor:"#e3e3e3",
        textAlignVertical:"top",
        width:500

    },
    button:{
        marginTop:20,
        marginVertical:8
    }
})


export default Bbswrite

 

 

 


member 폴더 안에 있는 파일들

Account.tsx
import axios from "axios";
import React, { useState } from "react";
import { Image, StyleSheet, View,TouchableHighlight, Text, TextInput, Alert} from "react-native";



export default function Account( {navigation}:any){
    const[id,setId]= useState('')
    const[msg,setMsg]=useState("msg")

    const[pwd,setPwd]=useState("")
    const[name,setName]=useState("")
    const[email,setEmail]=useState("")

    function idCheck(){
        console.log('idCheck')
        console.log(`${id}`)

        if(id.trim()===""){
            Alert.alert("아이디","아이디를 입력해 주십시오.")
            setId("")
        }else{

        axios.post("http://192.168.35.3:3000/getId",null,{params:{id:id}})
        .then(function(resp){
            console.log(resp.data)
            
            if(resp.data=="OK"){
                setMsg("사용 가능합니다.")
            }else{
                setMsg("사용중입니다.")
                setId("")
            }
        })
        .catch(function(err){
            console.log(err)
        })
    }
    }


    
    function regi(){
        if(id.trim()===""){
            Alert.alert("아이디","아이디를 입력해주십시오.")
        }
        else if(pwd.trim()===""){
            Alert.alert("패스워드","패스워드를 입력해주십시오.")
        }
        else{
            axios.post("http://192.168.35.3:3000/addmember",null,
            {
                params:{
                    id:id,
                    pwd: pwd,
                    name:name,
                    email:email
                    }
                 }
            ).then(function(resp){
                console.log(resp.data)

                if(resp.data=="YES"){
                   
                    Alert.alert('regi',
                                '회원가입 되었습니다.',
                                [{
                                   text:'확인',
                                   onPress:()=>navigation.navigate('login')     
                                }])
                }
                else{
                    Alert.alert("regi","가입되지 않았습니다.")
                }

            }).catch(function(err){
                console.log(err)
            })
        }
    }


    return(
        <View style={styles.container}>
            <Image style={styles.logo} source={require("../../assets/welcome.jpg")}/>

            <View style={styles.inputView}>                
                <TextInput
                    style={styles.textInput}
                    placeholder="아이디"
                    value={id}
                    underlineColorAndroid="transparent"
                    onChangeText={(id)=>setId(id)}/>
            </View>


            <View style={styles.idTextContainer}>
                <Text style={styles.idText}>{msg}</Text>
           
                <TouchableHighlight style={[styles.buttonContainer, styles.sendButton]} 
                                    onPress={()=>idCheck()}>
                    <Text style={styles.buttonText}>id확인</Text>
                </TouchableHighlight>
            </View>


            <View style={styles.inputView}>                
                <TextInput
                    style={styles.textInput}
                    placeholder="패스워드"
                    underlineColorAndroid="transparent"
                    onChangeText={(pwd)=>setPwd(pwd)}/>
            </View>

            
            <View style={styles.inputView}>                
                <TextInput
                    style={styles.textInput}
                    placeholder="이름"
                    underlineColorAndroid="transparent"
                    onChangeText={(name)=>setName(name)}/>
            </View>

             
            <View style={styles.inputView}>                
                <TextInput
                    style={styles.textInput}
                    placeholder="이메일"
                    underlineColorAndroid="transparent"
                    onChangeText={(email)=>setName(email)}/>
            </View>

            <TouchableHighlight style={[styles.buttonContainer, styles.sendButton]} onPress={()=>regi()} >
                <Text style={styles.buttonText}>회원가입</Text>
            </TouchableHighlight>



        </View>

        
    )
}

const styles = StyleSheet.create({
    container:{
        flex:1,
        justifyContent:"center",
        alignItems:"center",
        backgroundColor:"#fff"
    },
    logo: {
        justifyContent:"center",
        marginBottom:60
    },
    inputView:{
        borderBottomColor:"#f5fcff",
        backgroundColor:"#dcdcdc",
        borderRadius:30,
        borderBottomWidth:1,
        width:300,
        height:45,
        marginBottom:20,
        flexDirection:'row',
        alignItems:"center"
    },
    textInput:{
        height:45,
        marginLeft:16,
        borderBottomColor:"#fff",
        flex:1
    },
    buttonContainer:{
        height:45,
        flexDirection:'row',
        justifyContent:"center",
        alignItems:"center",
        marginBottom:20,
        width:100,
        borderRadius:30

    },

    sendButton:{
        backgroundColor:"#ff4500"
    },
    idTextContainer:{
        height:45,
        flexDirection:"row",
        justifyContent:"center",
        alignItems:"center",
        width:100,
        borderRadius:30
    },
    idText:{
        marginLeft:20,
        marginBottom:20,
        width:180
    },
    buttonText:{
        color:'white'
    }

})

 

Login.tsx
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import React, { useState } from "react";
import { 
    Image, 
    StyleSheet, 
    View, 
    TextInput, 
    Text, 
    TouchableOpacity,    
    Button,
    Alert
} from "react-native";

export default function Login( { navigation }:any ) {

    const [id, setId] = useState("")
    const [password, setPassword] = useState("")
    
    function login(){
        if(id.trim() == ""){
            Alert.alert("id", "아이디를 입력해 주십시오")
        }
        else if(password.trim() == ""){
            Alert.alert("password", "password를 입력해 주십시오")
        }
        else{
            axios.post("http://192.168.35.3:3000/login", null, 
                {
                    params: {
                        id: id,
                        pwd: password
                    }
                }).then(function(resp){
                    if(resp.data != null && resp.data != ""){
                        console.log("로그인 되었습니다")
                        console.log(resp.data)

                        AsyncStorage.setItem('login',JSON.stringify(resp.data))

                        //loginData()
                        navigation.navigate('bbs')
                        
                    }
                    else{
                        Alert.alert("login", "아이디나 패스워드를 확인하세요")
                    }

                }).catch(function(err){
                    Alert.alert("err", err)
                })
        }
    }

    const loginData = async () =>{
        try{
        let user = await AsyncStorage.getItem('login')
                        console.log("login 정보:"+user)
        }catch(err){}
    }

    return (
        <View style={styles.container}>            
            <Image style={styles.image} source={ require("../../assets/logo2.png") } />

            <View style={styles.inputView}>
                <TextInput
                    style={styles.textInput}
                    placeholder="id를 입력해 주세요"
                    placeholderTextColor="#003f5c"
                    onChangeText={(id)=>setId(id)} />
            </View>

            <View style={styles.inputView}>
                <TextInput
                    style={styles.textInput}
                    placeholder="password를 입력해 주세요"
                    placeholderTextColor="#003f5c"
                    secureTextEntry={true}
                    onChangeText={(password)=>setPassword(password)} />
            </View>

            <TouchableOpacity onPress={()=> navigation.navigate("account")}>
                <Text style={styles.forgot_button}>회원가입</Text>      
            </TouchableOpacity>

            <TouchableOpacity style={styles.loginBtn} onPress={()=>login()}>
                <Text>로그인</Text>
            </TouchableOpacity>     

        </View>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: "#fff",
        alignItems: "center",
        justifyContent: "center"
    },
    image: {
        marginBottom: 40
    },
    inputView: {
        backgroundColor: "#ffc0cb",
        borderRadius: 30,
        width: "70%",
        height: 45,
        marginBottom: 20,
        alignItems: "center"
    },
    textInput:{
        height: 50,
        flex: 1,
        padding: 10,
        marginLeft: 20
    },
    forgot_button: {
        height: 30,
        marginBottom: 30
    },
    loginBtn: {
        width: "50%",
        borderRadius: 25,
        height: 50,
        alignItems: "center",
        justifyContent: "center",
        marginTop: 40,
        backgroundColor: "#ff1493"
    }
})

 

 

 


실행화면

 

 

Comments