METABOX ํ”„๋กœ์ ํŠธ ๐Ÿš‚ - ๊ธฐ์–ตํ•˜๊ณ  ์‹ถ์€ ์ฝ”๋“œ

dudgus5766ยท2021๋…„ 10์›” 18์ผ
2

Projects

๋ชฉ๋ก ๋ณด๊ธฐ
3/3
post-thumbnail

2์ฐจ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๊ธฐ์–ตํ•ด๋†“๊ณ  ์‹ถ์€ ์ฝ”๋“œ๋“ค์„ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค! ๐Ÿ™‚

login, signup ๋ชจ๋‹ฌ

๋กœ๊ทธ์ธ๊ณผ ํšŒ์›๊ฐ€์ž…์˜ ์–‘์‹์„ ์‚ดํŽด๋ณด๋ฉด ๊ฐ™์€ input์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. input์€ ๋ชจ๋‘ ๊ฐ™์€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‹ค์‹œํ•˜๊ณ  ์œ ์ผํ•˜๊ฒŒ ํšŒ์›๊ฐ€์ž… ๋ถ€๋ถ„๋งŒ email ์ค‘๋ณต ๊ฒ€์‚ฌ๋ฅผ ์‹ค์‹œํ•œ๋‹ค. ๊ฐ™์€ input ํ‹€(์Šคํƒ€์ผ)์„ ์ด์šฉํ•ด login๊ณผ signup ๋‘ ๊ฐœ์˜ ๊ฐ์ฒด ์•ˆ์— input์— ๋“ค์–ด๊ฐ€๋Š” ๊ฐ๊ฐ์˜ data๋ฅผ ๋ฐฐ์—ด์œผ๋กœ ๋„ฃ์—ˆ๋‹ค.

โฌ‡๏ธ src/Nav/Nav.jsx

const INITIAL_INPUT = {
  name: '',
  birthday: '',
  email: '',
  emailOk: '',
  password: '',
  passwordOk: '',
};

function Nav() {
  const [showModal, setShowModal] = useState(false);
  const [isSignUp, setIsSignUp] = useState(false);
  const [loginInput, setLoginInput] = useState(INITIAL_INPUT);

  const LOGIN = {
    type: 'login',
    text: '๋กœ๊ทธ์ธ',
    data: [
      {
        type: 'email',
        name: 'email',
        value: loginInput.email,
        text: '์ด๋ฉ”์ผ',
        holder: '์ด๋ฉ”์ผ',
        data: '์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ ํ˜•์‹์ž…๋‹ˆ๋‹ค.',
      },
      {
        type: 'password',
        name: 'password',
        value: loginInput.password,
        text: '๋น„๋ฐ€๋ฒˆํ˜ธ',
        holder: '๋น„๋ฐ€๋ฒˆํ˜ธ',
        data: '๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์˜๋ฌธ,์ˆซ์ž,ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์กฐํ•ฉํ•˜์—ฌ 8~12์ž๋กœ ๊ตฌ์„ฑํ•˜์„ธ์š”.',
      },
    ],
  };

  const SIGNUP = {
    type: 'signUp',
    text: 'ํšŒ์›๊ฐ€์ž…',
    data: [
      {
        type: 'name',
        name: 'name',
        value: loginInput.name,
        text: '์ด๋ฆ„',
        holder: '์ด๋ฆ„',
        data: '์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.',
      },
      {
        type: 'birthday',
        name: 'birthday',
        value: loginInput.birthday,
        text: '์ƒ๋…„์›”์ผ 8์ž๋ฆฌ ex) 1990-05-05',
        holder: '์ƒ๋…„์›”์ผ',
        data: '์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ƒ๋…„์›”์ผ์ž…๋‹ˆ๋‹ค.',
      },
      {
        type: 'email',
        name: 'emailOk',
        value: loginInput.emailOk,
        text: '์ด๋ฉ”์ผ',
        holder: '์ด๋ฉ”์ผ',
        data: '์ •ํ™•ํ•˜์ง€ ์•Š์€ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.',
      },
      {
        type: 'password',
        name: 'password',
        value: loginInput.password,
        text: '๋น„๋ฐ€๋ฒˆํ˜ธ',
        holder: '๋น„๋ฐ€๋ฒˆํ˜ธ',
        data: '๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์˜๋ฌธ,์ˆซ์ž,ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ์กฐํ•ฉํ•˜์—ฌ 8~12์ž๋กœ ๊ตฌ์„ฑํ•˜์„ธ์š”.',
      },
      {
        type: 'password',
        name: 'passwordOk',
        value: loginInput.passwordOk,
        text: '๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ',
        holder: '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ•œ ๋ฒˆ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”',
        data: '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ•ด์ฃผ์„ธ์š”.',
      },
    ],
  };
  ...์ค‘๋žต

state๊ฐ’์œผ๋กœ ์ง€์ •๋œ issignup ๊ฐ’์ด false์ผ ๋•Œ๋Š” SIGNUP format์„, true์ผ ๋•Œ๋Š” LOGIN format์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ชจ๋‹ฌ์„ ๋ณด์—ฌ์ฃผ์—ˆ๋‹ค.

return (
    <>
      <Navigator>
        {showModal && (
          <Modal
            handleModal={handleModal}
            onClickWindow={onClickWindow}
            isSignUp={isSignUp}
            onClickSignUp={onClickSignUp}
            setValues={setValues}
            inputValue={loginInput}

// format ํ˜•์‹์„ state๊ฐ’์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค.
            format={isSignUp ? SIGNUP : LOGIN}
            onSign={onSign}
          />
        )}

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๋ถ€๋ถ„์„ ๋งก์œผ๋ฉด์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•œ ๊ตฌํ˜„ ์‚ฌํ•ญ์ด์—ˆ๋‹ค.
์‚ฌ์‹ค ์ด๋ฉ”์ผ์ด๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ™์€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ๋ฐฑ ์ชฝ์—์„œ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋ก ํŠธ์—์„œ๋Š” ๊ตณ์ด ํ•„์š”์—†๋Š” ๊ฒƒ์ด ์‚ฌ์‹ค์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ UI/UX๊ฐ€ ์ค‘์š”์‹œ ๋˜๋Š” ์š”์ฆ˜์˜ ์›น ์‚ฌ์ดํŠธ์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ณผ ๋™์‹œ์— ํ•ด๋‹น ํ•ญ๋ชฉ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ฒฐ๊ณผ(input๋ฐ‘์˜ ๋นจ๊ฐ„ ๊ธ€์”จ, ๋นจ๊ฐ„ ๋ฐ‘์ค„, ์ดˆ๋ก ์ฒดํฌ ํ‘œ์‹œ ๋“ฑ๋“ฑ)๋ฅผ ๋ฐ”๋กœ๋ฐ”๋กœ ์‹œ๊ฐํ™”ํ•˜์—ฌ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ด ๋‹น์—ฐ์‹œ ๋˜๊ณ  ์žˆ๋‹ค.

์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ํ•„์š”ํ•œ ํ•ญ๋ชฉ๋“ค์˜ ๊ฒ€์‚ฌ ๊ฐ’์„ ์ •๊ทœํ‘œํ˜„์‹ ํ˜•์‹์˜ ์ƒ์ˆ˜ ๋ฐ์ดํ„ฐ๋กœ ๋งŒ๋“ค์—ˆ๋‹ค. ๊ฒ€์‚ฌ๋ฅผ ์‹ค์‹œํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“  ํ›„ input์˜ value๊ฐ’์„ ๋ฐ›์•„ ํ•จ์ˆ˜๋ฅผ ๋Œ๋ฆฐ ํ›„ true๋‚˜ false ๋กœ ๋ฐ˜ํ™˜ํ•˜์—ฌ input๋ฐ‘์˜ (๋นจ๊ฐ„์ƒ‰์˜) guide text๋ฅผ ๋ณด์—ฌ์ฃผ์—ˆ๋‹ค.

โฌ‡๏ธ src/Modal/Inputs.jsx

//์ด๋ฉ”์ผ ์ •๊ทœํ‘œํ˜„์‹
const emailValid = /^[a-zA-Z\d+-.]+@[a-zA-Z\d+-.]+\.[a-zA-Z]{2,3}$/;
//๋น„๋ฐ€๋ฒˆํ˜ธ ์ •๊ทœํ‘œํ˜„์‹
const pwValid =
  /^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$/;
//์ƒ๋…„์›”์ผ ์ •๊ทœํ‘œํ˜„์‹
const birthValid =
  /^(19[0-9][0-9]|20\d{2})-(0[0-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/;

//์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ•จ์ˆ˜
function Inputs({ format, setValues, inputValue, onSign }) {
  const isValidInput = item => {
    const { email, emailOk, password, passwordOk, name, birthday } = inputValue;
    const INPUT_VALIDATION_MAP = {
      name: name === '' || (name && name.length > 1),
      birthday: birthday === '' || (birthday && birthValid.test(birthday)),
      email: email === '' || (email && emailValid.test(email)),
      emailOk: emailOk === '' || (emailOk && emailValid.test(emailOk)),
      password: password === '' || (password && pwValid.test(password)),
      passwordOk: passwordOk === '' || (passwordOk && passwordOk === password),
    };

    return INPUT_VALIDATION_MAP[item];
  };

  return (
    <InputAll>
      {format.data.map(input => {
        return (
          <AllInput key={input.id}>
            <Label>{input.text}</Label>
            <InputOver>
              <Input
                onChange={e => setValues(e, input)}
                name={input.name}
                type={input.type}
                placeholder={input.holder}
                value={
                  input.name === 'birthday'
                    ? input.value.replace(/(\d{4})(\d{2})(\d{1})/g, '$1-$2-$3')
                    : input.value
                }
              />
              {input.name === 'emailOk' && (
                <OverlapOk isValidInput={isValidInput}>์ค‘๋ณต ํ™•์ธ</OverlapOk>
              )}
            </InputOver>
            <GuideText
              name={input.name}
              input={input}
//false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด guide text๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค.
              isValidInput={isValidInput}
            />
          </AllInput>
        );
      })}
      <AuthBtn onClick={() => onSign(format)}>{format.text}</AuthBtn>
    </InputAll>
  );
}

์ƒ๋…„์›”์ผ์— ํ•˜์ดํ”ˆ(-) ๋„ฃ๊ธฐ

๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž… ๋ถ€๋ถ„์„ ๋‹ด๋‹นํ•˜๋Š” ๋ฐฑ์—”๋“œ ํŒ€์›์ด ์›๋ž˜ ์ง„ํ–‰ํ–ˆ๋˜ ์ƒ๋…„์›”์ผ ๋ฐ์ดํ„ฐ ๋ฐฉ์‹(900101)์—์„œ ๋…„,์›”,์ผ ์‚ฌ์ด์— ํ•˜์ดํ”ˆ์„ ๋„ฃ์€ ๋ฐฉ์‹(90-01-01)์œผ๋กœ ๋ณ€๊ฒฝํ•ด๋‹ฌ๋ผ๋Š” ์š”์ฒญ์„ ํ–ˆ๋‹ค.

            <Input
                value={
                  input.name === 'birthday'
                    ? input.value.replace(/(\d{4})(\d{2})(\d{1})/g, '$1-$2-$3')
                    : input.value
                }
              />

ํ•ด๋‹น input์ด name ==='birthday'์ผ ๋•Œ ์ •๊ทœํ‘œํ˜„์‹์„ ํ†ตํ•ด ํ•˜์ดํ”ˆ์„ ์ถ”๊ฐ€ํ–ˆ๋‹ค!


customFetch

customFetch ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด Nav.js์— ์žˆ๋Š” fetch ํ•จ์ˆ˜๋ฅผ ์กฐ๊ธˆ ๋” ๊ฐ„๋‹จํ•˜๊ณ  ๊น”๋”ํ•˜๊ฒŒ ์งค ์ˆ˜ ์žˆ์—ˆ๋‹ค.

โฌ‡๏ธ src/utils/api.js

import { GET_LOGIN } from '../config';

//์ปค์Šคํ…€ํŒจ์น˜ ํ•จ์ˆ˜
export const customFetch = (endpoint, options = {}, { onSuccess } = {}) => {
  const opts = {
    method: 'POST',
    ...options,
  };

  return fetch(GET_LOGIN + endpoint, opts)
    .then(res => res.json())
    .then(res => onSuccess && onSuccess(res));
};

โฌ‡๏ธ src/Nav/Nav.js


  const requestSignIn = loginInput => {
    const { email, password } = loginInput;
    
    //์ปค์Šคํ…€ํŒจ์น˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ
    customFetch(API.SIGNIN, {
      body: JSON.stringify(
        {
          email: email,
          password: password,
        },
        {
          onSuccess: res => {
            if (res.token) {
              localStorage.setItem('token', res.token);
            }
            if (res.MESSAGE === 'SUCCESS') {
              alert('METABOX์— ์˜ค์‹  ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค');
              handleModal();
            } else {
              alert('์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ•œ ๋ฒˆ ํ™•์ธํ•ด์ฃผ์„ธ์š”');
            }
          },
        }
      ),
    });
  };

window ํด๋ฆญ์‹œ ๋ชจ๋‹ฌ์ฐฝ ๋‹ซ๊ธฐ

โฌ‡๏ธ src/Nav/Nav.js


  const onClickWindow = e => {
    const clicked = e.target.closest('.modal');
    if (clicked) {
      return;
    }
    handleModal();
  };

์—ฌ๊ธฐ์„œ closest๋Š” ์ž์‹ ๋ถ€ํ„ฐ ๋ถ€๋ชจ ์š”์†Œ ๋‹จ์œ„๋กœ ์ถœ๋ฐœํ•ด , ๊ฐ ์š”์†Œ๊ฐ€ ์ง€์ •ํ•œ ์„ ํƒ์ž์— ๋งŒ์กฑํ•  ๋•Œ๊นŒ์ง€ ํƒ์ƒ‰ํ•œ๋‹ค. ( ์ถœ์ฒ˜ - ๊ณต์‹๋ฌธ์„œ )

โฌ‡๏ธ src/Modal/Modal.js

function Modal({
  onClickWindow,
}) {
  return (
    <Container onClick={onClickWindow}>
      <ContainerBox className="modal">
        <Form />
      </ContainerBox>
    </Container>
  );
}

๋”ฐ๋กœ ์ด๋ฒคํŠธ ์บก์ณ๋ง ๋ฒ„๋ธ”๋ง ์ง€์ •์„ ์•ˆํ•ด์คฌ์Œ์œผ๋กœ ๋ฒ„๋ธ”๋ง์œผ๋กœ ์ง„ํ–‰์ด ๋˜๊ณ 

  • ๋ชจ๋‹ฌ์„ ํด๋ฆญํ–ˆ์„ ์‹œ cliked์— <ContainerBox>์˜ className= "modal"์ด ๋“ค์–ด๊ฐ
  • ๋ชจ๋‹ฌ ์™ธ ์˜์—ญ(window ๋ถ€๋ถ„) ํด๋ฆญ ์‹œ cliked์— <Container>๊ฐ€ ๋“ค์–ด๊ฐ

cliked๊ฐ€ ์กด์žฌํ•˜๋ฉด : return; โžก๏ธ ๋ชจ๋‹ฌ ๊ทธ๋Œ€๋กœ
cliked null์ด๋ฉด : false โžก๏ธ handleModal ํ•จ์ˆ˜ ์‹คํ–‰(๋ชจ๋‹ฌ ๋‹ซ๊ธฐ)


์ถ”๊ฐ€ ๋กœ์ง๋“ค๐Ÿ‘จโ€๐Ÿ’ป

textarea ๊ธ€์ž ์ˆ˜ ์ œํ•œ

  const isCount = e => {
    const maxText = 50;
    const textVal = e.target.value;
    const textLen = textVal.length;
    setTextCount({ ...textCount, textLen });
    setCommentInput({ ...commentInput, comment: textVal });
    if (textLen > maxText) {
      alert('์ตœ๋Œ€ 50์ž ๊นŒ์ง€๋งŒ ์ž…๋ ฅ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.');
    }
  };

๋ถ„(minute)์œผ๋กœ ๋ฐ›์€ ์‹œ๊ฐ„ ์‹œ๊ฐ„, ์ผ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ํ•จ์ˆ˜

  const timeToday = value => {
    const minuteTime = value;
    if (minuteTime < 1) return '๋ฐฉ๊ธˆ์ „';
    if (minuteTime < 60) {
      return `${minuteTime}๋ถ„ ์ „`;
    }
    const hourTime = Math.floor(minuteTime / 60);
    if (hourTime < 24) {
      return `${hourTime}์‹œ๊ฐ„ ์ „`;
    }
    const dayTime = Math.floor(minuteTime / 60 / 24);
    if (dayTime < 365) {
      return `${dayTime}์ผ ์ „`;
    }
    return `${Math.floor(dayTime / 365)}๋…„ ์ „`;
  };

๋ชจ๋‹ฌ์ฐฝ์„ ๋„์› ์„ ๋•Œ ์™ธ๋ถ€ ์Šคํฌ๋กค ์ œํ•œ

  useEffect(() => {
    document.body.style.cssText = `
      position: fixed;
      top: -${window.scrollY}px;
      overflow-y: hidden;
      width: 100%;`;
    return () => {
      const scrollY = document.body.style.top;
      document.body.style.cssText = '';
      window.scrollTo(0, parseInt(scrollY || '0', 10) * -1);
    };
  }, []);
profile
RN App Developer

0๊ฐœ์˜ ๋Œ“๊ธ€