-
Notifications
You must be signed in to change notification settings - Fork 1
기술스택 소개
프로젝트 프론트엔드 기술스택은 다음과 같다.
- React
- Typescript
- Three.js + React-Three-Fiber
- Zustand
- Emotion
- Vite
- Yarn Berry
우리 팀은 처음부터 인터랙티브 웹 개발을 하려고 계획하고 있었다. 그리고 서비스를 구체화하면서 three.js를 사용하여 3D 형태로 보이는 화면을 구현해보기로 결정했다.
처음에는 three.js만 사용하기로 했는데, R3F (React Three Fiber)의 존재를 알고난 후 R3F도 사용하는 것이 좋겠다는 판단을 내렸다.
R3F의 공식문서에서는 R3F를 three.js를 위한 React renderer라고 소개하고 있다. 간단히 말하자면 three.js를 React에서 더 편리하게 사용할 수 있게 도와주는 라이브러리라고 할 수 있다. R3F에서는 three.js를 React의 선언적인 컴포넌트 방식으로 이용할 수 있다. three.js의 객체들을 React 컴포넌트로 감싸고, 이 컴포넌트로 3D Scene을 구성한다. 이를 통해 3D 그래픽의 상태 관리, 이벤트 처리를 React의 방식대로 처리할 수 있다.
R3F를 사용했을 시 우리 프로젝트에서 문제가 될 만한 기능적 단점들이 없다고 생각하여 R3F를 쓰기로 결정했다. 특히 이벤트 처리를 React 방식대로 할 수 있다는 점이 굉장히 매력적인 부분이었다. 우리는 프론트 팀원들 모두가 three.js 사용이 처음이다. 그런 상황에서 우리에게 주어진 시간이 6주밖에 없기 때문에 React에 최적화된 R3F를 사용하는 것이 좋겠다고 판단했다.
하지만 R3F가 일반 three.js에 비해 자료의 양이 확연히 적다는 점이 우려된다.
먼저 우리가 상태관리 라이브러리를 선택할 때 가장 기본적으로 고려한 것은 아래와 같다.
- 학습에 큰 어려움이 없어야 한다.
- 가벼워야 한다.
우리에게는 6주간의 시간밖에 없기 때문에, 상태관리 라이브러리 학습에 많은 시간을 투자할 수 없다. 또 Three.js같은 3D 라이브러리를 사용하기 때문에 렌더링 성능이 굉장히 중요하다. 그래서 상태 관리 라이브러리도 최대한 가벼운 것으로 선택하여 애플리케이션 성능에 주는 영향을 줄이고자 했다.
출처: npm trends
npm trends에서 검색한 상태관리 라이브러리 최근 1년간 다운로드 수이다. 우리 팀은 이 5개의 라이브러리를 후보로 두고 고민을 시작했다.
각자가 모두 다른 특성을 갖고있지만, 이 5가지는 상태 관리 유형에 따라 크게 3가지로 분류할 수 있다. 이들의 특징을 간단하게 알아보자.
Redux, Zustand가 이 유형에 속한다. 이 유형은 Flux 아키텍처 모델을 기반으로 하는 중앙 집중식 상태 관리 솔루션이다. Flux 패턴에서 데이터는 단방향으로 흐른다.
출처: https://haruair.github.io/flux/docs/overview.html
Action이 발생하면, Dispatcher에서 이를 해석한 후 Store에 저장된 정보를 갱신하고, 그 결과가 다시 View로 전달된다.
이 유형은 상태를 한 곳에 모아 관리하고 싶을 때 적합하다. 하지만 상태를 중앙 집중화하기 때문에 상태 업데이트가 많거나 복잡할 경우 성능 문제가 발생할 수 있다.
MobX가 이 유형에 속한다. 이 유형은 상태를 프록시 객체로 래핑한다. 그래서 직접 객체를 다루지 않고, 프록시를 통해 작업을 수행하게 된다. 이 특성 덕분에 중첩된 객체의 상태 관리에 유용하다. 일반적으로 중첩된 객체의 상태를 관리하려면 상태를 복사하고, 속성을 수정하고, 수정한 상태를 다시 덮어쓰는 과정이 필요하다. 하지만 여기서는 프록시를 통해 상태를 직접 변경할 수 있기에 중첩 객체 상태 관리가 훨씬 수월하다.
하지만 상태를 직접 변경하는 방식을 사용하기 때문에 오히려 불변성을 엄격하게 지키는 것이 어려워질 수도 있다. 또 프록시 개념에 익숙하지 않다면 다루기 어려울 수 있고, 특히 디버깅에 어려움을 겪을 수 있다.
그리고 이 유형은 객체지향 프로그래밍과 잘 맞다. 물론 함수형 프로그래밍과 함께 사용할 수 있긴 하지만, 이들의 핵심 원칙과 기능은 객체 지향 프로그래밍의 원칙과 더 잘 어울린다.
Recoil, Jotai가 이 유형에 속한다. 이 유형은 전체 상태를 원자 (Atom)으로 나누는 것을 추구한다. 원자는 업데이트 가능하고 구독 가능한 상태의 단위이며, 이들은 서로 다른 부분에서 독립적으로 사용된다. 때문에 상태 관리의 모듈화가 쉽고, 코드의 재사용성이 높아진다. 하지만 상태가 단순하고 재사용의 필요성이 낮을 경우, 이 모델을 사용했을 때 오히려 더 복잡해질 수 있다.
이러한 특징들을 고려했을 때, 우린 먼저 Proxy 방식을 1차로 탈락시켰다. 프로젝트 특성상 중첩된 객체가 많을 것 같지도 않았고, 결정적으로는 객체지향 친화적이라는 점이 마음에 들지 않았다. 물론 함수형 프로그래밍 방식에도 충분히 적용할 수 있다. 하지만 그렇게 하면서까지 이 유형을 써야 할 필요성을 느끼지 못했다.
남은 것은 Flux 방식과 Atomic 방식이다. 이 둘의 가장 큰 차이점은 상태를 관리하는 방식이다. Flux 방식은 모든 상태를 한 곳에서 관리한다. 여러 컴포넌트에서 공유되는 상태를 관리하는 데 효과적이다. Atomic 방식은 각 상태를 각각 다른 곳에서 독립적으로 사용하고 관리한다. 상태의 모듈화와 재사용성이 중요할 경우 사용하면 좋다.
우리 프로젝트의 경우 그렇게 복잡하거나 관리할 상태가 많지 않다. 굳이 상태를 작은 단위로 쪼개서 관리할 필요까지는 없다고 판단했다. 그래서 일단은 Flux 방식 쪽으로 마음이 더 기울었다.
그래도 Flux 방식 라이브러리 하나, Atomic 방식 라이브러리 하나를 골라 비교해보기로 했다.
Flux 방식에 속하는 Redux와 Zustand를 비교해보자. 먼저 아까 봤던 npm trends 자료를 보면 Redux의 다운로드 수가 압도적으로 높다..
출처: npm trends
하지만 Redux는 다른 라이브러리들에 비해 학습곡선이 가파르다. 짧은 기간동안 프로젝트를 진행하는 우리 상황에서, 배워야 할 내용과 작성해야 할 코드가 많은 Redux는 적합하지 않다고 판단했다. 또 간단한 프로젝트에 무겁고 복잡한 Redux를 쓰는 것은 '굳이?'라는 생각이 들었다. 더 깊게 생각할 것도 없이 Redux는 후보에서 제외해버렸다.
Atomic 방식에 속하는 Recoil과 Jotai를 비교해보자. Recoil은 Facebook에서 개발한 상태 관리 라이브러리이다. Jotai는 Recoil의 Atomic 패턴에서 영감을 받아 만든 라이브러리라고 한다. 그래서 둘의 사용 방식이 상당히 비슷하다.
결론적으로는 Jotai가 더 낫다고 판단했다. 첫 번째 이유는 번들 사이즈 차이이다. Jotai는 830kb, Recoil은 2.17mb로 꽤나 큰 차이가 난다.
또한 활발한 업데이트가 이루어지고 있는 Jotai에 비해 Recoil은 업데이트가 많이 더딘 편이다.
이외에 몇 가지의 차이점이 있었지만, 결과적으로는 Recoil보다 Jotai가 훨씬 단순하며 가볍다는 사실 때문에 Jotai를 선택하게 되었다. 둘의 사용 방식에 큰 차이가 없기 때문에 어떤 라이브러리가 더 간단한 상태 관리에 적합한지에 포커스를 두고 고민했다.
마지막으로 이 둘을 비교해서 최종 결정을 하기로 했다. 본격적으로 비교해보자.
Zustand와 Jotai의 기술적 차이가 궁금하다면 아래 내용을 읽어보는 것을 추천한다. https://github.com/pmndrs/jotai/issues/13
먼저 npm trends 다운로드 수를 다시 보자. 여기서 Redux는 제외했다.
출처: npm trends
다운로드 수는 Zustand가 압도적이다. 새 라이브러리를 학습하는 우리의 입장에서는, 사용자가 많을수록 학습자료가 많을 것이기에 장점으로 느껴졌다.
Zustand는 187kb, Jotai는 830kb이다. Zustand가 훨씬 가볍다는 것을 알 수 있다.
Jotai는 보통 Provider로 애플리케이션을 감싸야 하는 반면, Zustand는 Provider를 사용할 필요가 없다. 사실 이 부분은 양쪽 모두 장단점이 있기 때문에, 확실한 장점이라고 할 수는 없겠다.
Jotai는 React만 지원하지만 Zustand는 Vanilla JS도 지원한다. 하지만 우리는 React를 사용하기 때문에 이 부분도 큰 장점으로 다가오지는 않는다.
가장 큰 차이점이다. 사실 위의 내용들보다는 이 부분이 가장 중요한 것 같다. Zustand는 중앙집중화된 하나의 store 안에 여러 상태들이 담기는 반면, Jotai는 각각의 상태가 atom 형식으로 흩어져 있다. Top-down 방식과, Bottom-up의 차이이다.
위에서 언급했듯이 우리의 프로젝트에서는 상태를 여러 곳에서 관리하는 Atomic보다는 한 곳에서 관리하는 Flux 방식이 더 맞다고 생각했다.
그리고 이런 논리적인 이유들을 떠나서, 팀원 모두 Flux 방식의 라이브러리 사용 경험이 없었기 때문에 Zustand가 더 매력적으로 느껴지기도 했다. 결론적으로 우리는 Zustand를 사용하기로 했다.
- React: different types of state management
- Flux OverView
- Web:React Flux 패턴
- React 상태관리 라이브러리 리뷰
- Recoil
- Jotai
- Zustand
- React 최신 상태관리 라이브러리 비교하기
- 차세대 상태관리 라이브러리, Jotai vs Zustand
- How is jotai different from zustand?
우리팀은 스타일 언어로 Emotion을 선택했다.
이를 선택하는 과정에서 우리는 우리 프로젝트의 주요 기능이 각자의 은하를 꾸미고 감상하는 것이라는 점을 최우선으로 고려했다. 사용자는 우리 서비스를 이용하는 과정에서 대부분의 시간을 이 주요 기능에 집중하고 있을 것이다. 즉 CSS를 통해 꾸며져야할 UI와 같은 요소들은 그 자체로 매력적이기 보다 우리의 주요 기능인 3D 화면과 자연스럽게 어우러지기만 하면 문제가 없다.
우선 순수 CSS의 경우 팀프로젝트에 적합하지 않다고 판단했다. 가장 가벼운 CSS 이지만 class를 사용하건 id를 사용하건 서로가 구현한 selector의 이름까지 일일이 고려하며 구현 하는 것을 그리 효율적이지 않다. 가독성 또한 그리 좋지 않아 팀원에게 공유하기에도 그리 효율적이지 않다. 심지어는 어떤 CSS가 적용될지 예측하기도 어렵다. 그렇다고 우리 프로젝트가 순수 CSS를 사용해야 할 정도로 무겁지도 않기에 제외했다.
CSS에 중첩, 변수 선언, 연산자 등을 도입해 단점을 어느정도 개선한 CSS 전처리기이다. CSS의 단점을 어느정도 개선 했고 우리도 익숙한 만큼 이점은 있지만 selector 충돌 문제가 여전히 발생할 수 있고 JS에서 직접적인 스타일 수정이 불편한 문제가 있다. 또 여전히 어떤 스타일이 적용될지 예측하기 어렵다. 이에 CSS와 마찬가지로 제외했다.
class를 통해 스타일을 미리 설정하고 사용하는 유틸리티 우선 CSS 프레임워크다. 컴포넌트 단위 생산성을 높일수 있다는 점에서 상당히 효율적이지만 우리 프로젝트에는 그리 적합하지 않다고 판단했다. Tailwind CSS는 위에서 언급한 것처럼 반복적인 스타일을 사용하는 컴포넌트가 많은 경우 효과적이다. 하지만 우리가 진행하는 프로젝트는 컴포넌트도 그리 많지 않을 뿐더러 3D 화면에 어우러지는게 우선인 만큼 스타일에 많은 변동이 있을 수 있다. 이에 사전에 스타일을 설정하고 반복적으 사용하는 Tailwind CSS는 제외되었다.
CSS를 모듈화 하여 import해서 사용하는 방식이다. node-sass를 설치하면 SCSS에서도 동일하게 사용할 수 있다. 이 방식으로 class 이름을 설정하면 자동으로 뒤에 hash 값이 붙어 고유한 클래스로 인식하도록 한다. 이를 통해 selector 충돌 문제를 해결할 수 있다. 하지만 컴포넌트 단위의 React 프로젝트의 경우 각 컴포넌트 마다 CSS 파일을 만들어 줘야 한다.
CSS module의 방대한 CSS 파일 문제를 해결하기 위한 방법이다. 각 컴포넌트에 맞는 CSS를 컴포넌트에 같이 작성해 관리하도록 한다. 이를 통해 위의 대부분의 단점들을 해결할 수 있다. 또한 JS 코드와 상태값을 공유 할 수 있는 장점이 있다. 하지만 이 방법 또한 단점이 없지는 않다. CSS 파일을 따로 만들어 두지 않기 때문에 JS 내의 CSS 코드를 파싱하는 단계가 필요해 상대적으로 느리다. 매우 많은 컴포넌트가 사용되거나 복잡한 스타일 계산이 발생하는 경우 runtime overhead가 발생할 수 있다. 이를 해결하기 위한 zero-runtime과 nero-runtime이 나왔다. 하지만 우리 프로젝트의 경우 컴포넌트가 매우 적은 편에 속해 이러한 문제로 부터 자유롭다. 이에 상대적으로 최신에 등장해 호환성 문제가 있고 추가적인 설정이 필요한 zero-runtime이나 nero-runtime의 경우 제외했다.
runtime CSS in JS 중 styled-components와 Emotion 중 선택을 했다. 그런데 전반적인 스타일 기능도 동일하고 sass 문법을 사용해 문법적 차이도 없었다. 성능적으로도 유의미한 차이를 보이지 않았다.
그럼에도 Emotion을 선택한 이유는 아래와 같다.
- label 기능을 통해 디버깅을 더 편하게 할 수 있다. 아래와 같이 label을 달면 디버깅 과정에서 클래스 명을 보고 그 태그가 원래 어떤 컴포넌트였는지 알 수 있어 편하다.
const container = (props: Props) => css`
label: container;
}
`;
- CSS props 기능을 사용할 수 있다. props를 통해 CSS 속성을 넘겨줘 스타일링을 할 수 있어 컴포넌트 재활용성이 더 높다. 약간의 차이만 있는 컴포넌트를 사용할때 효과적으로 사용 가능하다.
Emotion와 styled-components 사이에는 성능적이 차이는 크게 두드러지지 않았다. 그러나 팀프로젝트를 진행함에 있어 우리가 생각하기에 좀 더 편리한 기능을 일부 지원해 Emotion을 선택하게 되었다.
이미지 출처 - 동네마트
위 사진보다 번들러를 더 잘 표현할 수 있는 사진이 있을까? 번들러의 역할은 여러 모듈을 단일 파일로 묶어주는 역할을 수행한다. 이미지 출처 - Webpack 공식 홈페이지
과자 네 봉지를 마트에서 구매해 집에 들고간다고 생각해보자. 과자 봉지들이 낱개로 존재하는 것보다는 번들로 묶여있는 편이 집에 들고가기 수월할 것이다. 브라우저도 똑같다. 스크립트 파일들이 수많은 모듈로 이루어져있으면, 각각의 파일들을 요청하고 받아오는 오버헤드가 정말 커질 수도 있다. 정말 그런지 테스트해보자.
이 테스트는 번들러의 필요성을 체감해보기 위한 초 간단한 테스트이다. 약간의 비약은 있을 수 있지만, 번들러의 필요성을 느끼기에는 충분한 테스트라고 생각된다.
먼저 다섯개의 js 파일을 불러오는 프로젝트이다. 각각의 js 파일은 간단하게 콘솔에 숫자 하나씩을 출력하는 스크립트이다.
VSCode의 live server
를 통해 프로젝트를 실행해보자.
의도한 대로 콘솔에 숫자가 잘 출력되는 모습이다. 크롬의 개발자 도구(F12)에서 Network
탭을 이용하면 서버와의 데이터를 주고받는 과정을 살펴볼 수 있다. 한 번 확인해보자.
각 스크립트들을 잘 가져오고 있는 모습이다. Time
부분을 보면 각각의 스크립트를 가져오는데 소요된 시간을 확인할 수 있고, Finish
부분을 통해 HTML, JS의 Blocking, Non Blocking 리소스가 모두 다운로드 되는데 까지 소요된 시간을 확인할 수 있다.
이 예시에서는 923 ms의 시간이 소요되었다. 그렇다면 이 다섯개의 스크립트를 하나로 합쳐보자.
다섯개의 스크립트를 bundle.js라는 이름으로 합쳐주었다. 이외의 다른점은 하나도 없다.
스크립트도 마찬가지로 콘솔에 숫자를 출력할 뿐이다. 이번에는 하나의 스크립트에서 수행한다는 점만이 차이이다.
결과도 전혀 다를것이 없다. 그렇다면 Network
탭을 한 번 확인해보자.
Finish
이벤트가 발생할 때까지의 시간차이가 보이는가? 아까 스크립트 파일이 5개 존재했을 때는 923 ms의 시간이 필요했지만, 같은 내용으로 하나의 파일을 불러오는데 까지는 907 ms의 시간이 소요되었다.
결론
같은 내용일지라도 여러개의 스크립트를 요청하고 받아오는 것과, 하나의 파일을 요청하고 받아오는 것은 기본적인 request
와 response
에 소요되는 시간때문에 차이가 발생한다. 위처럼 정말 간단한 예시에서는 체감하기 어려울 정도의 차이지만, 만약 스크립트가 굉장히 복잡해지고, 크기가 커지고, 파일의 개수가 늘어나면 그 많은 모듈을 요청하고 받아오는데에는 많은 시간이 필요해질 수도있다. 따라서 우리는 많은 모듈을 하나의 파일로 묶어줄 번들러가 필요한 것이다.
👔 즉 번들러는 우리가 직접 하나의 파일로 정리하지 않아도, 각각 모듈의 의존성, 함수 변수의 중복이름 등을 모두 알아서 처리, 단일 파일로 묶어주는 고마운 친구이다.
추가로, 번들러를 이용하면 ES6 모듈(import, export)을 지원하지 않는 브라우저에서 ES6 모듈을 사용해도 번들러가 알아서 잘 처리해준다! 또한 번들된 결과물을 추가로 난독화하는 등의 플러그인들을 수많이 지원하고 있으니 안쓸 이유가 없지않는가 👏
이미지 출처 - npm trends
번들러도 많은 종류가 존재한다. 아마 가장 유명한 친구들은 위 세가지 정도인 것으로 알고 있다. 역시나 가장 인기가 많은 것은 웹팩인것으로 확인할 수 있다. 이제 각각의 번들러들의 특징을 알아보자.
이미지 출처 - 위키피디아
웹팩은 출시된지 벌써 10년이 넘은 이 바닥의 고인물이다. 그런 만큼 이용자 수도 가장 많고, 그에 따라 커뮤니티가 굉장히 크다. 이말은 트러블 슈팅에도 용이하고, 지원이 뒷받혀준다는 말이다. 웹팩의 특징을 나열해 보면 다음과 같다.
- 간편한 설정
웹팩은 webpack.config.js
라는 파일을 통해 여러가지를 설정할 수 있다. 이 파일은 굉장히 직관적이고 따로 설정해줄 것이 많지 않아 간편하게 구성할 수 있다. 이마저도 CRA(Create-React-App)
와 같은 빌드 셋업 툴들을 활용하면 정말 건드릴 것이 크게 없다.
- 방대한 플러그인과 로더 상기했듯 강력한 개발 커뮤니티와 방대한 생태계가 존재하므로 많은 플러그인과 로더를 지원한다. 다양한 플러그인과 로더들이 오픈소스로도 개발되고 있다.
- 코드 스플리팅 공식문서에서 Webpack의 가장 매력적인 기능 중 하나라고 소개하는 기능이다. 이 기능을 사용하여 코드를 다양한 번들로 분할하고, 요청에 따라 로드 또는 병렬로 로드한다. 이는 로드 시간에 큰 영향을 끼칠 수 있다. 자세한 내용은 공식문서가 정말 잘 설명해주고 있으니 꼭 읽어보자. Code Splitting
- 강력한 개발 서버 웹팩이 지원하는 개발 서버는 매 코드 변경마다 빌드된 결과물을 확인할 수 있는 강력한 툴이다. 실제 번들링 된 결과물을 테스트해볼 수 있으니 상당히 유용하다.
이미지 출처 - Rollup 공식 홈페이지
Rollup은 2017년에 출시한 모듈 번들러이다. 공식 문서를 살펴보면 알겠지만 작은 코드조각을 더 크고 복잡한 라이브러리, 어플리케이션을 만들어준다는 것을 강조한다. Rollup의 특징을 살펴보자.
- 빌드 결과물을 ES6 모듈 형태로 출력할 수 있다.
아마 Rollup의 가장 큰 특징 중 하나일 것이다. ES6 모듈 형태로 결과물을 출력할 수 있으므로 라이브러리나 패키지에 활용할 수 있다. 이미
Vue
와 같은 라이브러리들이 Rollup을 활용해 번들링을 진행중이다. - 호이스팅 웹팩은 각 모듈을 함수로 감싸고 평가한다. 즉 전역스코프에 있던 변수들을 지역변수로 변경해 줌으로써 변수이름 충돌을 방지하는 것이다. 하지만 Rollup은 코드들을 동일한 수준으로 평가하고, 번들링을 한번에 진행하므로 웹팩보다 속도가 빠르다. 변수이름 충돌 문제는 식별자를 통해 방지한다.
Rollup은 웹팩보다는 가볍고 빠르지만, 웹팩이 조금 더 안정적이라고 할 수 있다. 일반적으로 라이브러리나 패키지등을 개발하고 있다면 Rollup을, 복잡한 종속성, 높은 안전성이 요구되는 어플리케이션을 개발한다면 웹팩을 이용한다.
이미지 출처 - esbuild 공식 홈페이지
ESBuild는 2020년에 출시된 차세대 번들러로써, 빠른 성능으로 최근 받고 있다. 공식 홈페이지에 들어가자마자 속도 그래프를 보여주는 것이 성능을 정말 중요시한다는 것을 확인할 수 있다. 이미지 출처 - esbuild 공식 홈페이지
ESBuild의 가장 큰 특징은 역시 성능이다. 그렇다면 ESBuild는 어떻게 이렇게 빠른것인가? 공식문서를 살펴보면 그 의문을 해소할 수 있다. Why is esbuild fast? 공식 문서에서는 ESBuild가 빠른이유로 다음을 이야기하고 있다.
-
Go
언어로 작성됨 다른 대부분의 번들러들은 자바스크립트로 작성되어있다. 그 태생적 한계를Go
언어로 해결한 것이 ESBuild이다.Go
언어는 간결함, 병렬성, 성능을 중시하는 언어로, 병렬성을 고려하지 않고 설계된 자바스크립트보다 훨씬 나은 성능을 뽑아낼 수 있다. - 병렬성
Go
언어의 강력한 병렬성을 적극 활용한 알고리즘들을 다수 활용하고 있다고 한다. - 자체 Parser 이용 공식 TypeScript 컴파일러를 파서로 이용하는 다른 번들러들과는 다르게, 처음부터 성능을 염두에 두고 설계된 자체 파서를 이용한다고 한다.
- 효율적인 메모리 사용 데이터 접근 최소화로 메모리 액세스 횟수를 줄여 효율성을 증대시켰다고 한다.
이러한 이유들이 합쳐져 ESBuild는 다른 번들러들보다 몇배씩이나 빠른 성능을 보여준다.
위와 같은 번들러들을 포함해 개발 서버 지원, 바벨, 린트 등의 복잡한 툴 설정을 한 번에 깔끔하게 처리해주는 빌드 툴로 CRA(Create-React-App)
와 최근 각광받는 Vite
가 존재한다. 이 둘을 비교해보고 우리 프로젝트에 Vite
를 도입하기로 한 이유를 알아보자.
이미지 출처 - CRA 공식 홈페이지
CRA는 React 앱을 만들때 가장 쉽고 간편하게 접근할 수 있는 빌드 툴이다. 공식 문서에서 제안하는 특징들은 다음과 같다.
- 쉬운 사용 복잡하고 많은 빌드 툴들을 배우고 설정할 필요 없이 즉시 앱 개발에 돌입할 수 있게 해준다.CRA를 통해 빌드 도구가 아닌 코드에 집중할 수 있게 해주는 것을 강조하고 있다.
- 하나의 의존성 CRA를 통해 모든 요소들이 복잡한 버전 불일치 없이 원활하게 작동하는지 테스트할 수 있다.
- No Lock-in
CRA는 내부적으로 웹팩을 사용한다. 추가적인 설정을 원하면 CRA에서
eject
를 통해 구성 파일을 직접 편집할 수 있다.
리액트를 처음 배울 때 반드시 사용하게 되는 가장 친숙한 빌드 툴이 아닐까 싶다. 간단하고, 배워야할 것이 딱히 없다시피 하다.
이미지 출처 - 위키피디아
이번 글의 주인공인 Vite
("비트"로 읽는다. "바이트"가 아니다!)이다. Vite는 2020년 새롭게 출시된 빌드 툴로, 속도와 성능에 중점을 둔 차세대 프론트엔드 도구를 표방하고 있다.
Vite aims to address these issues by leveraging new advancements in the ecosystem: the availability of native ES modules in the browser, and the rise of JavaScript tools written in compile-to-native languages. Why Vite
Vite는 브라우저에서의 네이티브 ES모듈 가용성과 네이티브 언어로 컴파일되는 JavaScript 도구의 발전 등 생태계의 새로운 진보를 활용한 느린 개발서버의 생산성 문제를 해결하는 것을 목표로 하고 있다고 한다. 즉 수천 개의 모듈이 포함되는 대규모 프로젝트에서 발생하는 성능 문제를 해결하는 것이 주된 목표라는 것이다.
실제로 웹팩을 이용하는 CRA는 이러한 대규모 프로젝트에서 개발서버를 가동하는데 몇 분(!)씩 걸리는 일도 발생한다. Vite가 어떻게 이 문제를 해결했는지는 공식문서에 잘 나와있다. 간단하게 요약하면 다음과 같다.
- 의존성과 소스코드의 분리
이미지 출처 - Vite 공식 홈페이지 매 번 모든 전체 코드를 번들링하던 기존방식과 다르게, 개발 중에 자주 변경되지 않는 의존성(패키지)와 소스코드를 따로 처리한다. 의존성은 esbuild를 통해 빠르게 사전 번들링하고, 소스코드는 네이티브 ES module을 통해 처리한다. 네이티브 ES module을 중심으로 개발 서버를 구축함에 따라 매번 모든 코드를 번들링하는 것이 아닌, 필요에 따라 소스코드를 변환하고 제공할 수 있다. 이를 통해 상당히 효율적으로 모든 작업을 처리할 수 있다. 2. 비동기 청크 로딩 최적화 이미지 출처 - Vite 공식 홈페이지 빌드 시에, 모든 Direct import 구문에 대해 사전 로드를 수행함으로써 낭비되는 네트워크 왕복을 줄이도록 구성한다. 이를 통해 더 빠른 빌드를 경험할 수 있다. 위 사진의 예시에서 기존에는 A가 모두 파싱된 이후에야 C의 필요성을 알게되고 C를 요청하지만, Vite는 A를 가져올 때 C를 함께 병렬적으로 가져오도록 해주는 것이다.
이러한 방식들로 하여금 Vite는 성능상에 큰 이점을 갖고있다.
아직까지 Vite 생태계보다는 웹팩을 사용하는 CRA의 생태계가 큰 것은 부정할 수 없다. 유용한 플러그인, 로더, 방대한 양의 자료와 커뮤니티를 생각하면 CRA도 매력적인 선택이지만, 우리의 프로젝트는 3D 렌더링이 주 기능이 될 예정이고, 부하를 많이 주는 작업이 다수 존재할 것으로 예상되어 성능상 이점이 있는 Vite를 사용하는 것이 개발 생산성에 있어 합리적이라고 판단했다.
Refereces
차세대 번들러 비교 및 분석 (feat. webpack, rollup, esbuild, vite)
4 Reasons Why You Should Prefer Vite Over Create-React-App (CRA)
이미지 출처 - pixabay 무료이미지
패키지에 대해 설명하라고 하면, 가져다 쓸 수 있는 코드 뭉치 정도로만 알고 있었는데, 이번 기회에 자세하게 그 정의를 알아보고 싶었다.
Module(모듈)
옛날 옛적에는, 자바스크립트로 큰 프로그램을 만들지 않았고 그 크기는 상당히 작았다. 하지만 웹의 생태계가 엄청난 성장을 이루면서 자바스크립트로 대규모 프로그램을 만드는 경우도 많아졌고, 그에 따라 코드를 분리해야할 필요성이 생겨났다.
이러한 배경속에 등장한 것이 모듈이다. export
키워드를 통해 변수, 함수를 모듈로 내보내 동일한 파일이 아닌 다른 파일에서도 사용할 수 있고, require
과 import
키워드로 모듈을 가져와 사용할 수 있다. 모듈은 이렇게 분리되어 사용될 수 있는 변수나 함수를 말한다.
JavaScript modules - JavaScript | MDN
Package(패키지)
패키지는 모듈의 집합체이다. 특정 기능을 구현하는데 필요한 모듈들을 묶어놓은 모음집인 것이다. 만약 그래프를 그리고 싶다면 그래프 관련 패키지를 가져와서 사용하면 되고, 서버와 통신을 하고 싶다면 통신 관련 패키지를 가져와 사용하면 내가 직접 기능들을 하나하나 구현하지 않아도 쉽게 서비스를 만들어 낼 수 있다.
현대의 웹 프로젝트에서는 적지 않은 수의 패키지들을 사용한다. 많은 패키지들은 내부적으로 다른 패키지들을 사용하고 있는 경우들이 있는데, 여러 패키지들을 함께 사용하면서 패키지들 간의 의존성들이 복잡하게 꼬여 해결하기 어려운 상황이 발생하게된다. 발생할 수 있는 문제들로는 버전 충돌, 간접 의존성등 여러 문제가 있고 이러한 의존성 문제를 Dependency Hell
이라고 한다.
의존성 문제를 해결하기 위해 패키지들을 관리해주어야할 필요성이 생겨났고, 그 역할을 수행하는 것이 Package Manager, PM이다.
패키지 매니저도 종류가 여러가지이다. 대표적으로 npm, yarn, pnpm 등등이 존재한다. 각각의 특징을 짧게 이야기 해보면
npm
이미지 출처 - WIKIPEDIA
Node.js 패키지의 공식 패키지 관리자로 가장 널리쓰이는 패키지 매니저이다. 쉽게 사용할 수 있고, 이용자가 많아 트러블 슈팅에도 용이하다.
Yarn
이미지 출처 - Yarn repository
병렬 패키지 설치 및 캐시를 활용해 npm보다는 빠른 속도로 의존성을 관리한다. 보안 측면에서도 약간의 이점이 있다고 한다.
pnpm
이미지 출처 - pnpm 공식 사이트
심볼릭 링크
라는 개념을 이용해 의존성을 저장하고 여러 프로젝트에서 공유함으로써 중복 설치와 디스크 공간 낭비를 줄여준다. 패키지를 중복설치하지 않고 메모리를 효율적으로 관리하여 빠른 설치와 업데이트를 가능하게 한다.
위 세가지 패키지 매니저도 각각의 장점이 있는 좋은 패키지 매니저들이지만, 우리는 Yarn berry를 선택했다. 그 이유를 알아보자.
기존의 PM들은 node_modules
라는 디렉토리에 모든 패키지들을 저장하는 방식을 가지고 있다. 이 방식은 node_modules
가 굉장히 무거워져 비효율적인 의존성 검색을 가져오는 문제를 일으킨다. 실제로 node_modules
디렉토리를 확인해보면 각각의 패키지들이 연속적으로 node_modules
를 가지고 있는 것을 확인해볼 수 있는데, 많은 수의 패키지들이 서로 의존하고 있다면 디렉토리 구조가 복잡해지는 것은 당연할 것이다.
이러한 문제를 해결하기 위해 최근의 PM들은 호이스팅 등의 개념을 이용하는데, Yarn berry는 PnP
라는 방식으로 이 문제를 해결했다.
이미지 출처 - Polygon
PnP
, Plug’n’Play는 하드웨어를 꽂기만 하면 별도의 사용자 조작이나 프로그램 설치 없이 바로 사용가능한 방식을 말한다.
Yarn berry는 패키지 설치 시 node_modules
를 이용하는 대신, .yarn/cache
에 패키지를 압축형태로 저장하고, .pnp.js
파일에 의존성의 위치를 기록한다.
(.pnp.js
의 일부, 패키지의 이름, 버전, 위치 등의 정보가 기록되어있다.)
이름 그대로 패키지를 플러그해두는 것으로 복잡한 설정이나, 추가 작업없이 패키지를 바로 사용할 수 있게 해주는 것이다. 이 방식을 통해 Yarn berry는
-
node_modules
의 복잡한 디렉토리 구조를 생성할 필요가 없고,.pnp.js
파일에서 모든 것을 관리하므로 패키지 설치 시간이 훨씬 짧게 걸린다. - 패키지를 여러 프로젝트에 별도 설치하는 대신 중앙화된 캐시에서 관리할 수 있어 디스크 공간을 절약할 수 있다.
와 같은 장점을 얻을 수 있다.
또한 Yarn berry는 패키지를 압축 형태인 zip파일의 형태로 저장하므로 공간을 훨씬 효율적으로 사용할 수 있는데, 이때문에 모든 패키지를 git
으로 관리 할 수 있다!
이 말이 무슨 말인가 하면, 프로젝트에서 사용하는 패키지들을 모두 git
으로 추적, github
에 올릴 수 있다는 말이다. 따라서 프로젝트를 clone, pull 시에 따로 패키지를 인스톨하는 과정 없이 바로 프로젝트를 시작할 수 있고, 이를 통해 CI/CD
시에 많은 시간을 절약할 수 있다. 이를 Zero install
이라고 한다.
github
레포지토리에 올라가있는 .yarn/cache
안의 패키지 압축파일의 모습이다.
우리 팀은 이러한 장점들 때문에 Yarn berry를 PM으로 선택했다. pnpm도 Yarn berry만큼의 효율성과 장점들을 갖고 있지만, 이전에 yarn을 사용해본 경험이 있기에, 커맨드와 같은 부분에서 조금이라도 익숙한 Yarn berry를 사용하는 것이 합리적이라고 생각했다.
우리 팀은 하나의 루트 폴더에서 client 코드와 server 코드를 디렉토리로 구분하여 관리하기로 하였다. 이렇게 하나의 저장소에서 두 개 이상의 프로젝트가 저장되는 것을 모노레포
라고한다. Yarn berry와 함께 모노레포를 구성하는 과정을 살펴보자.
Yarn berry는 yarn의 다른 버전일 뿐이므로, yarn이 설치되어 있다면 따로 준비할 것은 없다. 만약 yarn이 설치되어 있지 않다면 npm i yarn -g
커맨드를 통해 yarn을 설치해주자.
monorepo라는 이름으로 디렉토리를 생성하고 이동해주었다. 여기서 프로젝트를 생성할 것이다.
yarn init -y
커맨드를 통해 yarn 프로젝트로 초기화 해주자. package.json
파일이 생성된다. 아직까지는 Yarn berry가 아닌 yarn classic을 사용중이다.
yarn set version berry
명령을 통해 해당 프로젝트에서 사용하는 yarn의 버전을 berry로 변경할 수 있다.
yarn -v
명령어 또는 package.json
을 확인해보면 Yarn berry를 사용중임을 확인 할 수 있다. (yarn의 3.x 버전 이상은 Yarn berry이다.)
모노레포를 구성하기 위해서는 우선 모든 프로젝트가 위치할 workspace
를 지정해야한다. packages
라는 이름으로 디렉토리를 만들어 워크스페이스로 사용하겠다.
packages
라는 이름으로 디렉토리를 생성하고, package.json
에 “workspaces” : [ “packages/” ]
를 추가해주자. 이제 packages에 추가되는 모든 디렉토리를 워크스페이스로 인식할 것이다.
이제 packages
폴더로 이동해 프로젝트를 생성해주자. 프로젝트를 원하는 방식으로 생성해주면 된다. 우리는 vite
와 nest
를 이용한 클라이언트, 서버 프로젝트를 생성해주었다.
만약 프로젝트에서 TypeScript
를 이용한다면, 이런 오류를 만날 것이다. VSCode 환경에서 Yarn berry를 사용하려면 몇가지 설정이 추가적으로 필요하다.
-
yarn plugin import typescript
타입스크립트 플러그인을 가져와주자. 이는 크게 상관은 없지만, 자체 types가 없는 패키지를 추가할 때 자동으로 @types/ 패키지를 package.json에 종속성으로 추가해준다.
-
VSCode 익스텐션 중 ZipFS 설치
압축으로 관리되는 패키지를 살펴보기 위해 해당 확장 프로그램이 필요하다.
-
yarn dlx @yarnpkg/sdks vscode
위 명령어를 통해 sdk를 설치해주어야한다.
함께 진행해보자.
먼저 yarn add typescript -D
명령어를 통해 타입스크립트를 추가해준다.
이후 yarn install
명령어를 통해 아까 생성한 프로젝트들에서 필요로 했던 의존성들을 모두 설치해준다.
다음으로 yarn dlx @yarnpkg/sdks vscode
명령어를 통해 sdk를 설치하면, 다음과 같은 알럿창이 표시된다.
타입스크립트 버전에 관한 내용인데, Allow를 눌러주자. 만약 이러한 알럿창이 표시 되지 않았거나 Dismiss또는 X를 잘못눌러 꺼벼렸다면, cmd + shift + p
를 통해 명령 팔레트를 표시하고 TypeScript: Select TypeScript Version…
을 선택하자.
이후 Use Workspace Version
을 선택해주면 된다.
위 과정을 모두 잘 따라왔다면, 더이상 오류는 발생하지 않고 프로젝트도 잘 실행될 것이다. 추가로 워크스페이스 명령어를 실행하는 방법을 알아보자.
워크스페이스(위의 예시에서는 packages
디렉토리) 안의 각각의 프로젝트에 존재하는 스크립트를 루트 디렉토리에서도 실행할 수 있다. 각각의 프로젝트 이름을 확인 후, (프로젝트 생성 시에 입력한다. 만약 이름이 기억나지 않는다면 각 프로젝트의 package.json을 확인해보자.
name 필드에 적혀있는 것이 프로젝트의 이름이다.)
루트 폴더에서 yarn workspace 프로젝트이름 스크립트이름
명령어를 통해 각 프로젝트의 스크립트를 실행할 수 있다. 현재 우리 프로젝트에는 client
프로젝트와 server
프로젝트가 존재하고, 각각 dev
스크립트와 start
스크립트가 존재한다. 루트 디렉토리에서 실행시켜 보자.
vite
와 nest
가 각각 잘 작동하는 모습이다. 이렇게 루트 디렉토리에서 워크스페이스의 각 스크립트를 실행하거나, 또는 해당 프로젝트 디렉토리로 이동해서 일반적인 방식으로 (yarn vite
또는 yarn start
) 스크립트를 실행할 수 있다.
이렇게 모든 프로젝트 설정을 마쳤다고 생각했지만, 무언가 이상한 점을 발견했다. Yarn berry에 대해 학습하면서 .pnp.js
파일을 살펴보았는데,
packageLocation
필드가 좀 이상한 것을 발견했다. 패키지 저장 위치가 저장소 내부가 아닌, 내 로컬머신에 설치되고 가져오고 있는 것이었다. 또한 path가 상대경로로 되어있었는데, 여기서 의문이 들었다.
- 만약 내가 이 프로젝트를 다른사람과 공유할 때, 그 사람이 나와 같은 위치가 아닌 다른 위치에 프로젝트를 clone하면 저 상대경로는 틀린 경로가 될 것이다.
- 저 경로의 위치로 가보면,
이렇게 내 로컬 머신 안의 .yarn/berry/cahce라는 곳이 나오는데, 이 폴더는 git
으로 관리되고 있지 않으므로 만약 상대방의 머신에 패키지가 설치되어 있지 않다면 패키지 인스톨 과정을 거쳐야한다.
이 방식은 우리가 원했던 Zero install이 아니다! 분명히 문제가 될 것같다는 직감속에 실제로 github
레포지토리를 생성, 저장소를 올리고 내려받아 실행시켜보았다.
예상했던대로 의존성을 하나도 찾지 못하는 문제가 발생했다. 이 문제를 어떻게 해결해야하나 고민 끝에, 한가지 결론에 도달했다.
우리가 원했던 Zero install을 이루려면, 당연하게도 저 .yarn/cache
디렉토리는 프로젝트 내에 위치하여 git
으로 관리되어야만 한다. 현재의 방식은 로컬 머신의 글로벌 캐쉬 디렉토리에 패키지들이 저장되는 방식이라 git
으로 관리되지 못하는 것이다.
우리 프로젝트 내의 .yarn 디렉토리에는 cache 폴더가 존재하지 않는다!
어떻게 해야 프로젝트 내에 .yarn/cache
디렉토리를 생성하고 그 위치에 패키지들을 저장할 수 있을지 찾아보았다.
열심히 자료를 찾아보던중, 하나의 스택오버플로우 글에서 힌트를 얻었다.
Should i push the .yarn/cache folder to Github - Yarn2
.yarnrc.yml
파일에서 yarn과 관련된 설정들을 명시할 수 있는데, 변경할 수 있는 필드값들 중 enableGlobalCache: true
라는 값이 있다는 것을 위 글에서 알게되었다.
우리는 글로벌 캐시에서 의존성을 관리하는 것이 아닌, 프로젝트 내에서 의존성 관리를 하고싶으므로 enableGlobalCache 값을 false로 주면 되지 않을까? 라는 생각 아래에 그대로 시도해보았다.
이후 앞서 했었던 과정들을 동일하게 수행하면
우리가 원했던 대로 프로젝트 내의 .yarn/cache
디렉토리가 생성되고, 이곳에 패키지들의 정보가 저장된다.
드디어 git
을 통해 이것들을 추적할 수 있고, github
에 레포지토리를 생성해 push 후 clone을 통해 테스트해봐도 따로 추가적인 인스톨이나, 설정 필요없이 바로 프로젝트를 시작할 수 있었다.
대부분의 자료에서 이 옵션에 대한 설명이 없는데, 아마 디폴트 값이 이전에는 false였다가 최근들어 true로 바뀐것이 아닐까..? 하는 생각을 해본다. 확실히 아직 사용하는 사람들이 많지 않다보니 자료가 많이 부족하다는 단점은 존재하는 것 같다. 하지만 하나씩 헤쳐나가다보면 지식도 늘고 뿌듯함도 얻을 수 있다 😎
설정 끝!
References
Naver D2 - 모던 프론트엔드 프로젝트 구성 기법 - 모노레포 도구 편
Yarn berry (yarn pnp) 환경으로 React + Typescript 프로젝트 세팅하기
프로젝트 백엔드 기술스택은 다음과 같다.
- NestJS
- Typescript
- TypeORM
- MySQL
- MongoDB
- Redis
Node.js 기반의 백엔드 프레임워크 중 가장 대중적으로 사용되는 Express는 프레임워크의 간결성을 중요시하며 로직 구성이 매우 자유롭다는 특징이 있다.
따라서 컨벤션을 까다롭게 지정하지 않는 이상 타인의 코드를 이해하는 데 시간이 많이 소요되는 등 협업에 유리하지 않다고 판단했다.
또한 프레임워크의 간결성으로 인해, 단기간에 많은 기능을 개발해야 하는 이번 프로젝트의 특성상 다른 프레임워크에 비해 Express는 개발 신속성이 떨어진다고 판단했다.
따라서 이러한 단점을 보완하기 위해 Express를 기반으로 개발된 NestJS를 선정했다.
NestJS는 사전에 모듈 구조가 정의되어 있으며 데코레이터 기반으로 이를 사용하기 때문에 서비스 확장성과 유지보수성이 좋고, 코드 스타일을 통일하기가 쉽다.
또한 HTTP, 웹 소켓, 각종 미들웨어와 가드, 예외 필터, 로깅 등 서버 동작에 필수적인 기능을 프레임워크에 포함하여 제공하기 때문에, 직접 설치하거나 구현해서 사용해야 하는 Express에 비해 의존성 관리나 개발 신속성 면에서 유리하다.
JavaScript를 사용하면 예기치 않은 오류도 많이 발생하고 오류의 정확한 원인을 바로 찾기가 힘들어 디버깅이 어렵다.
TypeScript를 사용하면 런타임에 발생할 수 있는 오류들을 잡아주기 때문에 안정성 있는 코드를 작성하고 디버깅에서 도움을 받을 수 있다.
또한 변수 또는 함수 등의 타입을 명시할 수 있어 가독성, 유지보수 측면에서도 사용하는 것이 좋다고 판단했다.
단순 CRUD(Create, Read, Update, Delete) 기능 구현이 대부분이기 때문에 개발 신속성 면에서 유리한 ORM의 사용을 채택했다.
프로젝트의 특성상 복수 개(MySQL, MongoDB)의 DB를 활용하며, SQL문이 아닌 클래스의 메소드를 통해 일관된 인터페이스로 데이터베이스를 조작할 수 있는 ORM의 장점을 더욱 가져갈 수 있다. 이 점은 코드 일관성 및 유지보수 측면에서도 좋기 때문에 협업하여 개발하는 본 프로젝트에서 더 효과적일 것이라 기대된다.
TypeORM은 NestJS에서 공식 지원하는 ORM이다. 프로젝트에서 NestJS를 사용하기로 결정하였기 때문에, 가장 호환성이 좋은 TypeORM을 채택했다.
이후, 성능 문제가 발생하는 부분은 쿼리 최적화 과정을 거친 후 해당 메소드만 ORM이 아닌 SQL 쿼리를 사용해 직접 구현할 예정이다.
프로젝트 배포 기술스택은 다음과 같다.
- Docker
- GitHub Actions
- NGINX
- Naver Cloud Platform
Docker는 어플리케이션을 신속하게 구축, 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다.
Docker의 가장 큰 특징은 서버 운영체제를 가상화(컨테이너라고 부름)하여 그 안에서 어플리케이션을 실행하는 방식으로 동작한다는 것이다. 개발한 소프트웨어가 표준화된 실행 환경에서 동작하기 때문에, 실행 환경으로 인해 발생할 수 있는 예기치 않은 오류나 의존성 문제를 미연에 방지할 수 있다.
또한 컨테이너 이미지를 웹에 빌드, 저장 및 공유할 수 있는 Docker Hub를 활용할 수 있기 때문에, Docker를 사용하면 직접 클라우드 인스턴스에 어플리케이션을 배포하는 것 보다 더 효과적으로 배포를 수행할 수 있다.
6주 간 애자일 방식으로 단기간에 많은 기능을 개발, 빌드, 테스트 및 배포를 반복해야 하는 이번 프로젝트의 특성상 Jenkins, GitHub Actions와 같은 소스코드 Repository에 대한 지속적인 통합과 지속적인 배포(CI/CD) 서비스를 이용하면 구현 외 작업에 소요되는 시간을 획기적으로 단축할 수 있다.
그 중에서도 GitHub Actions는 대표적인 코드 Repository 서비스인 GitHub에서 직접 제공하는 CI/CD 서비스로, 특정 branch가 push되는 등 Repository와 관련된 다양한 event를 트리거로 앞서 언급한 빌드, 테스트 및 배포를 자동화할 수 있는 가상환경 내의 스크립트 실행 기능을 제공한다.
해당 기능을 활용하여 Docker 이미지를 생성하거나, SSH로 클라우드 인스턴스에 접근하여 원격 서버에서 스크립트를 실행할 수도 있기 때문에, 이번 프로젝트와 같은 소규모 프로젝트에서 GitHub Actions가 가장 신속하게 효과적으로 사용할 수 있을 것으로 기대되어 채택했다.
Git Repository에서 GitHub Actions Workflow 파일(YAML 파일)을 함께 관리할 수 있고, 로그도 GitHub 페이지에서 바로 확인할 수 있다는 점도 큰 장점이다.
© 2023 debussysanjang
- 🐙 [가은] Three.js와의 설레는 첫만남
- 🐙 [가은] JS로 자전과 공전을 구현할 수 있다고?
- ⚽️ [준섭] NestJS 강의 정리본
- 🐧 [동민] R3F Material 간단 정리
- 👾 [재하] 만들면서 배우는 NestJS 기초
- 👾 [재하] GitHub Actions을 이용한 자동 배포
- ⚽️ [준섭] 테스트 코드 작성 이유
- ⚽️ [준섭] TypeScript의 type? interface?
- 🐙 [가은] 우리 팀이 Zustand를 쓰는 이유
- 👾 [재하] NestJS, TDD로 개발하기
- 👾 [재하] AWS와 NCP의 주요 서비스
- 🐰 [백범] Emotion 선택시 고려사항
- 🐧 [동민] Yarn berry로 모노레포 구성하기
- 🐧 [동민] Vite, 왜 쓰는거지?
- ⚽️ [준섭] 동시성 제어
- 👾 [재하] NestJS에 Swagger 적용하기
- 🐙 [가은] 너와의 추억을 우주의 별로 띄울게
- 🐧 [동민] React로 멋진 3D 은하 만들기(feat. R3F)
- ⚽️ [준섭] NGINX 설정
- 👾 [재하] Transaction (트랜잭션)
- 👾 [재하] SSH 보안: Key Forwarding, Tunneling, 포트 변경
- ⚽️ [준섭] MySQL의 검색 - LIKE, FULLTEXT SEARCH(전문검색)
- 👾 [재하] Kubernetes 기초(minikube), docker image 최적화(멀티스테이징)
- 👾 [재하] NestJS, 유닛 테스트 각종 mocking, e2e 테스트 폼데이터 및 파일첨부
- 2주차(화) - git, monorepo, yarn berry, TDD
- 2주차(수) - TDD, e2e 테스트
- 2주차(목) - git merge, TDD
- 2주차(일) - NCP 배포환경 구성, MySQL, nginx, docker, docker-compose
- 3주차(화) - Redis, Multer 파일 업로드, Validation
- 3주차(수) - AES 암복호화, TypeORM Entity Relation
- 3주차(목) - NCP Object Storage, HTTPS, GitHub Actions
- 3주차(토) - Sharp(이미지 최적화)
- 3주차(일) - MongoDB
- 4주차(화) - 플랫폼 종속성 문제 해결(Sharp), 쿼리 최적화
- 4주차(수) - 코드 개선, 트랜잭션 제어
- 4주차(목) - 트랜잭션 제어
- 4주차(일) - docker 이미지 최적화
- 5주차(화) - 어드민 페이지(전체 글, 시스템 정보)
- 5주차(목) - 감정분석 API, e2e 테스트
- 5주차(토) - 유닛 테스트(+ mocking), e2e 테스트(+ 파일 첨부)
- 6주차(화) - ERD
- 2주차(화) - auth, board 모듈 생성 및 테스트 코드 환경 설정
- 2주차(목) - Board, Auth 테스트 코드 작성 및 API 완성
- 3주차(월) - Redis 연결 후 RedisRepository 작성
- 3주차(화) - SignUpUserDto에 ClassValidator 적용
- 3주차(화) - SignIn시 RefreshToken 발급 및 Redis에 저장
- 3주차(화) - 커스텀 AuthGuard 작성
- 3주차(수) - SignOut시 토큰 제거
- 3주차(수) - 깃헙 로그인 구현
- 3주차(토) - OAuth 코드 통합 및 재사용
- 4주차(수) - NestJS + TypeORM으로 MySQL 전문검색 구현
- 4주차(목) - NestJS Interceptor와 로거
- [전체] 10/12(목)
- [전체] 10/15(일)
- [전체] 10/30(월)
- [FE] 11/01(수)~11/03(금)
- [전체] 11/06(월)
- [전체] 11/07(화)
- [전체] 11/09(목)
- [전체] 11/11(토)
- [전체] 11/13(월)
- [BE] 11/14(화)
- [BE] 11/15(수)
- [FE] 11/16(목)
- [FE] 11/19(일)
- [BE] 11/19(일)
- [FE] 11/20(월)
- [BE] 11/20(월)
- [BE] 11/27(월)
- [FE] 12/04(월)
- [BE] 12/04(월)
- [FE] 12/09(금)
- [전체] 12/10(일)
- [FE] 12/11(월)
- [전체] 12/11(월)
- [전체] 12/12(화)