본문 바로가기
  • 삽질하는 자의 블로그
메인-프로젝트/React - Do-Health 프로젝트

11. [ 기능 ] FIRE-BASE AUTH 를 사용한, 로그인 작업 시작, with. env 를 사용하여, 중요한 개인정보(API KEY) 숨기기

by 이게뭐당가 2023. 1. 4.

열심히 React 를 다시하기 위한 프로젝트이다.

서버측 코드를 사용하고 싶으면, 따로 Node 를 만들던가, Nextjs 를 사용하겠지만 React 에 집중하기 위해 

파이어 베이스를 사용하여, DB 와 AUTH 를 사용해보았다.

 

파이어 베이스 AUTH 에서는,

기본 E-mail 기능을 사용했으며

GoogleOAuth 도 시도해 볼 예정이다.

 

1. 파이어 베이스 AUTH 

 

Firebase 인증 REST API

 

firebase.google.com

 

 

2. 환경변수를 사용하여, 파이어베이스의 API KEY 숨기기

환경변수를 사용하기 위해, dotenv 패키지를 사용해 보았다.

https://www.npmjs.com/package/dotenv

 

dotenv

Loads environment variables from .env file. Latest version: 16.0.3, last published: 3 months ago. Start using dotenv in your project by running `npm i dotenv`. There are 31974 other projects in the npm registry using dotenv.

www.npmjs.com

1. npm i dotenv
2. .env 파일을 root 디렉토리에 만든다.
3. 안에 "반드시" REACT_APP_ 으로 시작하는 이름으로 값을 만든다.

    ex) REACT_APP_FIREBASE_API_KEY=asdasdasdasfdasdas

    ** "", ; 등의 기호를 사용하지 않음에 주의

4. 사용한다.

    const API_KEY = process.env.REACT_APP_FIREBASE_API_KEY;
    console.log(API_KEY);

.gitignore 안에 .env 를 넣어, gitHub에 올라가는 일이 없도록 하자

 

 

3. FireBase Auth DOCS 를 보고, 회원가입 기능 만들기

 

1. 회원가입 helper 함수 만들기

<helper / signup.tsx >

    export const signupHandler = async (email: string, password: string) => {
        const sendRequest = await fetch(
        `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${process.env.REACT_APP_FIREBASE_API_KEY}`,
        {
            method: "POST",
            body: JSON.stringify({ email, password, returnSecureToken: true }),
            headers: {
            "Content-Type": "application/json",
            },
        }
        );
        const responseData = await sendRequest.json();

        return responseData;        // 반드시 헬퍼함수에서, return 해야한다.
    };


** 계속해서, 헬퍼함수 내에서 return 을 하지 않아서, response 를 받지 못하는 어처구니 없는 실수를 반복하고 있다.
** 꼭 retrun 하여, 밖에서 사용하도록 만들자.

* 환경변수를 사용하여, API 키를 숨기자

 

 

2. 회원가입 폼 만들기

< components / 5.sign-up / signup-form.tsx >

    import styles from "./signup-form.module.css";
    import { signupHandler } from "../../helper/signup-handler";
    import { useRef, FormEvent } from "react";

    const SignUpForm = () => {
        const emailRef = useRef<HTMLInputElement>(null);
        const passwordRef = useRef<HTMLInputElement>(null);

        const submitHanlder = async (e: FormEvent) => {
            e.preventDefault();
            const email = emailRef.current!.value;
            const password = passwordRef.current!.value;

            const responseData = await signupHandler(email, password);

            console.log(responseData);
        };

        return (
            <div className={styles.main_div}>
                <form onSubmit={submitHanlder}>
                    <div>
                        <label> 이메일을 입력하세요</label>
                        <input type={"email"} ref={emailRef} />
                    </div>
                    <div>
                        <label> 비밀번호를 입력하세요</label>
                        <input type={"password"} ref={passwordRef} />
                    </div>
                    <div>
                        <button> 제출</button>
                    </div>
                </form>
            </div>
        );
    };

    export default SignUpForm;

 

3. 사용자들이 잘못된 정보나, 동일한 email 로 가입할 경우, 경고 표시하기

import styles from "./signup-form.module.css";
import { signupHandler } from "../../helper/signup-handler";
import { useRef, FormEvent, useState } from "react";
import { useHistory } from "react-router-dom";

const SignUpForm = () => {
    const history = useHistory();
    const emailRef = useRef<HTMLInputElement>(null);
    const passwordRef = useRef<HTMLInputElement>(null);
    const [error, setError] = useState<string>("");

    const submitHanlder = async (e: FormEvent) => {
        e.preventDefault();
        const email = emailRef.current!.value;
        const password = passwordRef.current!.value;

        const responseData = await signupHandler(email, password);

        if (responseData.idToken) {		// idToken 이 res 되어왔다면, 올바른 것이다.
            history.replace("/");
        } else if (responseData.error.message === "EMAIL_EXISTS") {		// 잘못되었다면, error.message 가 온다.
            setError("이미 존재하는 이메일 입니다.");
        } else if ( responseData.error?.message === "WEAK_PASSWORD : Password should be at least 6 characters") {
            setError("패스워드가 6자리 이하입니다.");
        }
    };
    return (
        <div className={styles.main_div}>
                ...
            <form 3onSubmit={submitHanlder} className={styles.signup_form}>
                {error && <p className={styles.error}> {error}</p>}
                <div>
                    <label> 이메일을 입력해주세요</label>
                    <input type={"email"} ref={emailRef} required />
                </div>
                <div>
                    <label> 비밀번호를 입력해주세요</label>
                    <input type={"password"} ref={passwordRef} required />
                </div>
                <div className={styles.button_div}>
                    <button className="Just_Click_Button_Default"> 제출</button>
                </div>
            </form>
        </div>
    	);
	};

export default SignUpForm;

// "파이어베이스는, 잘못된 정보를 입력하면, error 를 리턴해준다."
// 각각돌려주는 프로퍼티들은 DOCS 를 참고하거나, log로 뽑아보자.

 

4. 로그인하고, react-cookie 를 사용하여, 쿠키를 이용해 로그인상태를 유지시킨다. 로그아웃 버튼을 만든다.

 

https://www.npmjs.com/package/react-cookie

 

react-cookie

Universal cookies for React. Latest version: 4.1.1, last published: a year ago. Start using react-cookie in your project by running `npm i react-cookie`. There are 541 other projects in the npm registry using react-cookie.

www.npmjs.com

리액트 쿠키는 브라우저의 쿠키에, 값을 저장 할 수 있게 만들어준다.

정확히는 쿠키의 기능을 사용할 수 있게 해주는 정도이다.

 

사용자의 정보는 "로컬 스토리지", "쿠키", "Session (DB)" 등에 저장 할 수 있다.

로컬 스토리지 는 내 로컬 컴퓨터에서 사용하기엔 적절할 지 모르나, 보안기능이 전혀 없다. 해킹당하기 쉽다.

 

세션 을 통한 저장은 괜찮지만 성능적인 면에서 꽤나 큰 단점이 있고, 백엔드와 프론트엔드 간의 긴밀한 관계가 없는 이상

    중간에 정보를 탈취 당하기 쉽다. 

https://dive-into-frontend.tistory.com/98

 

12. 리액트 [인증] - 인증의 기본 원리, 파이어 베이스 Auth편(1)

[인증의 기본 원리] 인증은 크게, 서버사이드 인증 방식과 토큰 인증 방식을 가진다. 1. 서버사이드 인증 방식 서버측에 사용자들의 정보를 저장하고, 유저가 정보를 제출하면, 확인하고, 그에 따

dive-into-frontend.tistory.com

 

쿠키 ( "토큰"을 쿠키에 저장) 를 사용한 인증 방식을 채택하여, 성능면으로나, 보안 면으로 좋은 이점을 가질 수 있다.

 

 

1) 쿠키 프로바이더 감싸기

< index.tsx >

    import { CookiesProvider } from "react-cookie";

    const root = ReactDOM.createRoot(
        document.getElementById("root") as HTMLElement
    );
        root.render(
        <CookiesProvider>
            <Provider store={store}>
                <BrowserRouter>
                    <Layout>
                        <App />
                    </Layout>
                </BrowserRouter>
            </Provider>
        </CookiesProvider>
    );

 

2) 로그인 컴포넌트에서, 로그인하면 쿠키에 idToken 을 등록한다.

import { useCookies } from "react-cookie";
import { useHistory } from "react-router-dom";

const SignInForm = () => {
  const history = useHistory();
  const [cookies, setCookie] = useCookies(["auth-cookie"]);		// 쿠키 등록을 위한 setCookie
  const [error, setError] = useState<string>("");
  const emailRef = useRef<HTMLInputElement>(null);
  const passwordRef = useRef<HTMLInputElement>(null);

  const submitHanlder = async (e: FormEvent) => {
    e.preventDefault();
    const email = emailRef.current!.value;
    const password = passwordRef.current!.value;

    const responseData = await signinHandler(email, password);

    if (responseData.idToken) {			    // idToken 이 있다면 OK
      setCookie("auth-cookie", {
        idToken: responseData.idToken,
        email: responseData.email,
      });
      history.replace("/");
    } else if (responseData.error.message === "EMAIL_NOT_FOUND") {	    // 아니면 에러메시지
      setError("이메일을 잘못 입력하셨습니다.");
    } else if (responseData.error.message === "INVALID_PASSWORD") {	   // 아니면 에러메시지
      setError("패스워드를 잘못 입력하셨습니다.");
    }
  };

  return (
    <div className={styles.main_div}>
      <div>
        <h1> 로그인</h1>

 

 

3) Layout 등, 필요한 곳에서, 쿠키가 없다면 나가게 만들거나, 쿠키가 없다면 버튼을 숨기는 기능을 추가한다.

<layout-header.tsx>

    import styles from "./layout-header.module.css";
    import { Link } from "react-router-dom";
    import { useCookies } from "react-cookie";
    import { useHistory } from "react-router-dom";

    const LayoutHeader = () => {
        const [cookies, setCookie, removeCookie] = useCookies(["auth-cookie"]);     // 쿠키
        const history = useHistory();

        const logoutHandler = () => {
            removeCookie("auth-cookie");
            history.replace("/");
        };

        return (
            <div className={styles.main_header}>
                <Link to="/" className={styles.header_logo}>
                    <img src="/main-logo.png" alt="logo" />
                    <h3> DO.HEALTH</h3>
                </Link>
                <ul className={styles.header_menu}>

                    {!cookies["auth-cookie"] && (       // 쿠키 없다면, "활성화"
                    <Link to="/sign-up">
                        <li> 회원가입</li>
                    </Link>
                    )}

                    {!cookies["auth-cookie"] && (       // 쿠키 없다면, "활성화"
                    <Link to="/sign-in">
                        <li> 로그인</li>
                    </Link>
                    )}

                    {cookies["auth-cookie"] && <li onClick={logoutHandler}> 로그아웃</li>}  // 쿠키 있다면, "활성화"
                </ul>
            </div>
        );
    };

    export default LayoutHeader;

** 강제로 Redirect 하는 기능은, useHistory 를 사용하여, 쿠키가 없으면 되돌려보낸다.

 

댓글