티스토리 뷰

728x90
반응형

이제 새롭게 시작하는 스터디는 바로 유튜브 페이지 만들기이다.

이전 학원 수강생이 만든 거 같았는데, 이렇게 혼자 공부하면서 다시 하게 될 거란 걸 상상도 못 했었다.

(이렇게 길게 취준생이 될거라 누가 상상했는가...)

이 글은 Infren의 유튜브 사이트 만들기를 모티브로 제작하였습니다.

 

1. 페이지 만들기

// client > src > components > views > VideoUploadPage > VideoUploadPage.js

import React from 'react'

function VideoUploadPage() {
    return (
        <div>
            Wow!
        </div>
    )
}

export default VideoUploadPage

ES7 React/Redux/GraphQL...의 확장자를 설치하게 되면, 단축키 rfce가 저 페이지 만드는 기본 틀을 바로 입력해준다.

 

 

2. 라우터 만들기

import React, { Suspense } from 'react';
import { Route, Switch } from "react-router-dom";
import Auth from "../hoc/auth";
// pages for this product
// ...
import VideoUploadPage from './views/VideoUploadPage/VideoUploadPage';

//null   누구나 페이지에 갈 수 있다.
//true   로그인 한 사람만이 들어갈 수 있는 페이지
//false  로그인 한 사람은 들어갈 수 없는 페이지

function App() {
  return (
    <Suspense fallback={(<div>Loading...</div>)}>
      <NavBar />
      <div style={{ paddingTop: '69px', minHeight: 'calc(100vh - 80px)' }}>
        <Switch>
	 <!-- 중략 -->
          <Route exact path="/video/upload" component={Auth(VideoUploadPage, true)} />
        </Switch>
      </div>
      <Footer />
    </Suspense>
  );
}

export default App;

현재 홈페이지에서 /video/upload를 들어갈 수 있는 라우터를 만들고, 이 페이지는 로그인 한 사람만이 들어갈 수 있게 보완성을 더욱 높여준다.

 

3. 탭 추가

// client > src > components > views > NavBar > Sections > RigthMenu.js
import React from 'react';
// 중략

function RightMenu(props) {
  const user = useSelector(state => state.user)

  const logoutHandler = () => {
    // 중략
  };

  if (user.userData && !user.userData.isAuth) {
    return (
      <Menu mode={props.mode}>
        <!-- 중략 -->
      </Menu>
    )
  } else {
    return (
      <Menu mode={props.mode}>
        <Menu.Item key="upload">
          <a href="/video/upload">video</a>
        </Menu.Item>
        <Menu.Item key="logout">
          <a onClick={logoutHandler}>Logout</a>
        </Menu.Item>
      </Menu>
      
    )
  }
}

export default withRouter(RightMenu);

기존에 있던 내용에서 upload부분을 새로 추가한다.

 

4. 라이브러리 설치

yarn add react-dropzone

OR

npm install react-dropzone --save

윈도나 일반적으로 많이들 사용하는 드래그 앤 드롭이라는 기능을 사용하기 위해 'react-dropzone'을 설치한다. 위치는 클라이언트에서 설치를 한다. 이후에도 실행이 되지 않는 경우에는 꼭 package.js에서 내가 설치한 라이브러리가 있는지 반드시 확인한다.

 

5. 코드 작성

다음으로는 코드를 작성해보려고 한다. 그 중에서 중요하다고 느끼는 부분만 집어서 써보려고 한다.

// client > src > components > views > VideoUploadPage > VideoUploadPage.js

import React, { useState } from 'react'
import { Typography, Button, Form, message, Input, Icon } from 'antd';

// 중략

const { TextArea } = Input;
const { Title } = Typography;

// 중략

function VideoUploadPage() {
    
    return (
        <div style={{maxWidth: '700px', margin: '2rem auto'}}>
            <div style={{ textAlign: 'center', marginBotom: '2rem' }} >
                <Title level={2}>Upload Video</Title>
            </div>
            <Form onSubmit>
                    <!-- 중략 -->
                <label>Title</label>
                <Input onChange={onTitleChange} value={VideoTitle} />
                <br />
                <br />
                <label>Description</label>
                <TextArea onChange={onDescriptionChange} value={Description} />
                <!-- 중략 -->
            </Form>
        </div>
    )
}

export default VideoUploadPage

Typography

antd에서 Typography를 가져와, 그 중 Title을 가져오는 것인지는 잘 모르겠다. 일단 Ant Design 사이트에는 정의는 이렇게 되어있다. Typography에서도 Title과 같은 기능을 가진 값은 Text, Paragraph, Link 등이 있다. (공식 홈페이지 참조)

 

TextArea

기본적으로 텍스트는 input을 이용하여 값을 이용하는데, Ant Design에서는 Input을 가져와서 해당하는 input에 TextArea를 덮어 좀 더 많은 내용을 적을 수 있는 공간이 생기는 듯하다.

 

// client > src > components > views > VideoUploadPage > VideoUploadPage.js

// 중략

const PrivateOptions = [
    {value: 0, label: "Private"},
    {value: 1, label: "Public"}
]

const CategoryOptions = [
    {value: 0, label: "Film & Animation"},
    {value: 1, label: "Autos & Vehicles"},
    {value: 2, label: "Music"},
    {value: 3, label: "Pets & Animals"}
]
function VideoUploadPage() {
    // 중략
    return (
        <div style={{maxWidth: '700px', margin: '2rem auto'}}>
            <!-- 중략 -->
            <Form onSubmit>
                <!-- 중략 -->
                <select onChange={onPrivateChange}>
                    {PrivateOptions.map((item, index) => (
                        <option key={index} value={item.value}>{item.label}</option>
                    ))}
                </select>
                <br />
                <br />
                <select onChange={onCategoryChange}>
                    {CategoryOptions.map((item, index) => (
                        <option key={index} value={item.value}>{item.label}</option>
                    ))}
                    
                </select>
                <!-- 중략 -->
            </Form>
        </div>
    )
}

export default VideoUploadPage

map()

원하는 배열을 만들어 새로운 배열을 반환해준다.

const CategoryOptions = [
    {value: 0, label: "Film & Animation"},
    {value: 1, label: "Autos & Vehicles"},
    {value: 2, label: "Music"},
    {value: 3, label: "Pets & Animals"}
]
<select onChange={onCategoryChange}>
  {CategoryOptions.map((item, index) => (
  	<option key={index} value={item.value}>{item.label}</option>
  ))}

</select>

CategoryOptions는 원하는 명칭을 써서 onChange 함수에 넣어준다. 값이 만약 하나가 아닌 경우, 대괄호로 한 번 더 묶어주면 하나의 index 값에서 여러 가지 값을 호출할 수 있다. 예를 들어 index값이 2면 value는 2, label은 Music값이 된다. (여기서 index는 첫 번째 값은 기본적으로 0번부터 시작한다.)

 

drop-zone

react Hook의 기능으로 HTML5에 호환되는 드래그 앤 드롭 영역을 만드는 라이브러리이다.

 

6. 전체 코드

// client > src > components > views > VideoUploadPage > VideoUploadPage.js

import React, { useState } from 'react'
import { Typography, Button, Form, message, Input, Icon } from 'antd';
import Dropzone from 'react-dropzone';


const { TextArea } = Input;
const { Title } = Typography;

const PrivateOptions = [
    {value: 0, label: "Private"},
    {value: 1, label: "Public"}
]

const CategoryOptions = [
    {value: 0, label: "Film & Animation"},
    {value: 1, label: "Autos & Vehicles"},
    {value: 2, label: "Music"},
    {value: 3, label: "Pets & Animals"}
]
function VideoUploadPage() {
    const [VideoTitle, setVideoTitle] = useState("");
    const [Description, setDescription] = useState("");
    const [Private, setPrivate] = useState(0);
    const [Category, setCategory] = useState("Film & Animation");

    const onTitleChange = (e) => {
        setVideoTitle(e.currentTarget.value)
    }
    const onDescriptionChange = (e) => {
        setDescription(e.currentTarget.value)
    }
    const onPrivateChange = (e) => {
        setPrivate(e.currentTarget.value)
    }
    const onCategoryChange = (e) => {
        setCategory(e.currentTarget.value)
    }
    return (
        <div style={{maxWidth: '700px', margin: '2rem auto'}}>
            <div style={{ textAlign: 'center', marginBotom: '2rem' }} >
                <Title level={2}>Upload Video</Title>
            </div>
            <Form onSubmit>
                <div style={{display: 'flex', justifyContent: 'space-between' }} >
                    {/* Drop zone */}
                    <Dropzone onDrop multiple maxSize>
                        {({getRootProps, getInputProps}) => (
                            <div style={{width: '300px', height: '240px', border: '1px solid lightgray', display: 'flex', alignItems: 'center', justifyContent: 'center'}} {...getRootProps()}>
                                <input {...getInputProps()} />
                                <Icon type="plus" style={{fontSize: '3rem'}} />
                            </div>
                        )}
                    </Dropzone>
                    
                    {/* Thumbnail */}
                    <div>
                        <img  />
                    </div>
                </div>
                <br />
                <br />
                <label>Title</label>
                <Input onChange={onTitleChange} value={VideoTitle} />
                <br />
                <br />
                <label>Description</label>
                <TextArea onChange={onDescriptionChange} value={Description} />
                <br />
                <br />

                <select onChange={onPrivateChange}>
                    {PrivateOptions.map((item, index) => (
                        <option key={index} value={item.value}>{item.label}</option>
                    ))}
                </select>
                <br />
                <br />
                <select onChange={onCategoryChange}>
                    {CategoryOptions.map((item, index) => (
                        <option key={index} value={item.value}>{item.label}</option>
                    ))}
                    
                </select>

                <Button type="primary" size="large" onClick>
                    Submit
                </Button>
            </Form>
        </div>
    )
}

export default VideoUploadPage

 

 

 

마치며...

오늘은 전체적으로 리액트의 복습 편이 되었다.

useState, map함수를 조금 더 이해를 하고 다양하게 쓸 수 있을 듯하다.

물론 실전을 해봐야 확실히 감이 잡히겠지만, 지금으로서는 이론도 어느 정도 이해 가는 것으로 매우 만족 중에 있다.

 

오늘의 코딩 바로가기

728x90
반응형

'Front-End' 카테고리의 다른 글

초기 레이아웃 설정하는 CSS  (0) 2021.05.09
DOM에 대하여 공부를 해보았다.  (0) 2021.04.27
Boiler-Plate 공부 순서 리스트  (0) 2021.04.20
댓글
250x250
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함