7. 토큰 인증으로 클래스 진입하기

7. 토큰 인증으로 클래스 진입하기

받은 토큰으로 클래스에 들어가보자.

채점 기준은 각 클래스마다 다를 것이다.

따라서 우리는 한 과제 당 한 클래스로 간주하고 생성하도록 했다.

각 클래스를 생성하고 채점 기준까지 작성하면 토큰이 생성되는데,

학생 입장에서 그 토큰으로 클래스에 들어가도록 하려면 어떻게 해야 할까?


토큰 입력 페이지는 이미 있다

그렇다면 토큰을 받아 이게 실제로 있는 토큰인지 검사하는 컴포넌트가 필요하다.

이 토큰이 있다면 해당 클래스로 이동시키고, 없다면 Home으로 리다이렉트하는 방법을 선택했다.


// file: 'index.d.ts'

export interface ClassroomProps {
    token: string,
    className: string,
    instructor: string,
    createDate: string,
}


// file: 'SectionClass.tsx'

function SectionClass(props: RouteComponentProps<RouteParamsProps>) {
  
    ...

    const initial = {
        token: "",
        className: "",
        instructor: "",
        createDate: "",
    };
    
    const [classroom, setClassroom] = useState(initial);
    const [valid, setValid] = useState(false);
    
    
    useEffect(() => {
        if (classroom === initial) {
            const currentClassroomState = async (): Promise<ClassroomProps[]> => {
                return await axios.get<ClassroomProps[]>('/api/token/')
                .then((response) => {
                    return response.data
                });
            };

            currentClassroomState()
            .then(response => {
                setClassroom(response.find(element => element.token === props.match.params.token) || initial);
                
                if (response.find(element => element.token === props.match.params.token) === undefined) {
                    props.history.push('/');
                    alert("클래스가 없습니다.😅");
                } else {
                    setValid(true);
                }
            })
            .catch(response => {
                props.history.push('/error');
            })
        }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    },[classroom]);
    
    ...
    


classroom 상수를 hook으로 관리하도록 선언한다. 이 때 initial로 초기화한다.

백엔드와 통신하여 index.d.ts 파일에서 정의한 ClassroomProps 인터페이스에 맞는 객체 배열을 가지고 온다.


currentClassroomState 함수는 axios.get 함수 실행 후 response.data를 리턴한다.

이 함수를 실행시켜 나온 결과를 뒤져 이 페이지에 진입하기 위해 입력한 토큰이 있는지 찾는다.

responseClassroomProps 배열이므로 find 함수를 통해 props에서 가져온 토큰과 일치하는 것이 있는지 확인한다.

만약 없으면 (= undefined라면) Home으로 리다이렉트시키며, 갑자기 이동되어 당황한 사용자를 위해 alert를 표시한다.

있다면, valid를 업데이트해 이후 컴포넌트를 렌더링한다.



클래스룸에서 어떤 기능이 필요할까?

이제 정상적으로 배포된 토큰을 입력하여 클래스룸에 진입했다.

그럼 학생들은 여기서 뭘 할까?


당연히 소스 파일을 넣어 채점을 하려고 할 것이다.

이전에 만들어 놓았덙 파일 전송 컴포넌트가 여기서 쓰이는 것!


// file: 'SectionClass.tsx'

  return (
        <>
            <AppBar position="fixed" style= >
                <Toolbar className={classesStyle.toolbar}> 
                    <Link
                        variant="h3"
                        underline="none"
                        color="inherit"
                        className={classesStyle.title}
                        href="/"
                    >
                        <img src="/assets/logo.png" alt="logo" className={classesStyle.logo} />
                    </Link>
                </Toolbar>
            </AppBar>
            <SectionLayout backgroundClassName={classesStyle.background} classes={classesLayout}>
                {}
                <img style= src={backgroundImage} alt="prioirty" />
                <Typographic color="inherit" align="center" variant="h2" marked="center" className={classesStyle.h2}>
                    {classroom.className}
                </Typographic>
                <Typographic color="inherit" align="center" variant="h5" className={classesStyle.h5}>
                    opened by <b>{classroom.instructor}</b> on {classroom.createDate}
                </Typographic>

                <TextField 
                    value={studentID} 
                    onChange={handleChange} 
                    label="학번"
                    variant="outlined"
                    style=
                    placeholder="학번 입력" 
                    margin="normal" 
                />
                {valid &&
                    <FileTransfer name={classroom.token} id={studentID} onCreate={handleCreate} />
                }
            </SectionLayout>
            <AppFooter />
        </>
    
    );
    


기존에는 다른 컴포넌트와 같이 Appbar를 집어넣었는데, 뭔가 차별화를 주고 싶어서 투명하게 한 다음 로고를 왼쪽 상단에 집어넣었다.

로고는 Home으로 이동되도록 Link 컴포넌트를 걸어 주었는데, 훨씬 보기 편하다✨


정상적으로 진입한 클래스룸은 ClassroomProps에 있는 정보가 다 있을 것이다.

타이틀로 클래스룸의 제목, 개설자 이름과 개설 날짜를 표시하여 학생들이 확인할 수 있도록 했다.


그 다음, 어떤 학생이 채점을 시도했는지 확인하기 위해 학번 입력 TextField를 만들었고, 이는 채점 시 백엔드로 넘겨줄 것이다.

이전에 업데이트 해줬던 valid를 이용해 파일 전송 컴포넌트 FileTransfer를 렌더링하고

백엔드에 넘겨줄 parameter를 위해 학번과 토큰을 넘겨준다.

채점이 됐을 때, 또는 실패했을 때를 알기 위해 onCreate 핸들링 함수 또한 넘겨줘야 한다.

그럼 이 핸들링 함수를 적절히 선언해보자.


// file: 'SectionClass.tsx'

    ...

    const handleCreate = (status: boolean, grading: Object) => {
        if (!status)
            props.history.push('/error');

        else
            props.history.push({
                pathname: `${props.match.url}/success`,
                state: { detail: grading },
            });
    }
    
    ...
    


status 매개변수는 채점이 성공적으로 완료되었는지, 아니면 서버 상 오류가 있는지 확인하기 위한 것이다.

statusfalse면 에러 페이지로 리다이렉트 되도록 했다.


채점이 성공적이었다면, 결과 페이지로 이동시키며, 채점 성적을 detail로 넘겨준다.

이것이 우리가 SectionClasspropsRouteComponentProps<RouteParamsProps> 타입을 선언해준 이유다!


🔥 사실 이 부분을 찾기가 힘들었는데, props를 단순히 어디로 이동시켜주는 건 상관없지만

state에 매개변수를 같이 넘겨주려면 필요했다. 기억할 것!!



🔥 TroubleShooting & Review

Issue:

  • 컴포넌트에서 매개변수와 핸들링 함수를 넘겨주는 건 익숙하지만 Route를 이용해 매개변수까지 넘겨주는 것은 처음이었다. 채점 결과가 JSON 형식의 객체인데, 라우트를 통해 같이 넘겨줄 수 있다는 게 굉장히 신기하고 편하다!

Review

  • currentClassroomState에서 작성한 코드가 조금 더러운 것 같다. find를 두 번 해놨는데, 좀 더 깔끔하게 고쳐야겠다…
  • 이슈 등록해 놓은 사항이지만, reCAPTCHA 추가해야겠다.