해당글은 회사 프로젝트를 진행하며 도입한 Remix 라는 라이브러리 도입기와
Remix 라이브러리에 대해 다룹니다.
어느날 회사에서 프로젝트 관련 요청 토큰이 할당되었습니다.
해당 요청은 새롭게 고객사 웹을 제작하라는 요청이였는데 전에 만들어 배포했던 식단관리 관련 앱의 유저 데이터를 한눈에 볼 수 있도록 관리자 페이지를 제작해야한다는 요청이였습니다.
평소 개발은 React 로 했으나 새롭게 만들어야할 고객사 웹은 관리자 페이지로 다른 웹들과 달리 데이터를 DB에서 가져올 경우가 많았는데, 해당 상황에서 CSR 를 사용한다면 서버 요청시간이 길어질수도 있던 상황이기도 했고 백엔드 엔지니어분들의 일정 부족으로 백엔드 코드까지 일부 건드려야할 상황이 생겼습니다.
해당 상황에서 저는 UI 와 API 를 분기하기보단 한 프로젝트에서 관리하는것이 편할것 같다고 생각했고 , API 요청과 UI 렌더링을 동시에 할 수 없는 CSR 보단 SSR 즉 , 서버 사이드 렌더링이 해당 프로젝트에 더욱 적합하다고 생각했습니다.
우선 왜 Remix 를 도입했는지 알기 위해선 위에서 잠깐 언급했던 두가지 렌더링 방법을 살펴보아야합니다.
간단하게 말하자면 CSR 은 웹페이지 렌더링이 클라우드(브라우저) 측에서 일어나는것을 의미합니다.
클라이언트 사이드 렌더링은 UI 렌더링을 브라우저에서 모두 처리하는 것입니다. 즉, 자바스크립트를 불러온다음에 실행이 되어야 우리가 만든 화면이 사용자에게 보여집니다.
CRA로 만든 프로젝트의 개발 서버를 실행한 다음, 크롬 개발자 도구의 Network탭을 열고 http://localhost:3000/ 페이지의 요청에 대한 응답을 보시면 다음과 같이 root 엘리먼트가 비어 있는 것을 확인할 수 있습니다.
즉, 해당 페이지는 처음에는 빈 페이지입니다. 해당 페이지는 JS 가 실행되어야 비로소 UI 가 로드됩니다.
하지만 SSR 은 SSR은 서버에서 첫 페이지의 렌더링을 클라이언트 측이 아닌 서버 측에서 처리해주는 방식입니다.
서버에서 페이지의 렌더링을 클라우드가 아닌 서버측에서 관리해주기때문에 JS 가 실행되지않아도 UI 가 이미 로드되어있습니다.
해당 상황은 웹 검색 기능을 구현하기에도 유리한데, 서버 사이드 렌더링을 하여 사용자에게 페이지를 더욱 일찍 보여준다면, 페이지의 모든 데이터가 로딩될 때 까지 기다리지 않고 검색 할 가능성이 있기 때문에 의도한 데이터가 제대로 검색되지 않을 수 도 있습니다.
제가 구현해야하는 웹은 검색기능 및 렌더링 측면과 함께 렌더링 시점에서 다량의 DB 데이터를 가져와야하기때문에 SSR 방식이 적합했습니다.
그래서 Remix 가 뭔데?
Remix는 React를 사용하는 풀스택 웹 프레임워크입니다. Prisma 및 디렉토리 기반 라우팅 구성으로 기존에 구현하기 까다로웠던 SSR 을 Remix 라이브러리로 간편하게 구현 할 수있습니다.
Remix 를 디렉토리 기반 라우팅 , loader 및 action , from 개념 등 실질적 웹 구현에 있어서
처음보는 Remix 만의 특징이 정말 신기했습니다.
디렉토리 기반 라우팅
기존에 우리가 리액트 라우터를 사용할 때 라우트를 구성할 때에는, 컴포넌트 선언 방식으로 설정을 했었던 반면, Remix의 경우에는 컴포넌트의 디렉터리에 기반하여 라우트 가 설정이 됩니다.
app/routes/index.jsx 와 같이 말이죠.
따라서 밑의 사진과 같이 프로젝트 파일이 구성되고 해당 파일 주소에 따라 라우팅이 되는 방식이 신기했습니다.
loader 및 action
Remix 에서는 최초 로딩시 loader 라는 함수가 호출됩니다. 해당 함수는 최초에 로딩되어야하는 UI 에 API 를 호출 할 때 유리한데 저같은경우는 식단 정보 및 사용자 정보 , 관리자 정보 등등 관리자 페이지에 있어서 미리 호출되어야할 함수나 API 를 다음과 같이 불러 올 수 있었습니다.
같은 양상으로 action 의 경우는 함수 이름대로 web 에서 어떤 동작이 주어지게 될 경우 실행되는 함수로 버튼이 눌리거나 input box 의 value 가 바뀌게 될 경우 호출되는 함수로,
또한,추가적으로 Loader 및 Action 함수를 실제 컴포넌트 단위에서 사용하기위해선 밑과같은 useLoaderData 나 useActionData 를 사용해 UserData.(변수명) 해당 값을 가져올 수 있었습니다.
Form
가장 핵심적인 부분입니다. 앞서 말했던 Action 에서 button 이나 다른 컴포넌트의 인자를 받고싶다면 form 으로 최상위 부모의 컴포넌트를 묶고 , 보이지 않는 Input 에 data 를 넣어 action 함수에 전달 해 줄 수 있습니다.
TypeError: Cannot read properties of undefined (reading ‘root’)
하지만 모두 잘 되진 않았습니다. TypeError: Cannot read properties of undefined (reading ‘root’) 열심히 로직을 작성하던중 해당 오류가 나오며 정상적으로 실행이 되지 않았습니다. 그래서 Remix 깃헙 이슈를 찾아보니, 저와 같은 문제로 고생중인 분들이 있었는데 원인은 브라우저 번들에 서버 환경에서만 실행 가능한 코드가 포함되었기 때문입니다.
해당 상황에서 해결법을 조금 더 찾아보니 서버 환경에서 실행되어야 하는 코드의 파일 이름을 something.server.ts 처럼 변경하는 방법이 가장 효과적임을 알 수있었고 이렇게 하면 해당 파일이 서버 환경에서만 필요하다는 것에 대한 힌트를 제공하는 방식이 된다는걸 알 수 있었습니다.
해결 방법
서버 관련한 파일은 server 를 붙여주거나 앞에 api 주소를 붙여 서버에서 사용되는 코드임을 명시하였습니다.
CSS 관련 이슈
웹 스타일 관련은 mui 와 tailwindcss 를 사용하였는데 , mui 커스텀을 할 일이 생겨 기존 react 에서 하던대로 css 파일을 임포트하면 적용이 되지않는 이슈가 발생했었습니다.
해결 방법
이부분은 공식 문서에도 잘 나와있는데, remix 에서의 css 파일은 root 파일에 반드시 import 를 해주어야합니다.
root 디렉토리 외의 api 호출
유저 파일정보나 세부정보같은 메인에서 호출할경우 로직이 꼬이거나 , 불필요하게 메인에서 호출하지 않아도 되는 api 의 경우 root 디렉토리 외의 컴포넌트에서 호출해야 합니다. 하지만 remix 특성상 외부에서 prisma api 를 호출 하려하면, 다음과 같은 오류가 뜨며 호출이 되지않고 웹에 error 가 발생합니다.
prisma 함수를 component 에서 직접 호출 할 수없습니다.
당연히 해당 오류는 db를 직접 참조하는 prisma 함수에서 의도적으로 막아놓은것으로 만약 클라이언트에서 해당 함수를 실행하게된다면 웹이 정상적으로 작동하지않을 수 있습니다.
해결 방법
다음과 같이 fetch 함수를 통해 api 라는 서버에서 쓰는 페이지임을 명시하는 페이지 주소를 만들고 해당 페이지에 호출 할 수 있도록 만들었습니다. 그러나 이 방법이 최선인지는 아직 모릅니다.
7월 초반쯔음 프로젝트가 마무리 되었고, 앞으로도 유지보수가 걸려있어 유지보수를 하겠지만
어드민은 여러모로 동기부여를 쓸 수 있도록 도와준 프로젝트 였습니다. 특히 파일 구조로 web 주소를 설정하는 부분이나 loader / action 같은 remix 만의 고유 funcion 을 이용하여 신기하고 즐겁게 코드를 쓰는 기분이 너무 좋았습니다. 역시 개발자는 새로운 기술을 배우고 실전에 응용할 때 더욱 기쁜것 같습니다.
혹시 본인처럼 어드민을 만들 계획이 있거나 필요한 사람이 있다면 꼭 Remix로 하지 않아도 된다고 말하고 싶습니다. 내 생각에 어드민에서 중요한 것은 프레임워크보다 코드의 반복을 줄이고, 컴포넌트를 선언적으로 구성할 수 있게 만드는 것이 더 중요하다고 봅니다. 사실 난 그냥… Remix를 써보고 싶었을 뿐
하지만 아쉬운 부분도 여럿 있었는데, 거의 대부분의 api 가 새로운 api root 를 통해 호출 되었다는점과 여러 커스텀 컴포넌트의 인자 type 구성이 유지보수 대상이나 아직까지 건드리지 않았던 점이 조금 아쉬웠습니다.
물론 아쉬운점은 유지보수측면에서 점차 수정해나갈 예정입니다.
가치 있는 정보 공유해주셔서 감사합니다.