ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 두 가지의 서로 다른 영역이 충돌해 만들어내는 문제 해결하기 (navigate, react-modal)
    Today I Learned 2022. 12. 4. 23:58

     

    오늘은 대부분의 작업을 예외적인 상황을 처리하거나 자잘한 오류들을 추적해 해결하는 위주로 진행했다. 기억에 남는 작업 중 하나로 사용자에게 특정 동작을 수행했음을 시각적으로 확인시켜주기 위한 Modal 출력이 의도와 다르게 작동하던 문제를 해결했던 것이 있었다.

     

     

    '게시글 작성하기' 기능에는 작성 페이지에서 게시글 작성을 마치고 게시글 목록 페이지로 이동했을 때 '게시글 작성이 완료되었습니다.' Modal을 출력해주는 동작이 있다.

     

    React-Modal 라이브러리를 이용해 구현한 Modal은 각 Modal 별로 별도의 컴포넌트로 정의되어 있으며, 각 페이지에서 useState로 Modal의 출력 상태를 관리하도록 구현한 상태이다. 게시글 작성 완료 Modal은 게시글 목록 페이지에서 출력되어야 하기 때문에 게시글 목록 페이지 컴포넌트 출력 영역에 Modal이 위치해 있다. 따라서 게시글 작성 페이지에서 게시글 목록 페이지로 navigate를 할 때 게시글 작성 상태를 식별할 수 있는 값을 전달해 값이 전달되었을 때는 Modal이 출력되도록 구현했다.

     

    // pages/PostFormPage.jsx
    
    export default function PostFormPage() {
      const navigate = useNavigate();
    
      const postFormStore = usePostFormStore();
    
      useEffect(() => {
        
      }, []);
    
      const createPost = async () => {
        const postId = await postFormStore.createPost();
        if (postId) {
          postFormStore.clearStates();
          navigate('/posts/list', {
            state: {
              postStatus: 'created',
            },
          });
        }
      };
    
      return (
        <PostForm
          createPost={createPost}
        />
      );
    }
    // pages/PostsPage.jsx
    
    export default function PostsPage() {
      const [actionMessage, setActionMessage] = useState('');
      const [confirmModalState, setConfirmModalState] = useState(false);
      
      const location = useLocation();
      
      const postStatus = !location.state
        ? null
        : location.state.postStatus;
        
      const seeConfirmModal = ({ message }) => {
        setActionMessage(message);
        setConfirmModalState(true);
      };
    
      useEffect(() => {
        if (postStatus) {
          seeConfirmModal({
            message: postStatus === 'created'
              ? '게시글 작성이'
              : '게시글 삭제가',
          });
        }
      }, [accessToken]);
      
      return (
        <>
          <Posts />
          {confirmModalState && (
            <ModalConfirm
              actionMessage={actionMessage}
              confirmModalState={confirmModalState}
              setConfirmModalState={setConfirmModalState}
            />
          )}
        </>
      );
    }

     

     

    사용자가 게시글 작성 폼의 모든 내용을 입력하고 작성하기 버튼을 누르면

     

     

     

    다음과 같이 게시글 작성 완료 안내 메시지가 출력된다.

     

     

     

    앱을 이용해보면서 확인된 문제가 하나 있었다. 저런 식으로 게시글 작성을 마치고, 게시글 상세 내용이나 글 작성하기 페이지로 다시 이동한 뒤, 뒤로가기 버튼을 눌러 다시 게시글 목록 보기 화면으로 이동했을 때, '게시글 작성이 완료되었습니다.' Modal 창이 게시글을 작성하고 난 뒤가 아님에도 출력되는 문제가 발생했다.

     

     

    위의 화면은 게시글 작성을 마친 직후, 작성한 게시글 상세 내용 페이지로 들어온 상태이다. 이 페이지에서 다른 동작을 하지 않고 화살표를 눌러 뒤로가기를 수행했을 때, 다음과 같이 게시글을 작성한 상태가 아님에도 게시글 작성이 완료되었다는 Modal 창이 출력되었다. 

     

     

     

    '뒤로 가기' 기능은 navigate(-1)을 이용해 이전 페이지로 돌아가는 방식을 취하고 있었다. 

     

    const navigate = useNavigate();
    
    const navigateBackward = () => {
      navigate('-1');
    };

     

    navigate(-1)을 이동해 이전 페이지로 이동할 경우, 이전 페이지가 가지고 있던 state 등의 속성을 모두 다시 이용하기 때문에 게시글 작성을 완료하면서 전달받았던 postStatus를 계속 사용하게 된다는 문제점이 있었지만, navigate(-1)을 이용하지 않고 뒤로가기를 구현할 수 있을지 방안이 쉽게 떠오르지 않았다. 

     

    헤더와 하단 네비게이터에서 이동할 수 있는 기능인 알림, 글쓰기 같은 경우, 헤더와 하단 네비게이터를 어느 화면에서도 확인할 수 있었기 때문에 navigate에 어느 링크에서 넘어왔는지를 특정해줄 수 없는 문제가 있었다. 예를 들면 게시글 목록 페이지에서도 알림 화면으로 넘어갈 수 있었고, 게시글 상세 내용 페이지에서도 알림 화면으로 넘어갈 수 있었다.

     

    navigate(-1)과 함께 state를 조작하는 방식으로는 문제가 해결되지 않았다.

     

    // 다음의 방식들을 시도했으나 실패
    
    navigate(-1, {
      state: {
        postStatus: '',
      },
    });
    
    navigate(-1, {
      state: {},
    });
    
    navigate(-1, {
      replace: true,
    });

     

    이 방식들은 설령 성공한다고 해도 문제가 있었다. 특히 가장 첫 번째의 postStatus에 빈 값을 주는 방식은 다른 기능 동작의 실패를 막기 위해 그 기능과는 전혀 상관없는 다른 기능의 동작에 영향을 끼치는 모양새였다.

     

     

    navigate(-1)을 유지한 상태로는 문제 해결이 어렵겠다는 생각이 들었다. 한 가지 떠오른 생각은 내 페이지가 어느 페이지로부터 navigate되었는지를 알아내 navigate(해당 페이지)의 방식으로 이동하는 것이었다. 그러면 '이전 페이지 주소로 이동한다'라는 의미도 해치지 않을 수 있고, 그러면서도 postStatus 값을 생성하지 않기 때문에 Modal이 출력되지 않도록 막을 수 있을 것 같았다.

     

    방식을 적용해보기 위해 navigate하는 쪽에서 state로 현재 페이지의 주소를 함께 전달하고, 도착한 페이지에서는 해당 주소를 받아 기억하고 있다가 뒤로가기가 호출될 경우 해당 주소로 navigate하는 것을 시도해보았다. 현재 주소는 location.pathname을 통해 가져오도록 했다. 어느 화면에서든 확인할 수 있는 하단 네비게이터의 기능 이동 링크에 해당 동작을 적용했다.

     

    // components/BottomNavigator.jsx
    
    export default function BottomNavigator() {
      const location = useLocation();
      const navigate = useNavigate();
    
      const navigateHome = () => {
        navigate('/');
      };
    
      const navigatePage = (link) => {
        navigate(link, {
          state: {
            previousPath: location.pathname,
          },
        });
      };
    
      return (
          <Container>
            <button
              type="button"
              onClick={navigateHome}
            >
              홈
            </button>
            <button
              type="button"
              onClick={() => navigatePage('/write')}
            >
              글쓰기
            </button>
            <button
              type="button"
              onClick={() => navigatePage('/chat')}
            >
              채팅
            </button>
          </Container>
      );
    }

     

    게시글 작성 페이지는 다음과 같이 수정했다. 이전 페이지에서 navigate되어 올 때, previousPath를 전달받아 저장한다. 이후 뒤로 가기가 수행될 경우, 전달받은 previousPath로 navigate를 수행하도록 했다. 주소창으로 접근한 경우에는 previousPath가 없을 것이므로 이때는 예외적으로 홈 페이지로 navigate하도록 했다.

     

    // pages/PostFormPage.jsx
    
    export default function PostFormPage() {
      const location = useLocation();
      const navigate = useNavigate();
    
      const previousPath = location.state !== null
        ? location.state.previousPath
        : null;
    
      const postFormStore = usePostFormStore();
    
      useEffect(() => {
        
      }, []);
    
      const navigateBackward = () => {
        navigate(previousPath || '/');
      };
    
      const createPost = async () => {
        const postId = await postFormStore.createPost();
        if (postId) {
          navigate('/posts/list', {
            state: {
              postStatus: 'created',
            },
          });
        }
      };
    
      return (
        <PostForm
          createPost={createPost}
        />
      );
    }

     

    앱을 실행한 결과 더 이상 뒤로가기를 수행해도 Modal을 출력하지 않는 것을 확인할 수 있었다.

     

     

     

    작업 내역

    navigate 관련

    - https://github.com/hsjkdss228/smash-frontend/pull/41

     

    기타

    - https://github.com/hsjkdss228/smash-backend/pull/29

    - https://github.com/hsjkdss228/smash-backend/pull/30

    - https://github.com/hsjkdss228/smash-frontend/pull/42 

    - https://github.com/hsjkdss228/smash-frontend/pull/43

     

     

     

     

     

     

    댓글

Designed by Tistory.