LikeLion 출석부 제작하기#3

최민석·2021년 1월 20일
1

Toyproject

목록 보기
3/8

파일 구성

파일 디렉토리에 대해서 간단하게 구상하여 명시해보았다.

파일 Components 및 Redux 구성

📦src
┣ 📂components
┃ ┣ 📂AuthPage
┃ ┃ ┗ 📜AuthPage.js
┃ ┗ 📂MainPage
┃ ┃ ┣ 📂MainPanel
┃ ┃ ┃ ┣ 📜AttendList.js
┃ ┃ ┃ ┣ 📜MainHeader.js
┃ ┃ ┃ ┣ 📜MainList.js
┃ ┃ ┃ ┗ 📜MainPanel.js
┃ ┃ ┣ 📂SidePanel
┃ ┃ ┃ ┣ 📜SideFooter.js
┃ ┃ ┃ ┣ 📜SideHeader.js
┃ ┃ ┃ ┣ 📜SideList.js
┃ ┃ ┃ ┗ 📜SidePanel.js
┃ ┃ ┗ 📜MainPage.js
┣ 📂redux
┃ ┣ 📂actions
┃ ┃ ┣ 📜schedule_action.js
┃ ┃ ┗ 📜types.js
┃ ┗ 📂reducers
┃ ┃ ┣ 📜index.js
┃ ┃ ┗ 📜schedule_reducer.js
┣ 📜App.css
┣ 📜App.js
┣ 📜firebase.js
┣ 📜index.css
┣ 📜index.js
┗ 📜reset.css

Firebase.js : 파이어베이스 SDK 키 값이 명시되어 있는 자료. (Git ignore 처리)
MainPage, AuthPage : 출석 처리 시스템이 보여질 페이지, Auth 어드민 인증을 위한 페이지
AttendList : 모든 세션에 대한 출석 리스트 패널

현재는 MainPage, AttendList까지 구현한 상태이다.

MainPanel, SidePanel

기존 정해놓은 UI와는 조금 다르게 변경 되었다.
벌금과 관련 해서는 구현 할 예정이다.

구현 한 기능

  • Schedule 생성 + 실시간 적용 업데이트 완료
  • Schedule 리스트 출력
  • Schedule 리스트 날짜 정렬 구현
  • User 모델 생성 + 실시간 적용 업데이트 완료
  • User 리스트 출력 + 실시간 적용 업데이트 완료
  • 유저 학번 순 정렬 구현
  • User Attendance State 생성 구현 + 실시간 업데이트 완료
  • User Attendance 무한 rendering Fix
  • Modal Page 구현 (스케줄, 유저 생성 Page)

굉장히 많이 했다고 생각 했는데,
사소한 부분이 아쉽게 프로그래밍 한 부분이 많다.
그리고 처음 접해보는 React + Firebase 라서
NOSQL에 대한 이해도가 떨어져서 오랫동안 고민중이다.

약.. 3일동안의 뻘짓으로 많은 걸 배웠다.


남은 생각한 구현

  • AttendPage 구현(리스트, 출석/지각/결석 여부..)
  • 벌금 시스템 구현
  • Auth 인증 페이지 구현 (운영진 전용 코드 제공)

일단은 얼마 남지 않은 걸로 보이지만...
파이어베이스가 예상 외의 복병으로 오류가 너무 많았고,
NoSQL에 대한 이해가 부족해서 많은 어려움을 겪었다...

그래도 극복하고 여기까지 온 내자신이 대견스럽다..^_^


🐞버그 FIX LIST

UseEffect() 오류

와 진짜 이건 미쳐버리는 줄 알았다.
그 전 React를 활용한 실시간 채팅 시스템에서도 발생한 오류이다.

Class Component를 활용한 라이프사이클 메소드를 사용해야 하는건가 했다.

ComponentDidMount() 메소드를 대신해, 함수형 컴포넌트 구현은 UseEffect()로
대신 사용 가능하다고 했다.

그런데? useEffect()를 라이프사이클 ComponentDidMount() 같이 적용하다가는 무조건 오류뜬다.
UseEffect()은 계속적으로 실행되기 때문에 무한렌더링이 걸리게 된다.
그래서 생각한 첫 방법이다.

1단계. 두번째 파라미터를 통한 ComponentDidMount() 구현하기

    useEffect(() => {
        addUserListeners(); // 실시간 유저 데이터 받아오기
        SearchState(); // 실시간 유저 출석 여부 데이터 가져오기
    }, []) 

이러한 무한 렌더링에 대해서 방지책으로 두번째 파라미터에 값을 넣지 않고 하는 방법이 있었다.

useEffect()의 두번째 파라미터에 어떠한 변수 값을 지정하면,
그 변수 값이 변경 될때마다 useEffect() 함수가 실행 된다는 점을 알았다.

그런데... 이렇게 하면 딱 한번 UseEffect()가 실행되어서 값이 업데이트가 되지 않았다.
그렇다면? 유저 데이터를 받아 올때마다 UseEffect()를 실행하면 되는거 아닌가?

즉, UserList를 저장한 State 값이 변경 되면 호출하면 되겠네! 라는 생각으로 적용해본다.


2단계. 리스트를 저장한 State 값을 파라미터에 넣으면 되겠네?

나는 문법 상, 그럴싸해서 당연히 될 줄 알았다. 그런데 치명적인 오류가 발생했다.

const [SearchUser, setSearchUser] = useState([]); // 파이어베이스로 받아온 유저 값을 배열로 Push.

위 코드는 유저 값을 받아와, 실시간으로 저장 될 배열이다.
나는 Users 값이 변경 될 때만 UseEffect()를 부르면 된다고 생각했다.

그런데 무한 랜더링 현상이 발생하여, 자꾸 홈페이지가 다운되었다.
왜 이런 상황이 발생하나 확인 해보았다.

// 해당 맞는 스케줄/유저의 상태 값 
    const SearchState = async () => {
        const searchuser = []
        await SearchAttendRef.ref(`Attendance/${schedule.id}`).on('child_added', DataSnapshot => {
            searchuser.push(DataSnapshot.val())
            setSearchUser(searchuser)
        })
    }

SearchState 함수는 계속해서 유저 데이터를 받아와,
searchuser 값을 계속 바꿔줘서 이러한 무한 렌더링 현상이 일어난 것이였다...

상상치도 못한 오류 발생이였다.
그래서 다른 비슷한 경우가 있는 예제를 보면,
포기하고 Class Component로 재구현해서 만드는 걸 보았다.

그렇지만 아무리봐도 단순하게 조건이 있다면 무조건적으로 가능하게 보였다.

반복되지 않으면서, 필요 할 때 출력해주는 역할을 말이다.
그래서 생각했던 방법으로 새로운 기준을 정해주는건 어떨까? 였다.


3단계. isLoadingState를 이용해서 무한 렌더링을 막자!

무한렌더링이 발생했던 부분은 스케줄마다의 유저 출석상태를 불러올 때 오류가 발생했다.
그래서 나는 임의로 isLoadingState 상태 값 변수를 만들어서 이에 대한 로직을 짰다.

// 로직 구현을 위한 임의의 Boolean 형태.
 const [IsLoadingState, setIsLoadingState] = useState(false);

// Onchange를 통해서 선택 시, 바로 파이어베이스 업데이트
    const handleAttend = async (user, schedule, value) => {
        if (isFormVaild(user, schedule, value)) {
            const newAttendance = {
                user_id: user.id, // 연결 된 유저
                schedule_id: schedule.id, // 연결된 스케줄
                state: value // 상태
            }
            try {
                // 스케줄 -> 유저이름으로 구분하여, 업데이트
                await AttendanceRef.child(`${schedule.id}/${user.Username}`).update(newAttendance)

                // 저거 자체 SearchUser 넣을려했더니 무한로딩 되서 하나 로딩만듬
                if (IsLoadingState)
                    setIsLoadingState(false)
                else
                    setIsLoadingState(true)
            } catch (error) {
                alert(error)
            }
        } else {
            alert("스케줄을 다시 선택해주세요.")
        }
    }

위와 같은 함수를 실행 할 때, isLoadingState 값을 변경시킨다.
그럼 해당 변수 값이 변경 할때마다 useEffect() 함수가 실행되면 된다는 걸 알 수 있다!

// 계속 실행
    useEffect(() => {
        addUserListeners();
        SearchState();
    }, [schedule, IsLoadingState]) 
// schedule 변경시 값 가져오고, state 값 설정 시 실행된다.
  

약 10시간을 뻘짓하면서 Class에서의 라이프사이클 메소드를 Hook을 이용해 해결한 사례이다.


Firebase의 다중 Filter 하기.

해당 사진을 보게되면, 스케줄 밑 세션 날짜의 정렬에 대해서 고민이 많았다.

파이어베이스의 문제점으로 하자면, 정수, DateTime, 소수 등 .
이러한 자료형을 따지지 않고 전부 String 형태로 저장 된다는 점이다.

그래서 정렬에 대해서 어려움을 겪었다.

Realtime Database Method?

여러 메소드가 존재해서 사실 유저에 관한 정렬은 해결 했다.

const [UserRef, setUserRef] = useState(firebase
                                       .database()
                                       .ref("Users")
                                       .orderByChild('Class'));

위와 같이 orderByChild()를 통해서 Class 필드를 기준해, 내림차순을 한다.
위의 Class 필드는 "학번" 이다.

아주 잘 정렬 된 걸 확인 할 수 있다.

그러나 위와 같은 경우는 하나의 기준으로만 정렬이 가능하다. 그런데 우리는..


우리의 문제는 년/월/일 정렬이다.

Datetime 형태로 보내도, 결국에는 String형식으로 데이터베이스에 저장되었다.
물론 여러 Datefield 변환법이 있지만, 너무 코드도 복잡하고 사용방식이 까다로워서

나는 슬라이싱을 이용한 날짜 쪼개기를 이용했다.

 const addSchedule = async () => {
        const key = ScheduleRef.push().key;
        const newSchedule = {
            year: Datetime.substring(0, 4),
            month: Datetime.substring(5, 7),
            day: Datetime.substring(8, 10),
            Description: Description,
            id: key
        }

이와 같이 format 형식을 YYYY-MM-DD 으로 오는 걸 나누어서 스케줄을 생성 해주었다.

그렇게 저장한 값을 가지고 Java Scripts sort 메소드를 이용해서 정렬 해주었다.

await ScheduleRef.on("child_added", DataSnapshot => {
            SchedulesArray.push(DataSnapshot.val());
            // 날짜, 월, 년 순으로 완전 정렬
            SchedulesArray.sort(function (a, b) {
                return parseInt(a.day) - parseInt(b.day)
            })
            SchedulesArray.sort(function (a, b) {
                return parseInt(a.month) - parseInt(b.month)
            })
            SchedulesArray.sort(function (a, b) {
                return parseInt(a.year) - parseInt(b.year)
            })
            setSchedule(SchedulesArray);
  1. Firebase로부터 받아온 Object 데이터를 배열에 저장한다.
  2. 저장한 Object데이터에서 year, month, day 값을 하나하나 올림차순으로 재정렬한다.
  3. 그렇게 정렬이 된 배열을 State에 적용시킨다.

결과로는 간단하게 나왔지만, 어디서 어떻게 정렬을 해야할 지 굉장히 막막했다...


추가적인 개발 내용에 대해서 다음 포스팅에 이어서 할 예정이다.

3일동안 많이 했는데 많이 못 만든것 같아서 아쉬움이 가득하다...
다음 프로젝트는 무난하게 진행해서 UI 작업까지 마무리 하려고한다.

profile
되돌아보며 성장합니다🔨

0개의 댓글