# Untitled

더미 데이터로 로그인하기

이번에는 로그인즉, 가짜로그인을 구현해보겠습니다.

먼저 LoginForm에 submit함수를 만들어주세요

```javascript
 const onSubmitForm = useCallback(() => {
    console.log(id, password);
    setIsLoggedIn(true); //Props로 AppLayout에서 받아옵니다.
  }, [id, password]);

```

{% hint style="info" %}
Ant-design에서는 onSubmit 대신 onFinish를 사용해&#x20;

e.preventDefault 즉 새로고침을 막아줄 수 있습니다.
{% endhint %}

AppLayout.js도 바꿔줘야 겠죠?

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

만약 로그인이 true면 프로필, false면 로그인창을 띄우는 화면입니다.

UserProfile.js 만들기

Ant-design의 Card를 이용해 유저 프로필을 만들어 보겠습니다.

Card는 정말 쓸모가 많으니 한번 구경다녀오세요

{% embed url="<https://ant.design/components/card/>" %}

자, 그럼 이제 UserProfile 컴포넌트를 작성해볼까요

UserProfile 컴포넌트 만들기

```javascript
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에 관한 자새한 내용은 다룰것이 많아서 위에 개발자문서를 보면 이해할 수 있습니다.

이제 홈에서 로그인 버튼을 누르면?

![이런 결과창이 나오게 됩니다](/files/-MDQ3VYVPs3diusYqFQF)

이제 가짜로그아웃 기능도 구현해볼게요

```javascript
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르 전달해줘야 하는걸 잊으면 안됩니다.

이번에는 프로필 페이지를 구성해볼까요?

프로필 페이지는 다음과 같이 구성되어있습니다.

```javascript
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부터 만들어볼까요?

```javascript
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;

```

다음은 복잡할수도 있는 리스트입니다.

```javascript
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>
      )}
    />
  );
}

```

{% hint style="info" %}
보면 복잡해보일수도 있지만 Ant-Design 공식문서에 다 나와있는 내용들입니다.

머리에 외울필요 없이 문서 보면서 쓰고싶은 부분만 가져다가 쓰면 되는 것이죠.
{% endhint %}

결과물을 한번 볼까요?

![레이아웃이 잘 잡힌 모습입니다.](/files/-MDQLfF3r4bzSiyxuTu7)

다음에는 커스텀 훅을 이용한 회원가입 페이지를 만들어 보겠습니다.

먼저 컴포넌트입니다.

```jsx
 <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를 여러번 사용해줘야 할것 같습니다.

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

하지만 이런식으로 모두 작성하는 것은 비효율적입니다.

해결방법은 두가지가 있는데요

1. 커스텀 훅을 만들어 사용하기
2. useState자체로 해결하기

커스텀훅을 먼저 만들어 볼까요?

{% code title="useInput.js" %}

```jsx
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];
}

```

{% endcode %}

훅 안에서 change되는 값들을 상태로 바꿔준뒤 valuedhk 바꾸는 함수를 내보내는 형식입니다.

그렇다면 이렇게 구현할 수 있습니다.

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

간단해지죠?

다음방법은 useState의 기본값을 객체로 만드는 방법입니다.

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

그뒤 값을 설정하는 함수를 구현해보면

```jsx
 const { id,password ,nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = (e) => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };
```

자 둘다 좋은 방법이니 편한 방법을 쓰면 될 것 같습니다.

완성된 전체 코드를 보겠습니다.

```jsx
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>
  );
}

```

길긴 하지만 완성본도 볼까요

![완성!](/files/-MDTGL55x6ipkZKZzxYS)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://conrad-maker.gitbook.io/nodebird/front/untitled-2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
