티스토리 뷰
이제 새롭게 시작하는 스터디는 바로 유튜브 페이지 만들기이다.
이전 학원 수강생이 만든 거 같았는데, 이렇게 혼자 공부하면서 다시 하게 될 거란 걸 상상도 못 했었다.
(이렇게 길게 취준생이 될거라 누가 상상했는가...)
이 글은 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함수를 조금 더 이해를 하고 다양하게 쓸 수 있을 듯하다.
물론 실전을 해봐야 확실히 감이 잡히겠지만, 지금으로서는 이론도 어느 정도 이해 가는 것으로 매우 만족 중에 있다.
'Front-End' 카테고리의 다른 글
초기 레이아웃 설정하는 CSS (0) | 2021.05.09 |
---|---|
DOM에 대하여 공부를 해보았다. (0) | 2021.04.27 |
Boiler-Plate 공부 순서 리스트 (0) | 2021.04.20 |
- Total
- Today
- Yesterday
- 코딩테스트
- array
- 리덕스
- 배열
- react
- java
- javascript
- node.js
- 리액트 유튜브
- mongodb
- 뷰
- programmers
- 리액트
- 재공부
- github
- 자바스크립트
- CSS
- 프로그래머스
- Visual Studio Code
- 자바
- 노드
- 함수
- 리액트 썸네일
- Coding Test
- node-sass
- redux
- node
- Git
- Switch
- 파이썬
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |