미스터카멜 과제 회고 (궁금증만 잔뜩 쌓인..)

junamee·2021년 7월 30일
2

개발후기집📚

목록 보기
11/12

🐪프로젝트 설명 및 코드🐫

회고~

이번 과제는 간단한 기획서를 토대로 과제를 구현해야했다. 기획서 내용이 읽는 사람마다 이해하는 바가 달라서 내용을 정리하는데에도 오랜 시간이 걸렸고, 간단하게 디자인 시안을 피그마로 작성하여 진행했다. 덕분에(?)기획서 내용을 더 꼼꼼하게 읽고 정리할 수 있었다.

또 클래스로 리액트를 구현하면서 고통을 많이 받았다.
함수형에서는 겪어본 적없는 고통으로 고민을 많이 했다. 아직도 해결하지 못한채 찜찜하게 글을 남겼는데 꼭 궁금한 점을 해결할 수 있길 바란다.


새로 배운 것 & 시도한 것

리액트 클래스 컴포넌트 구현

리액트는 함수형에 최적화있다는 이유로 함수형만 공부를 했다. 그런데 이번 과제가 클래스형을 요구해서 처음 시도하게 되었다. 클래스를 사용하지 않는 추세라하여 다소 마음에 들진 않았으나 나중에 현업에서 예전의 코드를 유지보수하는 일도 있을 것이라는 생각에 어떻게 쓰는건지 알긴 해야겠구나싶었다.

💦클래스형에서 주의할 점 => 메서드를 this와 바인딩 해주기

1) constructor에서 정해주기

constructor(props){
	super(props);
    this.handleClick = this.handleClick.bind(this)
}

2) arrow function으로 메서드 선언

handleClick = () => { ... }

생애주기와 에러처리

생애주기
그 외 사용법은 크게 다르지 않았으나 useEffect와 같은 리액트 훅을 사용할 수 없으니 생애주기를 활용하여야 했다. 주로 componentDidMount만 사용했다. 사용하면서 느낀 점은 dom에 접근할 수 있는 rendering을 모두 마친 후에 실행하는 것이어서 함수형에서의 useEffect와 동일하게 동작하는 것 같다.

생명 주기 중에 getSnapshotBeforUpdate는 사용하진 않았지만 신기했다. 함수형 컴포넌트에서는 없는 메서드라고 한다. 렌더링이 새로 그려지기 직전의 dom에 접근할 수 있어 해당 상태를 저장하고 렌더링이 된 후에 그 상태값을 snapshot이라는 변수로 접근해 활용할 수 있다.

에러처리
또한 componentDidCatch 메서드를 활용하여 ErrorBoundary를 적용했다.
componentDidCatch(error, info)로 에러의 정보와 에러의 발생 위치를 확인할 수 있다. 우리 프로젝트에서는 에러가 발생하는 경우가 디테일 페이지의 주소체계를 이상하게 접근했을 경우였다.

주소창 location path를 기반으로 데이터를 렌더링하기 때문에 path가 정확해야하는데 사용자가 임의로 체계에 맞지않는 주소에 접근할 경우 에러가 났다. 이 경우 ErrorBoundary로 에러를 감지하고 다시 메인페이지로 리다이렉팅 시키는 처리를 했다.

그 외 최근조회이력페이지에서 로컬스토리지에 저장했던 데이터가 삭제된 후 뒤로가기를 누르면
로컬스토리지에 데이터가 없기 때문에 에러가 발생했는데 그 부분은 다시 로컬스토리지가 빈 값이면 기본 데이터를 설정하게 해주어 에러가 발생하지 않도록 처리했다.👍


아쉬운 점 & 어려웠던 점

과제 구현사항을 있는그대로 받아들이지 못했다.
이번 과제 기술서를 해석하는 과정에서 꼼꼼히 정리를 하려다 보니 요구사항을 쉬운 방법을 나두고 조금 꼬아서(?) 어렵게 접근 한 것 같다.

제이슨서버로 받아온 목데이터를 수정한 것이 그 예의 하나다.
원래 목데이터는 {title:'상품명', brand:'브랜드명', price:'가격'} 이렇게 3가지만 담겨있었다. 이번 과제에서 조회순/가격 나열 및 브랜드/관심여부 필터링 기능이 있었기 때문에 데이터에 조회시간과 관심여부를 넣어주면 데이터를 다루기 편하지 않을까라는 생각을 했다. 때문에 받아온 목데이터를 {title:'상품명', brand:'브랜드명', price:'가격', disLike:T/F, visitedTime: new Date()}와 같이 수정했다.

1페이지에서 2페이지로 넘어갈때 가공한 이 데이터가 필요한 상황이었다. 만약 데이터를 가공하지 않았다면 고민없이 제이슨서버를 통해 다시 전체 데이터를 받아올 수 있었을 것이다. 물론 큰 문제는 아니다. history.push(/path,state: object)로 object에 데이터를 넘겨주어 해결했기 때문이다.(받아오는 쪽에서는 this.props.location.state로 확인할 수 있다.)

그리고 관심여부도 localStorage에 disLike라는 키를 두어 데이터를 관리하고, 조회순서는 구체적인 시간이 필요하지 않으니 localStorage에 visitedItem을 저장한 인덱스로 분별할 수 있었겠구나 라는 생각이 이제 든다. 과제에서는 이미 구현을 위해 충분한 데이터를 주고있었는데 굳이 데이터를 가공할 필요는 없었단것을 뒤늦게 알게 되었다.😪💤
그래도 그 덕에 history로 state를 넘겨주는 방법을 정확히 알게되었다.

아직도 헤메는 비동기 & 클래스형에서 context API
추가적인 기능 구현을 위해 contextAPI를 도입하려했다.
(2,3페이지에서의 관심없음 수정 상태 변경을 1 페이지에서 인식시키기 위한 목적)

함수형과는 사용하는 방법이 조금 달랐다. 어찌어찌 공문을 통해 파악하고
컨텍스트 API로 사용할 데이터를 <Consumer>에서 불러왔다. 성공적으로 원하는 데이터를 출력할 수 있어서 박수를 쳤다. 👐

그런데 문제는 페이지가 라우팅되면서 발생했다. 페이지가 변경되니 컨텍스트 api로 관리하려한 데이터가 초기화되었기 때문이다. 다은님과 어리둥절했는데... 지금 다시 살펴보면서 어디쯤에서 잘못된건지는 파악하게 되었다. contextAPI가 이상한 줄 알았는데.... 흠...

문제의 코드는 다음과 같다. (필요한 부분만 가져옴)

class App extends Component {
  state = {
    value: [],
  };

  componentDidMount() {
    getProductJsonData().then((res) => this.setState({ value: res }));
  }
  
  render() {
    return (
      <>
        <ProductContext.Provider value={this.state.value}>
           <Router history={history}>
              <Switch>
                <Redirect exact path="/" to="/productlist" />
                <Route path="/productlist" component={ProductList} />
                <Route path="/productdetail/:id" component={ProductDetail} />
                <Route path="/recentlist" component={ProductRecent} />
              </Switch>
      	   </Router>
       </ProductContext.Provider>
      </>
    );
  }
}

productlist페이지에서는 ContextAPI데이터를 사용하는 Consumer에서 Provider로 넘겨준 데이터를 잘 가져온다. 그런데 상품을 선택해서 디테일 페이지로 넘어가서는 Consumer에서 인지하는 데이터가 [ ]로 초기화되어버렸다.

그런데 ComponentDidMount에서 비동기 요청이 아니라 바로 특정 데이터를 정해준다면, 모든 페이지에서 contextAPI값이 모두 잘 반영된다.

  componentDidMount() {
    this.setState({ value: "헤이~" });
  }

이렇게 하면 모든 페이지의 Consumer에서 '헤이~'라는 값을 받을 수 있다.
뭐지?? 이유가 뭘까...

그럼 모든 데이터가 초기화된다면 아예 초기 state로 비동기요청한 데이터를 넣어주면 되지않을까? 싶었는데 Promise<pending>에 허우적 거렸다. ㅠ,ㅠ ComponentDidMount에서 해줄 때는 함수형컴포넌트에서 useEffect에서 요청하는 경우 동일하게 생각해 해결했었는데 , 초기값에서는 어떻게 바로 비동기요청결과를 넣어줄 수 있을지... 아직도 리액트와 비동기요청의 속성을 잘 이해하고 있지 못한 것 같다.

비동기의 문제인것 같으면서도,
캐쉬의 문제인 것 같으면서,
(app단에서 데이터를 가져오도록 맨 처음 렌더링 시에만 요청을 보내고 이후 요청을 하지 않는다...그래도 캐슁되었다면 캐슁된 값을 반환해야하는게 맞지않나?)
모르겠다. 근데 애초에 초기값으로 리셋되어버린다는 것 자체가 내가 이해하는 contextAPI가 아닌 듯하다.


흠.. 이렇게 해결법을 알지 못하고 글을 마무리하게 되어서 매우 찜찜하다.
클래스형에서만의 문제인건지 다른 속성으로 인한 문제인지는 현재로서는..
더 많은 프로젝트에서 contextAPI를 써보면서 이해하는 수밖에 없을 것 같다.

profile
아티클리스트 - bit.ly/3wjIlZJ

1개의 댓글

comment-user-thumbnail
2021년 8월 5일

질문 있으셔서 코드 같이 보려고 했더니, 과제에 context api 를 안 쓰신 것 같더라고요 ㅎㅎ 다시 빼셨나요? 나중에 질문 정리해서 알려주세요! 🙏🏻

답글 달기