Next.js 주의사항
- next js 에서 static page를 만들때에는 window, document를 참조하지 않아도 동작이 view가 가능하도록 코드를 짜줄 필요가 있다.
- html파일을 브라우저에서 렌더링을 먼저 할때 static page가 사용자에게 보여지게 되는데 이때 window객체를 참조해버리게 되면 이러한 문제가 발생하게 된다.
- window 나 document같은 전역객체의 참조시점은 view 뿐만 아니라 모든 웹페이지의 동작구성 요소들이 브라우저에 로드 되었을때 전역객체가 참조된다.
- classname 라이브러리 ←
배포 hobby 등급의 제한
추가로 비용을 지불하지 않은 계정은 hobby 등급으로 시작하며 그 제한은 아래와 같다.
- 배포
- 하루에 최대 100번
- Serverless 함수
- 배포당 최대 12번
- 실행시간은 10초 이내여야함
- redirection(원래 요청한 것과 다른 URL로 보내는 것)
- 프로젝트당 최대 1024번
- 팀멤버 불가
- 동시 빌드
- 1개
- 용량
- 13GB까지
개발 환경 세팅
개발 환경 세팅
기술 스택
- 언어 : TypeScript
- 스타일 : SCSS
- 통신 : Axios
- 번들러 : Vite
- 배포 : Netlify
- 코드 formatter : prettier, eslint
세팅 과정
1. vite를 통한 ts 프로젝트 세팅
npm create vite@latest carbook -- --vanilla-ts
- scss, axios 설치
npm i sass npm i axios
- eslint/prettier 설정
- Ts 파일을 import 하는 과정에서 경로를 잘못 입력하여 제대로 불러오지 못하는 문제가 있었지만, 경로를 제대로 입력하여 해결하였다.
절대 경로 설정
- tsconfig.json
{ "compilerOptions": { ... "baseUrl": "./", "paths": { "@": ["src"], "@/*":["src/*"] }, }, "include": ["src"], }
- vite.config.ts
import { defineConfig } from 'vite'; import * as path from 'path'; export default () => defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, });
ts-node 명령어를 이용해서 node 환경에서 테스트를 하려고 해서 오류가 났다…
여러개의 파일을를 하나의 index.ts로 export 하기
axios 객체 만들기
- 모든 파일들에서 사용할 axios 객체를 만들었다
- api/index.js
export { axiosInstance } from './axiosInstance';
- api/axiosInstance.ts
import axios from 'axios'; // fake 서버에 테스트 하기 위한 임시 url const BASEURL = 'https://reqres.in'; export const axiosInstance = axios.create({ withCredentials: true, baseURL: BASEURL, });
- cors를 해결하기 위해서 적용
- 클라이언트에서 api 통신을 요청할때 쿠키나 인증헤더등의 옵션을 포함해서 전송하려면 true로 설정해야한다. 링크
axiosInstance 테스트
- reqres라는 fake server를 이용한 axios 테스트 예제가 있길래 axiosInstance를 이용해 따라해보았다
- 갑자기 cors 오류가 생겨서 생각보다 오래걸렸다
- axios객체의 withCredentials을 true로 해놓은 것이 원인이었다.
- api/axiosInstance.ts
import axios from 'axios'; const BASEURL = 'https://reqres.in'; export const axiosInstance = axios.create({ // fake 서버에 요청하므로 인증정보 안보냄, 지금 true로하면 cors 에러 // withCredentials: true, baseURL: BASEURL, });
App.ts
import { Component } from '@/core'; export default class App extends Component { template(): string { return `<div> <input type="email" placeholder="email을 입력해주세용" id="email" value="" /> </div> <div> <input type="password" placeholder="비밀번호를 입력해주세용" id="pw" value="" /> </div> <input type="button" class ='button' value="로그인" />`; } }
main.ts
import App from '@/App'; import { axiosInstance } from '@/api'; const app = document.querySelector('#app') as HTMLElement; new App(app); const onLoggin = async () => { const email = document.getElementById('email') as HTMLInputElement; const password = document.getElementById('pw') as HTMLInputElement; try { // axios으로 로그인 요청 (post) let res = await axiosInstance.post('/api/login', { email: email.value, password: password.value, }); console.log(res); document.write(JSON.stringify(res)); } catch (err) { console.log(err); throw new Error((err = 'error')); } }; const btn = document.querySelector('.button'); btn?.addEventListener('click', onLoggin);
예제 로그인 폼
로그인 성공 시
로그인 실패 시
history Api를 이용한 라우팅 기능
참고 링크
해당 링크에서는 js로 구현하여 function을 이용했는데
현 프로젝트에서는 ts로하다보니 new로 인스턴스를 생성하는 과정에서 타입 에러가 나서 해결 방법을 찾아보다 function에서 new로 생성했을 때 타입 에러를 해결하는 방법보다 class형태로 선언하는 게 더 편리한 것 같아 class형태로 변경하였다.
그리고 navigate라는 메서드 보다 push, replace가 더 익숙한 편이라 따로 함수로 분리해주었다.
+ router 기능을 utils에 넣은 이유는 모든 컴포넌트에서 공통적으로 쓰일 수 있는 모듈이기에 utils로 분리했고, 아쉬운 점은 findMatchedRoute에서 routes를 매개변수로 받는 형태로 한다면 좀 더 범용적인 클래스가 될 것 같다고 느낀다.
추가로 customEvent에 대한 type을 어떻게 처리해야되는가를 많이 찾아봤는데
event 자체는 Event란 타입으로 지정해두고, e.detail을 가져올 때 e에다가 제네릭을 사용하여 customEvent타입을 붙여주었다니 오류가 해결되었다.
무한스크롤 최적화
마지막 요소를 관찰 시 api 를 호출하여 새로운 돔요소를 추가하는 방식은 돔 요소를 무한정으로 증가하기 때문에 dom 요소가 증가함에 따라 브라우저가 버벅이는 현상이 발생할 수 있다.
무한스크롤을 최적화 하는 방법을 찾아보았는데
첫번째 방법은 페이스북/인스타 그램의 최적화 방식이다.
페이스북의 무한스크롤 최적화 방식은 가상 스크롤 방식을 이용하는데, 현재 페이지에서만 보이는 것만 렌더링하고 보이지 않는 요소에 대해서는 제거하는 방식이다.
이 방법을 착안하여 단순히 안보이는 요소에 대해 display:none을 하면 어떨까 했는데
이 방법 또한 display:none이 됨에 따라 요소들이 렌더링 되지 않으면서 모든 요소들의 위치가 바뀜에 따라 reflow및 paint 작업이 다시 일어난다. 그래서 할 수 없다..
무한 스크롤 최적화에 대해 더 찾아보다가 오늘의 집이 무한 스크롤 개발기 라는 글을 발견했는데
어떠한 방식으로 가상 스크롤을 구현한지 알 수 있었다. 근데 너무 너무 너무 너무 복잡핟..
근데 이제 오늘의 집은 컨텐츠 길이가 다 달라서 그것을 계산하는 로직때문에 복잡한거지 우리 프로젝트는 이미지 크기가 고정되어있어서 계산하는 로직이 필요하지 않을 수도 있다…
가상 스크롤을 구현할 것인가….
아니면 다른 방식이 있다. 최적화라고 할 수있는지는 모르겠지만
스크롤을 내리다가 돔 개수가 일정 개수 이상 커지면 더이상 불러오지 않고 “추가 로드하기” 버튼을 만드는 것이다! 그 버튼을 누르면 이전 요소들이 삭제되고, 새로운 요소들이 새로 불러와 진다.
이전 요소들은 클라이언트상에서 저장하고 있고, 사용자가 위로 스크롤할 때 “이전 데이터 로드하기’ 버튼을 누르면 이 후 요소들이 삭제되고 이전 요소들이 다시 불러오는 방식이다.
로그인 기능
세션 쿠키 vs 웹 스토리지(localStorage, sessionStorage)