Untitled

๋”๋ฏธ ๋ฐ์ดํ„ฐ๋กœ ๋กœ๊ทธ์ธํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ๋กœ๊ทธ์ธ์ฆ‰, ๊ฐ€์งœ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € LoginForm์— submitํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”

 const onSubmitForm = useCallback(() => {
    console.log(id, password);
    setIsLoggedIn(true); //Props๋กœ AppLayout์—์„œ ๋ฐ›์•„์˜ต๋‹ˆ๋‹ค.
  }, [id, password]);

Ant-design์—์„œ๋Š” onSubmit ๋Œ€์‹  onFinish๋ฅผ ์‚ฌ์šฉํ•ด

e.preventDefault ์ฆ‰ ์ƒˆ๋กœ๊ณ ์นจ์„ ๋ง‰์•„์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AppLayout.js๋„ ๋ฐ”๊ฟ”์ค˜์•ผ ๊ฒ ์ฃ ?

 {isloggedIn ? <UserProfile /> : <LoginForm setIsLoggedIn={setIsLoggedIn}/>}

๋งŒ์•ฝ ๋กœ๊ทธ์ธ์ด true๋ฉด ํ”„๋กœํ•„, false๋ฉด ๋กœ๊ทธ์ธ์ฐฝ์„ ๋„์šฐ๋Š” ํ™”๋ฉด์ž…๋‹ˆ๋‹ค.

UserProfile.js ๋งŒ๋“ค๊ธฐ

Ant-design์˜ Card๋ฅผ ์ด์šฉํ•ด ์œ ์ € ํ”„๋กœํ•„์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Card๋Š” ์ •๋ง ์“ธ๋ชจ๊ฐ€ ๋งŽ์œผ๋‹ˆ ํ•œ๋ฒˆ ๊ตฌ๊ฒฝ๋‹ค๋…€์˜ค์„ธ์š”

์ž, ๊ทธ๋Ÿผ ์ด์ œ UserProfile ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณผ๊นŒ์š”

UserProfile ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ

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

export default function UserProfile() {
  return (
    <Card
      actions={[
        //๋ฐฐ์—ด์ด๊ธฐ๋•Œ๋ฌธ์— ํ‚ค๊ฐ’์„ ๋„ฃ์–ด์ค˜์•ผ ํ•œ๋‹ค.
        <div key="twit">
          ์งน์งน
          <br />0
        </div>,
        <div key="following">
          ํŒ”๋กœ์ž‰
          <br />0
        </div>,
        <div key="follower">
          ํŒ”๋กœ์›Œ
          <br />0
        </div>,
      ]}
    >
      <Card.Meta avatar={<Avatar>CD</Avatar>} title="Conrad" />
      <Button>๋กœ๊ทธ์•„์›ƒ</Button>
    </Card>
  );
}

Card์— ๊ด€ํ•œ ์ž์ƒˆํ•œ ๋‚ด์šฉ์€ ๋‹ค๋ฃฐ๊ฒƒ์ด ๋งŽ์•„์„œ ์œ„์— ๊ฐœ๋ฐœ์ž๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ ํ™ˆ์—์„œ ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด?

์ด์ œ ๊ฐ€์งœ๋กœ๊ทธ์•„์›ƒ ๊ธฐ๋Šฅ๋„ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒŒ์š”

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

export default function UserProfile({ setIsLoggedIn }) {
  const onLogOut = useCallback(() => {
    setIsLoggedIn(false);
  }, []);
  return (
    <Card
      actions={[
        //๋ฐฐ์—ด์ด๊ธฐ๋•Œ๋ฌธ์— ํ‚ค
        <div key="twit">
          ์งน์งน
          <br />0
        </div>,
        <div key="following">
          ํŒ”๋กœ์ž‰
          <br />0
        </div>,
        <div key="follower">
          ํŒ”๋กœ์›Œ
          <br />0
        </div>,
      ]}
    >
      <Card.Meta avatar={<Avatar>CD</Avatar>} title="Conrad" />
      <Button onClick={onLogOut}>๋กœ๊ทธ์•„์›ƒ</Button>
    </Card>
  );
}

๋ฌผ๋ก  Applayout์—์„œ Props๋ฅด ์ „๋‹ฌํ•ด์ค˜์•ผ ํ•˜๋Š”๊ฑธ ์žŠ์œผ๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ์—๋Š” ํ”„๋กœํ•„ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•ด๋ณผ๊นŒ์š”?

ํ”„๋กœํ•„ ํŽ˜์ด์ง€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

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

export default function profile() {
//๊ฐ€์งœ ๋ฐ์ดํ„ฐ
  const followerList = [
    { nickname: "conrad" },
    { nickname: "Roo" },
    { nickname: "boo" },
  ];
  const followingList = [
    { nickname: "conrad" },
    { nickname: "Roo" },
    { nickname: "boo" },
  ];
  return (
    <div>
      <Head>
        <title>ํ”„๋กœํ•„ | NodeBird</title>
      </Head>
      
      <AppLayout>
        //๋‹‰๋„ค์ž„ ์ˆ˜์ • ์ปดํฌ๋„ŒํŠธ
        <NicknameEditForm /> 
        //๋ชฉ๋ก ์ปดํฌ๋„ŒํŠธ 2๊ฐœ
        <FollowList header="ํŒ”๋กœ์ž‰ ๋ชฉ๋ก" data={followingList} />
        <FollowList header="ํŒ”๋กœ์›Œ ๋ชฉ๋ก" data={followerList} />
      </AppLayout>
    </div>
  );
}

๋‹ค์Œ์€ ๊ฐ„๋‹จํ•œ NicknameEditForm.js๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”?

import React, { useMemo } from "react";
import { Form, Input } from "antd";

function NicknameEditForm() {
  const style = useMemo( //useMemo๋ฅผ ํ†ตํ•œ ๋ฆฌ๋žœ๋”๋ง ์ตœ์ ํ™”
    () => ({
      marginBottom: "28px",
      border: "1px solid #d9d9d9",
      padding: "30px",
    }),
    []
  );
  
  return (
    <Form style={style}>
      <Input.Search addonBefore="๋‹‰๋„ค์ž„" enterButton="์ˆ˜์ •" />
    </Form>
  );
}

export default NicknameEditForm;

๋‹ค์Œ์€ ๋ณต์žกํ• ์ˆ˜๋„ ์žˆ๋Š” ๋ฆฌ์ŠคํŠธ์ž…๋‹ˆ๋‹ค.

import React from "react";
import { List, Button, Card } from "antd";
import { StopOutlined } from "@ant-design/icons"; //์•„์ด์ฝ˜์€ ์šฉ๋Ÿ‰์ด ํฌ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ์žˆ๋‹ค.

export default function FollowList({ header, data }) {
  return (
    <List
      style={{ marginBottom: 20 }}
      //list์ด์ง€๋งŒ grid๋กœ
      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} //props๋กœ ์ „๋‹ฌ๋ฐ›์€ ๋ฐ์ดํ„ฐ
      renderItem={(item) => ( //๋ฐ์ดํ„ฐ์˜ ์•„์ดํ…œ๋“ค์„ ํ†ตํ•ด mapํ•จ์ˆ˜์™€ ๊ฐ™์€ ๊ฐœ๋…
        <List.Item style={{ marginTop: 20 }}>
          <Card actions={[<StopOutlined key="stop" />]}>
            <Card.Meta description={item.nickname} />
          </Card>
        </List.Item>
      )}
    />
  );
}

๋ณด๋ฉด ๋ณต์žกํ•ด๋ณด์ผ์ˆ˜๋„ ์žˆ์ง€๋งŒ Ant-Design ๊ณต์‹๋ฌธ์„œ์— ๋‹ค ๋‚˜์™€์žˆ๋Š” ๋‚ด์šฉ๋“ค์ž…๋‹ˆ๋‹ค.

๋จธ๋ฆฌ์— ์™ธ์šธํ•„์š” ์—†์ด ๋ฌธ์„œ ๋ณด๋ฉด์„œ ์“ฐ๊ณ ์‹ถ์€ ๋ถ€๋ถ„๋งŒ ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ฐ๋ฉด ๋˜๋Š” ๊ฒƒ์ด์ฃ .

๊ฒฐ๊ณผ๋ฌผ์„ ํ•œ๋ฒˆ ๋ณผ๊นŒ์š”?

๋‹ค์Œ์—๋Š” ์ปค์Šคํ…€ ํ›…์„ ์ด์šฉํ•œ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

 <AppLayout>
      <Head>
        <title>ํšŒ์›๊ฐ€์ž… | NodeBird </title>
      </Head>
      <Form onFinish={onSubmit}>
        <div>
          <label htmlFor="user-id">์•„์ด๋””</label>
          <br />
          <Input name="user-id" value={id} onChange={onChangeId} />
        </div>
        <div>
          <label htmlFor="user-nickname">๋‹‰๋„ค์ž„</label>
          <br />
          <Input
            name="user-nickname"
            value={nickname}
            onChange={onChangeNickname}
          />
        </div>
        <div>
          <label htmlFor="user-password">๋น„๋ฐ€๋ฒˆํ˜ธ</label>
          <br />
          <Input
            name="user-password"
            type="password"
            value={password}
            onChange={onChangePassword}
          />
        </div>
        <div>
          <label htmlFor="user-check">๋น„๋ฐ€๋ฒˆํ˜ธํ™•์ธ</label>
          <br />
          <Input
            name="user-check"
            type="password"
            value={check}
            onChange={onChangeCheck}
          />
        </div>
        {passwordError && (
          <PasswordError>๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค.</PasswordError>
        )}
        <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>

ant-design์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž์„ธํ•œ ์–ธ๊ธ‰์€ ์•ˆํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ๋Š” input์˜ ์ƒํƒœ๊ฐ’์„ ๊ด€๋ฆฌํ•ด์ค„๊ฑด๋ฐ์š”

input์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— useState๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์‚ฌ์šฉํ•ด์ค˜์•ผ ํ• ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

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

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ์‹์œผ๋กœ ๋ชจ๋‘ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ ๋‘๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ์š”

  1. ์ปค์Šคํ…€ ํ›…์„ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ธฐ

  2. useState์ž์ฒด๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ

์ปค์Šคํ…€ํ›…์„ ๋จผ์ € ๋งŒ๋“ค์–ด ๋ณผ๊นŒ์š”?

useInput.js
import { useState, useCallback } from "react";

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

ํ›… ์•ˆ์—์„œ change๋˜๋Š” ๊ฐ’๋“ค์„ ์ƒํƒœ๋กœ ๋ฐ”๊ฟ”์ค€๋’ค valuedhk ๋ฐ”๊พธ๋Š” ํ•จ์ˆ˜๋ฅผ ๋‚ด๋ณด๋‚ด๋Š” ํ˜•์‹์ž…๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  const [id, onChangeId] = useInput("");
  const [nickname, onChangeNickname] = useInput("");
  const [password, onChangePassword] = useInput("");

๊ฐ„๋‹จํ•ด์ง€์ฃ ?

๋‹ค์Œ๋ฐฉ๋ฒ•์€ useState์˜ ๊ธฐ๋ณธ๊ฐ’์„ ๊ฐ์ฒด๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

  const [inputs, setInputs] = useState({
    id: '',
    password: '',
    nickname: ''
  });

๊ทธ๋’ค ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋ฉด

 const { id,password ,nickname } = inputs; // ๋น„๊ตฌ์กฐํ™” ํ• ๋‹น์„ ํ†ตํ•ด ๊ฐ’ ์ถ”์ถœ

  const onChange = (e) => {
    const { value, name } = e.target; // ์šฐ์„  e.target ์—์„œ name ๊ณผ value ๋ฅผ ์ถ”์ถœ
    setInputs({
      ...inputs, // ๊ธฐ์กด์˜ input ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ•œ ๋’ค
      [name]: value // name ํ‚ค๋ฅผ ๊ฐ€์ง„ ๊ฐ’์„ value ๋กœ ์„ค์ •
    });
  };

์ž ๋‘˜๋‹ค ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ˆ ํŽธํ•œ ๋ฐฉ๋ฒ•์„ ์“ฐ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์™„์„ฑ๋œ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

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

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

export default function signup() {
  const [id, onChangeId] = useInput("");
  const [nickname, onChangeNickname] = useInput("");
  const [password, onChangePassword] = useInput("");

  const [check, setCheck] = useState("");
  const [passwordError, setPasswordError] = useState(false);
  const [term, setTerm] = useState("");
  const [termError, setTermError] = useState(false);

  const onChangeCheck = (e) => {
    setCheck(e.target.value);
    setPasswordError(e.target.value !== password); //์ด๋ถ€๋ถ„์ด ๋‹ฌ๋ผ์„œ ๋ชปํ•œ๋‹ค.
  };

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

  //ํ•œ๋ฒˆ๋” ์ฒดํฌ
  const onSubmit = useCallback(() => {
    if (password !== check) {
      return setPasswordError(true);
    }
    if (!term) {
      return setTermError(true);
    }
    console.log(id, nickname, password);
  }, [password, check, term]);

  return (
    <AppLayout>
      <Head>
        <title>ํšŒ์›๊ฐ€์ž… | NodeBird </title>
      </Head>
      <Form onFinish={onSubmit}>
        <div>
          <label htmlFor="user-id">์•„์ด๋””</label>
          <br />
          <Input name="user-id" value={id} onChange={onChangeId} />
        </div>
        <div>
          <label htmlFor="user-nickname">๋‹‰๋„ค์ž„</label>
          <br />
          <Input
            name="user-nickname"
            value={nickname}
            onChange={onChangeNickname}
          />
        </div>
        <div>
          <label htmlFor="user-password">๋น„๋ฐ€๋ฒˆํ˜ธ</label>
          <br />
          <Input
            name="user-password"
            type="password"
            value={password}
            onChange={onChangePassword}
          />
        </div>
        <div>
          <label htmlFor="user-check">๋น„๋ฐ€๋ฒˆํ˜ธํ™•์ธ</label>
          <br />
          <Input
            name="user-check"
            type="password"
            value={check}
            onChange={onChangeCheck}
          />
        </div>
        {passwordError && (
          <PasswordError>๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค.</PasswordError>
        )}
        <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>
  );
}

๊ธธ๊ธด ํ•˜์ง€๋งŒ ์™„์„ฑ๋ณธ๋„ ๋ณผ๊นŒ์š”

Last updated