Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4주차 기본, 심화]🍁 로그인/회원가입🍁 #11

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rachel5640
Copy link
Contributor

✨ 구현 기능 명세

🌱 기본 조건

  • .env 파일 사용하기

🧩 기본 과제

[ 로그인 페이지 ]

  1. 로그인
    • 아이디와 비밀번호 입력후 로그인 버튼을 눌렀을시 성공하면 /mypage/:userId 로 넘어갑니다.
      (여기서 userId는 로그인 성공시 반환 받은 사용자의 id)
  2. 회원가입 이동
    • 회원가입을 누르면 /signup으로 이동합니다.

[ 회원가입 페이지 ]

  1. 중복체크 버튼

    • ID 중복체크를 하지 않은 경우 검정색입니다.
    • ID 중복체크 결과 중복인 경우 빨간색입니다.
    • ID 중복체크 결과 중복이 아닌 경우 초록색입니다.
  2. 회원가입 버튼

    • 다음의 경우에 비활성화 됩니다.
    • ID, 비밀번호, 닉네임 중 비어있는 input이 있는 경우
    • 중복체크를 하지 않은 경우
    • 중복체크의 결과가 중복인 경우
    • 회원가입 성공시 /login 으로 이동합니다.

[ 마이 페이지 ]

  1. 마이 페이지
    • /mypage/:userId 의 userId를 이용해 회원 정보를 조회합니다.
    • 로그아웃 버튼을 누르면 /login으로 이동합니다.

🌠 심화 과제

[ 로그인 페이지 ]

  1. 토스트
    • createPortal을 이용합니다.
    • 로그인 실패시 response의 message를 동적으로 받아 토스트를 띄웁니다.

[ 회원가입 페이지 ]

  1. 비밀번호 확인

    • 회원가입 버튼 활성화를 위해서는 비밀번호와 비밀번호 확인 일치 조건까지 만족해야 합니다.
  2. 중복체크

    • 중복체크 후 ID 값을 변경하면 중복체크가 되지 않은 상태(색은 검정색)로 돌아갑니다.

생각과제

  • API 통신에 대하여
  • 로딩 / 에러 처리를 하는 방법에는 어떤 것들이 있을까?
  • 패칭 라이브러리란 무엇이고 어떤 것들이 있을까?
  • 패칭 라이브러리를 쓰는 이유는 무엇일까?

💎 PR Point

파일구조

./src
├── 📦 components
│   ├── inputForm.jsx
│   ├── toastMessage.jsx
│   ├── Router.jsx
│   └── contentBox, cta, Title, info 등의 UI 컴포넌트
├── 📦 constants
│   └── POST_LIST.js
├── 📦 pages
│   ├── homepage.jsx
│   ├── login.jsx
│   ├── signup.jsx
│   └── userID.jsx
├── 📦 styles
│   ├── globalStyle.js
│   └── theme.js
├── App.jsx
└── main.jsx

point 1) 로그인 시 해당 페이지로 이동

//pages/login.jsx
  const handleLogin = async () => {
    try {
      const response = await apiClient.post(`/api/v1/members/sign-in`, {
        username: idValue,
        password: pwValue,
      });
      navigate(`/mypage/${response.data.id}`);
    } catch (err) {
      setShowToast(true);
    }
  };
  • ${response.data.id} 상대경로로 이동로 usenavigate 사용해서 이동
  • 400일 때 Toast 상태 True로 변경

point 2) 회원가입 버튼 활성화/비활성화

  const activateBtn = async () => {
    if (
      isClicked &&
      username &&
      nickname &&
      password &&
      passwordCheck &&
      password === passwordCheck
    ) {
      await doubleCheck();
      setSignupButton(true);
      return true;
    } else {
      setSignupButton(false);
      return false;
    }
  };

  useEffect(() => {
    activateBtn();
  }, [isClicked, username, nickname, password, passwordCheck]);

1. 조건문

  • username password nickname passwordCheck password=== passwordCheck 조건문
  • await 으로 doubleCheck가 실행되었을 때 아래 버튼이 활성화 될 수 있게 설정

2. 실시간으로 상태 업로드

  • 활성화 실시간으로 업로드되게 isClicked(선택되었을 때 파란색 또는 빨간색 뜨게 하는 버튼)username password passwordCheck 각각 모두 변화가 일어날 때 useEffect로 activateBtn를 실행

point 3) 마이페이지 조회

    let { userId } = useParams();
    const [username, setUsername] = useState("");
    const [nickname, setNickname] = useState("");

  const getLoginData = async () => {
    try {
      const response = await apiClient.get(`/api/v1/members/${userId}`, {});
      setUsername(response.data.username);
      setNickname(response.data.nickname);
    } catch (err) {
      console.log(err);
    }
  };

  getLoginData();
  • useParams 사용해서 해당 userID값 페이지로 이동

point 4) toast 구현 및 message 동적 생성

//components/ToastMessage.jsx

const ToastMessage = ({ message, onClose }) => {
  useEffect(() => {
    const timer = setTimeout(() => {
      onClose();
    }, 1500);
    return () => clearTimeout(timer);
  }, []);

  return ReactDOM.createPortal(
    <Toast>
      <p>{message}</p>
    </Toast>,
    document.body
  );
};
//pages/login
{showToast && (
        <ToastMessage message={toastMessage} onClose={handleToastClose} />
      )}
  • 1.5초후에 onClose 될 수 있게 useEffect 사용해서 구현하고, login 페이지 최하단에서 불러오기
  • showToast Defulat 가 false에서 true로 바뀌고, 그 때 ToastMessage 불러오기

point 5) 중복확인 체크해제

// componenets/inputForm.jsx
<Btn type="button"

  onClick={() => {
  doubleCheck();
  setIsClicked(true);
  }}
  isExist={isExist} // isExist 값을 전달
  isClicked={isClicked} // isClicked 값을 전달
   >중복확인 </Btn>

 const Btn = styled.button`
  background-color: ${({ theme, isExist, isClicked }) => {
    if (isClicked === true) {
      return isExist ? theme.red50 : theme.blue50;
    } else if (isClicked === false) {
      return theme.gray2;
    } else {
      return theme.gray2;
    }
  }};
  // 중복 체크 API
// pages/Login.jsx
  const doubleCheck = async () => {
    try {
      const response = await apiClient.get(`/api/v1/members/check`, {
        params: {
          username: username,
        },
      });

      setIsExist(response.data.isExist);
    } catch (err) {
      console.log("중복 체크 에러", err);
    }
  };
  • 스타일. isChecked선택시 red, blue 중 선택, 그 외에 isChecked false일때는 gray2
  • setIsExist 에 true, false 값을 넣어서 위에 ActiveBtn에 넣어서 버튼 색 나오게

🥺 소요 시간, 어려웠던 점

  • 3일
  • api 연결은 감을 잡으니까 어떻게 어떻게 해결이 됐는데 심화 과제하면서 state가 여러개 추가되면서 조금 헷갈려서 오래걸렸어요!

🌈 구현 결과물

  1. 로그인 시 해당 ID화면으로 이동
2023-11-17.9.16.19.mov
  1. 회원가입 활성화, 비활성화, 중복확인
2023-11-17.9.16.42.mov
  1. 아이디 재입력시 중복확인 선택해제
2023-11-17.9.17.31.mov
  1. 토스트, 토스트 메세지 동적생성
2023-11-17.9.18.08.mov

Copy link

@se0jinYoon se0jinYoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

심화과제까지 넘넘 잘해줬네요 !!!
axios, async-await, api 통신, 라우터 ... 등등 이번 과제의 핵심들을 너무너무 잘 적용해주고있어서 잘한다잘한다 !! 하면서 코드리뷰 달았답니당 !

이제 state로 전달하고 렌더링을 어떻게 해야하는지는 익숙해진 것 같으니 ! 어떻게하면 컴포넌트의 재사용성을 올릴 수 있을지, 페이지별로 중복되는 컴포넌트는 무엇인지 (공통 wrapper로 사용할 수 있는 레이아웃은 어떤게 있을지!)를 고민하면서 view구조를 짜고,
내가 이 데이터값(state값)을 어느어느 컴포넌트에서 사용해야하는지, 그렇다면 그 컴포넌트들의 공통 부모가 어디인지 ! 등을 고려해서 state을 정의할 위치, 정의할 방법 (useState, useReducer, 더 나아가면 context API까지!) 를 고민해보면서 디벨롭해가면 정말정말 좋을 것 같아요 !!

디자인도 넘넘 깔꼼하구 ... 잘했다잘했다 ! 수고했어요오 ! 💗💗💗

Comment on lines +4 to +6
headers: {
"Content-Type": "application/json",
},

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header까지 넣어서 만들었구나 ! 굿굿!
이번 과제에서는 header를 바꿀일이 없으니 이렇게 만들어서 export하는 것 좋네용 !

Comment on lines +26 to +27
const Cta = { Main, Secondary };
export default Cta;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수를 export할 때는 대문자로 해줍시당 !

const CTA = { Main, Secondary };
export default CTA;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

밑에 보다가 첨언하러 다시 올라왔어요 ! 코드 다시 보니깐 스타일에서 로그인인지 회원가입인지에 따라서 스타일링을 다르게 해주려고 서로 다른 버튼 컴포넌트를 만든 거였더라구요 !

styled-component에서도 동적 스타일링을 할 수 있답니다 ? prop을 전달해주어서 prop값에 따른 조건부 스타일링을 할 수 있어요 !
예를들면

// login.jsx
// 로그인 버튼일 경우 
<Main $login={true} >로그인</Main>

// 회원가입 버튼일 경우
<Main $login={false} >로그인</Main>

// �cta.jsx
const Main = styeld.div`
...
font-color: ${({ $login, theme }) => ($login ? theme.yellow : theme.gray2)};
`

이렇게 조건부 스타일링을 한다면 중복되는 코드를 줄이고, 하나의 버튼 컴포넌트로 렌더링할 수 있겠죠?

+) 변수 이름은 그 의미를 담고 있으면 좋아요 ! Main, Secondary보다는 LoginBtn, SignupBtn으로 짓는게 더 가독성 있겠죠 ? 💫

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요! css 에서는 저렇게 변경되는 state의 값에 따라 동적으로 조건부 렌더링을 해주기 위해 styled components 부분에 삼항 조건 연산자를 사용하는 경우도 꽤 자주 있답니다!!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니 앙큼토끼 서진언니 리뷰 폼 모야,, 앞으로 나아갈 방향성까지 조언해주는 금잔디가 있따,,? 나였으면 광광 울어따 (´。_。`)

import styled from "styled-components";
import React from "react";

const LoginForm = ({ idValue, setIdValue, pwValue, setPwValue }) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파일명이랑 컴포넌트 명을 맞추어주면 좋아요 !
지금 보면 inputForm 파일 안에 로그인 컴포넌트랑 회원가입 컴포넌트가 들어있는데, 하나의 파일에는 하나의 컴포넌트만 넣어두는게 좋답니다 !
그 말은 즉 로그인과 회원가입을 각자 다른 파일으로 분리하여 작성할 수 있다는 거겠죠 ? ☺️
로그인과 회원가입에서 각자 전달받는 prop도 다르기 때문에 분리하여 작성하면 가독성이 더 좋아질 거에요 ! 그리고 전달하는 prop들도 분리되어 데이터 전달 흐름도 개선할 수 있을 거에요 ! ㅎㅎ

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헤헤 요거 나도 리뷰 받았던 거 ! 그리구 파일명이랑 컴포넌트명을 동일하게 맞춰줌으로써 rsc 단축키로 쉽게 코드 생성이 가능하다고 함니다 - ?!

placeholder="아이디를 입력하세요"
value={username}
onChange={(e) => setUsername(e.target.value)}
></ShortTextholder>{" "}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기 뒤에 {" "} 는 뭘까요? 공백 ?!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저 공백이, vscode에서 태그들 내부에 실수로 공백이 포함된 채로 구현하게 되면, 자동 줄 맞춤이 될 때 저렇게 {" "} 이렇게 맞춰지더라고요?! 만약 저런게 추가될 경우 발견하는 족족 지워주면 좋을 것 같습니다!!!

Comment on lines +1 to +6
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import GlobalStyle from "./styles/globalStyle.js";
import { ThemeProvider } from "styled-components";
import theme from "./styles/theme.js";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import 순서를 맞춰주면 나중에 보기에 더 편하답니다 💡
민서 다른 파일들에서는 순서 너무너무 깔꼼하게 잘 맞추어주었는데 여기에는 안 되어있어서 살짝 언급하고 지나갑니다아 ~ ㅎㅎ

Comment on lines +24 to +40
const activateBtn = async () => {
if (
isClicked &&
username &&
nickname &&
password &&
passwordCheck &&
password === passwordCheck
) {
await doubleCheck();
setSignupButton(true);
return true;
} else {
setSignupButton(false);
return false;
}
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async - await 적용 너무너무 잘했네요 !

try {
await postData();
goToLogin();
alert("회원가입 완료");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 alert까지 야무져라

nickname: nickname,
password: password,
});
console.log(response);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인용 콘솔은 지워주기!

//
useEffect(() => {
activateBtn();
}, [isClicked, username, nickname, password, passwordCheck]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deps값 잘 넘겨주었네용!

Comment on lines +14 to +24
const getLoginData = async () => {
try {
const response = await apiClient.get(`/api/v1/members/${userId}`, {});
setUsername(response.data.username);
setNickname(response.data.nickname);
} catch (err) {
console.log(err);
}
};

getLoginData();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 로그인하기 버튼이 눌렸을 때 실행하게 하는게 어떨까요?
페이지가 넘어온 다음에 요청을 보내면 딜레이가 살짝 생길 수 있답니다 !

Copy link

@lydiacho lydiacho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라우팅과 api 통신 모두 새로운 개념이라 적용해보기 쉽지 않았을텐데 너무너무 수고 많았습니다!!! 👍 💯 ❤️

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react 프로젝트를 생성할 때마다 초기세팅의 일부로 이렇게 안쓰는 파일들은 지워주는걸 습관화하장!!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보통 theme 파일에서는 color 뿐만아니라 font 등도 지정해주곤 하는데, 그래서 여러종류의 값을 사전 설정 해주기 위해 각 분류 별 객체를 colors, fonts 등으로 이름붙여서 생성해줍니다!
따라서 theme에서 만들어준 친구를 불러와서 사용할 때도
background-color: ${({ theme }) => theme.gray1}; 이렇게보다
background-color: ${({ theme }) => theme.colors.gray1};이렇게 한뎁스 더 거쳐서 접근하는 것이 일반적인 방식이랍니당
참고참고!!!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유의해야할 점이! 리액트에서는 컴포넌트명을 파스칼케이스로 작성해줘야 리액트라 아~ 이게 컴포넌트구나! 하고 잘 알아먹느납니다 ㅎㅎ 따라서 일반 리액트 컴포넌트나 스타일드 컴포넌트 모두 꼭 대문자로 시작하는 것 잊지 말아요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문법이니까 꼭 지키기!

Comment on lines +26 to +27
const Cta = { Main, Secondary };
export default Cta;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요! css 에서는 저렇게 변경되는 state의 값에 따라 동적으로 조건부 렌더링을 해주기 위해 styled components 부분에 삼항 조건 연산자를 사용하는 경우도 꽤 자주 있답니다!!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props를 먼저 받아서 거치지 않고, 곧바로 곧바로 구조분해하는 방식 너무 좋아요! 👍 💯

Comment on lines +8 to +16
<Wrapper>
<Name>ID</Name>
<Textholder
type="text"
placeholder="아이디를 입력하세요"
value={idValue}
onChange={(e) => setIdValue(e.target.value)}
></Textholder>
</Wrapper>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이부분도 반복되는 걸 보니 내부 텍스트 따로 배열로 관리하고 전체적인 html 구조는 공통 컴포넌트로 분리하여 재사용하기 딱 좋아보이네요!! 🥺😎

placeholder="아이디를 입력하세요"
value={username}
onChange={(e) => setUsername(e.target.value)}
></ShortTextholder>{" "}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저 공백이, vscode에서 태그들 내부에 실수로 공백이 포함된 채로 구현하게 되면, 자동 줄 맞춤이 될 때 저렇게 {" "} 이렇게 맞춰지더라고요?! 만약 저런게 추가될 경우 발견하는 족족 지워주면 좋을 것 같습니다!!!

Comment on lines +23 to +40
//id 비밀번호
const handleLogin = async () => {
try {
const response = await apiClient.post(`/api/v1/members/sign-in`, {
username: idValue,
password: pwValue,
});
console.log(response.data.id);
navigate(`/mypage/${response.data.id}`);
} catch (err) {
if (err.response && err.response.data) {
setToastMessage(err.response.data.message);
} else {
setToastMessage("사용자를 찾을 수 없습니다");
}
setShowToast(true);
}
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api 통신 코드 너무 잘 구현해줬는데요!!
이제 여기서 한발짝 더 고려하면 좋을 부분은 바로 컴포넌트 및 파일 분리입니다!
하나의 파일, 하나의 모듈이 최대한 '한가지 일만 할 수 있도록' 컴포넌트를 분리해주면 좋은데용,
따라서 보통 UI를 렌더링하는 코드와 api통신을 하는 코드는 전혀 다른 역할을 하는 친구들이기 때문에 다른 파일로 관리하곤 한답니다!

해당 피드백 참고하여 api 통신과 관련된 코드는 별도의 파일로 분리하여 관리해주는 리팩토링 경험을 해보면 좋을 것 같아요 ❤️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문법이니까 꼭 지키기!

Comment on lines +27 to +28
username: idValue,
password: pwValue,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

애초에 state를 각각
const [userName, setUserName] = useState("");
const [password, setPassWord] = useState("");
이렇게 하면

Suggested change
username: idValue,
password: pwValue,
username,
password,

단축 프로퍼티라는 것에 의해 이렇게도 가능하답니당? 그냥 참고!!

Comment on lines +61 to +65
const response = await apiClient.post("/api/v1/members", {
username: username,
nickname: nickname,
password: password,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const response = await apiClient.post("/api/v1/members", {
username: username,
nickname: nickname,
password: password,
});
const response = await apiClient.post("/api/v1/members", {
username,
nickname,
password,
});

오오 여기서 이렇게 쓸 수 있겠다 ㅎㅎ

Comment on lines +10 to +14
const [username, setUsername] = useState("");
const [nickname, setNickname] = useState("");
const [password, setPassword] = useState("");
const [passwordCheck, setPasswordCheck] = useState("");
const [isExist, setIsExist] = useState("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state 선언이 너무 많고, 밑에 props로 넘겨줄 친구가 너무 많아진 문제를 동시에 해결할 수 있는 방법으로

Suggested change
const [username, setUsername] = useState("");
const [nickname, setNickname] = useState("");
const [password, setPassword] = useState("");
const [passwordCheck, setPasswordCheck] = useState("");
const [isExist, setIsExist] = useState("");
const initInputInfo = {
username: "",
nickname: "",
password: "",
passwordCheck: "",
isExist: "",
}
const [inputInfo, setInputInfo] = useState(initInputinfo);

이런식으로 객체 하나로 state를 관리할 수 있다는거! 그럼 각각이 변경되었을 때의 로직도 하나로 묶을 수 있게된답니당

Copy link
Member

@aazkgh aazkgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민서가 ✨우수잔디✨상을 타서 과제를 구경하러 왔어요💛 정말 같이 왕초보에서 시작했는데 민서 어디까지 성장하는 건가요 ? ? ? 정말 저만 잘하면 되겠어요,,~ 컴포넌트 분리해준 것도 그렇고 axios create 사용해서 API 붙인 것도 다 넘넘 대단해요 👍 정말 보고 배워야겠어요 ! 성장하는 민서 너무 멋져요 ! !! 💛

지금 봤는데 이 금잔디조에서 우수 과제를 2명이나 탄 거냐며ㅜ 여기 갓잔디조였네,,

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<style>
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+KR:wght@100;200;300;400;500;600;700&family=Noto+Sans+KR:wght@100;200;300;400;500;600;700;800;900&display=swap");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 재훈오빠한테 들은 건데 @import는 성능 문제가 있을 수 있다고 하더라구요 ?
https://webclub.tistory.com/127
ㄴ 위에는 관련 링크입니다 !

합세 때도 글로벌 스타일 안에 @import를 하니까 warning이 났어서 저는 link 태그를 이용했던 기억이,, 민서도 참고하면 좋을 것 같아요 ! !!

import styled from "styled-components";
import React from "react";

const LoginForm = ({ idValue, setIdValue, pwValue, setPwValue }) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헤헤 요거 나도 리뷰 받았던 거 ! 그리구 파일명이랑 컴포넌트명을 동일하게 맞춰줌으로써 rsc 단축키로 쉽게 코드 생성이 가능하다고 함니다 - ?!

background-color: ${({ theme }) => theme.gray1};
width: 100%;
margin: 0 auto;
font-family: 'Noto Sans KR', sans-serif;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

완전완전완전 사소한 거지만 여기 들여쓰기두 맞춰주면 좋을 듯 해요 !!!

@@ -0,0 +1,12 @@
const theme = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

역시 디자인팀 출신답게 야무지게 디자인까지 챙겨주고 💛

};

// 미입력시 회원가입 비활성화
const activateBtn = async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 API 가져다 붙일 때만 async - await 쓸 줄 아는데,, 어떻게 이런 생각까지,, 야무져요오,,, 👍👍👍👍👍👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants