210516 ReactJS TIL - 컴포넌트 폴더 구조, 해쉬태그 구현을 위한 정규표현식

React

컴포넌트 폴더 구조

컴포넌트를 작성할때 각각의 개별 컴포넌트를 하나의 개별 폴더로 분리해서 작성해주는 것이 좋다.
그 이유는 styled-components로 만든 컴포넌트들을 하나의 파일에 같이 작성을 해주게 되면 가독성에 좋지 않기 때문에 하나의 컴포넌트 폴더 내부에 index.jsstyles.js로 분리해서 styles.js 파일 내에는 styled-components로 작성해준 컴포넌트들만 따로 분리해서 작성해주도록 한다.

ex)
/components/ImagesZoom/index.js
/components/ImagesZoom/styles.js

해쉬 태그를 위한 정규 표현식

참고 사이트 : https://regexr.com/

(1) //g : g는 여러개 요소를 찾는 옵션
(2) /#./g : .은 # 뒤에 한 개 문자까지 선택
(3) /#../g : ..은 # 뒤에 두 개 문자까지 선택
(4) /#.+/g : .+는 # 뒤의 모든 문자들을 선택
(5) /#[^\s#]+/g : 공백(\s)을 제거하고 다음 해쉬 태그 기준으로 끊기

split 메서드와 함께 사용하는 경우에는 아래와 같이 정규표현의 조건부를 괄호로 감싸줘야 한다.

1
2
3
/(#[^\s#]+)/g;

'첫 번째 게시글 #해시태그 #익스프레스'.split(/(#[^\s#]+)/g);

210514 React TIL - ContextAPI의 사용, Custom Hook(useContext + React.createContext), Strict mode, action 객체와 reducer 함수의 작성, React에서 상태관리의 위치

20210418 Update - Custom hooks - context.js
20210514 Update - Custom hooks(input)

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

실습 Repository : https://github.com/LeeHyungi0622/react-basic-projects/blob/master/sidebar-modal/src/context.js

ContextAPI의 사용 & Custom Hook(useContext + React.createContext)

여지까지 ContextAPI를 모든 하위 컴포넌트들의 상위 컴포넌트에서 정의해서 사용했었는데, 이 ContextAPI부분을 별도의 파일로 정의를 해서 사용할 수 있다.
이렇게 별도의 context.js파일로 작성해서 관리를 하면 코드를 좀 더 가독성 좋게 만들 수 있는 것 같다.

그리고 ContextAPI에서 전달하는 값을 사용하는 하위 컴포넌트들에서는 매번 useContext를 import해서 인자로 React.createContext() 객체를 인자로 넣어서 값을 호출해서 사용하였는데 이 부분은 별도의 custom Hook으로 정의해서 사용을 할 수도 있다.

(custom Hook을 만들어서 사용할때에는 함수의 이름에 접두사 use를 꼭 붙여줘야 한다.)

context.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import React, { useState, useContext } from 'react';

const AppContext = React.createContext();

const AppProvider = ({ children }) => {
// modal, sidebar state
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);

const openSidebar = () => {
setIsSidebarOpen(true);
};

const closeSidebar = () => {
setIsSidebarOpen(false);
};

const openModal = () => {
setIsModalOpen(true);
};

const closeModal = () => {
setIsModalOpen(false);
};

return (
<AppContext.Provider
value={{
isModalOpen,
isSidebarOpen,
openModal,
openSidebar,
closeModal,
closeSidebar
}}
>
{children}
</AppContext.Provider>
);
};

// Custom Hook(useContext + AppContext)
export const useGlobalContext = () => {
return useContext(AppContext);
};

// index.js에서 최상위 App.js를 감싸주기 위해 AppProvider를 export해준다. (index.js)
export { AppProvider };
Read more

210505 screen.getBy*와 getBy의 차이, findBy*/getBy*/queryBy* method의 차이


테스트 코드를 작성하던 도중에 헷갈렸던 개념들에 대해서 반복학습을 위해 포스팅을 해두려고 한다.

screen.getBy vs getBy

screen을 사용하는 이점은 필요한 query를 추가할 때 더 이상 render 함수로 컴포넌트를 렌더링해서 최신 상태를 유지 하지 않아도 된다는 점이다.
beforeEach()함수 내에서 공통적으로 컴포넌트를 렌더링했다면, 이하의 test 블럭에서는 screen을 통해 렌더링된 컴포넌트를 통해 테스트 코드를 작성할 수 있다.

findBy* / getBy* / queryBy* method의 차이

  • getBy*

    조건에 맞는 요소를 찾았을때 query에 일치하는 노드를 반환
    조건에 맞는 요소를 못 찾았을때 에러를 반환
    → 존재하지 않는 DOM element를 assert할때에는 try-catch문으로 발생한 에러를 별도의 함수에 담아 tobedefined() matcher로 확인을 해야 한다. 이 경우에는 queryBy*를 사용해서 assert하는 것이 좋다.

  • queryBy*

    조건에 맞는 요소를 찾았을때 query에 일치하는 노드를 반환
    조건에 맞는 요소를 못 찾았을때 null을 반환
    → 존재하지 않는 DOM element를 assert할때 유용하다.

  • findBy*

    조건에 맞는 요소를 찾았을때 resolved Promise 반환
    조건에 맞는 요소를 못 찾았을때 rejected Promise 반환

    기존의 waitForElement()함수는 이제 deprecated되었기 때문에 findBy* 함수를 사용해야 한다.

    1
    2
    3
    4
    5
    6
    test('nowPlaying API method 테스트 - 데이터가 문제없이 화면에 로드되는지 확인', async () => {
    const { findByTestId } = render(<Movie />);

    await findByTestId('loading-text');
    await findByTestId('contents-list');
    });

210504 Error) You should not use Link outside a <Router>


<StaticRouter>와 <MemoryRouter>를 사용

이번에 컴포넌트 단위로 단위테스트를 하던 중에 You should not use XXX and <withRouter> outside a <Router>에러를 접했다.
Router의 내부에서만 사용할 수 있는 <withRouter>와 <Link>를 사용한 컴포넌트를 테스트를 할때에는 개별 컴포넌트만 호출해서 렌더링한 후에 테스트를 진행하기 때문에 실제로는 Router 내에 구현이 되어있다고 하더라도 위와같은 에러가 발생하게 되는 것이다.

위의 에러에 대한 해결책은 테스트 코드에서 <MemoryRouter> 또는 <StaticRouter>를 사용해서 테스트하려는 컴포넌트를 감싸서 렌더링해주면 된다.
Header.test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import Header from './Header';
import { MemoryRouter } from 'react-router';



describe('header 컴포넌트 내부의 메인 메뉴관련 테스트 코드', () => {
let header;
beforeEach(() => {
header = render(<MemoryRouter><Header/></MemoryRouter>)
});

describe('메뉴 클릭시 제대로 연결된 페이지가 렌더링되는지 확인하는 테스트 코드', () => {
test('Movie 메뉴 클릭시 Movie page component가 렌더링된다.', () => {
const { getByTestId } = header;
expect(getByTestId('movie-menu')).toBeTruthy();
fireEvent.click(getByTestId('movie-menu'));
expect(screen.findByTestId('movie-page-container')).toBeInTheDocument;
});
......

210429 React TIL - 프론트엔드 개발시 dummy 데이터 활용, Container - Presenter 구조, Re-rendering과 태그내에서의 인라인 스타일링, 함수형 컴포넌트에서의 리렌더링, eslint 설정

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

프론트엔드 개발시 dummy 데이터 활용

만약 백엔드가 개발이 안된 상태에서 프론트엔드 개발을 진행해야 되는 경우라면 더미 데이터를 활용해서 우선적으로 프론트엔드 개발을 하도록 한다.
예를들어 로그인 화면을 만드는데, 로그인과 로그아웃등 상황에 따른 다른 프론트 페이지를 만들어야 한다면, 이 경우에 간단하게 더미로 상태 데이터를 만들어서 화면을 만들도록 한다.
실무에서는 Redux나 Mobx를 사용해서 dummy state를 만든다.

Container - Presenter 구조

이전에 클래스 컴포넌트로 작성을 했을때는 데이터를 받아서 로직을 처리하는 부분(Container)과 화면에 출력해주는 부분(Presenter)을 나눠서 구조를 짰는데, hooks가 도입된 이후에는 위와같이 구조를 나누는 것을 추천하지 않는다고 한다.

반복되는 작업은 처음만 수작업으로, 그 다음부터는 라이브러리 사용

수작업으로 만드는 하나 하나 만들어보는 것도 의미가 있다. 하지만 매번 하나 하나 만들어보는 것보다는 라이브러리를 사용해보는 연습을 하는 것도 중요하다. 실제 실무에서 모든 것을 수작업으로 만들지는 않기 때문이다.

자식 component에 props로 넘겨주는 함수의 경우에는 반드시 useCallback으로 감싸서 처리한다.

Re-rendering과 태그내에서의 인라인 스타일링

React에서는 VirtualDOM을 검사해서 이전 VirtualDOM 상태와 달라지는 부분이 있다면 해당 부분만 다시 렌더링한다. 렌더링을 최적화 시키기 위해서는 태그 내에서 인라인 스타일링하는 것을 지양해야 한다. 왜냐하면 태그내에서 스타일링을 해주는 경우 스타일을 객체 형태로 정의하게 되는데, 객체는 주소참조로, 값이 같더라도 서로 비교를 하면 다른 값으로 인식된다.
따라서 styled-components를 사용해서 스타일을 적용한 개별 컴포넌트로 생성을 하거나 useMemo를 사용해서 style을 별도로 정의해서 인라인 스타일로 정의해야 한다. useMemo는 값 자체를 캐싱하기 때문에 deps에서 설정한 값이 바뀌지 않는 한 재사용을 한다.

1
2
3
4
5
import styled from 'styled-components';

const wrapper = styled.div`
margin-top: 10px;
`;
1
2
const style = useMemo(() => ({ marginTop: 10 }), []);
<div style={ style }>
Read more

210428 React TIL - React router, Dynamic route matching, props의 history, location, match 객체, QueryString과 URLSearchParams, render props, Switch와 exact

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

React Router

React router의 React에서의 역할과 Website 개발시에 어떤 역할을 하는지에 대한 내용을 중점으로 내용을 정리해보려고 한다.

1
$ npm i react-router react-router-dom

react-router는 web/app에서 react-router를 사용하기 위한 기본 뼈대라고 이해하면 된다. 그리고 react-router-dom가 우리가 웹 개발시에 실제로 사용하는 것으로 react-router는 react-router-dom의 내부적으로 사용되는 것이다.

Read more

210415 React TIL - useReducer, setState의 비동기적 처리, useReducer에서 dispatch를 통한 state 업데이트(비동기 처리), 성능 최적화

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

useReducer

React에서 Redux의 핵심 기능을 useReducer로써 구현하였다. useReducer + context API의 조합으로 Redux를 대체할 수 있을 것이라고 생각할 수 있다. 작은 앱의 경우에는 괜찮지만, 비동기 처리의 한계로 어플리케이션의 규모가 커지게 되면 Redux를 사용해서 구현하는 편이 좋다.
state의 갯수가 늘어나게 되면, useState를 통한 state관리 코드가 많아진다. 이러한 경우 useReducer를 사용해서 하나의 state와 setState로 통합해 줄 수 있다.

state 객체는 아무도 직접적인 수정이 불가하다. 다만 dispatch를 통해 action object를 인수로써 호출하게 되면 action object의 type에 정의된 action의 종류에 따라 reducder 함수에서 정의된 state 업데이트 방식에 의해 state가 업데이트된다.

1
2
3
4
5
// useReducer의 첫 번째 인수로는 정의한 reducer 함수를 넣어주고, 두 번째 인수로는 state 초기 상태를 정의해준다.
const [state, dispatch] = useReducer(reducer, initialState);

// event 처리부분
dispatch({ type: 'SET_RESULT', result: 'ABC' });

reducer 함수 예시

1
2
3
4
5
6
7
8
9
const reducer = (state, action) => {
switch (action.type) {
case 'SET_RESULT':
return {
...state,
result: action.result
};
}
};

나중을 위해 action의 이름은 개별 상수로 따로 빼서 관리하도록 한다.
추가적으로 관리해야 될 state가 있다면 action을 추가하고 해당 action에 대한 구체적인 state 업데이트 방식을 reducer 함수에 정의하도록 한다.

기존의 state를 업데이트할때 사용할 상태값은 reducer 함수의 두 번째 인자(action)로부터 넘겨준다.

(Redux는 Facebook의 Flux와 함수형 프로그래밍에 영감을 받아 2015년 6월경에 Dan Abramov에 의해 개발되었다)

Read more

210413 React TIL - PureComponent와 React.memo, useMemo와 useCallback, 자식 컴포넌트의 props로 함수를 넘겨줄때, hooks의 순서

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

PureComponent와 React.memo

클래스형 컴포넌트에서는 PureComponent를 상속받아서 컴포넌트 최적화를 시켜줄 수 있고, 함수형 컴포넌트에서는 함수몸체를 React.memo로 감싸서 PureComponent와 같이 컴포넌트를 최적화 시켜 줄 수 있다. 이렇게 컴포넌트로 다른 컴포넌트를 감싸는 것을 HOC(Higher Order Component라고 한다.

useCallback과 useMemo의 비교

useCallback은 함수자체를 기억하고, useMemo는 함수의 반환값 자체를 기억한다.
함수형 컴포넌트의 경우에는 재실행될 때 함수 전체가 재실행되기 때문에 한 번 생성한 함수를 재생성하지 않고 재활용하기 위해서는 useCallback을 사용한다.
useCallback의 내부에서 사용되는 state 값이 있는 경우에는, 반드시 두 번째 인자인 []내에 작성해줘야 한다.

이렇게 되면 비효율적이기 때문에 특정 값이 변경될때에만 해당 함수가 호출되어 초기 상태값을 업데이트 할 수 있도록 useMemo를 사용해서 작성해줘야 한다.
만약 특정 함수로부터 반환받은 값을 상태의 초기값으로 사용하는 경우, 함수 자체를 useState의 초기값에 넣어주게 되면, 해당 함수는 함수 컴포넌트가 실행될 때마다 재생성된다. 따라서 아래와 같이 useMemo를 사용해서 상태값을 갱신해서 사용해야 한다.

Read more

210412 React TIL - setTimeout(), useEffect()/useMemo()/useCallback(), useState와 useRef의 차이, jsx에서 if/for문 작성, React class/function - 라이프 사이클

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

React에서 jsx를 구조있게 작성하기

필요에 따라 jsx 작성 부분이 조건문이나 반복문으로 복잡해진다면, 별도의 함수나 자식 컴포넌트로 작성해주도록 한다.

setTimeout() 사용법

setTimeout의 첫 번째 인수로는 콜백 함수를 넣어주고 두 번째 인수로는 ms단위로 interval time을 적어준다. setTimeout()은 비동기 함수이기 때문에 외부에 선언한 변수를 참조할 경우, 클로저 문제가 생긴다.
따라서 setTimeout()내에서 사용할 변수의 경우에는 setTimeout() 내에서 처리하도록 한다.
setTimeout()을 해줬다면 clearTimeout()을 해줘야 한다. 만약 clearTimeout()을 해주지 않으면, setTimeout()을 처리한 컴포넌트가 제거되더라도 call stack에 남아 계속 실행될 수 있다.

Read more

210411 React TIL - 상태의 불변성, React DevTools, 배포모드, 성능에 대한 이야기, React.createRef, props와 state 연결하기

React

본 포스팅 내용은 과거에 개인적으로 공부할때 정리했던 ReactJS의 내용을 복습의 목적으로 다시 정리하는 포스팅입니다.

상태의 불변성

push를 이용해서 컴포넌트의 상태를 직접 바꿔주게 되면, React가 구체적으로 무엇이 바뀌었는지 감지하지 못한다. 따라서 원본은 그대로 두고, 바뀌는 상태 데이터는 원본을 복사해서(얕은 복사) 상태를 업데이트 해주도록 한다.

React의 Rendering 기준은 이전 상태값과 현재 상태값이 다른 경우 Re-rendering을 해준다.

  • arr1 === arr2 // false (Re-rendering) - 이전 상태와 현재 상태가 같지 않은 경우만 렌더링
  • arr1 === arr2 // true

내부에서 this를 사용하지 않는 함수의 경우에는 외부로 빼주도록 한다.

setState에서의 함수형 업데이트

상태를 업데이트 해주는 setState 함수는 비동기 방식이기 때문에 setState함수를 여러번 연달아 사용하는 경우 문제가 생길 여지가 있다. 따라서 setState 내부에서 또 다른 상태값을 참조해서 상태를 업데이트 시키는 경우에는 함수형으로 이전 상태값을 받아서 새로운 상태로 업데이트시켜줘야 한다.
(class component, functional component(hooks) 둘 다 )

1
2
3
const [tries, setTries] = useState([]);

setTries((tries) => [...tries, { try: value, result: 'good!' }]);
Read more