[백로그] 알림기능 & PWA

Jade·2023년 2월 23일
1

프로젝트

목록 보기
20/28

완성된 알림 기능

알림 기능은 웹소켓으로 구현하지는 않았기 때문에 이전에 다른 기능들과 같이 마크업 작업 + 백엔드 팀원이 개발해준 API를 연결하는 방식으로 구현했다.

마크업 작업은 프론트 팀원 A가 Figma에 만들어 주신 것을 토대로 팀원 B가 작업해주셨다.

나는 개인적인 사정으로 조금 늦게 합류했고, 헤더의 종 버튼을 눌렀을 때 내려오는 드롭다운 내부에 GET 요청으로 받은 알림 데이터들을 뿌려주는 일, 개별 알림과 전체 알림 각각에 대한 확인, 삭제 기능을 구현했다.


알림 데이터 다루기

GET 요청에 대한 응답으로 받는 알림 데이터는 아래와 같은 형식의 객체가 담긴 배열인데, alarmType을 구분해서 해당하는 문구와, 경로를 설정해주는 것이 중요했다. 경로 설정을 위해서는 해당 메시지룸 아이디나 특정 과외의 아이디가 필요한데, alarmType에 따라 contentId는 tutoringId가 들어올 수도 있고, messageRoomId가 들어올 수도 있다.

{
"alarmId":1,
"profileName":"test",
"contentId":1,
"alarmType":"MESSAGE",
"alarmStatus":"CHECK"
}

마크업 단계에서 메시지 문구 분기는 해주셨기 때문에 각 알림 컴포넌트를 클릭했을 때 이동할 경로를 정하기 위해서 아래와 같은 함수를 만들기로 했다.

  const changeTypeToPath = (type) => {
    if (type === 'MESSAGE') return '/message';
    if (type === 'TUTORING_REQUEST') return '/message';
    if (type === 'TUTORING_MATCH') return '/tutoringlist';
    if (type === 'DATE_NOTICE') return '/tutoring';
    if (type === 'WAIT_FINISH') return '/tutoring';
    if (type === 'FINISH') return '/tutoring';
  };

메시지나 과외 매칭 요청 알림의 경우에는 해당 메시지가 도착한 메시지룸으로 이동해야 한다. 해당하는 메시지룸에 입장하기 위해서는 메시지룸의 id가 필요한데, 이 아이디는 recoil 상태로 관리되고 있었다.

알림 컴포넌트를 클릭했을 때 해당 경로로 이동하기 전에 경로가 '/message'인 경우에는 메시지 룸 아이디를 관리하는 recoil 상태를 변경시켜주도록 했다.

//setCurrentRoomId는 CurrentRoomId라는 상태를 변경하기 위한 함수
 const setCurrentRoomId = useSetRecoilState(CurrentRoomIdState);


//clickHandler는 개별 알림을 클릭했을 때 실행되는 함수 
 const clickHandler = () => {
    const path = changeTypeToPath(alarmType);
   //changeTypeToPath의 반환 값인 path에 따라 분기 
    if (path === '/message') {
      setCurrentRoomId(contentId);
      navigate(path);//useNavigate 사용한 것 
    } else {
      //메세지룸이 아닌 페이지로 이동하는 경우에는
      //useLocation의 state를 이용할 수 있도록
      //tutoringId라는 키로 값을 담아준다
      navigate(path, {
        state: { tutoringId: contentId },
      });
    }
  };

알림 확인/삭제 기능 구현

단순히 patch와 delete API를 연결하는 것은 어렵지 않았다.
다만 이게 실시간 기능이 아니다 보니 브라우저상에서 확인이나 삭제버튼을 누르더라도 바로 반영이 되지 않았다.
클라이언트에서 확인/삭제 된 것처럼 보이도록 처리해주는 것이 필요했다.

patch나 delete의 then의 콜백 속에서 noticeList라는 상태를 변경해주는 로직을 추가했다.

patch의 경우에는 개별 확인은 map으로 이전값들을 순회하면서 alarmId가 확인된 alarmId와 동일한 경우 alarmStatus를 'CHECK'로 변경해준다. alarmStatus에 따라 className이 설정되고, 확인된 경우 어두운 색의 CSS가 적용된다. 전체 확인은 map으로 순회하며 모든 alarmStatus를 'CHECK'로 변경하면 된다.

delete의 경우에는 전체 삭제는 그냥 상태를 빈 배열로 변경하고, 개별 삭제는 filter를 통해 해당하는 alarmId를 제외한 배열로 상태를 변경시키면 된다.

//개별 알림 컴포넌트 

//알림 개별 확인 
  const patchAlarm = async () => {
    await axios
      .patch(`/alarm/detail/${alarmId}`)
      .then(() => {
        setNoticeList((prev) => {
          return prev.map((el) =>
            el.alarmId === alarmId ? { ...el, alarmStatus: 'CHECK' } : el
          );
        });
      })
      .catch((err) => console.log(err));
  };

//알림 개별 삭제 
 const deleteAlarm = async () => {
    await axios
      .delete(`/alarm/detail/${alarmId}`)
      .then(() => {
        setNoticeList(noticeList.filter((el) => el.alarmId !== alarmId));
      })
      .catch((err) => console.log(err));
  };
//개별 알림을 감싸는 박스 컴포넌트

//알림 전체 확인 
const patchAllAlarm = async () => {
    await axios
      .patch(`/alarm/all/${profileId}`)
      .then(() => {
        setNoticeList((prev) =>
          prev.map((el) => ({ ...el, alarmStatus: 'CHECK' }))
        );
      })
      .catch((err) => console.log(err));
  };


//알림 전체 삭제 
const deleteAllAlarm = async () => {
    await axios
      .delete(`/alarm/all/${profileId}`)
      .then(() => {
        setNoticeList([]);
      })
      .catch((err) => console.log(err));
  };

PWA

Progressive Web App은 안정성 및 설치 용이성이 보장되도록 설계된 웹 애플리케이션이다.

원래 백로그로는 실시간 채팅, 알림 기능 정도만 존재했지만 이야기하는 과정에서 PWA까지 한 번 구현해보는 게 어떻겠냐는 의견이 나왔다. 당시에는 반응형 구현 전이라 반응현이 구현되고 나서 이야기해볼 사안이라고 좀 더 미뤘었는데, 반응형이 구현이 잘 되었고, 실시간 채팅 기능이나 알림 기능도 모두 잘 구현이 되었으므로 PWA까지만 하고 과외차이 프로젝트를 마무리 짓기로 했다.

PWA로 만들기 위해서는 두 가지 파일이 필요하다. mainfest.json 파일이 설정되어 있어야 했고, 추가로 service-worker라는 파일을 사용해서 (그 전에 서비스 워커는 보안 상의 이유로 HTTPS에서만 동작하기 때문에 HTTPS 설정이 필수로 필요) 캐싱을 해주어야 한다.

서비스 워커를 사용하기 위해서 편법(?)을 사용했는데, 이 링크의 방법2를 참고했다. 서비스 워커가 포함된 CRA 프로젝트를 새롭게 만들고, 만들어진 초기 탬플릿들을 이미 만들어진 프로젝트로 가져와서 사용하는 방법이다.
(처음부터 PWA를 할 생각이면 npx create-react-app 프로젝트명 --template cra-template-pwa명령어를 통해서 프로젝트를 생성해주는 게 좋을듯)

PWA가 제대로 적용되었는지를 확인하기 위해서는 개발자도구의 Applicatio -> Service Workers를 확인해보면 된다.

처음에는 그냥 빈 화면만 떠서 뭐지 왜 적용이 안 되는 거지...한참 삽질을 했는데, 적용이 되지 않은 것처럼 보였던 이유는 npm run build를 한 뒤 이 빌드된 파일을 실행시켜야 했는데 그냥 npm start를 해서 확인하고 있었기 때문이었다. (그냥 빌드한 html 파일을 실행시키면 빈 화면만 나오므로 꼭 서버라는 패키지를 이용해 띄운다)

manifest.json 파일은 프로젝트 마무리 당시에 favicon 설정과 메타 데이터 설정을 해주면서 함께 해주었기 때문에 서비스 워커만 설정해주니 PWA 설정을 어렵지 않게 마무리할 수 있었다..!


오프라인 알림 컴포넌트 설정

어플리케이션을 사용하다 네트워크 상태가 오프라인으로 변경되면 아래와 같은 화면을 띄우도록 설정했다.

리액트에서 현재 네트워크 상태를 확인하는 방법은 이 블로그를 통해서 확인했고, addEventListener로 online이벤트와 offline 이벤트를 감지하고, isOffline 상태 변경 함수를 변경시키도록 했다.

isOffline 상태의 초기값인 navigator는 객체는 브라우저에 관한 정보를 가진 객체인데, 브라우저의 콘솔에서 navigator를 찍어보면 아래 이미지와 같은 내용들을 확인할 수 있고, 내부에 onLine이라는 키도 가진 것을 확인할 수 있다.

//App.jsx

//Offline을 알리는 컴포넌트를 임포트 
import Offline from './components/Offline';

const App = () => {
  const [isOffline, setIsOffline] = useState(!navigator.onLine);
  
  //중략
  
   useEffect(() => {
    if (!sessionStorage.getItem('authorization')) resetProfile();

    window.addEventListener('online', () => {
      setIsOffline(false);
    });

    window.addEventListener('offline', () => {
      setIsOffline(true);
    });

    return () => {
      window.removeEventListener('online', () => {
        setIsOffline(false);
      });

      window.removeEventListener('offline', () => {
        setIsOffline(true);
      });
    };
  }, []);

  return (
    //중략
    
    //isOffline이 true이면 Offline 컴포넌트가 띄워진다
    {isOffline && <Offline />}
      </Router>
      <Footer />
    </div>
  );
};

profile
키보드로 그려내는 일

0개의 댓글