PJH's Community Site - Auth

박정호·2022년 11월 22일
0

Community Project

목록 보기
3/14
post-thumbnail

🚀 Start

이제 회원가입 UI를 생성하고, 기능도 추가해보자.스타일링은 간단하게 레이아웃만 생성해주고, 추후에 모든 UI 생성후 Tailwind CSS를 통해서 스타일링 해주자.



🖥 Client

✔️ register page 생성

pagesregirster.tsx를 생성하자.



✔️ UI 작성

form 태그 안에는 input태그button태그가 존재.

  • onSubmit으로 handleSubmit이라는 메서드 호출.

input태그를 재사용할 수 있도록 별도의 컴포넌트로 생성하여 InputGroup 컴포넌트import

  • props로 필요한 값들을 전달

만약 회원가입이 되어있다면 로그인 페이지로 이동할 수 있도록 Link태그 사용

const register = () => {


  return (
    <div>
      <div>
        <div>
          <h1 >회원가입</h1>
          <form onSubmit={handleSubmit}>
            <InputGroup
              placeholder="Email"
              value={email}
              setValue={setEmail}
              error={errors.email}
            />
            <InputGroup
              placeholder="Username"
              value={username}
              setValue={setUsername}
              error={errors.username}
            />
            <InputGroup
              placeholder="Password"
              value={password}
              setValue={setPassword}
              error={errors.password}
            />
            <button> Sign Up</button>
          </form>
          <small> 이미 가입하셨나요?<Link href="/login">로그인 </Link> </small>
        </div>
      </div>
    </div>
  );
};

export default register;


✔️ InputGroup 컴포넌트 생성

전달받은 props에 대한 type을 Interface에서 정의

  • placeholder: Email, Username, Password
  • value : 각각 input에 입력한 값
  • setValue : value값을 변경하는 기능

cls를 이용하여 error시에 다음과 실행

  • input의 테두리가 빨개지며, 요청에 대한 값으로 받은 error를 출력.
    (즉, error값이 true일시에 active, 평상시 error는 빈값이므로 아무런 출력 X)
className={cls({ 'border-red-500': error } )}
...
<small>{error}</small>

💡 잠깐) cls
: classNames를 조건부로 결합하기 위한 간단한 JavaScript 유틸리티.

npm install classnames —save

자세히 👉 https://www.npmjs.com/package/classnames

interface InputGroupProps {
 
  placeholder?: string;
  value: string;
  error: string | undefined;
  setValue: (str: string) => void;
}

const InputGroup: React.FC<InputGroupProps> = ({
  placeholder = '',
  error,
  value,
  setValue,
}) => {
  return (
    <div>
      <input
        type='text'
        style={{ minWidth: 300 }}
        className={cls({ 'border-red-500': error } )}
        placeholder={placeholder}
        value={value}
        onChange={e => setValue(e.target.value)}
      />
      <small>{error}</small>
    </div>
  );
};

export default InputGroup;


✔️ register 기능 추가

State 생성

  • 다음과 같은 state들을 api요청에 담길 데이터들
  const [email, setEmail] = useState('');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [errors, setErrors] = useState<any>({});

axios 설치

  • axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리
npm install axios —save

form태그의 submit으로 실행되는 handleSubmit 생성

 1️⃣ 백엔드에 회원가입을 위한 요청 (email, password, username을 post)
 2️⃣ 회원가입 후 로그인 페이지 자동 이동
 3️⃣ error시 setErrors를 통해 reponse값 저장.(에러에 대한 문구)
  const router = useRouter();

  const handleSubmit = async (event: React.FormEvent) => {
    event.preventDefault();
    try {
      const res = await Axios.post('/auth/register', {
        email,
        password,
        username,
      });
      console.log('res:', res);
      router.push('/login');
    } catch (error: any) {
      console.log('error:', error);
      setErrors(error?.response?.data || {});
    }
  };

✔️ BaseURL 설정

상위파일에서 axios를 이용해서 요청을 보내는 BaseURL을 글로벌하게 지정

// _app.tsx
export default function App({ Component, pageProps }: AppProps) {
  
  Axios.defaults.baseURL = process.env.NEXT_PUBLIC_SERVER_BASE_URL + '/api';
  
  return <Component {...pageProps} />;
}
// .env
NEXT_PUBLIC_SERVER_BASE_URL= 백엔드url


📡 Server

지금까지 본 것은 client에서 입력한 회원정보에 대해서 server로 데이터를 post하고 요청하는 것이었다. 따라서, 그에 맞는 api를 생성하여 response를 보내주자.



✔️ 요청에 따른 api 생성

routes폴더 안에 auth.ts 생성

src/routes/auth.ts

const register = async (req: Request, res: Response) => {
  const { email, username, password } = req.body;
  console.log(email, username, password);
}

const router = Router();
router.post('/register', register);

export default router;

Entry파일에 auth route import

src/server.ts

...
app.use('/api/auth', authRoutes);
...

CORS 에러 처리

import cors from 'cors';

const origin = 'http://localhost:3000';

app.use(cors({ origin }));


✔️ register 함수 작성

1️⃣ 이메일과 유저이름을 이미 저장하여 사용되고 있는지를 확인.

2️⃣ 이미 있다면 errors 객체에 넣어준다.

3️⃣ error가 존재한다면 return 값으로 error를 response로 보내준다.

4️⃣ user 정보와 user 인스턴스 생성.

5️⃣ Entity에 정해 놓은 조건으로 user 데이터의 유효성 검사 실시
( class-validator: 데코레이터 및 비데코레이터 기반으로 유효성 검사를 할 수 있다.)

6️⃣ 유저 정보를 user table에 저장.

7️⃣ 저장된 유저정보를 response로 전달

8️⃣ 에러를 response로 전달.

const register = async (req: Request, res: Response) => {
  const { email, username, password } = req.body;

  try {
    let errors: any = {};
  
    const emailUser = await User.findOneBy({ email });  // 1️⃣ 번
    const usernameUser = await User.findOneBy({ username });  // 1️⃣ 번
	
    if (emailUser) errors.email = '이미 등록된 이메일 주소입니다.'; // 2️⃣ 번
    if (usernameUser) errors.username = '이미 등록된 사용자 이름입니다.'; // 2️⃣ 번

    if (Object.keys(errors).length > 0) { //3️⃣ 번
      return res.status(400).json(errors);
    }

    const user = new User(); // 4️⃣ 번
    user.email = email;
    user.username = username;
    user.password = password;

    errors = await validate(user); // 5️⃣ 번

 
    await user.save(); // 6️⃣ 번
    
    return res.json(user); // 7️⃣ 번
  } catch (error) {
    console.log('error', error); // 8️⃣ 번
    return res.status(500).json({ error });
  }
};




✔️ 유효성검사 에러 처리

errorsentity에서 설정했던 error에 대한 값들일 것이다.

그리고 앞서 보았듯 validate check(유효성 검사)를 해주었고, errors가 배열 형태로 있는 것을 확인 할 수 있다.

   errors = await validate(user);

따라서 mapErrors 메서드를 통하여 errors를 인자로 받고, reduce와 Object.entries를 통해서 배열의 각 error값을 prev객체에 전달한다.

const mapErrors = (errors: any[]) => {
  return errors.reduce((prev: any, err: any) => {
    prev[err.property] = Object.entries(err.constraints)[0][1];
    return prev;
  }, {});
};

...

// error가 존재한다면 errors
if (errors.length > 0) return res.status(400).json(mapErrors(errors));

객체의 값을 확인해보자.

console.log(Object.entries(err.constraint));
console.log(prev)



⭐️ 회원가입 확인




📷 Photos

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글

관련 채용 정보