patch-package

서드파티 라이브러리를 커스텀한 상태가 배포상태에서도 지속되도록 관리해주는 패키지.

즉, node_modules에 수정한 사항이 git으로 관리되고 어떠한 실행 환경에서도 적용되도록 한다.

patch-package를 사용할 시 매번 버전이 달라질 때마다 patch 파일의 버전을 인스톨 되어 있는 버전과 맞춰줘야 하는 단점이 있다.

 

1. 설치

npm install —save-dev patch-package

 

2. 설정

// package.json

"scripts": {
	"postinstall": "patch-package", // 해당 스크립트 추가
},

 

 

3. 변경하고자 하는 패키지 코드 수정

패키지 코드를 원하는대로 수정한다.

 

 

4. patch 적용

npx patch-package [변경한 패키지 이름]

 

5. “patches”라는 폴더가 생기고, 폴더 안에 변경한 패치 내용이 저장되어 있음을 확인

 

 

6. node_moduels 을 지웠다가 npm install 해보면 자동으로 패치내용들이 적용되어 있음

 

주의) 수정한 패키지의 patches 버전과 package.json의 패키지 버전이 다르면 에러를 유발하니 버전 체크하는 것이 중요!

반응형

서론

우선 리액트 공식 문서에서 리액트를 '사용자 인터페이스를 구축하기 위한 선언적이고 효율적이며 유연한 JavaScript 라이브러리'로 소개하고 있다. SPA(Single Page Application)형태로 하나의 페이지에서 보여지는 컴포넌트 조합을 동적으로 바꿔가며 화면을 표현하는 형태다. Virtual DOM이라는 개념을 사용해 이러한 SPA를 구현하고 있다. 장, 단점이 존재하지만 자세한 내용은 나중에 다루도록 하겠다.

 

React 특징

1. Component 구조

2. JSX

3. Data Flow

4. Virtual DOM

 

 

1. Component

"컴포넌트"라고 불리는 작고 고립된 부품을 이용해 복잡한 UI를 구성하도록 해주는 것이 리액트이다. 컴포넌트에는 몇 가지 종류가 있다. 크게 2가지로 나누자면 '함수형 컴포넌트', '클래스형 컴포넌트'가 있다. 우선 클래스형 컴포넌트는 다음과 같은 구조를 가지고 있다. 아래에 간단한 예시를 작성해봤다.

class MyClassComponent extends React.Component {
    render() {
    	return(
            <div className="my-component">
            	<h1>Hello World!</h1>
                <ul>
                    <li>React!</li>
                    <li>React Native</li>
                </ul>
            </div>
        );
    }
}

이러한 컴포넌트는 재사용 가능하고 여러 컴포넌트들을 조합해 다양한 형태의 UI를 쉽게 만들 수 있다. 불필요한 반복적 코드를 줄이고 빠르게 UI를 구성할 수 있어 개발 생산성에 있어 장점을 가진다. 또한 작은 형태의 컴포넌트는 테스트하기 용이해 코드를 유지보수하기에도 도움이 된다. 아래에 여러 작은 컴포넌트를 조합한 예시 코드가 있다.

class App extends Component {
  render() {
    return (
      <Layout>
        <Header />
        <Navigation />
        <Content>
          <Sidebar></Sidebar>
          <Router />
        </Content>
        <Footer></Footer>
      </Layout>
    );
  }
}

 

 

2. JSX

리액트에서는 HTML의 태그 문법과 비슷한 형태로 ui 화면을 표현하고 있다. HTML과 비슷하지만 HTML과는 다르다. 리액트에서는 JSX라 하는 JavaScript를 확장한 문법을 사용한다. 간단하게 JavaScript이지만 화면을 표시하기 위해서 조금 더 확장된 개념이라고 보면 된다. 

JSX란?
Javascript를 확장한 문법입니다.

React에서는 이벤트가 처리되는 방식, 시간에 따라 state가 변하는 방식, 화면에 표시하기 위해 데이터가 준비되는 방식 등 렌더링 로직이 본질적으로 다른 UI 로직과 연결된다는 사실을 받아들입니다.

React는 별도의 파일에 마크업과 로직을 넣어 기술을 인위적으로 분리하는 대신, 둘 다 포함하는 “컴포넌트”라고 부르는 느슨하게 연결된 유닛으로 관심사를 분리합니다. 이후 섹션에서 다시 컴포넌트로 돌아오겠지만, JS에 마크업을 넣는 게 익숙해지지 않는다면 이 이야기가 확신을 줄 것입니다.

React는 JSX 사용이 필수가 아니지만, 대부분의 사람은 JavaScript 코드 안에서 UI 관련 작업을 할 때 시각적으로 더 도움이 된다고 생각합니다. 또한 React가 더욱 도움이 되는 에러 및 경고 메시지를 표시할 수 있게 해줍니다.

출처: React 공식문서

 

 

3. Data Flow - 단방향 데이터 바인딩

단방향 데이터 바인딩은 간단히 말해 데이터의 흐름이 한 방향으로만 이뤄진다. 부모 요소에서 자식 요소로 전달이 되고 적절한 Event를 통해 데이터를 갱신하게 된다. 양방향 바인딩은 앱의 규모가 커질수록 추적하기 어려워지고 그에 따라 복잡해지게 된다. 이런 단점을 보완해 데이터 흐름을 보다 예측 가능하도록 하고자 리액트에서 단방향 형태로 바인딩을 하도록 했다고 한다. 단방향, 양방향 각각 장, 단점이 존재하기에 사용하는 프레임워크나 라이브러리에 따라 사용되는 데이터 바인딩 형태도 다 다르다. 조금 더 깊은 내용은 검색을 하면 많이 나오니 참고하면 될 것 같다.

 

 

4. Virtual DOM

브라우저는 화면을 그리기 위해서 DOM(Document Object Model)이라는 개념을 사용한다. DOM은 HTML 파일 내용을 토대로 만들어지는데, JavaScript와 같은 스크립팅 언어로 수정할 수 있도록 만들어진, 웹 페이지의 객체 지향 표현이다. DOM은 브라우저가 화면을 그리기 위해서 필요한 정보가 트리 형태로 저장된 데이터다. DOM에 변화가 생기면 렌더 트리를 재생성하고 레이아웃을 만들고 다시 보여주는 과정이 반복된다. DOM 트리가 재생성되는 과정에서 모든 요소들이 다시 계산되고 많은 연산을 반복하게 되면서 비효율적이게 된다. React에서는 이런 불필요하고 비효율적인 방식을 개선하고자 Virtual DOM이라는 개념을 도입했다.

React에서 Virtual DOM은 UI의 이상적인 또는 가상의 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 실제 DOM과 동기화하는 프로그래밍적 개념이다. 뷰에서 변화가 생기면 실제 DOM에 적용되기 전 가상 DOM에서 먼저 적용이 되고 비교를 통해 최종적 결과가 실제 DOM으로 전달이 된다. 이런 DOM 관리 과정을 리액트 내에서 자동화, 추상화를 통해 불필요한 연산 비용을 줄이고 성능을 높이도록 해준다.

 

참고자료: 리액트 공식문서 - Virtual DOM

 

반응형

2021년이 어느덧 지나가고 2022년을 맞이하면서 회고를 작성하게 되었다. 처음으로 작성해보는 회고록이라.. 어떤 내용으로 채워나가야 할지 모르겠다. 작년에 있었던 일들을 하나씩 떠올리며 글을 작성하려고 한다.

 

 

 

불행 중 다행?

작년 1월, 2월 졸업을 앞두고 취직을 하기 위해 이곳저곳 이력서를 제출하고 면접을 보러 다니며 바쁘게 보냈다.

기억으로는 40~50개 정도 지원했던 것 같다. 친구들은 전공을 살려 취직을 했고, 나는 조금은 다른 길인 개발자로 진로를 정하면서 불확실성에 대한 두려움이 많았었다. 그래도 처음으로 내가 하고 싶었던 일이었고 지금이 아니면 계속해서 수동적으로 삶을 살 거 같아 공부를 열심히 했던 기억이 있다.

 

지원했던 회사들 중 몇 군데 면접 제의가 와서 서울과 대구를 왕복하며 면접을 보러 다녔다. 그렇게 첫 회사에 취직을 하게 됐다. 처음에는 원래 이런 식으로 일을 하는 것인가 싶었다. 아직 아무것도 모르는 상태였으니, 그냥 그런가 보다 하며 회사 생활을 했다. 동료 직원 분들도 다 좋으신 분들이었고 잘 지낼 수 있어 재밌었다. 하지만 회사가 경영적으로 문제가 있었는데.. 이 문제로 난 이직을 하게 됐다.(지금 생각해도 문제가 심히 많았다..) 첫 사회생활? 경험을 했다고 생각하고 빠르게 이직을 했다.  다른 분들도 다 이직을 하셨다고 알고 있다. ㅎ

 

 

새로운 회사, 첫 이직 도전!

2021년 6월, 첫 이직이자 나의 2번째 회사인 '반려생활'로 오게 됐다. 뭔가 생각했던 이직 과정보다 후다닥 빠르게 진행됐었다. 서류를 넣고 면접을 2번을 보고 합격하기까지 1주일 안에 이뤄졌다. 처음에는 솔직히 느낌이 살짝 싸했었다..ㅎㅎ 생각보다 너무 쉽게 이직을 해버려서 다들 이런가 하는 생각이 들었다. 아무튼, 성공적인 이직을 하고 현재까지 열심히 일 하고 있다!

 

처음 한 달 정도는 파일럿 과제를 수행하고 강의를 들었다. 로그인 페이지를 만드는 것이었는데 React와 TypeScript를 사용했다. 그리고 클래스형을 사용해 객체 지향 형태로 코드를 작성했었다. 간단할 줄 알았는데.. 쉽지 않았다.. 우선은 class 사용에 익숙하지 않았고 TypeScript 또한 처음으로 사용해봤기 때문이다. 이 과제를 수행하면서 레이아웃과 아키텍처에 대한 필요성을 알게 됐고, 그 외에 정말 많은 것들을 알게 됐다. 내가 여태 했던 것들은 정말 막? 하는 것이었던 걸.. 알 수 있었다. 아직까지도 갈 길이 멀다 ㅜㅜ

 

입사한 지 2달쯤 돼서 첫 일을 하게 됐다. React Native로 만든 앱 프론트 단을 담당하게 됐다. 간단한 것부터 시작해 조금씩 더 많은 작업들을 해나갔다. 일을 하면서 느낀 것들이 정말 많다. 그중 하나가 코드를 작성할 때 고려해야 할 것들이 정말 많고, 단순히 코드를 작성할 줄 안다고 일을 잘하는 게 아니라는 것이다. 

 

React Native를 사용하면서 가장 답답했던? 부분이 자료나 예시를 찾기가 쉽지 않았던 것이다. 관련 라이브러리나 자료들이 업데이트가 엄청 많았고 그래서 사용하던 기능들 중 바꿔야하는 부분들이 생각보다 많았었다. 뭐가 이리도 빠르게 바뀌는지.. 그래도 코드를 작성하고 배포, 빌드, 테스트까지 직접 해볼 수 있었고 그런 것들이 많은 공부가 됐다. 배포하니까 생각난 건데 안드로이드 배포할 때 구글 정책 때문에 reject을 진짜 수 없이 당했다..하.. 처음 2~3번은 그럴 수 있지 하다가 어느새 짜증이 났었다ㅎ

거절 사유에 대한 명확한 정보를 주지 않고 거절만 하니 정말 답답했었다. 불과 몇일 전인 12월에도 새 버전이 같은 정책 사유(유저가 직접 콘텐츠를 만들고 참여하는 부분이 있으면, 유저나 콘텐츠에 대해 신고/차단 기능이 있어야 한다)로 거절당했다. 을의 입장이니 어쩌겠나.. 따라줘야지..ㅜㅜ 그래도 덕분에 '이런 문제도 고려해야 하는구나'  알게 됐으니 좋게 생각하고 있다.

 

 

1년간의 서울살이..그리고 2022년!

서울에 올라와 많은 일도 있었고 좋은 사람들도 많이 만났던 한 해였다. 좋은 기억들만 남기고 새해에는 더 알차게 보내고자 한다. 2022년에는 해보고 싶은 일들을 다 진행하고 또 많은 사람들도 만났으면 좋겠다. 그리고 개발자로서 자기개발도 꾸준히 하고 계획한 것들을 착실하게 이루면서 인생에 있어 중요한 시점이 되는 한 해가 되길 바란다. 내년 이맘때쯤 작성할 새로운 회고록에 좋은 것들만 있었으면 좋겠다. 돈도 많이 벌고 여행도 많이 다닐거다!!

 

반응형

리액트에서 테스트를 위해서 많이 사용하는 도구 중 하나인 React Testing Library에 대한 포스트입니다.

테스트에 사용되는 라이브러리들이 많으니 각자 본인에게 맞는 것을 활용해 공부하며 적용해 보는 것이 좋을 것 같습니다.

아래에 리액트 공식 사이트에서 사용되는 테스트 유틸 및 라이브러리 예시를 확인해보세요.

  • Enzyme: React를 위한 JavaScript 테스트 유틸.
  • Jest: React를 포함한 JavaScript 테스트 프레임워크.
  • react-testing-library: 가벼운 React DOM 테스트 유틸.
  • React-unit: React를 위한 가벼운 단위테스트 라이브러리.
  • Skin-deep: 얕은 렌더링을 지원하는 React 테스트 유틸.
  • Unexpected-react: React 컴포넌트와 이벤트를 발생시켜주는 플러그인.

설치

yarn add @testing-library/react @testing-library/jest-dom

npm install --save react-testing-library @testing-library/jest-dom

npm 또는 yarn을 이용해 react-testing-library를 설치한다. react-testing-library@testing-library/react 로 변경되었으니 주의!

jest-dom@testing-library/jest-dom 으로 변경됐다고 한다.

react-testing-library는 '사용자 관점'에서 테스트를 진행한다. 보통 Enzyme를 이용한 테스트 방식은 상태값, 상태 변수에 대해 테스트를 한다. 하지만 전자는 상태 관리는 컴포넌트의 구현 세부사항일 뿐이다. 즉, 상태 변수가 언제든 다른 컴포넌트로 옮겨지거나, react에서 vue.js로 바뀌더라도 테스트에는 문제가 없어야한다.

다양한 쿼리

  • getBy* 쿼리 (ex. getByTestId, getByText, getByRole): 이 함수들은 동기적(synchronous)이며 그 요소가 현재 DOM 안에 있는지 확인한다. 즉, 현재상태만 확인한다. 그렇지 않으면 에러를 발생시킨다.
  • findBy* 쿼리 (ex. findByText): 이 함수들은 비동기적(asynchronous)이다. 그 요소를 찾을 때까지 일정 시간(기본 5초)을 기다린다. 만약 그 시간이 지난 후에도 요소를 찾을 수 없으면 에러를 발생시킨다.
  • queryBy* 쿼리: 이 함수들은 getBy* 처럼 동기적이다. 하지만 요소를 찾을 수 없어도 에러를 발생시키지 않는다. 단지 null 값을 리턴한다.

테스트 디버깅하기

render(<SomeComponent />);
screen.debug(); //원하는 실행 지점에서 DOM트리를 console에 출력해, 디버깅할 수 있다.

screen.debug() 는 브라우저의 개발 도구처럼 편리하고 상호작용적이진 않지만, 테스트 환경에서 어떤 일이 일어나고 있는지 확실히 알 수 있도록 도와준다.

const link = screen.getByRole('link', { name: /how it works/i });
screen.debug(link);

위와 같이 debug 함수에 파라미터를 전달하면 해당 요소만을 콘솔에 출력해준다.

Mocking

  • jest.fn: Mock a function
  • jest.mock: Mock a module → 자동적으로 모듈의 모든 함수를 mocking 해준다.
  • jest.spyOn: Spy or mock a function → 마찬가지로 모든 함수를 mocking 해주면서, 원래의 함수를 다시 복원할 수도 있다.

가장 기본적인 사용 방식은 함수를 mock 함수로 재할당하는 것이다. 재할당 된 함수가 쓰이는 어디서든지 mock 함수가 원래의 함수 대신 호출 될 것이다.

예시
  1. jest.fn() mocking
// app.js
import * as math from './math.js';

export const doAdd      = (a, b) => math.add(a, b);
export const doSubtract = (a, b) => math.subtract(a, b);
export const doMultiply = (a, b) => math.multiply(a, b);
export const doDivide   = (a, b) => math.divide(a, b);
// math.js
export const add      = (a, b) => a + b;
export const subtract = (a, b) => b - a;
export const multiply = (a, b) => a * b;
export const divide   = (a, b) => b / a;
// app.test.js

import * as app from "./app";
import * as math from "./math";

math.add = jest.fn();
math.subtract = jest.fn();

test("calls math.add", () => {
  app.doAdd(1, 2);
  expect(math.add).toHaveBeenCalledWith(1, 2);
});

test("calls math.subtract", () => {
  app.doSubtract(1, 2);
  expect(math.subtract).toHaveBeenCalledWith(1, 2);
});

  1. jest.mock() mocking
jest.mock('./math.js');

위의 코드로 mocking하는 것은 본질적으로 아래 코드처럼 하는거랑 같다.

export const add      = jest.fn();
export const subtract = jest.fn();
export const multiply = jest.fn();
export const divide   = jest.fn();
// app.test.js

import * as app from "./app";
import * as math from "./math";

// Set all module functions to jest.fn
jest.mock("./math.js");

test("calls math.add", () => {
  app.doAdd(1, 2);
  expect(math.add).toHaveBeenCalledWith(1, 2);
});

test("calls math.subtract", () => {
  app.doSubtract(1, 2);
  expect(math.subtract).toHaveBeenCalledWith(1, 2);
});
반응형

+ Recent posts