[수습 과제] 리액트 상태관리 관련 세미나를 진행 과제를 받았다.

Lemon·2022년 9월 17일
1

수습과제

목록 보기
2/4
post-thumbnail

🙏🏻 1주일 동안 준비한 내용을 바탕으로 발표를 준비했습니다.
틀린 내용이 있다면 피드백 부탁드립니다.

🔗 좀 더 자세한 정리 자료
https://velog.io/@remon/상태-관리-라이브러리-및-특징
https://velog.io/@remon/React-리액트-라이프-사이클
https://velog.io/@remon/React-Hook에-대하여
https://velog.io/@remon/React-React-컴포넌트와-This-란

1. 상태관리 라이브러리 및 특징

상태 관리 라이브러리 종류


리액트의 단방향 바인딩의 문제점을 보안하기위해 상태관리 라이브러리가 나왔습니다.
다양한 종류의 라이브러리가 있지만, 가장 대표적인 것은 Redux, MobX, Recoil입니다.
이 상태관리 라이브러리들은 전역 상태 관리를 지원합니다. 전역 상태 관리 라이브러리를 사용하면 컴포넌트끼리 넘겨주는 방식을 거치지 않더라도 여기저기 컴포넌트에서 접근 할 수 있습니다. 이러면 일단 props가 복잡해지는 문제는 해결됩니다. 이런 라이브러리의 경우 진짜로 값을 할당하는 부분이 한 곳에서만 일어난다는 것과, 상태 업데이트부터 컴포넌트 리랜더링까지 데이터의 흐름을 한 방향으로, 한 지점을 거쳐가도록 설정한다는 것입니다.


Redux가 나오기 전 리액트를 포함한 대부분의 프로젝트는 MVC 아키텍처가 많이 사용되었습니다. 컴트롤러가 여러 모델을 제어하고 모델과 뷰가 서로 바라보는 구조양방향으로 영향을 미치기 때문에 프로젝트 규모가 커지고 상태가 많아질수록 관리가 어렵습니다.


그래서 페이스북이 새로 내놓은 아키텍처가 Flux 아키텍처 입니다.
Flux 아키텍처데이터의 흐름이 단방향으로 흐르는 구조입니다.
이러한 Flux 아키텍처를 가져가는 대표적인 라이브러리가 리덕스입니다.


Redux

순수 함수란?
부수 효과가 없는 함수로 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수이고, 외부의 상태를 변경하지 않는 함수입니다.


MobX

리덕스와 다르게 불변성에 신경쓰지 않아도 될 정도로 규칙만 잘 신경쓰면 최적화가 잘 됩니다.
리덕스보다 다루기 쉬운 라이브러리지만 그만큼 단점이 존재합니다.
MobX의 경우 리덕스와 달리 store가 여러개가 될 수 있어서 상태 변경 시 다수의 스토어가 영향을 받을 수 있습니다. 또한 스토어의 데이터를 액션의 발행없이 업데이트 할 수 있어서 구현은 쉬우나 테스트나 유지보수 측면에서는 문제를 일으킬 수 있습니다.

그렇기에 장기적인 프로젝트, 유지보수 및 확장성을 고려해야 하는 프로젝트의 경우 MobX는 좋지않은 선택일 수 있습니다.

하지만 리덕스보다 러닝커브가 낮고 보일러플레이트 코드의 양 또한 적기 때문에 프로젝트의 규모가 크지 않다면 MobX를 사용하는 것은 좋은 해결책이 되리라 생각합니다.


Recoil


Recoil은 페이스북에서 Dave McCabe가 개발한 상태관리 라이브러리입니다. 비교적 최근에 나왔습니다. Recoil은 AtromsSelectors로 이루어져있습니다.
Atoms는 상태의 단위이며, 업데이트와 구독이 가능합니다.
Atom이 업데이트 되면 각각의 구독된 컴포넌트는 새로운 값을 반환합니다.

Atomsatom 함수를 사용해 생성합니다.

const fontSizeState = atom({
  key: 'fontSizeState',
  default: 14,
});

컴포넌트에서 atom을 읽고 쓰려면 useRecoilState라는 훅을 사용합니다. useState와 비슷하지만 상태가 컴포넌트 간에 공유될 수 있다는 차이가 있습니다.

Selectorsatoms이나 다른 selectors를 입력으로 받아들이는 순수함수(pure function)입니다. selectors가 업데이트되면 하위의 selector함수도 다시 실행됩니다. 비동기처리 뿐만 아니라 데이터 캐싱 기능도 제공합니다.

캐싱이란?
캐싱은 기본 데이터 저장소 앞에 데이터 복사본을 저장하는 전략입니다. 캐싱의 장점에는 더 빠른 응답 시간과 데이터를 빠르게 제공하여 사용자 경험을 향상시키는 기능이 있습니다. 캐시 저장소는 일반적으로 기본 저장소보다 사용하는 클라이언트에 더 가깝습니다.
캐싱은 클라이언트 인스턴스가 동일한 데이터를 반복해서 읽을 때 특히 다음 조건이 모두 원래 데이터 저장소에 적용되는 경우에 가장 효과적입니다.
보유한 데이터가 많을수록 그리고 이 데이터에 액세스해야 하는 사용자 수가 많을수록 캐싱의 이점은 더 커집니다. 이는 캐싱이 원래 데이터 저장소의 많은 양의 동시 요청 처리와 관련된 대기 시간 및 경합을 줄이기 때문입니다.

상태를 기반으로 하는 파생 데이터를 계산하는데 사용합니다.
컴포넌트의 관점에서 보면 selectors와 atoms는 동일한 인터페이스를 가지므로 서로 대체할 수 있습니다.

리코일보일러플레이트가 별로 없고, 리액트 지역 상태로서 단순한 get/set 인터페이스로 상태를 공유할 수 있어서 러닝 커브가 낮습니다. 또한 상태를 분산적으로 둘 수 있기 때문에 코드 스플리팅이 가능합니다. 단점은 아직 안정화 단계에 있지 않다는 점입니다.

코드 스플레팅이란?
코드에서 당장 사용하는 부분만을 로딩하고, 현재 필요하지 않은 코드 부분은 따로 분리시켜 나중에 로드함으로써 로딩시간을 개선하는 것

상태관리 라이브러리와 특징에 대한 결론은 각 라이브러리마다 장단점이 있고, 프로젝트마다 특성이 다르니 특성에 맞게 골라 쓰는게 맞다는 것입니다.


2. Lifesycle

라이프 사이클은 크게 3가지 유형으로 분류됩니다.

생성 될 때를 뜻하는 마운팅, 업데이트 될 때를 뜻하는 업데이팅, 제거 될 때를 뜻하는 언마운팅이 있습니다.

라이프 사이클은 총 9가지의 메서드로 분류됩니다.

1. constructor 메소드
클래스 인스턴스 객체를 생성하고 초기화하는 메서드입니다.
리액트에서 컴포넌트를 만들 때 처음으로 실행합니다.
React.Component를 상속한 컴포넌트의 생성자를 구현할 때는 다른 구문에 앞서 super(props)를 호출해야합니다.

💡 클래스형에서는 초기 state를 정할 때 constructor를 사용해야합니다. 하지만 Hook에서는 useState hook을 사용하면 초기 상태를 쉽게 설정해줄 수 있습니다.

2. static getDerivedStateFromProp
React 16.8버전 이후에 생긴 메서드로 props로 받아 온 값을 state에 동기화시키는 용도로 사용합니다. 컴포넌트가 마운트 될 때와 업데이트 될 때 호출하고 render 메서드를 호출하기 직전에 호출됩니다.
정적 메서드이기 때문에 this를 호출할 수 없습니다.
이유와 상관없이 렌더링 때마다 매번 실행되므로 주의해야합니다.

3. shouldComponentUpdate
propsstate를 변경했을 때, 리렌더링을 할지 말지 결정하는 메서드입니다. 반드시 truefalse를 반환합니다. 기본적으로 true를 반환하지만 조건에 따라 false를 반환하면 해당 조건에는 render 함수를 호출하지 않습니다.
해당 메서드는 오직 성능 최적화만 위해 사용합니다.

💡 Hook을 사용하면 propsReact.memo, stateuseMemo를 활용하면 렌더링 성능을 개선할 수 있습니다.

4. render
실제 로딩이 이루어지는 가장 기초적이면서 가장 중요한 메서드입니다. 컴포넌트가 렌더링할 때 필요한 메서드 중 유일하게 필수인 메서드로 최종적으로 component에서 작업한 결과물을 return하는 메서드입니다.
React Element는 보통 JSX를 사용해서 작성합니다. render 메서드가 실행되면서 JSX가 HTML로 변환되어 우리가 보는 웹 브라우저로 나타납니다. 컴포넌트가 로딩될 때에도 실행되지만 컴포넌트의 데이터(state, props)가 업데이트 되었을 때에도 동작합니다.

5. getSnapShotBeforeUpdate
render에서 만들어진 결과가 브라우저에 실제로 반영되기 직전에 호출됩니다. 업데이트 되기 직전에 snapshot(props & states)를 확보하는게 목적입니다. 인자값에서 반환되는 prevPropsprevState는 변경되기 전에 값을 보여줍니다. 채팅 화면처럼 스크롤 위치를 따로 처리하는 작업이 필요한 UI에 사용되는 메서드입니다.

6. componentDidMount
마운팅의 마지막 부분입니다. 초기 컴포넌트의 로딩 이후에 한번만 실행됩니다. render 메서드에 있는 모든 부분들이 브라우저에 나타나게 되었을 때만 실행합니다. 컴포넌트를 만들고 첫 렌더링을 마친 후에 실행된다는 말입니다.
DOM을 직접 조작할 수 있습니다.

💡 useEffect Hook의 의존성 배열 ( [ ] )을 비우면 똑같은 메서드를 구현할 수 있습니다.

7. compoenentDidUpdate
컴포넌트의 변경이 완료되었을 때 수행되는 메서드입니다. 조금 복잡한 작업을 수행하기에 최적화 되어있습니다. 처음 렌더링 될 때는 실행되지 않고, 리 렌더링을 완료한 후 실행합니다. stateprops가 변경되어서 DOM에 변화가 발생한 뒤에 실행된다는 뜻입니다. prevProps 또는 prevState를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근할 수 있습니다.

8. componentWillUnmount
DOM에서 제거되는 것을 뜻합니다. JSX에 포함되었다가 이후에 제거되는 경우에 발생합니다.

💡 useEffectcleanUp 함수를 통해 해당 메서드를 구현할 수 있습니다.

9. componentDidCatch
컴포넌트 렝더링 도중에 에러가 발생 했을 때 애플리케이션이 멈추지 않고 오류 UI를 보여줍니다.


3. Hook

React Hook은 React가 16.8로 업데이트 되면서 추가된 기능입니다. 기존에 Class 형식의 리액트 컴포넌트에서만 할 수 있었던 state 관리 및 라이프 사이클 등을 함수형 컴포넌트에서도 사용할 수 있게 만들어주는 새로운 기능입니다.

React Hook은 Class Component를 사용하면서 느꼈던 불편함이나 문제점들을 해결하기 위해서 개발되었습니다. React Hooks가 나오기 전에는 라이프사이클을 함수형 컴포넌트에서는 사용하지 못했기 때문에 함수형 컴포넌트가 더 간결하고 빠르더라도 클래스형 컴포넌트를 써왔습니다.

하지만 React가 16.8로 업데이트하면서 함수형 컴포넌트에서도 함수형 컴포넌트를 사용할 수 있게 되었습니다.
왼쪽 예시에 있는 코드는 일반 클래스 컴포넌트에서 생명주기를 사용한 예시입니다. 오른쪽 코드는 동일한 역할을 하는 코드를 React Hooks를 이용해서 구현한 코드입니다. 왼쪽 코드와 오른쪽 코드를 보면 선명하게 코드가 간결해진 것을 볼 수 있습니다.
Class형 컴포넌트로 라이프사이클 함수를 이용해서 코드를 구현하려면 componentDidMount, componentDidUpdate, componentWillUnmount를 사용해서 구현해야했지만, React Hook을 사용해서 코드를 구현하면 useEffect 안에서 다 처리할 수 있습니다.

또한 HOC 컴포넌트를 Custom React Hooks로 대체해 많은 Wrapper 컴포넌트를 줄일 수 있습니다.
HOC란 Higher Order Component로 화면에서 재사용 가능한 로직들만 분리해서 component로 만들고, 재사용 불가능한 UI와 같은 다른 부분들은 parameter로 받아서 처리하는 방법입니다.
모든 페이지에 필요한 부분들이 HOC로 만들어져서 감싸진다면 Wrapper 컴포넌트가 너무 많아집니다. Wrapper가 너무 많아지면 데이터 흐름을 파악하기가 힘들어집니다.

위와 같은 문제점들을 Custom React Hooks를 이용해서 해결할 수 있습니다. 왼쪽에 있는 class형 컴포넌트 코드를 오른쪽 예시와 같이 custom hook을 이용해서 굳이 감싸주기 않고 사용할 수 있게 되었습니다.

React Hook이 나와서 코드가 간결해지고 가독성이 향상되었습니다. 많은 라이브러리들도 Hook으로 나오고 있습니다. 또한 HOC 헬을 벗어날 수 있고, 불필요한 보일러플레이트 코드도 제거되었습니다. 따라서 이제는 클래스 컴포넌트를 사용해서 컴포넌트를 만들 이유가 많이 없어졌습니다. Hook을 사용하면 써야되는 코드도 많이 줄어들게 되고 보고 읽기도 편하기 때문입니다.


4. This

this란 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다.
thisthis가 바라보고 있는 객체이며, 어디서 호출하는냐에 따라 달라집니다. 일반적으로는 this 키워드는 부모를 가리키고, 아무것도 없는 상태에서 this를 가리키게 된다면 window를 가리킵니다.
왼쪽의 예시 코드는 this는 부모를 가리키기 때문에 this.atestObject에 있는 a를 출력하지만 오른쪽 예시 코드는 this의 실행 시점이 어떤 특정한 변수에 담겨졌기 때문에 더 이상 그 메서드에서 가리키는 this가 원래의 부모 객체인 testObject를 가리키지 않고 글로벌한 this를 가리키게 됩니다. 따라서 this가 결정되는 시점은 this가 선언된 시점이 아닌 누가 실행하는지에 따라서 결정됩니다.

class형 컴포넌트의 this를 라이프사이클 함수 안에서 console.log로 체크해보면 호출한 해당 컴포넌트인 App을 가리키는걸 볼 수 있습니다.

리액트에서는 이벤트에 메서드를 넘기기 때문에 이벤트에서 실행될 때는 이미 this는 사라진 상태입니다. 그래서 리액트에서는 this를 강제로 선언 시점에 고정해두는 bind 메서드를 사용합니다. 그리고 화살표 함수(Arrow Function)를 사용해서 작성할 경우 다른 함수와 달리 따로 this를 가지고 있지 않고 this가 항상 상위 스코프의 this를 그대로 참조하게 할 수 있습니다.

Class형 컴포넌트의 this를 정리하자면 this는 선언 시점에서 결정되는 것이 아니고, 메소드와 함수를 어떤 주체가 실행 하는지에 따라서 결정되고, 이를 무시할 수 있는 방법은 bind를 사용해서 강제로 지정하거나 화살표 함수를 사용하는 방법이 있다는 것입니다.

React 함수형 컴포넌트의 this는 Class형 컴포넌트에서만큼 중요하지 않습니다. 함수형 컴포넌트의 상태 값은 useState Hook으로 관리되기 때문에 컴포넌트의 this로부터 자유롭습니다. 함수형 컴포넌트 자체와 함수형 컴포넌트 안에서 선언한 함수들 모두 전역 객체를 this로 가지기 때문에 애초에 this가 다 같습니다. Hook을 포함해서 함수형 컴포넌트 내에 선언한 함수나 변수들이 순서대로 작동하기 때문에 this 바인딩을 신경 쓸 필요도 없습니다.


느낀점 & 피드백

피드백 😱

  • 전체적으로 아쉬운 세미나였다.
  • 예시 코드가 있었으면 좋겠다.
  • 리액트를 모르는 사람들은 이해하기 힘든 내용이다.
  • 프롭스 드릴링이라는게 정확히 무슨 뜻인가?
  • 처음 시작을 JS → React로 넘어간 이유에 대한 예시가 있었으면 좋았을 것
  • Class형 컴포넌트는 지금 사용하진 않아도 알아야한다.
    → 공식문서에도 Class를 없앨 생각이 없다고 나온다.
    Class형 컴포넌트를 알아야 함수형 컴포넌트가 왜 이렇게 만들어졌는지를 이해할 수 있다.
  • 객체 지향 언어를 이해해야한다.
  • 상태 관리 라이브러리로 리코일 도입할 예정이다.
  • 과거의 개발자들을 이해시키기 위해서는 (+설득력) 가지기 위해서는 과거의 코드를 설명할 줄 알아야한다.
    +내가 현재 사용하고 있는 언어가 왜 이렇게 발전되었는지를 알아야 동작 과정을 이해하고 성장 속도가 빨라질 것
  • 핵심은 Flux 패턴이다.
    프론트엔드에서 MV* 아키텍쳐란 무엇인가요?
    옵져버 패턴하고 비슷하다는데 옵져버 패턴이 뭐지??🤔
  • hook을 JS로 만들 수 있는가?

등등..


느낀점

머리가 띵 맞은 느낌의 피드백이었습니다...!
세미나는 개발팀원 분들 앞에서 진행하는데, 그냥 막연하게 React를 다 아시겠지?생각하면서 만들었습니다. 각자 맡은 파트가 달랐는데 말이죠ㅠㅠ
세미나 첫 장부터 상태는 뭔가요? state는 뭔가요? props는 뭔가요?? js에서 react로 넘어간 이유부터 알아야할 것 같은데요?? 라는 질문 폭격이 날아왔습니다. 세상에나... 이때부터 당황해서 멘탈이 조금 나갔어요. 자바스크립트에서 불편했던점을 예시코드로 작성하고 React로 바뀐 후에 어떻게 나아졌는지도 예시코드로 작성해줄 수 있겠냐고 했는데, 그대로 얼어버렸습니다..🤦🏻‍♀️ 그 후에 직원분들이 서로 토론을 했습니다. 이게 이말인 것 같다-라고 하면서 서로 얘기를 나누시는데, 저는 또 그분들의 말을 못알아듣겠는거죠.. 정말 신기한게 제 말은 사수인 프론트엔드 분을 제외하고는 알아듣지 못하시는데, 저를 제외한 나머지분들은 소통이 된다는 거였어요. 너무 우물안의 개구리 였다는 생각이 확 들더라고요. 그러면서 또 느꼈던것은 내가 설명하고자 하는 것들을 다른 사람들에게 설득시키기 위해서는 그 언어를 왜 쓰는지를 본질적으로 알기 위해서 기초를 정확히 알고있어야한다는 것이었습니다. 자바스크립트는 어떤 언어이고 뭐가 불편해서 리액트라는 라이브러리가 생겼는지, 그리고 객체지향언어를 바탕으로 지금 언어의 발전이 이루어진것이라고 했는데, 객체지향언어가 무엇인지에 대해서 알고있어야겠다는 생각을했습니다. 이런것들을 알게되면 개발 언어가 전체적으로 이어진다는 생각을 할 수 있을거라는 조언도 받았어요. 정말 다들 진심으로 피드백도 해주시고 조언도 해주셔서 너무 뜻깊은 시간이었어요..!!ㅠㅠ 물론 그 과정에서 조금 거칠게 말하시는 분도 계셨지만...ㅎㅎㅎ.... 덕분에 다음에는 더 잘해야겠다는 생각이 들었습니다!!!!!
(+사수님...진짜 최고ㅠㅠ 혼자였다면 너무 힘들었을 것 같은데, 사수님이 저를 많이 케어해주셔서 잘 이겨내고 있었습니다. 물론 다른 직원분들도 너무 좋아요🙏🏻 )
세미나가 끝나고 나서 추가적으로 자체 세미나 준비하는게 어떨까하는 의견도 나왔습니다(달에 1개씩이라도?). 사수님은 굳이 안하셔도 됐는데 같이 해주시겠다고 해서 너무 감동이었어요😭 일도 많은데 저때문에 자꾸 사수님의 일이 늘어나는 것 같아서 맘이 안좋지만... 신경써주신만큼 열심히 해야겠다고 다짐합니다!!🔥🔥🔥


TMI 🗣

💪🏻 수습 과제가 다 끝나면 해야될 것들

  • 네트워크에 대해 알아보기
  • 객체 지향 파보기
  • 추가 세미나 준비하기

당장은 과제부터 잘 마무리하기!!

profile
프론트엔드 개발자 가보자고~!!

0개의 댓글