ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 풀리지 않는 문제가 있다면 문제의 범위를 좁혀보자
    Today I Learned 2022. 11. 5. 23:00


    다음의 소스코드를 보자.

    사용자가 게시물 리스트에서 특정 썸네일을 클릭하거나 URI와 path variable로 게시글의 id를 직접 입력할 경우 게시물을 상세 정보를 화면에 출력하는 동작을 수행하는 로직의 일부로 PostPage 컴포넌트를 정의했다. PostPage 컴포넌트는 전달받은 postId에 해당하는 게시물의 정보를 백엔드 서버로부터 가져와 postStore의 상태에 저장한 뒤, 상태를 가져와 UI 컴포넌트에 전달하는 컴포넌트이다.

    export default function PostPage() {
      const location = useLocation();
    
      const postId = location.state !== null
        ? location.state.postId
        : Number(location.pathname.split('/')[2]);
    
      const postStore = usePostStore();
    
      useEffect(() => {
        postStore.fetchPost(postId);
      }, []);
    
      const { postInformation, postPositions } = postStore;
    
      return (
        <Container>
            <PostInformation
              information={postInformation}
            />
            <PostPositions
              teamsAndPositions={postPositions}
            />
        </Container>
      );
    }



    URI와 path variable을 직접 입력해 화면에 미리 설정한 값을 출력하는지 확인한 결과 다음과 같은 오류를 출력했다.


    information.images는 백엔드 서버로부터 가져온 게시물 객체에 포함되어 있는 이미지 객체 배열을 가져온 것이다. 객체가 왜 비어있는지 확인해보기 위해 console.log를 찍어보았다.

    export default function PostPage() {
      console.log('페이지 컴포넌트 진입');
    
      const location = useLocation();
    
      const postId = location.state !== null
        ? location.state.postId
        : Number(location.pathname.split('/')[2]);
    
      const postStore = usePostStore();
    
      useEffect(() => {
        console.log('fetchPost 호출');
        postStore.fetchPost(postId);
      }, []);
    
      const { postInformation, postPositions } = postStore;
    
      console.log('images', postInformation.images);
    
      return (
        <Container>
          <PostInformation
            information={postInformation}
          />
          <PostPositions
            teamsAndPositions={postPositions}
          />
        </Container>
      );
    }



    useEffect 내부에 있는 fetchPost가 순서상으로 나중에 호출되고 있는 것을 발견했다. 컴포넌트, 스토어, API 호출 영역을 살펴봐도 다른 어떤 영역에 문제가 있는지 파악되지 않아 동료분께 도움을 요청했다.



    동료분은 지금 문제가 발생하는 지점이 진짜로 그 지점의 문제가 맞는지 찾아봐야 하는 영역이 너무 크기 때문에 인지자원을 집중하기 위해 문제가 발생하는 지점을 한정시켜볼 것을 제안했다. 이를 위해 가장 먼저 전체 프로세스를 PostPage에 한정시켜보기로 했다.

    PostPage가 호출하는 UI 컴포넌트를 상수값으로 대체하고, postStore.FetchPost를 호출할 때에도 고정 값을 직접 상태로 갖도록 했다. 이를 통해 문제의 범위를 PostPage로 한정시켰다.

    export default class PostStore {
      constructor() {
        // Post
        this.postInformation = {};
        this.postPositions = [];
    
        this.listeners = new Set();
      }
    
      // subscribe, unsubscribe, publish methods
    
      async fetchPost(postId) {
        // const data = await postApiService.fetchPost(postId);
    
        this.postInformation = {
          // 게시글, 게임과 관련된 데이터를 data로부터 분배받음
          // 문제 영역 축소를 위해 상수값으로 설정
        };
        this.postPositions = [
          // 팀, 포지션, 멤버와 관련된 데이터를 data로부터 분배받음
          // 문제 영역 축소를 위해 상수값으로 설정
        ];
    
        // this.makePost(data);
        this.publish();
      }
    
    //  makePost(data) {
    //    // data로 가져온 값을 UI에 출력할 형태로 분배
    //  }
    //
    //  calculateAverageMannerScore(teams) {
    //    // 모든 사용자 정보를 찾아 매너 점수를 계산
    //  }
    //
    //  countCurrentTotalParticipants(teams) {
    //    // 게임의 현재 참가자 수를 계산
    //  }
    //
    //  countTargetTotalParticipants(teams) {
    //    // 게임의 목표 모집 참가자 수를 계산
    //  }
    }
    export default function PostPage() {
      console.log('페이지 컴포넌트 진입');
    
      const location = useLocation();
    
      const postId = location.state !== null
        ? location.state.postId
        : Number(location.pathname.split('/')[2]);
    
      const postStore = usePostStore();
    
      useEffect(() => {
        console.log('fetchPost 호출');
        postStore.fetchPost(postId);
      }, []);
    
      const { postInformation, postPositions } = postStore;
    
      console.log('images in PostPage', postInformation.images);
    
      return (
        <Container>
          {/* <PostInformation
            information={postInformation}
          />
          <PostPositions
            teamsAndPositions={postPositions}
          /> */}
          Contents of Post
        </Container>
      );
    }


    문제의 범위를 한정시키자 문제가 나타나는 방식이 확인되었다. 모든 페이지가 렌더링된 뒤에 useEffect 내부의 fetchPost가 호출되고 있었다.

    문제를 해결하기 위해 다음과 같이 페이지에서 사용되는 데이터를 아직 가져오지 않았을 때에는 다른 안내 메세지를 출력하도록 하고, 상태에 업데이트되었을 때 게시글의 상세정보를 화면에 출력하는 컴포넌트를 호출하도록 했다.

    // PostPage.jsx의 반환 값
    return (
        <Container>
          {postInformation && postPositions
            ? (
              <>
                <PostInformation
                  information={postInformation}
                />
                <PostPositions
                  teamsAndPositions={postPositions}
                />
              </>
            ) : <p>Now Loading...</p>}
        </Container>
      );
    // PostInformation.jsx의 반환 값
    export default function PostInformation({ information }) {
      const { images } = information;
    
      return (
        <Container>
          {images ? (
            <>
              // 게시물의 내용
            </>
          ) : <p>Now Loading...</p>}
        </Container>
      );
    }

    매너점수 값은 0에서 10 사이의 랜덤하게 뽑아진 값을 적용한 것으로 다른 의도는 없다.



    그러자 고정 값으로 정의해놓은 Store의 상태를 정상적으로 가져와 UI에 전달하는 것을 확인할 수 있었다. 문제를 확인했으니 고정 값으로 설정한 값을 다시 백엔드 서버에서 가져오는 값으로 바꿀 수 있을 것이다.


    문제의 근본적인 원인을 알기 위해 읽어볼 글 목록을 정리했다.

    Using the Effect Hook – React

    A JavaScript library for building user interfaces

    ko.reactjs.org

    리액트 useEffect는 어떤 모습일까?

    지난 시간에 이어 useEffect를 직접 만들어 보자. 순수함수 순수함수란 무엇일까? 두 가지 특징이 있다. (참고: 위키피디아 Pure function) 입력이 같으면 결과도 같다. (the function return values are identical fo

    jeonghwan-kim.github.io



    동료들의 소스코드 상의 문제를 볼 때는 문제점을 상대적을 어렵지 않게 파악할 수 있지만 내가 작성한 소스코드의 문제를 내가 스스로 파악하기는 쉽지 않은 이유는 무엇일까?

    동료들의 소스코드를 볼 때는 일단 내가 동료의 앱의 모든 소스코드를 알지 못하고, 보통 동료가 알려주는 내용을 집중적으로 살펴본다. 따라서 문제 영역 이외의 부분은 자연스럽게 차단하게 되고, 문제가 발생하는 그 부분이나 직접적으로 연결되는 부분에 한정해서 문제의 원인을 고민할 수 있다.

    반면 내가 작성한 소스코드는 특정 동작에 관련된 모든 로직이 머릿속에 그려지기 때문에 문제 해결을 위해 어느 영역에 집중해야 하는지 시선이 분산되는 것 같다는 것을 오늘 동료분의 도움을 받으면서 느꼈다.


    큰 산을 하나 넘은 만큼 월요일까지 이번 주 목표인 참가자 관점에서의 신청 로직과 작성자 관점에서의 신청 수락, 거절 기능 작성을 마칠 수 있도록 열심히 달려보도록 하자.


    댓글

Designed by Tistory.