본문 바로가기

공부일지/Project

React Project ) Slack Clone, 회원가입 페이지, 로그인 페이지 만들기, redux

728x90
반응형

안녕하세요.

프론트 공부중인 비전공자 에디터 M입니다.

회사 업무와 여려가지 이유로 잠시 블로그에 글을 작성하지 못했는데 포기하지 않고 다시 블로그 글을 작성하러왔습니다.

 

그럼 제가 블로그를 잠시 하지 않는동안 만들었던 Slack Clone의 코드 리뷰를 해보려고 합니다. 

리뷰에 앞서 git주소를 알려드릴께요. 자세한 코드와 package들은 git을 보시면 됩니다.

https://github.com/piwe2004/slack-clone

 

GitHub - piwe2004/slack-clone

Contribute to piwe2004/slack-clone development by creating an account on GitHub.

github.com

 

그럼 시작해 볼께요. GOGO~~

 

React Project Slack Clone 기본 세팅


  • mui: css프레임워크 mui만 설치한건 아니고 관련 라이브러리들을 전부 설치하였습니다.
  • firebase : 구글에서 만든 백엔드 쪽을 구성해주는 사이트의 라이브러리
  • react-avatar-editor : 아바타 이미지를 생성해주는 라이브러리 다양한 종류와 컨셉의 아바타가 있다.
  • react-redux : 상태관리에 필요한 라이브러리
  • react-colorful : 색상을 선택할 수 있는 기능
  • react-router-dom : 페이지 이동과 파라미터 등 router 기능을 해주는 라이브러리
  • uuid : 유니크한 아이디를 만들어주는 라이브러리

위의 라이브러리 외에도 다른 라이브러리들을 사용했는데 자세한건 github에서 확인하시면 됩니다.

 

그리고 이번 코드 리뷰에서는 firebase의 설정을 따로 작성하지 않겠습니다. 구글링하셔서 한번 직접 해보시는걸 추천 드려요.

 

회원가입 페이지 만들기


먼저 완성된 UI 이미지먼저 보여 드릴께요.

slack clone 회원가입 페이지 ui

return (
    <Container component="main" maxWidth="xs">
        <Box sx={{ 
                display: "flex", 
                flexDirection:'column', 
                alignItems: 'center',
                justifyContent: 'center',
                height: '100vh'
            }}
        >
            <Avatar sx={{m:1, bgcolor:'secondary.main'}} >
                <TagIcon />
            </Avatar>
            <Typography component="h1" variant='h5'>
                회원가입
            </Typography>
            <Box component='form' noValidate onSubmit={handleSubmit} sx={{mt:3}}>
                <Grid container spacing={2}>
                    <Grid item xs={12}>
                        <TextField name="name" required fullWidth label='닉네임' autoFocus />
                    </Grid>
                    <Grid item xs={12}>
                        <TextField name="email" required fullWidth label='이메일 주소' autoComplete="off" />
                    </Grid>
                    <Grid item xs={12}>
                        <TextField name="password" required fullWidth label='비밀번호' type="password" />
                    </Grid>
                    <Grid item xs={12}>
                        <TextField name="confirmPassword" required fullWidth label='비밀번호 확인' type='password' />
                    </Grid>
                </Grid>
                {
                    error 
                        ? <Alert sx={{mt:3}} severity='error'>{error}</Alert>
                        : ''
                }
                <LoadingButton type='submit' fullWidth variant='contained' color='secondary' loading={loading} sx={{mt:3, mb:2}}>회원가입</LoadingButton>
                <Grid container justifyContent="flex-end">
                    <Grid item>
                        <Link to="/login" style={{textDecoration:'none', color:'blue'}}>
                            이미 계정이 있으신가요? 로그인으로 이동
                        </Link>
                    </Grid>
                </Grid>
            </Box>
        </Box>
    </Container>
)

완성된 UI 코드들은 MUI css 프레임워크를 사용해서 크게 어려운건 없었습니다.

 

기능 부분 코드를 먼저 보기전에 store에서 user 부분을 먼저 볼게요.

const SET_USER = "SET_USER";
const CLEAR_USER = "CLEAR_USER";

export const setUser = (user) => ({
    type:SET_USER, currentUser:user
})
export const clearUser = (user) => ({
    type:CLEAR_USER
})

const initialState = {
    currentUser:null,
    isLoading: true,
}

const userReducer = (state = initialState, action)=>{
    switch(action.type){
        case SET_USER:
            return {
                currentUser:action.currentUser,
                isLoading:false,
            };
        case CLEAR_USER:
            return{
                currentUser:null,
                isLoading: false,
            };
        default:
            return state;
    }
}


export default userReducer;

크게 복잡하거나 어려운 코드는 없고 회원가입 누르면 SET_USER에 데이터를 보내고 currentUser에 받은 user의 데이터를 저장 후 로딩을 false로 로딩 이미지를 안나오게 합니다.

 

이제 기능부분과 회원가입 버튼을 클릭하면 firebase db에 데이터 저장과 스토어에도 데이터를 전달하는 기능을 구현한 코드를 한번 볼께요.

const IsPasswordValid = (password, confirmPassword) => {
    if(password.length < 6 || confirmPassword < 6){
        return false;
    }else if(password !== confirmPassword) {
        return false;
    }else{
        return true
    }
}
function Join() {
    const dispatch = useDispatch()
    const [error, setError] = useState("");
    const [loading, setLoading] = useState(false)

    const handleSubmit = (e) => {
        e.preventDefault();
        const data = new FormData(e.currentTarget);
        const name = data.get('name');
        const email = data.get('email');
        const password = data.get('password');
        const confirmPassword = data.get('confirmPassword');

        if(!name || !email || !password || !confirmPassword){
            setError('모든 학목을 입력해 주세요.')
            return;
        }

        if(!IsPasswordValid(password, confirmPassword)){
            setError('비밀번호를 확인해주세요.')
            return;
        }
        postUserData(name, email, password)
        
    }

    const postUserData = async (name, email, password)=>{
        setLoading(true);
        try {
            const {user} = await createUserWithEmailAndPassword(getAuth(), email, password)
            await updateProfile(user,{
                displayName:name,
                photoURL: `https://www.gravatar.com/avatar/${md5(email)}?d=retro`
            })
            
            await set(ref(getDatabase(), "users/" + user.uid), {
                name: user.displayName,
                avatar: user.photoURL
            });
            dispatch(setUser(user))
            // store에 user정보 저장
        } catch (e) {
            setError(e)
            setLoading(false);
        }

    }

    useEffect(()=>{
        if(!error) return;
        setTimeout(() => {
            setError('');
        }, 3000);
    }, [error])

    return (UI 코드)
}

export default Join

이메일, 아이디, 비밀번호에 빈값이 있는지 체크를 하고 비밀번호 같은 경우에는 중복여부해서 값이 동일한지 체크 한후 오류가 없으면 firebase에 user정보를 보내 회원가입을 진행합니다. photoUrl은 위에 라이브러리 목록에서 avatar라고 설치한 라이브러를 불러와 해당 라이브러리의 이미지를 저장합니다.

위에서 말은 하지 않앗지만 avatar의 코드는 md5라고 해서 해쉬코드로 암호화 하는 기능을 제공하는 라이브러리 입니다.

 

회원가입 페이지 작업에서 제일 중요했던거는 firebase의 유저 정보에 제대로된 데이터가 들어가는지 db에 유저의 정보가 저장이 되었는지 였습니다. 다행이도 큰 문제없이 저장이 되어 회원가입 페이지 코드는 완성 할 수 있었습니다.

 

회원가입은 했으니 이제 저장된 유저 데이터로 로그인이 되는지 확인해 봐야 되니 로그인 기능도 볼까요?

 

아 그리고 비밀번호 확인하는거는 Join이 function인데 function 안에 또 function을 선언해서 사용할 수 없으니 위에 정의했어요.

로그인 페이지 만들기


로그인 페이지 완성본 UI 도 먼저 보여드릴께요

Slack Clone 로그인페이지

<Container component="main" maxWidth="xs">
    <Box sx={{ 
            display: "flex", 
            flexDirection:'column', 
            alignItems: 'center',
            justifyContent: 'center',
            height: '100vh'
        }}
    >
        <Avatar sx={{m:1, bgcolor:'secondary.main'}} >
            <TagIcon />
        </Avatar>
        <Typography component="h1" variant='h5'>
            로그인
        </Typography>
        <Box component='form' noValidate sx={{mt:3}} onSubmit={handleSubmit} >
            <TextField margin='normal' required fullWidth label="이메일 주소" name="email" autoComplete="off" autoFocus />
            <TextField type="password" required fullWidth label="비밀번호" name="password" autoComplete="off" autoFocus />
            {
                error 
                    ? <Alert sx={{mt:3}} severity='error'>{error}</Alert>
                    : ''
            }
            <LoadingButton type='submit' fullWidth variant='contained' color='secondary' loading={loading} sx={{mt:3, mb:2}}>로그인</LoadingButton>
            <Grid container justifyContent="flex-end">
                <Grid item>
                    <Link to="/join" style={{textDecoration:'none', color:'blue'}}>
                        계정이 없으신가요? 회원가입으로 이동
                    </Link>
                </Grid>
            </Grid>
        </Box>
    </Box>
</Container>

UI는 회원가입 페이지에서 이메일 주소와 비밀번호 입력하는 Input만 사용하였습니다.

 

const [error, setError] = useState("");
const [loading, setLoading] = useState(false)
const handleSubmit = (e) =>{
    e.preventDefault();
    const data = new FormData(e.currentTarget)
    const email = data.get('email');
    const password = data.get('password');        

    if(!email || !password){
        if(!email && !password) {
            setError('이메일과 비밀번호를 확인해 주세요.')
        }else{
            setError(!email ? '이메일을 확인해 주세요' : '비밀번호를 확인해 주세요')
        }
        return;
    }
    loginUser(email, password);
}

const loginUser = async (email, password) => {
    setLoading(true)
    try {
        await signInWithEmailAndPassword(getAuth(), email, password);
    } catch (error) {
        setError(error.message)
        setLoading(false)
    }
}

useEffect(()=>{
    if(!error) return;
    setTimeout(() => {
        setError('');
    }, 3000);
}, [error])

이메일, 비밀번호 입력 여부를 화긴 후 firebase에 메일과 비밀번호로 로그인하는 함수를 불러서 거기에 유저정보를 가져오는 라이브러리와, 입력한 이메일, 패스워드를 전달해 있으면 로그인하고 없으면 에러가 나오도록 하였습니다.

 

생각보다 로그인과 회원가입은 특별하거나 어려운 코드는 없죠???

 

다음시간부터는 조금 어려운게 있을 수도 있어요. 나한테만 어려운건지는 모르겠지만요....

 

이제 로그인 후 채널과, 채팅 등 다양한 내용이 있으니 다음 시간에 봐요 빠잉~~

728x90
반응형