4.1. 요청과 응답 실행하기
1. 서버와 클라이언트
⇒ 서버와 클라이언트의 관계
- 클라이언트가 서버로 요청(request)를 보내면 서버는 해당 요청을 처리한다.
- 처리된 결과를 서버가 클라이언트로 응답(response)을 보낸다.
2. 노드로 http 서버 만들기
⇒ http 요청에 응답하는 노드 서버
- createServer로 요청 이벤트에 대기
- req 객체는 요청에 관한 정보를 담고 있고, res 객체는 응답에 관한 정보를 담고있다.
1) http 서버 만들기
// server1.js
const http : require('http');
const server = http.createServer((req,res) => {
// res는 스트림이다.
// response의 header정보를 설정한다.
res.writeHead({'Content-type' : 'text/html; charset=utf-8'});
res.write('<h1>Hello Node</h1>');
res.write('<p>Hello Server</p>');
res.end('<p>Hello zerocho</p>');
})
.listen(8085); // 포트정보 설정
// 실행중일때 해당 내용이 수행된다.
server.on('listening',() => {
console.log('8085포트에서 대기중');
});
// node에서는 error처리를 해줘야 한다.
// 프로세스 사망의 원인
server.on('error', (err) => {
console.error(err);
});
// cmd에서 node server1 파일을 실행
cmd C:\work\005_study\http> node server
8085서버에서 접근 대기중
2) fs로 HTML 파일을 읽어 제공하기
// server2.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Node.js 웹서버</title>
</head>
<body>
<h1>Node.js 웹 서버</h1>
<p>만들 준비되셨나요??</p>
</body>
</html>
// server.js
const http = require('http);
const fs = require('fs').promises;
// 비동기 함수는 try ~ catch로 예외처리를 해줘야 한다.
const server = server.createServer(async (req, res) => {
try {
res.writeHead(200, {'Content-type' : 'text/html; charset=utf-8'});
const data = await fs.readFile('./server2.html');
res.end(data);
} catch (err) {
console.error(err);
// plain은 문자열을 의미
res.writeHead(200, {'Content-type' : 'text/plain; charset-utf-8'});
res.end(err.message);
}
})
.listen(8085);
server.on('listening',() => {
console.log('8085서버에서 접근 대기중');
});
server.on('error', (error) => {
console.error(error);
});
// cmd
cmd C:\work\005_study\http> node server2
8085서버에서 접근 대기중
5. localhost와 포트
⇒ localhost는 컴퓨터의 내부 주소이므로 외부에서는 접근이 불가능하다.
⇒ 포트는 서버 내에서 프로세스를 구분하는 번호이다.
- 기본적으로 http는 80포트를 https는 443포트를 사용하며 주소창에서 해당 포트는 생략 가능하다.
- 다른 포트를 통해 데이터베이스나 다른 서버를 동시에 연결이 가능하다.
ex) 21(tfp), 23(telnet), 3306(mysql), 27017(MongoDB)
4.2. REST API 라우팅
⇒ 서버에서 요청을 보낼 때는 주소를 통해 요청의 내용을 표현한다.
- /index.html이면 index.html을 보내달라는 뜻이다.
- 항상 html을 요구할 필요는 없으며 서버가 이해하기 쉬운 주소가 좋다.
⇒ REST API(Representional State Transfer)
- 서버의 자원을 정의하고 자원에 대한 주소를 지정하는 방법(방식)
- 여기서 말하는 url을 동사에 속한다.
- /user 일 경우 사용자 정보에 관한 정보를 요청하는 것
- /post 일 경우 게시글에 관련된 자원을 요청하는 것
⇒ HTTP 요청 메서드
GET | 서버 자원을 가져오라고 할 때 사용4 | |
POST | 서버에 자원을 새로 등록하고자 할 때 사용 | |
PUT | 서버에 자원을 요청에 들어오는 자원으로 치환 하고자 할때 사용 | ex) 제로초라는 사람을 김재익으로 바꿈 |
PATCH | 서버 자원의 일부만 수정하고자 할 때 사용 | ex) 제로초라는 사람이 1살을 더 먹음 |
DELETE | 서버의 자원을 삭제 하고자 할때 사용 |
⇒ 클라이언트가 누구든 서버와 HTTP로 소통이 가능하다.
- iOS, 안드로이드, 웹이 모두 같은 주소로 요청을 보낼 수 있다.
- 서버와 클라이언트의 분리
⇒ RESTful
- REST API를 사용한 주소 체계를 사용하는 서버를 RESTful한 서버라고 말한다.
ex) GET /user 는 사용자를 조회하는 요청이다
ex2) POST /user는 사용자를 등록하는 요청이다.
- RESTful한 주소 구조
HTTP 메서드 | 주소 | 역할 |
GET | / | resFront.html 파일 제공 |
GET | /about | about.html 파일 제공 |
GET | /users | 사용자 목록 제공 |
GET | 기타 | 기타 정적 파일 제공 |
POST | /users | 사용자 등록 |
PUT | /users/사용자id | 해당id의 사용자 수정 |
DELETE | /users/사용자id | 해당id의 사용자 제거 |
- 개발자도구의 network에서 localhost정보 참조
: Headers는 데이터들의 데이터라고 볼 수 있다.
예를 들어 GET "/" 요청을 보냈을 경우 응답이 오면 그 html에 대한 데이터를 헤더로 담아서 보내준다.
보내줄 때 html이라는 것을 알려주고 utf-8인것, 몇시에 응답한건지, 성공적(200)인지 등등을 알려준다.
그 외에도 request response정보들이 다양하게 존재하므로 잘 살펴볼 필요성이 있다.
// REST API서버 중 GET 메서드를 통한 서버 구현
const http = require('http');
const fs = require('fs').promises;
const path = require('path');
const users = {}; // 데이터 저장용
const server = http.createServer(async (req, res) => {
try {
if(req.method === 'GET') {
if(req.url === '/') {
const data = await fs.readFile(path.join(__dirname,'restFront.html'));
res.writeHead(200, {'Content-Type' : 'text/html; chatset=utf-8;'});
return res.end(data);
} else if(req.url === '/about') {
const data = await fs.readFile(path.join(__dirname,'about.html'));
res.writeHead(200,{'Content-Type' : 'text/html; chatset=utf-8'});
return res.end(data);
} else if(req.url === '/users') {
res.writeHead(200,{'Content-Type' : 'application/json; chatset=utf-8'});
return res.end(JSON.stringify(users));
}
// url이 '/' or '/about' or '/users' 아니면
// 그 외의 GET 요청은 css, js파일을 요청하는 것
try {
const data = await fs.readFile(`.${req.url}`);
return res.end(data);
} catch (err) {
// 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
}
} else if(req.method == 'POST') {
if(req.url === '/user') {
let body = '';
// request는 스트림이기 때문에 청크드를 모아서 body로 받는다.
// 3.6.2절의 버퍼와 스트림에서 배웠던 readStream
req.on('data', (data) => body += data);
return req.on('end', () => {
console.log('POST 본문(Body):', body);
const { name } = JSON.parse(body);
const id = Date.now();
users[id] = name;
// 201 요청성공 and 리소스가 생성되었다.
res.writeHead(201, {'Content-Type' : 'text/plain; chatset=utf-8;'});
res.end(JSON.stringify(users));
});
}
} else if(req.method == 'PUT') {
// .startswith : /user/로 시작할 경우 true
if(req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
let body = '';
req.on('data',(data) => body += data);
return req.on('end', () => {
console.log('PUT 본문(BODY) : ', body);
users[key] = JSON.parse(body).name;
res.writeHead(200, {'Content-Type' : 'text/plain; chatset=utf-8;'});
return res.end(JSON.stringify(users));
});
}
} else if(req.method == 'DELETE') {
if(req.url.startsWith('/user/')) {
const key = req.url.split('/')[2];
let body = '';
req.on('data',(data) => body += data);
return req.on('end', () => {
console.log('DELETE 본문(BODY) : ' + body);
delete users[key];
res.writeHead(200, {'Content-Type' : 'text/plain; chatset=utf-8;'});
return res.end(JSON.stringify(users));
});
}
}
res.writeHead(404);
return res.end('NOT FOUND');
} catch (err) {
console.error(err);
res.writeHead(500, {'Content-Type' : 'text/plain; chatset=utf-8'});
res.end(err.message);
}
}).listen(8085, () => {
console.log('8085번 포트에서 서버 대기중 입니다.');
});
server.on('error',(err) => {
console.error(err);
});
// restFront.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>RESTful SERVER</title>
<link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<div>
<form id="form">
<input type="text" id="username">
<button type="submit">등록</button>
</form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./restFront.js"></script>
</body>
</html>
// restFront.js
async function getUser() {
try {
// axios로 /users를 호출 해 users객체를 불러온다.
const res = await axios.get('/users');
const users = res.data;
const list = document.getElementById('list');
list.innerHTML = '';
// 사용자마다 반복적으로 화면 표시 및 이벤트를 연결한다.
Object.keys(users).map((key) => {
const userDiv = document.createElement('div');
const span = document.createElement('span');
span.textContent = users[key];
const edit = document.createElement('button');
edit.textContent = '수정';
// 수정버튼 클릭 이벤트
edit.addEventListener('click', async () => {
const name = prompt('바꿀 이름을 입력하세요'); // 이름 입력창 호출
if(!name) {
return alert('이름을 반드시 입력하셔야 합니다.');
}
try {
await axios.put('/user/' + key, { name });
getUser();
} catch(err) {
console.error(err);
}
});
const remove = document.createElement('button');
remove.textContent = '삭제';
remove.addEventListener('click', async() => {
try {
await axios.delete('/user/' + key);
getUser();
} catch (err) {
console.error(err);
}
});
userDiv.appendChild(span);
userDiv.appendChild(edit);
userDiv.appendChild(remove);
list.appendChild(userDiv);
console.log(res.data);
});
} catch(err) {
console.log(err);
}
}
window.onload = getUser;
document.getElementById('form').addEventListener('submit', async(e) => {
// preventDefault는 submit의 기본동작을 방지한다.
// form submit의 원시적인 기능을 정지한다고 보면 된다.
e.preventDefault();
const name = e.target.username.value;
if(!name) {
return alert('이름을 입력하세요');
}
try {
await axios.post('/user', { name });
getUser();
} catch(err) {
console.error(err);
}
});
4.3 쿠키와 세션 이해하기
1. 쿠키의 중요성
⇒ 요청(request)에는 한 가지 단점이 있다. 정확히 누가 요청을 했는지 알 수 없기 때문이다(IP, 브라우저 정보만 앎)
해당 문제를 해소하기 위해서는 쿠키나 세션을 통해 로그인 처리를 해 사용자 정보를 담아 해소할 수 있다.
⇒ 쿠키는 키=값 이 한쌍의 형태로 이뤄져있다. 브라우저에 저장된 쿠키는 매 요청마다 서버에 동봉되어 보내지며
쿠키를 읽어 사용자가 현재 누구인지 파악할 수 있다.
2. 쿠키 서버 만들기
const http = require('http');
http.createServer((req, res) => {
console.log(req.url, req.headers.cookie); // 요청url주소와 요청헤더의 쿠키 정보
res.writeHead(200, {'Set-Cookie' : 'mycookie=zerocho'}); // 쿠키 파라미터 설정
res.end('Hello Cookie');
})
.listen(8085, () => {
console.log('8085 포트에서 대기중입니다.');
});
4. 헤더와 본문
⇒ http 요청과 응답은 헤더와 본문을 갖는다(개발자 도구에서 확인가능)
- 헤더는 요청 또는 응답에 대한 정보를 갖고 있다.
- 본문은 주고 받은 실제 데이터를 갖고 있다.
- 쿠키는 부가적인 정보이므로 헤더에 저장되어 있다.
5. http 상태 코드
⇒ http.writeHead 메서드에 첫번째 인수로 넣는 값은 브라우저에게 보내는 상태코드이다.
- 2XX : 성공을 알리는 코드이다. 200(성공), 201(작성됨)을 많이 사용한다.
- 3XX : 리다이렉션(다른페이지이동)을 알리는 데 사용한다. 어떤 주소를 입력 했는데 다른 주소로 넘어갈 때
이 코드가 사용된다. 300(영구이동), 301(임시이동)이 있다.
- 4XX : 요청 자체에 오류가 있을때 표기되며 401(권한없음), 403(금지), 404(찾을 수 없음)이 있다.
- 5XX : 서버 오류를 나타내며 욫어은 정상적이나 서버에 오류가 났을 때 발생한다. 이 오류를 클라이언트로 사용자가
직접 보내는 경우는 없고, 예기치 못한 에러 발생 시 서버가 5XX코드로 보내준다.
500(내부서버오류), 502(불량게이트웨이), 503(서비스를사용할수없음)이 자주 사용된다.
6. 쿠키로 내 정보를 입력
// cookie2.js
const http = require('http');
const fs = require('fs').promise;
const url = require('url');
const qs = require('querystring');
// parseCookies : 쿠키 문자열을 객체로 변환한다.
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
http.createServer(async (req, res) => {
const cookies = parseCookies(req.headers.cookie);
/login인 경우 쿼리스트링으로 온 이름을 쿼리로 저장.
if(req.url.startsWith('/login') {
const { query } = url.parse(req.url);
const { name } = qs.parse(query);
const expries = new Date();
expries.setMinutes(expries.getMinutes() + 5);
// url의 시작이 /login이 포함되어 있는 경우
// encodeURIComponent : URI로 데이터를 전달하기 위해 문자열 인코딩처리
// Exprires : 쿠키의 만료시간을 설정할 수 있다.
// Path : 설정한 패스 하위로 해당 쿠키를 사용할 수 있다.
// HttpOnly : 자바스크립트로 쿠키에 접근할 수 없도록 처리한다.
res.writeHead(302, {
Location : '/'
'Set-Cookie' : `name=${encodeURIComponent(name)}; Expries=${expries.toGMTString()}; HttpOnly; Path=/',
});
res.end(name);
// cookie에 name이존재할 경우
// HTTP Only Cookie가 활성화 되어 XSS같은 공격을 차단한다.
} else if(cookies.name) {
res.writeHead(200, {'Content-Type' : 'text/plain; charset=utf-8;'});
res.end(`${cookies.name}님 안녕하세요`);
} else {
try {
const data = await fs.readFile('./cookie2.html');
res.writeHead(200, {'Content-Type' : 'text/html; charset=utf-8'};
res.end(data);
} catch (err) {
res.writeHead(500, {'Content-Type' : 'text/plain; charset=utf-8'});
res.end(err.message);
}
}
});
7. 쿠키옵션
⇒ Set-Cookie의 다양한 옵션
- 쿠키명=쿠키값 : 기본적인 쿠키의 값이다. mycookie=test 또는 name=zerocho 같이 설정한다.
- Expires=Date : 쿠키 만료 기한을 설정할 수 있다. 설정할 때는 Date 객체를 넣는다.
- Max-age=초 : Expires와 기능은 동일하나 날짜 대신 초로 만료기한을 설정할 수 있다.
- Domain=도메인명 : 쿠키가 전송될 도메인을 특정할 수 있다. 기본 값은 현재 도메인이다.
- Path=URL : 쿠키가 전송될 URL을 특정할 수 있다. 기본 값은 '/'이고 이 경우 모든 url에서 쿠키를 전송할 수 있다.
- HttpOnly : 설정 시 자바스크립트에서 쿠키를 접근할 수 없다. 쿠키 조작을 방지하기 위해 사용한다.
9. 세션 사용하기
⇒ 쿠키의 정보는 클라이언트(브라우저)에 노출되어 수정되는 위험이 있다.
- 중요한 정보는 서버에서 관리하고 클라이언트(브라우저)에는 세션 키(uniqueKey)만을 제공하면 된다.
- 서버에 세션 객체(session)생성 후, uniqueInt(키)를 만들어 속성명으로 사용한다.
- 속성 값에 정보를 저장하고 uniqueInt를 클라이언트(브라우저)로 보낸다.
const http = require('http');
const fs = require('fs').promises;
const url = require('url');
const qs = require('querystring');
const parseCookies = (cookie = '') =>
cookie
.split(';')
.map(v => v.split('='))
.reduce((acc, [k, v]) => {
acc[k.trim()] = decodeURIComponent(v);
return acc;
}, {});
// 세선정보를 저장하는 공간
const session = {};
http.createServer(async (req, res) => {
const cookie = parseCookies(req.headers.cookie);
if(req.url.startsWith('/login')) {
const { query } = url.parse(req.url);
const { name } = qs.parse(query);
const expires = new Date();
expires.setMinutes(qxpires.getMinutes() + 5);
const uniqueInt = Date.now();
// 서버 내에서만 name, expires 속성정보를 확인 가능하다.
// 쿠키에는 세션의 키(uniqueInt)만 전달한다.
session[uniqueInt] = {
name,
expires,
};
res.writeHead(302, {
Location : '/',
'Set-Cookie' : `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.end();
// 세션이 있거나 세션만료일시가 지나지 않았을 경우(크로스체크)
} else if(cookie.session && session[cookie.session].expires > new Date()) {
res.writeHead(200, {'Content-Type' : 'text/plain; charset=utf-8'});
res.end(`${session[cookies.session].name}님 안녕하세요`);
} else {
try {
const data = await fs.readFile('./cookie2.html');
res.writeHead(200, {'Content-Type' : 'text/html; charset=utf-8;'});
res.end();
} catch(err) {
res.writeHead(500, {'Content-Type' : 'text/plain; charset=utf-8'});
res.end(err.message);
}
}
});
4.4 https와 http2
1. https
⇒ 웹 서버에 SSL 암호화를 추가하는 모듈이며 요청/응답 간 데이터를 암호화 처리한다.
- 최근에는 개인정보에 대한 보안이 필수이므로 https는 기본적으로 사용된다.
2. https서버
⇒ http서버를 https로 변환하고 자 할 때에는 암호화를 인증서를 발급 받아야 한다.
- 인증서 무료기관 : https://letsencrypt.org/ko/
// https.js
const https = require('https');
const fs = require('fs');
// readFileSync는 동기 메서드이므로 최초 초기화 할때 사용하기 유용하다.
// https는 인증서 정보를 넘겨줄 매개변수가 하나 더 추가된다.
https.createServer({
cert : fs.readFileSync('도메인 인증서 경로'),
key : fs.readFileSync('도메인 비밀키 경로'),
ca : [
fs.readFileSync('상위 인증서 경로'),
fs.readFileSync('상위 인증서 경로'),
],
},(req, res) => {
res.writeHead(200, { 'Content-Type' : 'text/html; charset=utf-8'});
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(443, () => {
console.log('443 포트에서 대기중입니다.');
});
3. http2
⇒ SSL 암호화와 더불어 최신 HTTP 프로토콜인 http/2를 사용하는 모듈이다.
- 요청 및 응답 방식이 기존 http/1.1보다 개선되어 웹의 속도가 향상 되었다.
- 자원들의 동시성을 늘려 여러 개의 요청을 한번에 처리하여 빠르다.
const http2 = require('http2');
const fs = require('fs');
// http2는 ssl과 동일하나 서버를 생성하는 메서드가 다르다.
// createSecureServer
http2.createSecureServer({
cert : fs.readFileSync('도메인 인증서 경로'),
key : fs.readFileSync('도메인 비밀키 경로'),
ca : [
fs.readFileSync('상위 인증서 경로'),
fs.readFileSync('상위 인증서 경로'),
],
}, (req, res) => {
res.writeHead(200, {'Content-Type' : 'text/html charset-8'});
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Server!</p>');
})
.listen(443, () => {
console.log('443번 포트에서 대기중입니다');
});
4.5 cluster
1. cluster
⇒ 노드는 기본적으로 싱글 스레드이기 때문에 CPU코어를 하나만 사용하지만 CPU코어를 모두 사용할 수
있게 해주는 모듈이다.
- 포트를 공유하는 노드 프로세스를 여러 개 둘 수 있다.
- 요청이 많이 들어왔을 때 병렬로 실행된 서버의 개수만큼 요청이 분산되기 때문에 서버에 무리가 덜 간다.
- 코어가 8개인 서버가 있을 때 노드는 코어 하나만 활용하지만, cluster로 코어 하나당 노드 프로세스를
하나씩 배정할 수 있어 성능이 8배가 되는건 아니지만 병렬 처리이기 때문에 대폭 향상된다.
- 단점은 컴퓨터자원(메모리, 세션 등)에 대한 공유가 안되므로 Redis 등 별도의 서버로 해결해야 한다.
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length; // 현재 PC의 CPU코어수를 얻어온다.
if(cluster.isMaster) {
console.log(`마스터 프로세스 아이디 : ${process.pid}`);
// cpu개수만큼 워커 프로세스를 생성
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// 워커가 종료 되었을 경우
// 에러로 워커가 종료 되었을 경우에도 서버를 다시 살리는 로직을 구성해야 한다.
// 실제 운영할 때에도 이러한 형태는 아니지만 서버를 지속적으로 부활시킨다.
cluster.on('exit', (worker, code, signal) => {
console.log(`${worker.process.pid}번 워커가 종료 되었습니다.`);
console.log(`code : ${code}, signal : ${signal}`);
cluster.fork();
});
} else {
// 워커들이 포트에서 대기
// 로컬호스트에 접근하면 setTimeout을 이용하여 1초 후 프로세스 하나를 강제로 종료시킨다.
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type' : 'text/html; charset=utf-8'});
res.write('<h1>Hello Node!</h1>');
res.end('<p>Hello Cluster!</p>');
setTimeout(() => {
process.exit(1);
},1000);
})
.listen(8085, () => {
console.log('8085 포트에서 대기중입니다');
});
console.log(`${process.pid}번 워커 실행`);
}
⇒ 마스터 프로세스와 워커 프로세스
- 마스터 프로세스는 CPU 개수만큼 워커 프로세스를 만듦(worker_threads랑 구조가 비슷)
- 요청이 들어오면 워커 프로세스에 고르게 분배된다.
5.1 npm 알아보기
1. npm이란
⇒ Node Package Manager
- 노드의 패키지 매니저이며 다른 사람들이 만든 소스들을 모아둔 저장소이다.
- 남의 코드를 사용하여 프로그래밍이 가능하기 때문에 직접 구현할 필요가 없어 효율적이다.
- 오픈 소스 생태계를 구성중이다.
- 패키지 : npm에 업로드된 노드 모듈
- 모듈이 다른 모듈을 사용할 수 있듯 패키지도 다른 패키지를 사용할 수 있다(의존관계)
2. package.json 속성들
⇒ package name : 패키지의 이름
⇒ version : 패키지의 버전이며 npm의 버전은 다소 엄격하게 관리되고 있다.
⇒ entry point
: 자바스크립트 실행 파일의 진입점이다. 보통 마지막으로 module.exports를 하는 파일을 지정한다.
package.json의 main 속성에 지정한다.
⇒ test comand
: 코드를 테스트 할 때 입력할 명령어를 의미한다. package.json scripts 속성 안에 test 속성에 저장된다.
⇒ git repository
: 코드를 저장해 둔 Git 저장소 주소를 의미한다. 추후 소스에 문제가 발생 시 사용자들이 저장소에 들러
이의를 제기할 수 있고, 코드 수정본을 직접 올릴 수도 있다. package.json의 repository 속성에 저장된다.
⇒ keyword : 키워드는 npm 공식 홈페이지(https://npmjs.com)에서 패키지를 쉽게 찾을 수 있게 해준다.
⇒ license : 해당 패키지의 라이센스를 넣어주면 된다 (ex) MIT)
{
"name": "npmtest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "JaeikKim",
"license": "MIT",
"dependencies": {
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.6",
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.2",
"rimraf": "^5.0.5"
}
}
5. node_modules
⇒ npm install 시 node_modules 폴더가 생성된다.
- 내부에 설치한 패키지들이 들어있다.
- express 패키지를 dependency했을 때 해당 패키지 외에 의존관계인 타 패키지들도 함께 설치된다.
⇒ package-lock.json에서 express의 패키지간 의존관계를 명확하게 확인할 수 있다.
6. 여러 패키지 동시에 설치하기
⇒ npm i cookie-parser body-parser -D
7. 개발용 패키지로 패키지 설치하기
⇒ npm i nodeman -D
8. 글로벌(전역) 패키지
⇒ npm i -g 패키지명
- 모든 프로젝트와 콘솔에서 패키지를 실행할 수 있지만 dependency에 명시되지 않는 단점이 있어 권장하지 않는다.
- 디펜던시 개발용 패키지에 포함시켜 'npx 패키지명 명령어' 로 글로벌 설치 없이 명령어 사용이 가능하다.
5.3 패키지 버전 이해하기
1. SemVer 버저닝
⇒ 노드 패키지의 버전은 SemVar(유의적 버저닝) 방식을 따른다.
- Major(주 버전), Minor(부 버전), Patch(수 버전)
- 노드에서는 배포를 할 때 항상 버전을 올려야 한다.
- Major는 하위 버전과 호환하지 않은 수정사항이 생겼을 때 올린다.
- Minor는 하위 버전과 호환되는 수정사항이 생겼을 때 올린다.
- Patch는 기능에 버그를 해결했을 때 올린다.
2. 버전 기호 사용하기
⇒ 버전 앞에 기호를 붙여 의미를 더한다.
- ^1.1.1 : 패키지 업데이트 시 minor 버전까지만 업데이트가 되도록 고정한다(2.0.0버전은 안됨)
- ~1.1.1 : 패키지 업데이트 시 patch 버전까지만 업데이트가 되도록 고정한다.(1.2.0버전은 안됨)
- >=, <=, >, < 는 이상, 이하, 초과, 미만을 의미한다.
- @latest는 최신을 의미하며 패키지 버전을 @3.2.1 이런식으로 명시해서 받을 수도 있다.
- @next로 정식 배포판이 아닌 가장 최신 배포판을 사용해볼 수도 있다.(불안정)
- 알파/베타/RC 버전이 존재할 수도 있다.(1.1.1-alpha.0, 2.0.0-beta.1, 2.0.0-rc.0)
5.4 기타명령어
1. 기타 명령어
⇒ 명령어는 수정되는 경우가 있어 https://docs.npmjs.com/cli/v10/commands 에서 확인하는 편이 좋다.
⇒ npm outdated
: 어떤 패키지에 기능 변화가 생겼는지 알 수 있다. Current는 현재 버전이며 Wanted까지는 'npm update' 명령어를 통해
업데이트가 자동으로 된다. Latest 메이저가 변경되는 경우이며 직접 확인하고 업데이트 처리해야 한다.
⇒ npm uninstall 패키지명 : 패키지 삭제(npm rm 패키지명으로도 가능하다)
⇒ npm search 검색어 : npm패키지를 검색할 수 있다(npmjs.com에서도 가능)
⇒ npm info 패키지명 : 패키지의 세부 정보 파악 가능
⇒ npm adduser : npm에 로그인을 하기 위한 명령어(npmjs.com에서 회원가입)
⇒ npm whoami : 현재 로그인 된 계정이 어떤 계정인지 알려준다.
⇒ npm logout : 로그인한 계정을 로그아웃 처리한다.
⇒ npm version 버전
: package.json 버전을 올린다. 이 명령어를 통해 버전을 올리는 이유는 git도 동시에 commit 되어
버전관리를 처리해주기 때문이다.
ex) 'npm version major', 'npm version minor', 'npm version patch' 각 버전단위 1씩 올릴 수 있다.
⇒ npm deprecate [패키지명][버전] [메시지]
: 패키지를 설치할 때 경고 메시지를 띄우게 함(오류가 있는 패키지에 적용)
⇒ npm publish : 자신이 만든 패키지를 배포한다.
⇒ npm unpublish --force : 자신이 만든 패키지를 배포 중단한다(배포 후 72시간 내에만 가능)
⇒ npm ls 패키지명 : 해당 패키지가 있는지, 해당 패키지가 어떤 패키지의 dependencies인지 보여준다.
'BackEnd > Node' 카테고리의 다른 글
[노드교과서] 섹션 6. 데이터베이스 (0) | 2024.01.03 |
---|---|
[노드교과서] 섹션5. 익스프레스 웹 서버 만들기 (0) | 2023.12.31 |
[노드교과서] 섹션 2. 노드 기본 기능 익히기 (0) | 2023.12.17 |
[노드교과서] 프런트엔드 자바 스크립트 (0) | 2023.12.13 |
[노드교과서] 섹션 1. 알아두어야 할 자바스크립트 (0) | 2023.12.12 |