로그인 창을 만들어보자고.

김재즈·2023년 11월 28일
1
post-thumbnail

오늘은 저번시간에 만들어둔 MainLayout의 로그인 버튼을 살려내볼것이다.

시작

우선 로그인창을 어떻게 만들것이냐면.
최대한 간지나게. 간결하게. 아주 great 하게 만들거다.

마치 해외 사이트에 들어가면 나오는 로그인창 마냥.
그러면 불필요하게 로그인 페이지를 만들 필요가 없다고 생각했다.

바로.
모달을 사용해볼 것이다.

모달이 왜 좋냐? 의 chat gpt의 의견입니다.

  1. 일시적 중단과 집중
  2. 사용자 입력 요구
  3. 경고 및 알림
  4. 일관된 디자인 패턴

그냥 저런건 내버려두고 말해보자구요.
사실 모달이 이쁨.

불필요하게 다른 페이지이동할 필요 없이,
해당 사이트에서 진행사항을 버리지 않고 작은 창 하나 띄워서
문제 해결이 가능하잖아요?

그리고 제일 중요한건 이쁨.


Chakra Templates을 뒤지다가 찾은 예시 모달인데

해당 모달을 모델로 삼아 비슷하게 만들어보겠음.

정리

일단 해당 기능을 추가해주기 전에 MainLayout 파일을 세분화 해주기로 했다.

Nav 기능 외에도 많은 기능이 MainLayout에 들어갈 것 같아,
Nav파일을 따로 만들어 MainLayout 파일에 넣어주기로 했음.

// MainLayout.tsx
import React, { useState } from 'react';
import { Box } from '@chakra-ui/react';
import Nav from './Nav';

type PageProps = {
  children: React.ReactNode;
};

const MainLayout: React.FC<PageProps> = (props) => {
  return (
    <Box>
      <Nav>
      <Box p={4}>{props.children}</Box>
    </Box>
  );
};

export default MainLayout;
import React from 'react';
import { Button, Stack, useColorMode, useColorModeValue } from '@chakra-ui/react';
import { MoonIcon, SunIcon } from '@chakra-ui/icons'

interface NavProps {
  openSignInModal: () => void;
}

const Nav: React.FC<NavProps> = () => {
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Stack
      as="nav"
      direction="row"
      spacing={7}
      align="center"
      justify="space-between"
      p={4}
      bg={useColorModeValue('gray.100', 'gray.900')}
    >![](https://velog.velcdn.com/images/jeong011010/post/5bc75e0a-18ca-4629-8968-ee2d5f3951d3/image.png)

      <Stack direction={'row'} spacing={7} align="center">
          <Button onClick={toggleColorMode}>
            {colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
          </Button>
          <Button as={'a'} fontSize={'sm'} fontWeight={400} variant={'link'}>
            Sign In
          </Button>
        </Stack>
      </Stack>
  );
};

export default Nav;

위 결과는

이렇게 깔끔해졌다. SingIn 버튼은 나중에 오른쪽으로 옮겨주는걸로 하고.

이제 모달창을 만들어봅시다.

// SignInModal.tsx
import React from 'react';
import {
  Modal,
  ModalOverlay,
  ModalContent,
  ModalHeader,
  ModalBody,
  ModalCloseButton,
  Button,
  Input,
  Stack,
  Text,
} from '@chakra-ui/react';

interface SignInModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const SignInModal: React.FC<SignInModalProps> = ({ isOpen, onClose }) => {
  return (
    <Modal isOpen={isOpen} onClose={onClose} size="sm">
      <ModalOverlay />
      <ModalContent>
        <ModalHeader textAlign="center">Sign In</ModalHeader>
        <ModalCloseButton />
        <ModalBody>
          <Stack spacing={4}>
            {/* ID 입력창 */}
            <Input type="text" placeholder="Enter your ID" />

            {/* PW 입력창 */}
            <Input type="password" placeholder="Enter your password" />

            {/* 로그인 버튼 */}
            <Button colorScheme="blue">
              Login
            </Button>

            {/* 작은 텍스트 버튼들 */}
            <Stack direction="row" justify="space-between" fontSize="sm">
              <Text as="span" color="blue.500">
                Create Account
              </Text>
              <Text as="span" color="blue.500">
                Forgot Password
              </Text>
            </Stack>
          </Stack>
        </ModalBody>
      </ModalContent>
    </Modal>
  );
};

export default SignInModal;

우선 코드 투척이요.

해당 Modal을 열 수 있도록 하는 props를 상위 컴포넌트에서 조작해줄 계획이다.

대충 코드를 해석해보자면.

interface SignInModalProps {
  isOpen: boolean;
  onClose: () => void;
}
const SignInModal: React.FC<SignInModalProps> = ({ isOpen, onClose }) => {}

위 코드는 해당 컴포넌트가 어떤 Props를 받아야 하는지 명시해주는 역할이다.

쉽게 설명하자면 SignInModal 컴포넌트가 SignInModalProps 타입의 속성을 받도록 선언해서
해당 컴포넌트를 사용하는 곳에서는 isOpen, onClose 라는 콜백 함수를 반드시 제공하도록 명시해주는 것이다.

그니까. SignInModal 컴포넌트에서 SignInModalProps로 props정의를 했죠?
그러니까 이 SignInModal 컴포넌트를 상위 컴포넌트에서 사용할 때 SignInModalProps에 들어있는
isOpen, onClose를 꼭 사용하시고, 해당 props의 타입은 boolean, void형 함수로 사용하라는 것이다!!!!

코딩 매무리

이렇게 적용해주고, 상위 컴포넌트 코드에도 SignInModal에 관련된 코드를 적용시켜준다.

// MainLayout.tsx
import React, { useState } from 'react';
import { Box } from '@chakra-ui/react';
import Nav from './Nav';
import SignInModal from './SignIn';

type PageProps = {
  children: React.ReactNode;
};

const MainLayout: React.FC<PageProps> = (props) => {
  const [isSignInModalOpen, setIsSignInModalOpen] = useState(false);

  const openSignInModal = () => {
    setIsSignInModalOpen(true);
  };

  const closeSignInModal = () => {
    setIsSignInModalOpen(false);
  };

  return (
    <Box>
      <Nav openSignInModal={openSignInModal} />
      <Box p={4}>{props.children}</Box>

      {/* SignIn 모달 */}
      <SignInModal isOpen={isSignInModalOpen} onClose={closeSignInModal} />
    </Box>
  );
};

export default MainLayout;
// Nav.tsx
import React from 'react';
import { Button, Stack, useColorMode, useColorModeValue } from '@chakra-ui/react';
import { MoonIcon, SunIcon } from '@chakra-ui/icons'

interface NavProps {
  openSignInModal: () => void;
}

const Nav: React.FC<NavProps> = ({ openSignInModal }) => {
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Stack
      as="nav"
      direction="row"
      spacing={7}
      align="center"
      justify="space-between"
      p={4}
      bg={useColorModeValue('gray.100', 'gray.900')}
    >
      <Stack direction={'row'} spacing={7} align="center">
          <Button onClick={toggleColorMode}>
            {colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
          </Button>
          <Button as={'a'} fontSize={'sm'} fontWeight={400} variant={'link'} onClick={openSignInModal}>
            Sign In
          </Button>
          {/* ... (다른 버튼 등 추가) */}
        </Stack>
        {/* ... (다른 코드 및 메뉴 등 추가) */}
      </Stack>
  );
};

export default Nav;

이렇게 해 주 면?

Kia~ 완벽했다.

다음 글에서는 제대로 공부하고 코딩한 뒤에 글을 써보도록 할게요.

사실 미친주흔씨가 자꾸 벨로그 글쓰기 폭탄돌리기를 하셔서
급하게 코딩하고 바로 글 쓰느라 좀 내용이 부족한데요.

근데 생각해보니까 굳이 항상 글 쓸 때마다 많은 내용을 담아야할까?
그러면 내가 나중에 까먹어서 볼 때 더 복잡해지니까.
그냥 이렇게 조금씩의 내용을 조금씩 조금씩 정리해서 올리는게

훨씬 이득인것 같다는 정신승리를 하며 글을 마칩니다.

profile
개발의 천재

1개의 댓글

comment-user-thumbnail
2023년 11월 29일

욕망의 항아리를 발동하고 턴을 종료한다.

답글 달기