JavaScript/React

백엔드 개발자의 리액트 입문: Quick Start React

ri5 2024. 3. 17. 22:14

시작하게 된 계기

 

이전에도 웹을 어느정도 개발 해왔기 때문에 jquery와 thymeleaf와 같은 도구를 활용하여 쉽게 개발을 해왔었지만 리액트나 vue와 같은 프론트 기술스택을 활용을 해야될 때에는 만들어진 프로젝트 위에 하드 코딩을 하거나 프론트 개발자를 구해 개발을 했어야 했다.

 

그래서 내 마음 한켠에서는 항상 불편함이 자리 잡고 있었고. 프론트 개발을 제대로 하지 못한다는 자책감을 가지게 되었다. 그리고 시시각각 변하는 프론트엔드의 시장에서의 나는 점점 프론트엔드 개발자들과의 소통이 더욱 어려워지기 시작했고 요구사항에 맞춰 소프트웨어를 함께 설계하고 개발해 나가는 과정 속에서 지속적인 커뮤니케이션의 어려움을 느끼며 큰 부담으로 다가오게 되었다.

 

그렇게 그런 생각이 희미해져 갈 때 쯤 회사에서 갑작스럽게 회사 경영 방향에 변경이 생기면서 백엔드 팀에 갑작스러운 회의가 열렸었다. 거기서 CTO가 이런 말을 해줬었는데 "너희는 백엔드 엔지니어라는 포지션을 담당하고 있지만 모든 엔지니어는 프로덕트 엔지니어다. 프론트(React나 Vue)가 필요하다면 프론트를 할 수 있어야 한다. 풀스택으로 개발하게 되면서 백엔드에 존재하는 장점을 프론트를 개발할 때 활용 할 수 있고 프론트에 존재하는 장점을 백엔드에 활용할 수 있게 된다."라는 이야기를 듣고 난 뒤 프론트를 공부해야겠다는 생각이 머리에 떠나질 않았고 이번에 사이드 프로젝트에 마침 프론트 개발도 필요했기에 직접 공부해보고 시도해보기로 했다.

 

리액트가 나오게 된 배경

IT에 새로 등장하게 되는 모든 기술은 특정 문제들을 해결하기 위해 새로 등장하고 그런 문제를 해결하기 위해 나온 방법들로 인해 기술의 한계점 또한 보이게 된다. 리액트 또한 예외는 아니였다. 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="addListItem">항목 추가</button>
    <ul id="list"></ul>

    <script src="app.js"></script>
</body>
</html>

 

위의 HTML을 샘플로 아주 간단한 예제를 만들려고 한다. 버튼을 클릭하면 ul태그 안에 Element들이 하나씩 추가되는 기능을 자바스크립트로 구현하려고 하면 아래와 같이 작성된다. 아래의 코드를 살펴보자

// 버튼과 리스트를 선택
const addButton = document.getElementById('addListItem');
const list = document.getElementById('list');

// 버튼에 클릭 이벤트 리스너 추가
addButton.addEventListener('click', function() {
    // 새로운 li 요소 생성
    const newItem = document.createElement('li');
    // li 요소에 내용 추가 (예: "항목")
    newItem.textContent = '새 항목';
    // 생성된 li 요소를 ul 리스트에 추가
    list.appendChild(newItem);
});

 

 

위의 코드를 살펴보면 가장 먼저 이벤트가 발생하는 버튼과 이벤트를 처리해야되는 ul Element를 가져오고 리스트를 하나씩 추가하는 것을 알 수 있다. 실제 브라우저에 동작하는 과정까지 설명하자면 아래와 같을 것이다.

  1. 브라우저에서 html을 렌더링
  2. 스크립트 태그에 있는 자바스크립트 임포트하면서 실행
  3. html에 있는 버튼 Element과 Ul Elment를 가져오고 메모리에 저장한다.
  4. 버튼 Element에 클릭 이벤트 리스너를 추가하여 해당 이벤트 발생시 등록된 이벤트를 실행한다.
  5. 버튼 클릭
  6. 이벤트 핸들러에서 등록된 클릭 이벤트를 브라우저에 전달
  7. 브라우저에서 메모리에 있는 Ul Element를 가져와서 새로운 li Element 추가

 

 

 

이벤트 핸들러의 동작방식

위와 같은 예제의 소스코드는 왠만해서는 문제가 발생하지 않을 것이고 코드를 이해하기도 편할 것이다. 하지만 이런 이벤트 핸들러가 하나의 페이지에 수백개에서 수천개가 된다면 어떻게 될까? 각각의 DOM에서 어떤 이벤트가 동작하고 어떤 순서대로 작동하는지 이해하기 어려워질 것이고 지금과 같이 웹과 웹앱 그리고 반응형 웹등의 다양한 UI 요소를 보여주기 위해서는 위와같은 방식으로는 관리하기가 쉽지 않을 것이다. 그렇다면 리액트는 이런 문제점을 어떻게 해결했길래 많은 개발자들이 사용하는 대중적인 라이브러리가 되었을까?

ORM과의 유사한  구조

orm의 구조

 

백엔드 영역에서의 데이터 관리와 프론트엔드의 DOM 관리는 서로 다른 영역이지만, 비슷한 문제에 직면해 있었다. 초기 백엔드 개발에서 는 데이터베이스에서 데이터를 가져와 객체에 매핑하는 과정은 직접적인 SQL 쿼리 실행에 대한 결과를 데이터들을 직접 객체에 매핑하는 단순한 형태로 시작되었다.

이러한 접근 방식은 처음에는 직관적이고 간단해 보였지만, 특정 데이터 요구 사항에 맞추어 쿼리를 작성하게 되면서, 재사용성이 낮은 SQL과 객체 역직렬화 코드가 점차 증가하는 문제를 일으켰고 필요한 쿼리와 객체를 찾는 데에 많은 시간이 소요되었다.

 

그리고 가장 큰 문제점 중 하나는 데이터베이스에 대한 강한 의존성으로 인해, 트랜잭션과 롤백을 처리하는 코드가 중복되어 WET(Write Everything Twice)한 코드 패턴이 반복되게 되면서 유지보수를 어렵게 만들었다.

이 문제를 해결하기 위한 하나의 방법으로, SQL 쿼리를 별도의 파일로 분리하고, Mybatis와 같은 라이브러리를 사용하여 역직렬화 코드를 추상화하는 방식이 도입하였다. 이러한 접근 방식은 코드의 중복을 줄이고, 데이터 관리를 보다 체계적으로 할 수 있도록 도울 수 있었지만, 결국 데이터 중심의 개발 방식에 대한 문제점을 해결하지 못했고 여전히 객체지향적이지 않은 코드 구조를 만들게 되어, 개발자들이 비즈니스 로직을 이해하기 어렵게 만들었다.

이에 대한 해결책으로 등장한 ORM(Object-Relational Mapping)은 데이터베이스와의 의존성을 줄이고, 객체를 중심으로 데이터를 관리할 수 있도록 하는 기술이 나오게 되면서 개발자가 데이터베이스의 데이터를 객체로 추상화하여 관리할 수 있도록 하여 비즈니스 로직과 데이터 관리 사이의 격차를 좁혔다.

프론트엔드 영역에서도 마찬가지로 DOM과의 강한 의존성으로 인해 개발의 복잡성을 높이는 문제를 일으켰고 그러한 문제를 풀기 위해 리액트는 초기에 DOM의 직접적인 조작 대신, 사용자의 상태 변경 이벤트에 따라 전체 DOM을 다시 렌더링하는 방식을 채택했다.  이러한 해결책은 DOM에 대한 의존성을 줄이는 효과적인 접근 방식이었지만, 사용자의 모든 이벤트에 대해 전체 DOM을 다시 렌더링하는 것은 성능 저하와 브라우저에 대한 부담을 줄 수 밖에 없었다.

그래서 리액트는 이 문제를 해결하기 위해 ORM에서 영속성 컨텍스트가 데이터를 관리하는 것처럼, 메모리 내에 가상 DOM을 구축하여 관리하도록 하였고, 가상 DOM은 실제 DOM과의 차이점을 최소화하면서 변경된 부분만을 식별하고 업데이트하도록 하여 리액트는 높은 성능을 유지하면서 효율적으로 DOM 업데이트를 수행할 수 있었다.

 

리액트의 단점

백엔드에서 영속성 컨텍스트의 동작구조에 제대로 이해하지 못하고 사용하면 문제가 되듯이 리액트 또한 가상돔과 변경을 탐지하는 Diffing 의 개념을  제대로 이해하지 못한다면  불필요한 리렌더링이나 잘못된 컴포넌트 트리로  인한 성능 저하가 발생할 수 있다. 비록 Virtual DOM이 성능 최적화를 많이 돕지만, 개발자가 구조를 성능을 신경 써서 관리해야 하는 부분이 많다.

 

그리고 클라이언트 사이드로 개발을 하게 될시 브라우저가 가상돔에서 모든 돔객체를 렌더링하기 전에 검색 엔진 크롤러가 미완성된 컨텐츠들을 크롤링하면서 SEO의 성능 또한 저하될 수 있다는 단점을 가지고 있다.

 

리액트 환경 세팅

현재 환경

  • 운영체제: MacOS
  • 패키지 관리자: Home Brew

NVM 다운로드 및 설정

리액트는 Node.js 라는 런타임 환경에 동작하기 때문에 Node.js를 다운로드 해야한다. 하지만 개발하다보면 여러개의 Node.js 버전을 활용할 때가 종종 발생하는데 NVM은 이런 변동적인 노드환경을 지원해주는데 매우 도움을 준다.

 

1. NVM 다운로드

brew install nvm

 

2. NVM 설정

brew를 통해 nvm을 다운로드하게 되면 nvm이 터미널 환경에 동작하도록 쉘스크립트를 입력해줘야 한다고 아래와 같이 설명해준다.

다운로드 후 설정하라는 메시지

 

 

a. nvm 디렉토리 생성

 mkdir ~/.nvm

 

b. 아래에 쉘 프로필이 어떤 형태로 되어 있는지 확인 후 추가합니다

cd ~ # 기본 위치로 변경
ls -a # 파일 전체 조회
vi .zprofile # 프로필 파일 수정

아래의 스크립트를 입력하고 wq를 통해 저장합니다.

export NVM_DIR="$HOME/.nvm"
  [ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"  # This loads nvm
  [ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"

 

c. 소스 적용

source .zprofile

 

d. 최신 LTS 버전 다운로드

nvm install --lts

LTS 버전은 간단하게 설명하자면 다른 버전과 다르게 지원 기간이 긴 버전을 의미한다. 오랫동안 지원하는 버전인 만큼 안정적인 버전이라고 생각하면 좋다.

 

리액트 프로젝트 생성

1. 리액트 프로젝트 생성

아래와 같은 커맨드로 리액트 프로젝트를 생성하면 클라이언트 사이드 기반의 리액트 웹 프로젝트가 생성되며 `quick-start-react`이름의 디렉토리 밑에 리액트를 사용하는데 필요한 디펜던시들이 같이 다운로드 된다. 버전을 명시하고 싶다면 골뱅이와 함께 아래와 같이 버전을 추가하면 된다. 

npx create-react-app quick-start-react

 

npx create-react-app@3.4.1 my-app

 

그외에도 넥스트나 개츠비와 같은 앱과 같이 만드는 방법들도 존재한다.

npx create-next-app@latest # 넥스트 프로젝트 생성

npx create-remix # 리믹스 프로젝트 생성

npx create-gatsby # 게츠비 프로젝트 생성

npx create-expo-app # 엑스포 앱 생성

 

2. 리액트 프로젝트 실행

cd quick-start-react
yarn start # 리액트 실행

 

 

정리하면서

지금까지 개발자의 생산성을 올리기 위한 발전은 다양한 방법들과 기술들을 통해 계속 발전해왔다. 하지만 AI의 시대가 오기전에 여러 방법론과 기술들로 한명의 인력의 생산성을 극한까지 올리는데 한계가 있었다고 생각한다. 하지만 AI의 세계가 오게 되면서 한명의 생산성을 증대시키는 것은 물론이고 러닝커브가 높은 영역에 대해서도 비교적 쉽게 학습할 수 있는 시대가 왔다.

 

그래서 이전의 유니콘이라고 부르던 풀스택의 개발자가 이제는 농담으로 보이지 않는 시간이 온 것 같다. AI가 하루가 다르게 발전하면서 간단한 질문의 수준의 응답뿐만 아니라 사람이 미처 고려하지 못한 부분까지 대답을 해줄 수 있도록 발전하게 되었고 이제 AI와 함께한 개발자는 제네럴리스트이자 스페셜 리스트가 되어가는 상황이 되었다. 

 

이전에는 기업도 하나의 분야에 전문가들이 모여서 팀을 이루는 것이 아니라 풀스택을 지향하고 필요에 따라 포지션이 자유롭게 전환되는 프로덕트 엔지니어를 선호하게 되었다. 나도 마찬가지로 이런 시장 분위기에 휩쓸려 프론트에 대한 공부를 해야겠다고 마음먹게 되었지만 프론트를 공부하다보면 우리가 백엔드를 설계하면서 놓쳤던 API에 대한 구조나 블랙박스로 되어 있던 전체적인 웹 통신 흐름에 대해 이해할 수 있게 되면서 더 단단한 설계를 할 수 있게 되었다. 앞으로 계속 프론트를 공부하면서 기초적인 내용의 글을 쓰겠지만 이전에 그냥 나열했던 것과 달리 백엔드를 공부하면서 학습했던 것과 비교하거나 이러한 구조가 나온 배경에 대해 이해하면서 프론트의 정을 붙여야겠다.