오티스의개발일기

[REACT NATIVE] 인스타그램 클론 코딩 (18) 마지막....ㅠㅠ react-native-image-picker 를 사용한 새 게시물 업로드 본문

개발/react-native

[REACT NATIVE] 인스타그램 클론 코딩 (18) 마지막....ㅠㅠ react-native-image-picker 를 사용한 새 게시물 업로드

안되면 될때까지.. 2023. 1. 4. 22:05
728x90

 


< 이전글

 

2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (17) navigation을 이용하여 새 게시물 스택 만들기

 

[REACT NATIVE] 인스타그램 클론 코딩 (17) navigation을 이용하여 새 게시물 스택 만들기

< 이전글 2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (16) react-native-paper 사용하여 메뉴바 만들기! [REACT NATIVE] 인스타그램 클론 코딩 (16) react-native-paper 사용하여 메뉴바 만들기! <

otis.tistory.com


 

 

오늘은 image-picker 를 활용하여 새업로드 ui 밑 파일업로드하는 기능을 만들것이다.

사실상 업로드는아니고 단순 사진 미리보기 정도이다..

 

그것보다 왜 마지막인지 설명부터 하겠다.

 

이번 프로잭트를 하면서 배운것도 많고 힘든것도 많았다.

가장 중요한건 인스타그램을 완전구현하기엔 실력도 부족하고 공부하면서 알게된것들은 사용하고싶지만

이미 너무 먼길을 걸어왔기 떄문에 새로하기가 매우 부담스러운 상환이였다.

 

나에게는 3 개의 선택지가있다.

 

1. 그냥 무작정 다 만들어보기

2. 다시 처음부터 만들어보기

3. 다른 프로잭트로 시작하기

 

나는 이중에 3번을 택했다

 

일단 프로잭트에대한 애정과 흥미도 점점 사라지고있기 때문이고 

본업과 같이 하고있기때문에 일하고 집에와서 작업하고...

거의 잠자는 시간빼고 일만한거같아 너무 스트레스를 받아

조금 쉬면서 재밌게해야겠다는 생각이들었다...

 

어찌됬든 상황은 그리되었고 

이제부터는 포스팅 자체를 처음부터 하나한 찍어나가는것보다 기능위주로 포스팅할예정이다.

 

여태 인스타그램 클론코딩을 봐준 구독자들과 사람들께 감사하다.

 

그럼 마지막 인스타 클론코딩을 시작하겠다.

 

오늘은 위에서 언급한것처럼 단순 ui 와 파일업로드전 미리보기기능을 구현하였다.

 

 

 

그럼 시작하도록 하겠다..

 

일단 라이브러리 부터 받아보겠다.

 

npm install react-native-image-picker

 

 


# 오늘 작업할 파일목록

  • Header.js
  • NewFeedForm.js
  • NewFeedTab.js

 

# 0. 폴더 구조



 

 

 

 

 

 

# 1. NewFeedTab.js

 

import React, { useEffect } from 'react'
import styled from 'styled-components'
import Ionicons from "react-native-vector-icons/Ionicons";
import { useDispatch, useSelector } from "react-redux"; // userDispatch = 데이터 변경시 사용 // useSelector = 데이터 가져올때 사용
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
const NewFeedForm = (props) => {
    const dispatch = useDispatch();
    console.log(props)
    const [preView, setPreView] = React.useState(null);
    const createFormData = (photo) => {

        let data = new FormData()
        if (photo.uri) {
            data.append('image', {uri: photo.uri, name: 'image.jpg', type: 'image/jpg'})
        }
        setPreView(photo.uri)
        return data;
      };
    
    const openPicture = () => {
        launchImageLibrary({ noData: true }, (response) => {
            if (response) {
                props.setFile(createFormData(response.assets[0]))
            }
          });
    }

  const theme = useSelector((state) => state.themeSlicer.theme);
  
  return (
    <Container>
    <Box>
    <TouchableOpacity onPress={openPicture}>
        {
            preView == null ?
            <ImageBox>
                <ImageButton>
                    <Ionicons name="image" size={28} color={theme.mode === 'dark' ? 'white' : 'black'} />
                </ImageButton>
            </ImageBox>
        :
            <Image source={{uri : preView}}/>
        }
      </TouchableOpacity>
      <TextBox>
        <TextInput
            placeholder="내용을 입력해주세요"
            onChangeText={(value) => props.setContent(value)}
            autoFocus={true}
        />
        
      </TextBox>
    </Box>
    <ValidationBox>
    <ValidationTextBox>
        <ValidationText>
            {
              props.content.length === 0 ? '내용을 입력해주세요'
              :
              props.content.length > 1 && props.content.length > 50 ? '최대 50글자까지 입력가능합니다.' : '' 
            }
        </ValidationText>
        </ValidationTextBox>
    </ValidationBox>
</Container>
  )
}
const ValidationBox = styled.View`
  flex-direction: row;
  padding: 0px 15px;
`  

const Box = styled.View`
    flex-direction: row;
    padding: 10px 15px;
`

const Container = styled.View`
    background-color: ${props => props.theme.backgroundColor};
   
`


const ImageBox = styled.View`
    height: 100px;
    background-color: ${props => props.theme.LigtherBackGroundColor};
    justify-content: center;
`

const ImageButton = styled.View`
    align-items: center;
`

const Image = styled.Image`
    height: 100px;
`

const TextBox = styled.View`
    width: 100%;
    margin-left: 20px;
    padding: 3%;
    background-color: ${props => props.theme.LigtherBackGroundColor};
`

const TouchableOpacity = styled.TouchableOpacity`
    width: 30%;
`;


const TextInput = styled.TextInput`
color: ${props => props.theme.TextColor};
`

const ValidationTextBox = styled.View`
  margin-top: 8px;
  margin-bottom: 8px;
`

const ValidationText = styled.Text`
  color: red;
  font-size: 10px;
`


const Text = styled.Text``
export default NewFeedForm

 

 

 

이번에는 redux를 사용안하고 useState 를 활용해 자식에게 props로 넘겨줬다 그이유는 

본인은 나누는것을 정말 좋아하기때문에

헤더와 바디를 나누고싶었기때문이다.

 

인스타그램 피드를 올릴때는 상단 헤더에 공유 버튼이있는데

파일이 없는지 확인 글을썼는지 안썻는지 확인하고 공유하기를 활성화 시키려면

이게 유일한 방법이기도했다.

 

 

그러면 이제 Header.js 를 만들어보겠다.

 

 

2. Header.js

import React from 'react'
import styled from "styled-components/native";
import { useDispatch, useSelector } from "react-redux"; // userDispatch = 데이터 변경시 사용 // useSelector = 데이터 가져올때 사용
import Ionicons from "react-native-vector-icons/Ionicons";
import LightHeaderLogo from '../../../../assets/header-logo.png'
import BlackHeaderLogo from '../../../../assets/black-header-logo.png'
import { Button, Menu, Divider, Provider } from 'react-native-paper';
import {ROUTES} from '../../../constants/routes'
const Header = ({navigation}) => {
    const theme = useSelector((state) => state.themeSlicer.theme);
    const dispatch = useDispatch();
    const [visible, setVisible] = React.useState(false);

    const openMenu = () => setVisible(true);
  
    const closeMenu = () => {
        setVisible(false);
        navigation.push(ROUTES.NEWFEEDTAB)
    }
    return (
        <Container>
            <HeaderBox>
                <TouchableOpacity>
                    {
                        theme.mode === 'dark' ? <Image source={LightHeaderLogo} /> : <Image source={BlackHeaderLogo} />
                    }
                </TouchableOpacity>
                <HeaderIconBox>
                    <Menu
                        visible={visible}
                        anchor={<TouchableOpacity onPress={openMenu}>
                        <Ionicons name="add-circle-outline" size={28} color={theme.mode === 'dark' ? 'white' : 'black'} />
                    </TouchableOpacity>}>
                        <Menu.Item icon="information" disabled={!visible} onPress={() => {
                            var cnt = 0
                            if (visible && cnt === 0) {
                                cnt++
                                closeMenu();
                            }
                        }} title="새 게시물"/>
                    </Menu>
                   
                    <TouchableOpacity>
                        <UnderDot />
                        <Ionicons name="heart-outline" size={28} color={theme.mode === 'dark' ? 'white' : 'black'}/>
                    </TouchableOpacity>
                    <TouchableOpacity>
                        <UnderRedIcon>
                            <UnderRedIconText>11</UnderRedIconText>
                        </UnderRedIcon>
                        <Ionicons name="paper-plane-outline" size={28} color={theme.mode === 'dark' ? 'white' : 'black'} />
                    </TouchableOpacity>
                </HeaderIconBox>
            </HeaderBox>
        </Container>
    )
}


export default Header

const Text = styled.Text`
  color: ${props => props.theme.TextColor};
`;

const Image = styled.Image`
  width: 175px;
  height: 51px;
`;

const TouchableOpacity = styled.TouchableOpacity`
    margin-left: 15px;
`;

const Container = styled.View`
    padding-right: 10px;
    background-color: ${props => props.theme.backgroundColor};
`;

const UnderDot = styled.View`
    background-color: red;
    position: absolute;
    top: 0;
    right: 0;
    border-radius: 50px;
    width: 10px;
    height: 10px;
    z-index: 100;
`;

const UnderRedIcon = styled.View`
    background-color: red;
    border-radius: 50px;
    position: absolute;
    align-items: center;
    right: 0;
    bottom: 20px;
    padding: 5% 20%;
    z-index: 100;
`;

const UnderRedIconText = styled.Text`
    color: white;
    font-weight: 600;
`;

const HeaderBox = styled.View`
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
`;

const HeaderIconBox = styled.View`
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
`;

 

 

 

 

# 3. NewFeedForm.js

import React, { useEffect } from 'react'
import styled from 'styled-components'
import Ionicons from "react-native-vector-icons/Ionicons";
import { useDispatch, useSelector } from "react-redux"; // userDispatch = 데이터 변경시 사용 // useSelector = 데이터 가져올때 사용
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
const NewFeedForm = (props) => {
    const dispatch = useDispatch();
    console.log(props)
    const [preView, setPreView] = React.useState(null);
    const createFormData = (photo) => {

        let data = new FormData()
        if (photo.uri) {
            data.append('image', {uri: photo.uri, name: 'image.jpg', type: 'image/jpg'})
        }
        setPreView(photo.uri)
        return data;
      };
    
    const openPicture = () => {
        launchImageLibrary({ noData: true }, (response) => {
            if (response) {
                props.setFile(createFormData(response.assets[0]))
            }
          });
    }

  const theme = useSelector((state) => state.themeSlicer.theme);
  
  return (
    <Container>
    <Box>
    <TouchableOpacity onPress={openPicture}>
        {
            preView == null ?
            <ImageBox>
                <ImageButton>
                    <Ionicons name="image" size={28} color={theme.mode === 'dark' ? 'white' : 'black'} />
                </ImageButton>
            </ImageBox>
        :
            <Image source={{uri : preView}}/>
        }
      </TouchableOpacity>
      <TextBox>
        <TextInput
            placeholder="내용을 입력해주세요"
            onChangeText={(value) => props.setContent(value)}
            autoFocus={true}
        />
        
      </TextBox>
    </Box>
    <ValidationBox>
    <ValidationTextBox>
        <ValidationText>
            {
              props.content.length === 0 ? '내용을 입력해주세요'
              :
              props.content.length > 1 && props.content.length > 50 ? '최대 50글자까지 입력가능합니다.' : '' 
            }
        </ValidationText>
        </ValidationTextBox>
    </ValidationBox>
</Container>
  )
}
const ValidationBox = styled.View`
  flex-direction: row;
  padding: 0px 15px;
`  

const Box = styled.View`
    flex-direction: row;
    padding: 10px 15px;
`

const Container = styled.View`
    background-color: ${props => props.theme.backgroundColor};
   
`


const ImageBox = styled.View`
    height: 100px;
    background-color: ${props => props.theme.LigtherBackGroundColor};
    justify-content: center;
`

const ImageButton = styled.View`
    align-items: center;
`

const Image = styled.Image`
    height: 100px;
`

const TextBox = styled.View`
    width: 100%;
    margin-left: 20px;
    padding: 3%;
    background-color: ${props => props.theme.LigtherBackGroundColor};
`

const TouchableOpacity = styled.TouchableOpacity`
    width: 30%;
`;


const TextInput = styled.TextInput`
color: ${props => props.theme.TextColor};
`

const ValidationTextBox = styled.View`
  margin-top: 8px;
  margin-bottom: 8px;
`

const ValidationText = styled.Text`
  color: red;
  font-size: 10px;
`


const Text = styled.Text``
export default NewFeedForm

 

 

일단 react-native-image-picker 를 활용하면 크게 사진촬영과 사진 겔러리에있는 사진을 가져올수있다.

 

나는 그중 lanunchImageLibrary 만 사용해서 구현하였다.

 

일단 파일을 받으면 type name .. 그리고 uri 가 나온다 저 uri 로 미리보기가 가능하다.

그걸 받아서 변수에 저장해두고 Image 태그를 사용해 보여주면 끝이다.

 

 

 

실행하는 모습을 보도록하겠다.

 

 

 

 

 

정상적으로 동작하는걸 확인할수있다!!

 

다음시간에 새로운 프로잭트로 찾아뵙도록하겠다.!

 

 

 

 

 

# 깃허브 주소


https://github.com/1domybest/react-native-ig-clone.git

 

GitHub - 1domybest/react-native-ig-clone

Contribute to 1domybest/react-native-ig-clone development by creating an account on GitHub.

github.com


< 이전글

2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (17) navigation을 이용하여 새 게시물 스택 만들기

 

[REACT NATIVE] 인스타그램 클론 코딩 (17) navigation을 이용하여 새 게시물 스택 만들기

< 이전글 2023.01.02 - [개발/react-native] - [REACT NATIVE] 인스타그램 클론 코딩 (16) react-native-paper 사용하여 메뉴바 만들기! [REACT NATIVE] 인스타그램 클론 코딩 (16) react-native-paper 사용하여 메뉴바 만들기! <

otis.tistory.com


 

728x90
Comments