React

 

 

버전이 다르기 때문에 소스가 강좌와 다를 수 있다.

버전

next:  13.0.4

antd:  5.0.1

 

 

1. Next.js 실행해보기

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48785?category=questionDetail&tab=curriculum

 

 

next 설치

$ npm install next@9


 

$ npm install react react-dom

 

 

$ npm i prop-types   

 

 

 

 

2. page와 레이아웃

강의 

:

 

 

AppLayout.js

import React from 'react';
import PropTypes from 'prop-types';

const AppLayout = ({ children }) => {
    return (
        
공통메뉴
{children}

); }; AppLayout.prototype = { children: PropTypes.node.isRequired } export default AppLayout;

 

 

index.js

import AppLayout from './../components/AppLayout';
const Index = () => {
    return (
        Hello.Next

    );
};

export default Index;

 

 

 

 

 

 

 

3. Link와 eslint

 

$ npm i eslint -D

$ npm i eslint-plugin-react -D

$ npm i eslint-plugin-import  -D

$ npm i eslint-plugin-react-hooks -D

 

 

 "devDependencies": {
    "eslint": "^8.28.0",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-react": "^7.31.11",
    "eslint-plugin-react-hooks": "^4.6.0"
  }

 

 

 

.eslintrc

{
    "parserOptions": {
        "ecmaVersion": 2022,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "env":{
        "browser":true,
        "node":true,
        "es6":true
    },
    "extends":[
        "eslint:recommended",
        "plugin:react/recommended"
    ],
    "plugins": [
        "import",
        "react-hooks"
    ],
    "rules": {

    }


}

 

 

 

 

 

4. Q&A

 

강의 :https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48788?category=questionDetail&tab=curriculum

 

 

 

 

 

 

antd 사용해 SNS 화면 만들기

 

 

5. antd와 styled-components

 

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48790?category=questionDetail&tab=curriculum

 

$ npm i antd styled-components  @ant-design/icons

 

 

AppLayout.js

import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link'
import { Menu } from 'antd';

const AppLayout = ({ children }) => {

    return (
        

노드버드 프로필 회원가입

{children}

); }; AppLayout.prototype = { children: PropTypes.node.isRequired } export default AppLayout;

 

 

 

 

 

 

 

 

6. _app.js와 Head

 

강의 :

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC/unit/48791?category=questionDetail&tab=curriculum

 

pages 디렉토리에 _app.js  파일을  생성하면 넥트스에서  자동적으로  인식한다.

이 파일은 전체 공통적으로 적용할 라이브러리 코드들을 작성하면 된다.

 

_app.js

import React from 'react';
import PropTypes from 'prop-types';
import 'antd/dist/antd';
import Head from 'next/head';


const NodeBird = ({ Component }) => {
    return (
        <>
            

NodeBird ); }; NodeBird.propTypes = { Component: PropTypes.elementType.isRequired, } export default NodeBird;

 

 

 

 

 

 

7. 반응형 그리드 사용하기.

 

component/AppLayout.js

import React from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link'
import { Menu, Input, Row, Col } from 'antd';

const AppLayout = ({ children }) => {

    return (
        

노드버드 프로필 회원가입

왼쪽 메뉴 {children} Made by macaronics

); }; AppLayout.prototype = { children: PropTypes.node.isRequired } export default AppLayout;

 

 

 

 

 

 

 

8. 로그인 폼 만들기

 

AppLayout.js

 

import React, { useCallback, useState } from 'react';
import PropTypes from 'prop-types';
import Link from 'next/link'
import { Menu, Input, Row, Col, MenuProps } from 'antd';
import { useNavigate } from 'react-router-dom';

import UserProfile from './UserProfile';
import LoginForm from './LoginForm';


const items = [
    { label: 

노드버드, key: 'item-1' }, { label:

프로필, key: 'item-2' }, { label: , key: 'item-3' }, { label:회원가입, key: 'item-4' }, ]; const AppLayout = ({ children }) => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [current, setCurrent] = useState('item-1'); const onClick = useCallback((e) => { console.log('click ', e); setCurrent(e.key); }, []); return (

{isLoggedIn ? : } {children} Made by macaronics

); }; AppLayout.prototype = { children: PropTypes.node.isRequired } export default AppLayout;

 

 

 

 

LoginForm.js

import React, { useCallback, useState } from 'react';
import { Form, Input, Button } from 'antd';
import Link from 'next/link'

const LoginForm = () => {

    const [id, setId] = useState('');
    const [password, setPassword] = useState('');

    const onChangeId = useCallback((e) => {
        setId(e.target.value);
    }, []);

    const onChangePassword = useCallback((e) => {
        setPassword(e.target.value);
    }, []);


    return (
        
아이디
비밀번호
로그인

회원가입); }; export default LoginForm;

 

 

 

 

 

 

9. 리렌더링 이해하기

AppLayout.js

import styled from 'styled-components';

const SearchInput = styled(Input.Search)`
    vertical-align: 'middle' ;
`;

const items = [
    { label: 

노드버드, key: 'item-1' }, { label:

프로필, key: 'item-2' }, { label: , key: 'item-3' }, { label:회원가입, key: 'item-4' }, ]; ~

 

 

Warning: Prop `className` did not match. when using styled components with semantic-ui-react

오류 메시지가 나오면 next.config.js  파일에  styledComponents: true 추가

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  styledComponents: true
}

module.exports = nextConfig

 

 

 

 

 

10. 리렌더링 이해하기

 

AppLayout.js

 

                <Col xs={24} md={6} >
                    {isLoggedIn ? <UserProfile setIsLoggedIn={setIsLoggedIn} /> : <LoginForm setIsLoggedIn={setIsLoggedIn} />}
                </Col>

 

 

LoginForm.js

import React, { useCallback, useState } from 'react';
import { Form, Input, Button } from 'antd';
import styled from 'styled-components';
import Link from 'next/link';


const ButtonWrapper = styled.div`
    margin-top:10px
`;

const FormWrapper = styled(Form)`
    margin-top: 100px;
    padding:10px;
`;



const LoginForm = ({ setIsLoggedIn }) => {

~


    const onSubmitForm = useCallback(() => {
        console.log(id, password);
        setIsLoggedIn(true);
    }, [id, password])

    return (
        <FormWrapper onFinish={onSubmitForm}>

~

 

UserProfile.js

import React, { useCallback } from 'react';
import { Card, Avatar, Button } from 'antd';

const UserProfile = ({ setIsLoggedIn }) => {
    const onLogOut = useCallback(() => {
        setIsLoggedIn(false);
    }, []);

    return (
        <Card
            actions={[
                <div key="twit">짹짹<br />0</div>,
                <div key="followings">팔로잉<br />0</div>,
                <div key="follower">팔로워<br />0</div>,

            ]}
        >

            <Card.Meta
                avatar={<Avatar>ZC</Avatar>}
                title="Macaronics"
            />
            <Button onClick={onLogOut}>로그아웃</Button>
        </Card>
    );
};

export default UserProfile;

 

 

 

 

 

 

 

 

11. 프로필 페이지 만들기

 

pages/profile.js

import React from 'react';
import AppLayout from './../components/AppLayout';
import Head from 'next/head';
import FollowList from './../components/FollowList';
import NicknameEditForm from './../components/NicknameEditForm';

const Profile = () => {
    const followingList = [{ nickname: 'aaaa' }, { nickname: 'bbbb' }, { nickname: 'ccc' }];
    const followerList = [{ nickname: 'ddd' }, { nickname: 'eee' }, { nickname: 'fff' }];
    return (
        <>
            <Head>
                <title>프로필 | NodeBird</title>
            </Head>
            <AppLayout>
                <NicknameEditForm />
                <div style={{ marginBottom: 20 }}></div>

                <FollowList header="팔로잉 목록" data={followingList} />
                <FollowList header="팔로워 목록" data={followerList} />
            </AppLayout>
        </>
    );
};

export default Profile;

 

 

 

components/FollowList.js

import React from 'react';
import PropTypes from 'prop-types'
import { Button, Card, List } from 'antd';
import { StopOutlined } from '@ant-design/icons'


const FollowList = ({ header, data }) => {
    return (
        <List
            style={{ marginBottom: 20 }}
            grid={{ gutter: 4, xs: 2, md: 3 }}
            size="small"
            header={<div>{header}</div>}
            loadMore={<div style={{ textAlign: 'center', margin: '10px 0' }}><Button>더 보기</Button></div>}
            bordered
            dataSource={data}
            renderItem={(item) => (
                <List.Item style={{ marginTop: 20 }}>
                    <Card actions={[<StopOutlined key="stop" />]}  >
                        <Card.Meta description={item.nickname} />
                    </Card>
                </List.Item >
            )
            }
        >
        </List >
    );
};

FollowList.prototype = {
    header: PropTypes.string.isRequired,
    data: PropTypes.array.isRequired
}


export default FollowList;

 

 

 

 

 

 

 

12. 회원가입 페이지 만들기(커스텀 훅)

 

 

pages/signup.js

import Head from 'next/head';
import React, { useCallback, useState } from 'react';
import AppLayout from '../components/AppLayout';
import { Form, Input, Checkbox, Button } from 'antd';
import useInput from '../hooks/useInput';
import styled from 'styled-components';

const ErroMessage = styled.div`
    color:red;
`;

const SignUp = () => {

    const [id, onChangeId] = useInput('');
    const [nickName, onChangeNickname] = useInput('');
    const [password, onChangePassword] = useInput('');
    const [passwordCheck, setPasswordCheck] = useState('');
    const [passwordError, setPasswordError] = useState(false);
    const [term, setTerm] = useState("");
    const [termError, setTermError] = useState(false);


    const conChangePasswordCheck = useCallback((e) => {
        setPasswordCheck(e.target.value);
        setPasswordError(e.target.value !== password);
    }, [password]);


    const onChangeTerm = useCallback((e) => {
        setTerm(e.target.checked);
        setTermError(!e.target.checked);
    }, [])


    const onSubmit = useCallback(() => {
        if (password !== passwordCheck) {
            return setPasswordError(true);
        }
        if (!term) {
            return setTermError(true);
        }
    }, [password, passwordCheck, term]);


    return (
        <>
            <AppLayout>
                <Head>
                    <title>회원 가입 | NodeBird</title>
                </Head>


                <Form onFinish={onSubmit}>
                    <div>
                        <lable htmlFor="user-id">아이디</lable>
                        <br />
                        <Input name="user-id" value={id} require onChange={onChangeId} />
                    </div>


                    <div>
                        <lable htmlFor="user-nickName">닉네임</lable>
                        <br />
                        <Input name="user-nickName" value={nickName} require onChange={onChangeNickname} />
                    </div>


                    <div>
                        <lable htmlFor="user-password">비밀번호</lable>
                        <br />
                        <Input name="user-password" type="password" value={password} require onChange={onChangePassword} />
                    </div>

                    <div>
                        <br />
                        <lable htmlFor="user-passwordCheck">비밀번호</lable>
                        <Input name="user-passwordCheck" type="password" value={passwordCheck} require onChange={conChangePasswordCheck} />
                    </div>

                    {passwordError && <ErroMessage>비밀번호가 일치하지 않습니다.</ErroMessage>}

                    <Checkbox name="user-term" checked={term} onChange={onChangeTerm}>
                        동의합니다.
                    </Checkbox>
                    {termError && <div style={{ color: 'red' }}>약관에 동의하셔야 합니다.</div>}

                    <div style={{ marginTop: 10 }}>
                        <Button type="primary" htmlType='submit'>가입하기</Button>
                    </div>

                </Form>
            </AppLayout>
        </>
    );
};


export default SignUp;

 

 

hooks/useInput.js

import { useCallback, useState } from 'react';


export default (initialValue = null) => {
    const [value, setValue] = useState(initialValue);
    const handler = useCallback((e) => {
        setValue(e.target.value);
    }, []);
    return [value, handler];
}

 

 

 

 

 

 

 

 

 

 

 

 

 

react

 

about author

PHRASE

Level 60  라이트

그리스도의 출현은 인간의 이상(理想)이 육신으로 현현(顯現)된 것이다. -도스토예프스키

댓글 ( 4)

댓글 남기기

작성