Port has been used 에러 해결하기
블로그 설정을 새로 하던 중에 사용중인 port가 있어서 로컬서버가 열리지 않았다. 4000번은 이미 사용중이라는 에러가 발생하였다.
1 | netstat -ano | findstr 4000 |
4000번을 쓰고있는 pid 찾기
1 | taskkill /f /pid 76860 |
강제로 종료하기
강제로 종료한 뒤에 확인해보니 정상적으로 종료되었고 로컬에서 정상적으로 서버도 열렸다.
블로그 설정을 새로 하던 중에 사용중인 port가 있어서 로컬서버가 열리지 않았다. 4000번은 이미 사용중이라는 에러가 발생하였다.
1 | netstat -ano | findstr 4000 |
4000번을 쓰고있는 pid 찾기
1 | taskkill /f /pid 76860 |
강제로 종료하기
강제로 종료한 뒤에 확인해보니 정상적으로 종료되었고 로컬에서 정상적으로 서버도 열렸다.
드래그는 클릭 -> 마우스 이동 -> 마우스 떼기로 이루어진다.
마우스 클릭(mousedown)에 이벤트를 등록하고 마우스 떼기(mouseup)에서 이벤트를 지워주면 된다.
또한 브라우저에서 기본으로 제공하는 드래그 이벤트를 없애주면 된다.
mousedown에 mousemove와 mouseup 이벤트를 등록하고 mouse up에서 이를 모두 지워주었다.
1 | const slider = document.querySelector(".slider"); |
렌더링 엔진은 script태그를 다운로드하거나 실행하는 중에는 파싱을 멈춘다.
자바스크립트 파일은 css나 html을 변경시킬 가능성이 있기 때문이다.
파싱을 시작하자마자 script 태그를 다운로드한다.
파싱을 막기 때문에 script태그를 먼저 다운로드하고 실행 후 파싱한다.
실행 순서를 정할 수 있다.
다만 존재하지 않는 DOM요소에 접근시 에러가 발생할 수 있다.
파싱이 끝난 뒤에 script 태그를 다운로드 시작한다.
js파일에 의존성이 높을경우 사용자경험이 좋지 못하다.
병렬로 백그라운드에서 다운로드한다.
다운로드 하는 중에도 HTML 파싱을 멈추지 않는다.defer
스크립트의 실행은 페이지 구성이 끝날 때까지 지연된다.
defer
스크립트는 DOM이 준비된 후에 실행되지만 DOMContentLoaded
전에 실행된다.
아래는 예시 코드이다.
1 | <p>...스크립트 앞 콘텐츠...</p> |
페이지의 콘텐츠가 모두 출력된다.
DOMContentLoaded
이벤트는 defer
스크립트가 실행된 후에 발생하므로 alert창은 DOM트리가 완성되고 defer
스크립트의 실행이 완료된 후에 실행된다.
async
async
도 마찬가지로 백그라운드에서 다운로드 된다. 따라서 HTML 페이지는 async
스크립트 다운이 완료되길 기다리지 않고 페이지 내 컨텐츠를 출력한다.( 하지만 async
스크립트 실행중에는 HTML 파싱이 멈춘다.)
DOMContentLoaded
이벤트와 async
스크립트는 서로를 기다리지 않는다.
async
스크립트 다운로드가 끝났을 경우 DOMContentLoaded
이벤트는 async
스크립트 실행 전에 발생할 수 있다.async
스크립트가 DOM트리를 만들기 전에 다운로드 되었을 경우 async
스크립트의 실행이 끝난 다음 DOMContentLoaded
이벤트가 발생한다.아래는 예제 코드이다.
1 | <p>...스크립트 앞 콘텐츠...</p> |
async
스크립트는 그 특징 떄문에 실행순서를 보장할 수 없다. 먼저 다운로드가 끝난 스크립트 순으로 실행된다. 즉 small.js가 long.js보다 먼저 실행된다.
또한 alert의 실행시점도 예측할 수 없다.
이러한 특징으로 인해 구글 어널리틱스같은 독립적인 역할을 하는 서드파티 스크립트를 삽입할 때에 유용하다. 개발중인 스크립트에 의존하지 않고 독립적으로 동작하기 때문이다.
1 | <script async src="https://google-analytics.com/analytics.js"></script> |
사전처리 및 상황별 최적화
사전처리 및 상황별 최적화 후 GZIP을 적용하여 최소화된 출력을 압축하면 큰 절감효과를 얻을 수 있다.
Cache-Control
헤더를 통해 관리할 수 있다.1 | body { |
1 | body { |
1 | @media print { |
1 | <link rel="stylesheet" href="style.css"> |
JS 는 DOM과 CSSOM 모두 조작 가능
script 태그를 만났을 때는 DOM 생성을 중단하고 자바스크립트가 실행되기를 기다려야한다
script 태그가 DOM 생성을 막기 때문에 js는 parser blocking이다.
인라인 스크립트 대신 외부파일로 스크립트를 사용할 경우엔 파서가 script 태그를 발견하면 파일을 받아서 실행한다. 파일을 가져오는 동안에 DOM 생성을 계속할 수 없으므로 CRP가 늦어진다.
인라인 스크립트를 사용하는 것은 요청을 줄이는 데에 도움을 주지만 코드가 반복되고 과도하게 사용될 수 있는 단점이 있다.
js는 CSS를 조작할 가능성도 있기 때문에 script는 css가 도착하고 CSSOM을 생성할 때 까지 실행되지 않는다.(CSSOM 생성 후 JS가 실행된다.) 따라서 js 최적화는 css최적화와 깊은 연관이 있다.
사용자 분석 등 랜더링에 영향을 주지 않는 스크립트
async 속성(CRP를 막지 않는 script)
을 통해 최적화 할 수 있다. async 속성을 붙일경우 브라우저의 DOM 생성을 막지 않는다. script 요청을 처리하고 dom을 파싱한다. 또한 CSSOM에 영향을 받지 않는다. CSSOM생성 전에 script를 사용 가능하다면 바로 실행할 수 있다.1 | <script src="analytics.js" async></script> |
브라우저의 랜더링 과정과 CRP에 대해 알아보자
객체
로 변환된다.이러한 구조로 부분 HTML을 먼저 로드하여 성능개선을 할 수 있다.
부분 css의 사용은 불가능하다. 잘못된 스타일을 사용 할 수 있기 때문이다.
브라우저는 모든 CSS를 받고 처리할 때 까지 페이지 랜더링을 차단한다.
1 | h1 { |
첫 번째 태그는 h1에 일괄적으로 적용하지만 두 번째 태그는 모든 p 태그를 찾고
부모노드가 div인 것을 찾아서 스타일을 적용하기 때문에 브라우저가 더 많은 작업을 해야한다.
display:none
과 같은 style이 있는 노드의 경우 포함되지 않는다.%
의 경우 메타태그에서 설정한 뷰포트의 너비 or 부모의 너비를 상속한다.1 | <meta name="viewport" content="width=device-width"> |
이를 피하기 위해 여러번의 레이아웃 이벤트를 피하고자 업데이트를 한 번에 반영하는 것이다.
클론코딩을 하던 중 이런 에러를 마주하였다.
스크롤을 내려보면 runAllEffect에서 발생한 에러임을 알 수 있다.
TypeError: Cannot read property ‘forEach’ of null
액션을 구독할 때에 take함수를 쓰는데, import를 해주지 않아서 발생한 에러였다. take를 import 해주었다.
자바스크립트라 어이없는 실수가 많다..
1 | import { all, delay, fork, take } from "redux-saga/effects"; |
재밌어서 색상을 바꾸고 강조해보았다(div태그 이용).
오랜만에 리액트를 만지니까 재밌다.
오늘은 프로젝트에 리덕스를 적용하였다.
next에는 redux를 적용할 수 있도록 도와주는 고마운 라이브러리가 있다.
또한 기존의 redux와 달리 라이브러리에서 자동으로 provider로 감싸주기 때문에 provider로 감싸지 않아도 된다.
라이브러리들을 설치해준다.
1 | npm i next-redux-wrapper로 설치 |
먼저 루트 디렉터리에 reducer 폴더를 만든다.
위 reducer 폴더에 user.js파일을 만들고 개요를 작성해준다.
1 | export const initialState = { |
마찬가지로 reducer 폴더에 post.js파일을 만들고 개요를 작성해준다.
1 | export const initialState = { |
마지막으로 해당 경로에 index.js파일을 만들고
리듀서들을 합쳐준다.
HYDRATE는 서버사이드 랜더링을 위한 것이라고 한다.
1 | import { HYDRATE } from "next-redux-wrapper"; |
루트 디렉토리에 store폴더를 만들고 configureStore.js 파일을 작성해준다.
redux devtools또한 적용하였다.
개발모드일 때랑 배포모드일 때 미들웨어를 다르게 하였다.
마지막으로 래퍼를 생성해 반환한다.
1 | import { createWrapper } from "next-redux-wrapper"; |
가장 상위 파일인 app.js를 다음과 같이 감싸준다.
그러면 라이브러리에서 provider로 자동으로 감싸준다.
1 | import PropTypes from "prop-types"; |
기존 redux와 동일하게 useSelector, useDispatch를 사용할 수 있다.
리덕스에 비동기 작업들을 처리하는 리덕스 사가를 사용해보자!
본 게시글은 next.js +redux + redux-saga기준으로 작성하였다.
1 | npm i redux-saga |
Store를 만들고 미들웨어를 추가해준다.
그 후 반환 전 사가 미들웨어를 실행해준다.
1 | import { createWrapper } from "next-redux-wrapper"; |
중단점이 있는 함수이다 next()를 통해 다음 중단점 전까지 실행한다.
return된 값은 value에 저장된다.
saga에서는 이벤트 리스너로 활용한다.
1 | const func = function* () { |
1 | function* logIn(action) { |
sagas/index.js에 이펙트를 작성하였다.
1 | export default function* rootSata() { |
1 | export default function* rootSata() { |
1 | function* handleInput(input) { |
LOG_IN_REQUEST
액션 발생시 logIn함수를 실행한다.1 | function* watchLogIn() { |
1 | function* logIn(action) { |
액션별로 함수를 지정해주고 액션이 발생시 그 함수를 호출해준다.
이펙트 앞에는 yield를 붙여주어야 보장된다.
rootSaga에서 해당 이벤트가 발생시 핸들러에게 action을 같이 넘긴다.
기존 redux와 같이 action.data, action.type로 이벤트 타입과 같이 넘겨진 데이터에 접근할 수 있다.
1 | import { all, fork, take, call, put } from "redux-saga/effects"; |
1 | function* watchLogOut() { |
1 | function* watchLogOut() { |
1 | function* watchLogOut() { |
1 | function* handleInput(input) { |
1 | import { delay } from 'redux-saga' |
1 | function* handleInput(input) { |
인프런에서 제로초(조현영)님이 진행하시는 React로 NodeBird SNS 만들기을 보고 따라해보기로 했다.
이전에 한 번 진행하였던 과정인데, node+react 기반의 풀스택 프로젝트 경험이 필요하여 진행하게 되었다.
매일매일 조금씩 진행해볼 예정이다.
시작하기 전에 몇 개의 원칙을 정하였다.
폴더 구조는 다음과 같다.
components
여러 컴포넌트들을 정의하였다.
레이아웃의 공통부분인 AppLayout.js
을 정의하고 이를 각 페이지에서 받아와서 썼다.
pages
클론코딩에는 next.js가 사용되었다.
next.js는 js 프레임워크인 리액트의 프레임워크이다.
즉 js 프레임워크의 프레임워크이다.
때문에 여러 편리한 기능들을 제공한다.
next js는 루트폴더의 pages
라는 폴더 내부의 파일명으로 spa를 만든다.
나는 front 페이지에서 next를 설치하고 작업하였다.
pages/index.js
파일은 현재 경로에 아무것도 없을 때 실행된다.
1 | import AppLayout from "../components/AppLayout"; |
pages/_app.js
파일은 여러 파일에 공통적인 부분들을 적는 파일이다.
index.js에서 export된 내용들이 props.Component로 들어온다.
next에서 제공하는 Head 태그를 사용하여 head태그를 수정하였다.
1 | import PropTypes from "prop-types"; |
나머지 파일들은 /파일명
으로 접근할 수 있다.
Head 태그
next에서 제공하는 Head 태그를 이용하여 여러 meta 태그들을 직접 다루어보고 적용해주고 싶다..!
Login Form에 라이브러리 적용하기
react-hook-form과 같은 validation 라이브러리를 적용하여 안정성을 더하고 싶다.
시멘틱 태그 적용하기
구현이 어느정도 되었을 때의 이야기지만, 시맨틱 태그를 적용하여 검색엔진에 쉽게 걸리도록 하고 싶다.
typescript 적용하기
(이건 온전히 내 타입스크립트 실력을 위해서) 아직 타입스크립트가 낯설다 ㅠ
특히 제네릭을 사용하는 부분은 거의 외워서 작성하게 된다.
연습을 위해 타입스크립트를 적용해보아야겠다..! 이것도 나중에!
BEM 적용하기
BEM 네이밍 방식의 연습을 위해 컴포넌트 wrapper에는 styled-components를 사용하고 아래에는 BEM네이밍 방식을 적용할 생각이다.
예전에 했을 때는 진짜 따라가기에 벅찼는데, 지금 다시 해보니 새로운 부분들이 많이 보인다.
최대한 열심히 많은 것을 얻어갈 수 있도록 진행해보아야겠다!
라고 호기롭게 글을 적었으나,,
여러 글을 참조하던 중 이미 예쁘게 정리해주신 분의 아티클이 있어 공유합니다.
저도 이 분처럼 처음 보는 입장에서도 쉽게 이해가 가는 예쁜 글을 적고싶어요.
갈 길이 멀어요..!