INSTACOOLIKE 플젝 TIL

YEONGHUN KO·2022년 6월 6일
1

REACT JS - PRACTICE

목록 보기
4/15
post-thumbnail
post-custom-banner

Karl Hadwen의 insta clone 프로젝트를 참고로 하여 나만의 사진 공유 프로그램을 만들어 보았다. 그리고 그 과정에서 새로 알게된 지식, 디버깅 했던 것들을 정리해보려고 한다.

JS

photos 자동 업데이트

usePhotos를 사용해서 photos를 자동 업데이트 하려고 했다.

// App.js
const { user } = useAuthListner();
const { activeUser = {} } = useUser(user?.uid);
const { userId, following } = activeUser;

const [postPhotos, setPostPhotos] = useState([]);
const [orginalPhotos, setOriginalPhotos] = useState([]);
const [userFollowing, setUserFollowing] = useState([]);
const { photos } = usePhotos(userId, following);

useEffect(() => {
  setOriginalPhotos(photos);
  setPostPhotos(photos);
  setUserFollowing(following);
}, [photos, following]);

그런데 이렇게 되면 suggestedProfile에 follow를 눌러도 usePhotos가 실행되지 않는다. 왜냐면 usePhotos는 userId, following을 dependency로 가지고 있기 때문이다. 그말은 following이 바뀌지 않는다는 뜻이다.

following이 바뀌려면 useUser가 바뀌어야 하는데 useUser는 또다시 user.uid를 dependency로 가지고 있기 때문. 따라서 following대신 userFollowing을 pass해주고 follow버튼을 누르면 setUserFollowing을 실행하기로 했다.

그래서 아래와 같이 코드를 바꾸어 주었다.

const [postPhotos, setPostPhotos] = useState([]);
const [orginalPhotos, setOriginalPhotos] = useState([]);
const [userFollowing, setUserFollowing] = useState([]);
const { photos } = usePhotos(userId, userFollowing);

useEffect(() => {
  setOriginalPhotos(photos);
  setPostPhotos(photos);
}, [photos]);

useEffect(() => {
  setUserFollowing(following);
}, [following]);

그럼 userFollowing이 바뀔때 마다 usePhotos가 바뀌고 usePhotos가 바뀌면 originalphotos, postphotos에 새로운 photos가 업데이트 되면서 timeline이 업데이트 된다.

dependency를 잘 생각하고 흐름을 짜니까 userFollowing에 의한 photos 자동 업데이트를 구현할 수 있게 되었다!

memory leakage

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

UserProfile 컴포넌트에서 위와 같은 warning 이 발생하였다. 말그대로 컴포넌트가 없는 상태에서 state를 업데이트 한다면 메모리 누수가 발생한다는 것이다.

주로 api요청이후 setState를 하는 과정에서 중간에 다른페이지로 navigate되는 경우 메모리 누수가 발생할 수 있다.

이걸 고치려면 useEffect에서 return 값을 정해주어야 한다. 그래야 unmount될때 clean없이 될거니깐.

해법은 여러가지가 있는데 boolean 값을 이용해서 clean up 해주기로 했다.

const [{ profile, photosCollection, followersCount }, dispatch] = useReducer(
  reducer,
  initState
);

useEffect(() => {
  let isMounted = true;
  async function getUserPhotos() {
    const photos = await getUserPhotosByUserId(user?.userId);
    if (photos && isMounted) {
      dispatch({
        profile: user,
        photosCollection: photos,
        followersCount: user.followers.length,
      });
    }
  }

  if (user) {
    getUserPhotos();
  }

  return () => {
    isMounted = false;
  };
}, [user?.userId]);

photos를 요청하고 나서 photo가 있고 mount가 되었을때 dispatch를 이용해서 state를 변경해줘야 메모리 누수를 막을 수 있다.

CSS

overflow-scroll

scroll할 대상에 height가 있어야만 overflow되었을때 scroll이 생긴다. Profile페이지 안에서 Post 구현하다가 알게된 사실!

responsive center

  • 안에 들어가는 콘텐트가 페이지의 크기가 변할 때 마다 보기 좋게 가운데 위치하게 하고 싶다면?
    margin auto값을 쓰면 된다. (display가 block이 된 상태에서만 적용!)
.content {
  display: block;
  margin-left: auto;
  margin-right: auto
}

//tailwind

<div class="mx-auto">

Apple 클론 할때 잠깐 배운적이 있다.

참고

tailwind custom config function

  • function을 사용하면 자연스레 theme obj를 제공받을 수 있다.
module.exports = {
  theme: {
    fill: {
      gray: ({ theme }) => theme('colors.gray')
    }
  }
}
// somewherre.js

<div class='fill-gray'>

요런식으로 설정하고 사용하면 된다!
참고

TIP

eslint 컴파일 에러

eslint설치하고 rules에 코드를 맞추지 않으면 컴파일 에러가 난다. eslint 에러는 고스란히 표시하면서 동시에, eslint에 의한 컴파일에러를 막고 싶으면?

아래 링크 참고!

요기1
요기2

data structrue

  • photos와 users에 userName이 중복되어 존재하는 것이 불필요하다고 판단하여 karl은 users에만 userName을 기입하였다.
    데이터를 설계할때 특정 값이 과연 필요하며 필요하다면, 어느 데이터에 더 가까운지 생각해보자.

특정 data의 위치

  • 만드는 도중 profile - photos에서 original photos가 필요했다. photo를 삭제할 경우 Post 컴포넌트가 사용되는데 Post의 Header component는 originalPhotos와 postPhotos의 context가 필요하다.(삭제할 경우 originalPhotos와
    postPhotos의 setState가 작동하니깐) 그런데 profile에는 context가 없다. 그래서 아예 상위 컴포넌트인 App으로 context를 옮기는 게 좋을 것 이라고 생각했다.

애초에 앱을 만들기 전에 설계에 대해 좀 더 깊이 고민했다면, Post 컴포넌트가 profile, dashboard 두개의 page에서 사용한다는 것을 알고 애초에 post에 관한 context를 App에 설치했을 것이다.

설계에 대해 깊이 고민하자.

  1. context는 어디에 두는게 좋을지.
  2. 컴포넌트 분리는 어디까지 하면 좋을지.
  3. state관리는 부모에서 할지 아님 자식에서 할지.

re-use

이미 만들어져 있는 것을 재사용할 수 있는지 항상 생각!

  • firebase안에서 내가 지금 필요한 기능이 있는 함수가 이미 만들어져있는지 고민.
  • profile - header 에서 이미 로그인된 user의 정보를 가져오려고 할 때 useUser hook을 재사용하려고 하는점.

unique Id

  • profile 에서 photos를 가져올때 username으로 가져오면 안된다. username은 얼마든지 바뀔 수 있기 때문. 그래서 바뀔 가능성이 가장 적은 userId를 통해서 가져왔다.

그래서, POST, PROFILE같은경우는 여러가지 내용이 바뀌더라도 알아볼 수 있는, 정체성을 확인할 수 있는 고유의 id를 가지고 있으면 좋을 것 같다고 생각했다.

data를 설계할 때 참고하자!!

번외 - 바보같은 실수

  • 문제 상황
    profile페이지에서 A라는 post에 좋아요를 클릭하고 dashboard로 돌아가면 좋아요가 original에는 적용되는데 postPhotos에는 적용안된다.

Header 컴포넌트에 getOriginalPhotos가 profile -> dashboard로 넘어갈때 작동안함. (왜냐면, App에 있는 postPhotos state가 업데이트 안되어있으니깐.)

  • 추측
    추측을 해보자면, profile에서 dashboard로 넘어갈때 link에 의해서 setPostPhotos가 묻혀버린것 같다.(컴포넌트가 unmount되고 다시 mount되면서 묻혀버린것 같다.)

근데 dashboard에서 다시 한 번 로고를 클릭하면 postPhotos state가 업데이트 된다.(react dev tools에서 확인 가능하다.) 근데 A - Post에 좋아요 표시가 안뜬다.

근데 react dev tools을 통해서 A - Post를 보면 좋아요 표시는 되어있다. 업데이트된 state가 진입했다는 뜻이다. 근데 좋아요표시가 화면에 반영안된걸 보니 랜더링 된것 같지는 않다.

Post가 memo되었기 때문에 render되지 않는것인가...
그래서 dashboard에서 로고를 누르고 나서 profile로 갔다가 다시 dashboard로 가면 업데이트된 state대로 랜더링 되어있다.

  • 디버깅
    음 그래서 memo를 지우고 다시 똑같이 해보았는데.... 좋아요 표시는 여전히 되지 않았다.. 그럼 memo때문이 아니라는 건데... 뭐때문이지?? 좋아요 버튼을 누르고 나면 postPhotos까지 업데이트 되게 해야하나?? --- postphotos 까지 업데이트 하니 잘된다. 근데 업데이트를 두번 하기에는 좀 그렇다.

  • 해결 방법
    아!! 이제 알겠다.... setPostPhotos는 prop으로 넘어오는데 dashboard에서는 Header에 prop으로 넘겨주었으나 profile page에서는 prop으로 넘겨주지 않고 있기 때문에 setPostPhotos가 없는 것이다...

그래서 그냥 아예 독립시키기 위해서 prop으로 넘겨주는 대신 context로 넘겨주기로 했다. 그러니 잘된다.


이렇게 프로젝트를 마무리하고 테스트 코드도 짜보았다. 테스트 코드를 짜면서 겪게된 시행착오또한 상당한데 그 시행착오는 아래 링크를 통해 볼 수 있다.
instacoolike testing TIL

아쉬웠던 점

  • Post에 댓글을 남기거나 좋아요를 클릭하면 실시간으로 반영되지 않는게 아쉽다.
  • 백엔드를 firebase를 통해 구현하였다. 다음엔 내가 직접 서버를 구축해보고 싶다.
  • 트래픽이 많이 늘어나는 현상을 경험하지 못하였다. 분명 유저가 많아지면 생기는 문제가 있을 것이다.
profile
'과연 이게 최선일까?' 끊임없이 생각하기
post-custom-banner

0개의 댓글