Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발일기

[내일배움캠프] 최종 프로젝트 - Sweetalert를 이용해 alert/confirm창 만들기, 공통 Input 컴포넌트 생성 본문

카테고리 없음

[내일배움캠프] 최종 프로젝트 - Sweetalert를 이용해 alert/confirm창 만들기, 공통 Input 컴포넌트 생성

코찡 2023. 8. 20. 18:13

1. Sweetalert를 이용해 alert/confirm창 만들기

1) 패키지 설치

yarn add sweetalert2

2) index.html의 body 부분에 다음의 코드 심어주기

 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.css" />
 <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.4.10/dist/sweetalert2.min.js"></script>

3) Alert 및 Confirm 함수 생성 (message 커스텀 가능하도록)

// components 폴더 하위 common 폴더 하위 modal 폴더 하위 alert.ts

import Swal from 'sweetalert2';

type AlertProps = {
  title: string;
  position?: 'top' | 'top-start' | 'top-end' | 'center' | 'center-start' | 'center-end' | 'bottom' | 'bottom-start' | 'bottom-end';
};

type AlertErrorProps = {
  title?: string;
  text?: string;
};

// 기본 alert창 -> position과 title 바꿔서 재사용 가능
export const Alert = ({ title, position = 'center' }: AlertProps) => {
  Swal.fire({
    position,
    icon: 'success',
    title,
    showConfirmButton: false,
    timer: 1000,
  });
};

// alert창 (error) -> title과 text 바꿔서 재사용 가능
export const AlertError = ({ title = '에러가 발생했습니다.', text = '다시 시도해 주세요!' }: AlertErrorProps) => {
  Swal.fire({
    icon: 'error',
    title,
    text,
  });
};

// 삭제 시 confirm 창 -> deleteMessage 입력 필수
export const ConfirmDelete = (deleteMessage: string) => {
  Swal.fire({
    title: '정말 삭제하시겠습니까?',
    text: '이 작업은 되돌릴 수 없습니다.',
    icon: 'warning',
    showCancelButton: true,
    confirmButtonColor: '#3085d6',
    cancelButtonColor: '#d33',
    confirmButtonText: '네, 삭제하겠습니다.',
    cancelButtonText: '취소',
  }).then((result) => {
    if (result.isConfirmed) {
      Swal.fire('삭제됨', deleteMessage, 'success');
    }
  });
};

// 수정사항 저장 시 confirm 창
export const ConfirmSave = () => {
  Swal.fire({
    icon: 'question',
    title: '수정사항을 반영하시겠습니까?',
    showCancelButton: true,
    confirmButtonText: '확인',
    cancelButtonText: '취소',
  }).then((result) => {
    if (result.isConfirmed) {
      Swal.fire('수정되었습니다!', '', 'success');
    }
  });
};

4) Alert 및 Confirm 함수 사용 예시

import { Alert, AlertError, ConfirmDelete, ConfirmSave } from '../components/common/modal/alert';

const Intro = () => {
  // title 입력 필수, position은 입력하지 않으면 default 값 = center
  const handleAlert = () => {
    Alert({ title: '로그인 성공', position: 'top-end' });
  };
  // 빈 배열 넣으면 기본 error 메시지로 alert
  // 다음과 같이 error 메시지 커스텀 가능 -> AlertError({ title: '에러다.', text: '이제 나가줘' });
  const handleAlertError = () => {
    AlertError({});
  };
  // 삭제 메시지 입력 필수
  const handleConfirmDelete = () => {
    ConfirmDelete('해당 댓글이 삭제되었습니다.');
  };

  const handleConfirmSave = () => {
    ConfirmSave();
  };

  return (
    <div>
      <button onClick={handleAlert}>Alert 버튼</button>
      <button onClick={handleAlertError}>AlertError 버튼</button>
      <button onClick={handleConfirmDelete}>ConfirmDelete 버튼</button>
      <button onClick={handleConfirmSave}>ConfirmSave 버튼</button>
    </div>
  );
};

export default Intro;

2. 공통 Input 컴포넌트 생성

1) Input.tsx -> InputProps 타입 지정 및 Textarea와 Input 구분

// components 폴더 하위 common 폴더 하위 input 폴더 하위 Input.tsx

import * as St from './style';

export interface InputProps {
  id?: string;
  name?: string;
  type: 'text' | 'textarea' | 'email' | 'password';
  inputStyleType: 'comment' | 'auth' | 'search' | 'write' | 'apply';
  placeholder?: string;
  value?: string;
  onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
  border: boolean;
  disabled?: boolean;
  autoFocus?: boolean;
  forwardRef?: React.MutableRefObject<HTMLInputElement | HTMLTextAreaElement | null>;
  autocomplete?: string;
}

export const Input = ({ id, name, type, inputStyleType, placeholder, value, onChange, border, disabled, autoFocus, forwardRef, autocomplete }: InputProps) => {
  if (type === 'textarea') {
    return (
      <St.Textarea
        id={id}
        name={name}
        inputStyleType={inputStyleType}
        placeholder={placeholder ?? ''}
        value={value}
        onChange={onChange}
        border={border}
        disabled={disabled}
        autoFocus={autoFocus}
        ref={forwardRef as React.MutableRefObject<HTMLTextAreaElement | null>}
        autoComplete={autocomplete ?? 'off'}
      />
    );
  } else {
    return (
      <St.Input
        id={id}
        name={name}
        type={type}
        inputStyleType={inputStyleType}
        placeholder={placeholder ?? ''}
        value={value}
        onChange={onChange}
        border={border}
        disabled={disabled}
        autoFocus={autoFocus}
        ref={forwardRef as React.MutableRefObject<HTMLInputElement | null>}
        autoComplete={autocomplete ?? 'off'}
      />
    );
  }
};

2) Input의 css 관리하는 styled-components 코드

// components 폴더 하위 common 폴더 하위 input 폴더 하위 style.ts

import styled, { css } from 'styled-components';
import { InputProps } from './Input';

// Textarea는 type 값을 갖지 않으므로 InputProps 타입에서 빼고 적용
type TextareaProps = Omit<InputProps, 'type'>;

interface BaseStylesProps {
  inputStyleType: 'comment' | 'auth' | 'search' | 'write' | 'apply';
  border: boolean;
}

const baseStyles = css<BaseStylesProps>`
  outline: none;

  ${(props) => {
    switch (props.inputStyleType) {
      case 'comment':
        return css`
          width: 60vw;
          height: 15px;
          padding: 7px 13px;
          border-radius: 20px;
          box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
        `;
      case 'auth':
        return css`
          width: 350px;
          height: 15px;
          padding: 7px 10px;
        `;
      case 'search':
        return css`
          width: 150px;
          height: 20px;
          padding: 4px;
          border-radius: 20px;
          text-align: center;
          box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
        `;
      case 'write':
        return css`
          width: 40vw;
          height: 13px;
          padding: 7px 13px;
          border-radius: 20px;
        `;
      case 'apply':
        return css`
          width: 40vw;
          height: 20vw;
          padding: 10px;
          &::-webkit-scrollbar {
            display: none;
          }
        `;
    }
  }}

  ${(props) =>
    props.border
      ? css`
          border: 1px solid gray;
        `
      : css`
          border: none;
          border-bottom: 1px solid gray;
        `}
`;

export const Input = styled.input.attrs<InputProps>((props) => ({
  type: props.type,
}))`
  ${baseStyles}
`;

export const Textarea = styled.textarea<TextareaProps>`
  ${baseStyles}
`;

3) 공통 Input 사용 예시

import { Input } from '../components/common/input';

const Intro = () => {
  return (
    <div>
      <Input type="text" inputStyleType="comment" placeholder="댓글을 입력해주세요." border={true} />
      <br />
      <br />
      <Input type="email" inputStyleType="auth" placeholder="이메일을 입력해주세요." border={false} />
      <br />
      <Input type="text" inputStyleType="auth" placeholder="닉네임을 입력해주세요." border={false} />
      <br />
      <Input type="password" inputStyleType="auth" placeholder="비밀번호를 입력해주세요." border={false} />
      <br />
      <br />
      <Input type="text" inputStyleType="search" placeholder="검색어를 입력해주세요." border={true} />
      <br />
      <br />
      <Input type="text" inputStyleType="write" placeholder="원활한 동료찾기를 위해 지역명을 함께 입력해주세요." border={true} />
      <br />
      <br />
      <Input type="textarea" inputStyleType="apply" placeholder="간단한 자기소개를 입력해주세요." border={true} />
    </div>
  );
};

export default Intro;