[개인 프로젝트] my dibs! 프로젝트 일지 (2)

Jade·2023년 3월 27일
1

프로젝트

목록 보기
24/28
post-thumbnail

부제 : 인간의 욕심은 끝이 없고...


🟢 현재까지 완료된 사항들

✅ 마크업 완료

✅ Context API를 통해 다크모드 구현

✅ 회원가입 및 로그인 기능 구현

✅ Next.js에서 .env 파일 만들기


🟢 진행중 공부

📎 Next.js의 서버리스

프로젝트를 혼자 한다고 생각했을 때 가장 마음에 걸렸던 부분이 백엔드가 없다는 것이었기 때문에 Next.js에서 간편하게 서버리스 함수를 통해 API를 구현할 수 있다는 게 가장 큰 장점이라고 생각했고 사용해보고 싶었다.

사실 서버부분까지 구현하게 되면 원래 계획했던 기획에서 추가되어야 할 부분이 많아지기 때문에 (ex: 로그인, 로그아웃 등의 유저 구분 기능, 유저에 따른 찜 리스트 관리) 욕심이라면 욕심이지만, 대부분의 발전은 그런 새로운 것에 대한 욕구에서 시작되기 마련이라고 생각해서... 잘 몰라도 일단 뛰어들고 싶었다.

pages/api 폴더에 파일을 만들면 해당 경로로 api를 만들 수 있게 된다.
이번 프로젝트에서 만든 api 경로는 다음과 같다.

── pages/api
	├── user ── index.js
	├── auth.js
	└── item.js
    └── users.js

user api는 get요청을 이용해 패스워드 부분만 제거한 user 데이터를 반환한다.
이렇게 가져온 user 데이터를 localStorage에 넣거나 상태 관리 라이브러리로 관리할 예정이다. (user 데이터가 있느냐 없느냐에 따라 login여부가 판단되고, 헤더도 변경된다.)

auth api는 post 요청을 통해 로그인을 하거나, delete를 이용해 로그아웃이 가능하다.

item api는 유저들이 가진 item 목록을 가져오거나, 생성하거나, 수정하는 용도로 쓰일 예정이다. (get, post, patch)

users api는 회원을 새롭게 생성하는 역할을 하고, 그 내부에서 유저 네임, 패스워드가 들어오지 않았을 때 에러를 보여주는 validation이나, 이미 사용되고 있는 유저 네임인 경우도 if문으로 처리해준다. 패스워드를 해쉬된 상태로 DB에 저장해주도록 하고, 회원가입이 완료된 유저인 경우 유저 정보를 받아와 user 변수에 넣어주고 passport의 로그인 함수를 사용해 로그인시킨다.

아마 수정 사항이 생길 수도 있겠지만 지금으로썬 이런 상태로 설계되어있다!

참고로... 정말 많이 도움을 받은 블로그가 있어서 공유한다.
login, signup, logout 기능을 어떻게 구현해야할지 막막했을 때 많은 도움이 되었다! (next.js에서 환경변수를 어떻게 사용해야하는지도 나와있다)

https://cpro95.tistory.com/463


📎 MongoDB와 NoSQL

프로젝트를 진행하기 위해서 참고하고 있는 강의에서 MongoDB를 사용했기 때문에 나도 데이터베이스로 MongoDB를 따라 사용하게 되었다. 아는 게 하나도 없었기 때문에 데이터를 저장하기 전에 MongoDB에 대해서 공부하는 시간을 좀 가졌다.

MongoDB는 여러 데이터베이스들 중에서 Nosql에 해당하는 데이터베이스라고 한다. MongoDB에서 Nosql에 대해 제공하는 설명은 아래와 같다.

NoSQL 데이터베이스(일명 "SQL만을 사용하지 않는 데이터베이스")는 표 형식이 아니며, 관계형 테이블과는 다른 방식으로 데이터를 저장합니다. NoSQL 데이터베이스는 데이터 모델에 따라 유형이 다양합니다. 주요 유형으로는 문서, 키 값, 와이드 컬럼, 그래프가 있습니다. 이들은 유연한 스키마를 제공하며, 대량의 데이터와 높은 사용자 부하에서도 손쉽게 확장이 가능합니다.

Nosql의 장점은 기존의 관계형 데이터베이스보다 유연하고, 다양한 데이터 타입과 사용 사례를 처리할 수 있다는 점인 것 같았다. 그리고 좀 더 찾아본 결과 Nosql의 한계점은 '복잡한 데이터 트랜잭션이나 높은 수준의 일관성이 필요한 애플리케이션에는 적합하지 않을 수 있다. 또한 NoSQL 데이터베이스에는 기존 관계형 데이터베이스의 표준화 및 잘 정의된 스키마가 없기 때문에 데이터 관리가 더 복잡해질 수 있습니다.'는 점인 것 같았다. (참고)

그리고 MongoDB는 NoSQL 데이터베이스의 유형 중에서 문서 데이터베이스에 속한다.

문서 데이터베이스는 JSON(JavaScript Object Notation) 객체와 비슷한 문서에 데이터를 저장합니다. 각 문서에는 필드와 값의 쌍이 포함되어 있습니다. 일반적으로 값은 문자열, 숫자, 불리언(boolean), 어레이, 객체 등 유형이 다양할 수 있으며, 구조는 개발자가 코드에서 사용하고 있는 객체에 맞춰 조정됩니다. 다양한 필드 값 유형과 강력한 쿼리 언어 덕분에 문서 데이터베이스는 다양한 사용 사례에 적합하며, 범용 데이터베이스로도 사용할 수 있습니다.

인용문들 참고

사실 내가 어플리케이션에서 사용할 데이터가 그리 복잡하지는 않기 때문에 확장성이 중요하다거나, 데이터 관리가 어려워질 것 같지는 않았다.

혼자서 굿노트에 끼적이면서 생각해보기에... MongoDB를 사용하는 두 개 정도의 방법이 있을 것 같았다.

  1. 콜렉션을 user 하나로 통일하고, user 내부에 있는 사용자 데이터 각각이 사용자의 찜 리스트도 포함한다.
  2. 콜렉션을 user와 itemList 두 가지를 만들고, itemList 각각은 userid 키와 items 키까지 두 개의 키를 가지고, items는 값으로 배열인 찜 리스트를 가진다.

1번 방법을 사용하는 게 아무래도 콜렉션을 하나만 관리하면 되기도 하고, 어플리케이션 자체가 아주 복잡한 로직을 필요로 하지는 않기 때문에 좀 더 적합하지 않을까 생각이 들었다. (우선 내 생각이기 때문에 실제로도 그럴지는... 구현을 해봐야 알 것 같다.)

만약 2번을 사용하면 user가 새롭게 생성될 때 user 콜렉션 뿐만 아니라 itemList 콜렉션에 해당 사용자의 id를 id로 가지는 데이터가 함께 생성되어야 할 것 같은데, 굳이 한 번 더 데이터를 insert하는 과정을 줄이기 위해서 1번을 선택하기로 했다.


📎 SSR, SSG, CSR을 적절하게 사용하기

next.js는 vercel에 의해 개발된 하이브리드 react앱을 위한 프레임워크이며 렌더링 시 pre-rendering과 hydration을 이용한다.

what is hydration? (참고)
서버사이드 렌더링이 없이는 루트 노드는 비어있는 상태이다. ReactDOM.render이 실행되어야 리액트는 빈 노드를 우리의 어플리케이션으로 채우게된다. 우리가 SSR을 사용할 때는 노드가 이미 콘텐츠를 가지고 있게 된다. 그 콘텐츠들을 교체(replace)하는 대신에, 우리는 리액트를 첨부(attach)하고 싶다. 여기서 hyration이라는 개념이 들어올 수 있는데, 리액트는 빠르게 가상 돔과 이미 존재하는 콘텐츠들을 비교해서 실제 돔을 불필요하게 조작하지 않을 수 있다. 이것이 hydration이 SSR을 가치있게 만드는 것이다.

(위 글은 참고 블로그를 보며 임의로 해석한 것이라서 틀린 부분이 있을 수 있습니다.)

Next.js를 이번 프로젝트의 프레임워크로 선택한 이유 중 또 하나가 바로 서버사이드렌더링이 가능하다는 것이었다. SEO가 중요해지면서 SEO에 유리한 SSR이 주목받고 있는데, 이 서버 사이드 렌더링을 실제로 어떻게 구현하는지, 내가 CSR로 구현하던 리액트 페이지와는 어떤 점이 다른지 궁금했다.

next.js는 서버사이드 렌더링, 정적 페이지 생성 모두 지원하고 있고 아래 함수들을 이용한다.

  • getStaticProps는 정적 사이트 생성시 사용하는 함수이다.
  • getStaticPaths는 동적 라우팅 처리를 위해 정적 사이트 생성과 함께 사용하는 함수이다. (When you export a function called getStaticPaths (Static Site Generation) from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by getStaticPaths.) 이 함수는 위의 getStaticProps와 꼭! 함께 사용해야 한다. 그리고 아래의 getServerSiceProps 함수와는 함께 사용할 수 없다.
  • getServerSideProps를 이용해 서버사이드 렌더링을 구현할 수 있음.

Next.js를 학습하면서 위 함수를 모두 사용해보았는데, 어플리케이션은 로그인 시 헤더에 변화가 있어야하고, 사용자가 데이터를 저장하는 경우 Feed의 업데이트도 빈번하게 일어날 수 있으므로 SSG보다는 SSR을 위주로 프로젝트에서는 적용하게 될 것 같다.

아래는 CSR, SSR, SSG에 대한 글들을 보며 정리한 내용이다. (참고한 블로그)

💁‍♀️ CSR (Client-Side-Rendering)

CSR에서 서버는 뼈대만을 제공하고 로직, 데이터, 템플릿, 라우팅을 통한 페이지 전환은 모두 JavaScript가 처리한다. (CSR에서 서버는 주로 API 응답을 담당)

CSR에서는 사용자가 브라우저의 다른 경로로 이동하더라도 페이지를 새로고침 하지 않고 동적으로 라우팅을 관리하며 사용자와 상호작용이 많은 경우 더 나은 사용자 경험을 제공하기 위해 사용할 수 있다.

그러나 페이지의 복잡도가 증가하는 만큼 페이지를 렌더링하기 위해 필요한 JavaScript의 크기가 증가하면 JavaScript 번들의 크기가 커지고, FCP와 TTI도 함께 증가하게 된다. 이는 오히려 사용자 경험에 악영향을 줄 수도 있다.

그리고 JavaScript 코드가 많아지고 처리 내용들이 복잡해질 경우 크롤링에 의미 있는 내용들이 포함되지 않을 수 있어서 SEO최적화를 위한 추가적인 작업을 해 주어야 한다.

💁‍♀️ SSR (Server-Side-Rendering)

SSR은 웹 페이지를 브라우저로 보내기 전 서버에서 렌더링을 완료하는 것을 말한다. 사용자의 페이지 요청에 대해 전체 HTML을 만들어서 응답하며 렌더링 된 컨텐츠에는 데이터 저장소나 외부 API로부터 받은 데이터가 포함되어 있다.

서버 사이드 렌더링을 사용하면 데이터를 조회하고 템플릿 렌더링하기 위하여 서버와 별도로 통신하지 않아도 되고, 클라이언트 측에 렌더링 코드는 필요하지 않기 때문에 따로 코드를 전송하지 않아도 된다.

렌더링 코드를 서버 측에서 실행하여 자바스크립트의 크기를 줄이면 자바스크립트의 크기가 작을수록 빠른 FCP와 TTI를 갖게 된다.

검색엔진 크롤러는 서버사이드 렌더링 앱의 컨텐츠에 대해서는 쉽게 크롤링 할 수 있고 페이지의 검색 엔진 최적화 수준을 향상시킬 수 있다.

그러나 ! 모든 프로세스가 서버에 의해 처리되므로 동시에 아주 많은 사용자들이 데이터 로딩을 시작하거나, 네트워크가 느리거나, 서버 코드가 최적화 되어 있지 않다면 서버의 응답이 지연될 수 있다.

그리고 모든 코드를 클라이언트에서 사용할 수 없어 주요 동작들에 대해 서버 데이터가 필요한 경우 전체 페이지를 새로고침해야 할 수 있다. 이렇게 되면 사용자가 인터렉션 중간에 더 오래 기다려야 하므로 결과적으로 인터렉션 시간이 더 늘어날 수 있다. 따라서 서버사이드 렌더링에서는 SPA가 매우 어렵다. (이런 단점들을 극복하기 위해 요즘 프레임웍과 라이브러리들은 같은 앱을 서버, 클라이언트 양쪽에서 렌더링할 수 있게 되어있다고 한다.)

💁‍♀️ SSG (Static Rendering)

SSG는 사이트를 빌드할 때 미리 렌더해둔 HTML컨텐츠를 서빙하므로 TTFB/FCP/TTI와 같은 성능이 좋은 편이라고 한다.

정적 렌더링은 로그인 된 사용자를 위한 UI변경 같은 것이 없는 정적 컨텐츠에 적합한데, SSG로 만들어진 사이트는 컨텐츠가 변경될 때 마다 매번 새로 빌드해야 하므로 자주 변경되어야 하는 컨텐츠에 대해 SSG를 적용하는것은 적합하지 않다.

🤔 SSR, SSG, CSR 관련 글 읽다가 기억 안 나서 다시 정리하는 웹성능 개선 지표들

  • TTFB (Time To First Byte) : HTTP 요청을 했을때 처음 byte (정보) 가 브라우져에 도달하는 시간으로 우선적으로 검색 순위를 결정할 때(SEO) TTFB가 중요한 요소라고 한다.
  • FCP (First Contentful Paint) : 사용자가 화면에서 콘텐츠를 볼 수 있는 페이지 로드 타임라인의 첫 번째 지점을 표시하기 때문에 사용자가 감지하는 로드 속도를 측정할 수 있다. (우수한 UX를 제공하기 위해서는 FCP가 1.8초 이하여야 함.)
  • TTI (Time To Interactive) : 페이지가 로드되는 시점부터 사용자와의 상호작용이 가능한 시점까지의 시간을 측정함. 페이지가 완전히 상호 작용 가능하기까지의 기준은 "페이지에 FCP로 측정된 컨텐츠가 표시됨 / 이벤트 핸들러가 가장 잘 보이는 페이지의 엘리먼트에 등록됨 / 페이지가 0.05초안에 사용자의 상호작용에 응답함" 등이 있음.

예전에 공부했던 최적화 관련 포스팅


🟢 앞으로 남은 여정

📎 TDD 강의를 본 나 : 오...?

📎 github action을 이용한 CI/CD

📎 서버리스 배포 (feat. 혼자)

profile
키보드로 그려내는 일

0개의 댓글