npm은 명령어 집합이라고 생각하시면됩니다. npm을 통해서 자바스크립트 라이브러리를 설치하고 관리합니다.
node 설치로 npm을 사용할 수 있습니다.
npm init
{
"name": "npmtest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "fdsa09876",
"license": "MIT"
}
기본적으로 생성되는 파일입니다. 나머지는 명령어 과정에서 생성하지만 scripts는 알아서 생성됩니다. scripts는 터미널에 치는 명령어의 별칭 모임입니다. 터미널에 명령어를 실행하면 Error: no test specified\" && exit 1에러를 발생합니다.
npm run test
run은 생략가능합니다. 보통 scripts에 start: index로 수정해서
npm start로 실행합니다.
package.json은 프로젝트의 전반에 대한 정보와, npm을 활용하기 위한 정보가 모여있는 파일입니다.
일반적으로 루트 디렉토리에 위치하기 때문에 npm을 통해 만들어지는 node-modules 파일과 헷갈리면 안됩니다.
npm로 라이브러리를 다운받으면 node-modules에 저장되고 package를 통해 정보를 알려준다고 생각하시면 됩니다.
node-modules은 실제 프로그램을 실행시키기 위한 모듈이 저장되어 있습니다. package.json은 말했듯이 정보를 모아놓은 파일입니다.
package.json을 설명드린 이유는 npm을 사용하면 package.json는 자동 생성되기 때문입니다.
package파일이 필요한 이유는 다양합니다. 대표적인 이유는 프로젝트 사용 및 배포할 때 패키지를 참고해서 어떤 라이브러리가 필요한지 확인할 수 있습니다.
정보만 공유하면되니까 사용자는 선택적으로 라이브러리를 사용할 수있고, 배포 속도는 빨라질 것입니다. 팀 프로젝트라면 더욱 필요성이 높을 것입니다.
배포할 때 node-modules은 빼고 package.json만 배포합니다.
npm i
이후 npm i를 통해 package에 있는 라이브러리를 다운받습니다.
배포뿐만 아니라 init가 아니고 직접 package를 작성했더라도 npm i를 통해서 모듈과 lock 파일을 다운받아야 합니다.
package파일을 사용하지 않는다면 일일이 script를 통해서 라이브러리를 사용해야 하므로 불편합니다.
package.json 파일 목록에 대한 명세는 방대하고 선택적임으로 링크를 참고해서 파악하시는 게 좋을 듯 합니다.
package.json 관리 목록 중 많이 헷갈리는 의존성에 대해서만 살펴보겠습니다.
단적으로 devDependencies는 개발용, dependencies는 배포용입니다.
npm install --save or npm i
dependencies <배포 의존성>
npm install --save-dev or npm i -D
devDependencies <개발 의존성>
devDpendencies은 최종 애플리케이션 빌드시 코드 안에 포함되지 않습니다. 따라서 배포용으로 사용하지 않는 라이브러리는 devDependencies에 저장하면 됩니다.
브라우저 상에서 동작하는 JS는 script tag로 로드하며 복수의 JS 파일을 로드할 경우 하나의 파일로 merge되며 동일한 유효범위를 갖게된다.
Node.js는 모듈 단위로 각 기능을 분할한다.
모듈은 module.exports 또는 exports 객체를 통해 정의하고 외부로 공개한다. 공개된 모듈은 require 함수를 사용해 임포트한다.
모듈 안에 선언한 모든 것들은 기본적으로 해당 모듈 내부에서만 참조 가능하다.
이를 외부에 공개하여 다른 모듈에서 사용할 수 있도록 해주는 것이 export 객체이다.
전역 함수 require()로 추출한다.
// circle.js
const { PI } = Math;
exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;
// app.js
const circle = require('./circle.js'); // == require('./circle')
console.log(`지름이 4인 원의 면적: ${circle.area(4)}`);
console.log(`지름이 4인 원의 둘레: ${circle.circumference(4)}`);
export 객체는 프로퍼티/메소드를 여러개 정의 할 수 있는 것에 반해 module.exports에는 하나의 값(원시 타입, 함수, 객체)을 할당할 수 있다.
require()로 할당받은 변수는 module.exports에 할당한 값 자체이다.
exports는 module.exports의 참조이며 module.exports의 alias이다. 즉, exports는 module.exports와 같다고 보아도 무방하다.
module.exports에 함수를 할당하는 방식
// foo.js
module.exports = function(a, b) {
return a+b;
};
// app.js
const add = require('./foo');
const result = add(1, 2);
console.log(result);
module.exports는 1개의 값만을 할당할 수 있기에 다음과 같이 객체를 사용하여 복수의 기능을 하나로 묶어 공개하는 방식을 사용할 수 있다.
exports에 객체를 할당하는 방식
// foo.js
module.exports = {
add (v1, v2) { return v1 + v2 },
minus (v1, v2) { return v1 - v2 }
};
// app.js
const calc = require('./foo');
const result1 = calc.add(1, 2);
console.log(result1); //3
const result2 = calc.minus(1, 2);
console.log(result2); //-1
require 함수의 인수에는 파일뿐만 아니라 디렉터리를 지정할 수도 있다.
모듈을 명시하지 않는 경우에는 해당 디렉터리의 index.js를 로드한다.
project/
├── app.js
└── module/
├── index.js
├── calc.js
└── print.js
// app.js
const myModule = require('./module');
// module/index.js
module.exports = {
calc: require('./calc'),
print: require('./print')
};
app.js 에서의 한 번의 require로 module/ 디렉터리 하위의 모든 모듈들을 사용할 수 있다.
간단하게 비유하면 "비조리형 배달음식"으로 볼 수 있다.
식당에서 재료만 손질해서 전달 주면 고객이(client) 알아서 음식을 완성해서 식사하는게 최종 형태를 브라우저에서 완성하는 CSR과 비슷하다고 할 수 있다.
조금 더 사전적으로 정의하여보면, "웹 페이지의 렌더링이 클라이언트 측에서 이루어지는 것"을 의미한다.
브라우저가 최초로 서버에 Request를 보내면, html, css, js파일을 차례로 다운로드 한다.
React를 사용하여 페이지를 구현하였을 경우, html만 받아졌을 때는 페이지가 비어져있다가 javascript가 모두 다운로드 완료되어야 그제서야 화면에 컨텐츠가 그려진다.
우리가 React를 CRA를 사용해서 생성할 경우 렌더링이 CSR방식으로 일어나게 된다.
CSR과 반대로 "완조리형 배달음식"을 떠올리면 된다.
식당에서 음식을 완성해서 배달하면 고객은 먹기만 하면 되는거다.
일일이 조리를 다 해야하기에 주방이(서버가) 바쁘다는 특징이 있다.
이것도 사전적으로 정의해보면, "첫 페이지 렌더링을 클라이언트가 아닌 서버에서 진행하는 것"이라고 볼 수 있다.
서버에서 먼저 HTML,CSS를 렌더링 해서 정적인 화면을 먼저 client측에 전달해주고 나중에 동적인 요소가 필요한 경우에 client로 JS를 가져와서 활용하는 형태이다.
SEO 최적화를 이루어내면서 동시에 SPA를 활용하여 쾌적한 사용환경을 만들 수 없을지 여러 사람들이 고민하였고, 그 결과로 SPA for SSR이 등장하게 되었다.
그 방법인 즉슨 1. 첫 렌더는 SSR방식으로 진행하고, 2. 두번째 렌더부터는 CSR로 진행하는 것이다.