Javascript/React

[React] Spring Boot with React 채팅 서버 : 3. pages

noahkim_ 2022. 3. 9. 01:17

1. login

SWR

  • 원격상태와 (리액트의) 로컬상태를 하나로 통합해주는 모듈
    • 적절한 타이밍에 지속적으로 원격서버에 데이터를 fetch하므로 최신데이터가 갱신됨
    • 또한 컴포넌트간 swr의 키값이 같을경우 캐싱된 값을 전달하므로 데이터의 전역적인 공유가 가능해짐
  • 프로젝트에서는 로그인 성공시 validate() 함수를 호출하여 swr이 서버에 사용자정보를 로그인 과정에서 인증에 성공한 이메일을 가지고 사용자 정보를 요청하게 된다
    • 사용자 정보 요청이 성공하게 될 경우 localStorage 에 성공 이메일을 저장하고 다른 컴포넌트에서 해당 이메일을 가져와 
      사용자정보를 swr로 요청함
    • 이러한 경우 요청한 사용자 정보의 데이터가 일치하면 캐싱된 데이터만 반환하므로 트래픽 과중에 대한 우려가 사라지게 됨

Login 요청 후 동작 흐름

  • 사용자 정보를 서버로부터 얻게될 경우, 기본 창으로 리다이렉트하여 우리 서비스를 인증 및 인가된 사용자로서 제공하면 된다
    • 우리 서비스의 프론트에는 채팅기능만 구현되었으므로 /messenger로 리다이렉트 시킨다.
  • 또한 로그인이 만약 실패할 경우 useState로 선언된 logInErro값을 true로 바꾸어줌으로써 로그인창 아래에 에러 문구를 띄울 수 있게 한다

 

const LogIn = () => {

    const [email, onChangeEmail] = useInput(' ');
    const [password, onChangePassword] = useInput('');
    const [logInError, setLogInError] = useState(false);
    const {
        data: userData,
        error,
        revalidate,
        mutate
    } = useSWR<IChatUser | false>(email, fetchByEmail, {
        revalidateOnMount: false,
        revalidateOnFocus: false,
        onSuccess() {
            localStorage.setItem("email", email)
        }
    });
    
    const onSubmit = useCallback(
        (e) => {
            e.preventDefault();
            setLogInError(false);
            let bodyFormData = new FormData();
            bodyFormData.append("email", email)
            bodyFormData.append("password", password)
            axios({
                method: 'post',
                url: '/api/login',
                headers: {'Content-Type': 'multipart/form-data'},
                data: bodyFormData,
                withCredentials: true,
            })
                .then((response) => {
                    revalidate()
                    console.log("login success!")
                })
                .catch((error) => {
                    // @ts-ignore
                    console.log(error.response.status);
                    setLogInError(error.response.status === 404)
                });
        },
        [email, password],
    );

    if (userData) {
        console.log("redirect to messenger");
        return <Redirect to="/messenger"/>;
    }

    return (
        <div id="container">
            <Header>HCS</Header>
            <Form onSubmit={onSubmit}>
                <Label id="email-label">
                    <span>이메일 주소</span>
                    <div>
                        <Input type="email" id="email" name="email"
                               value={email} onChange={onChangeEmail}/>
                    </div>
                </Label>
                <Label id="password-label">
                    <span>비밀번호</span>
                    <div>
                        <Input type="password" id="password" name="password"
                               value={password} onChange={onChangePassword}/>
                    </div>
                    {logInError && <Error>이메일과 비밀번호 조합이 일치하지 않습니다.</Error>}
                </Label>
                <Button type="submit">로그인</Button>
            </Form>
            <LinkContainer>
                아직 회원이 아니신가요?&nbsp;
                <Link to="/signup">회원가입 하러가기</Link>
            </LinkContainer>
        </div>
    );
};

export default LogIn;

 

2. signup

  • email, password 값을 input태그에 작성할 경우 화면에 새로 랜더링이 되어야 하므로 onChangePassword, onChangePasswordCheck 함수를 useCallback 으로 메모이제이션 한다
const SignUp = () => {

    const [email, onChangeEmail] = useInput('');
    const [nickname, onChangeNickname] = useInput('');
    const [password, , setPassword] = useInput('');
    const [passwordCheck, , setPasswordCheck] = useInput('');
    const [mismatchError, setMismatchError] = useState(false);
    const [signUpError, setSignUpError] = useState('');
    const [signUpSuccess, setSignUpSuccess] = useState(false);

    // 비밀번호를 바꿀 경우
    const onChangePassword = useCallback(
        (e) => {
            setPassword(e.target.value);
            setMismatchError(e.target.value !== passwordCheck);
        },
        [passwordCheck],
    );

    // 비밀번호 확인을 바꿀 경우
    const onChangePasswordCheck = useCallback(
        (e) => {
            setPasswordCheck(e.target.value);
            setMismatchError(e.target.value !== passwordCheck);
        },
        [password],
    )

    const onSubmit = useCallback(
        (e) => {
            e.preventDefault();
            if (!mismatchError) {
                axios.post('/api/user/', {
                    email, nickname, password
                })
                    .then((response) => {
                        console.log(response)
                        setSignUpSuccess(true);
                    })
                    .catch((error) => {
                        console.log(error.response)
                        setSignUpError(error.response.data);
                    })
                    .finally(() => {
                    })

            }
        }, [email, nickname, password, passwordCheck, mismatchError]
    )

    return (
        <div id="container">
            <Header>HCS</Header>
            <Form onSubmit={onSubmit}>
                <Label id="email-label">
                    <span>이메일 주소</span>
                    <div>
                        <Input type="email" id="email" name="email"
                               value={email} onChange={onChangeEmail}/>
                    </div>
                </Label>
                <Label id="nickname-label">
                    <span>닉네임</span>
                    <div>
                        <Input type="text" id="nickname" name="nickname"
                               value={nickname} onChange={onChangeNickname}/>
                    </div>
                </Label>
                <Label id="password-label">
                    <span>비밀번호</span>
                    <div>
                        <Input type="password" id="password" name="password"
                               value={password} onChange={onChangePassword}/>
                    </div>
                </Label>
                <Label id="password-check-label">
                    <span>비밀번호 확인</span>
                    <div>
                        <Input
                            type="password"
                            id="password-check"
                            name="password-check"
                            value={passwordCheck}
                            onChange={onChangePasswordCheck}
                        />
                    </div>
                    {mismatchError && <Error>비밀번호가 일치하지 않습니다.</Error>}
                    {!nickname && <Error>닉네임을 입력해주세요.</Error>}
                    {signUpError && <Error>{signUpError}</Error>}
                    {signUpSuccess && <Success>회원가입되었습니다! 로그인해주세요.</Success>}
                </Label>
                <Button type="submit">회원가입</Button>
            </Form>
            <LinkContainer>
                이미 회원이신가요?&nbsp;
                <Link to="/login">로그인 하러가기</Link>
            </LinkContainer>
        </div>
    );
}

export default SignUp;

 

 

출처 :
https://www.inflearn.com/course/%ED%81%B4%EB%A1%A0%EC%BD%94%EB%94%A9-%EC%8B%A4%EC%8B%9C%EA%B0%84%EC%B1%84%ED%8C%85/dashboard
https://github.com/ZeroCho/sleact/tree/master/alecture/pages