내게는 Django 뿐일 줄 알았는데...
21장. 백엔드 프로그래밍 : Node.js
웹 애플리케이션을 만들 때,
React와 같은 프론트엔드 기술만으로 필요한 기능을 모두 구현할 순 없다.
왜? 데이터를 여러 사람과 공유하려면... 저장할 공간이 필요하니까!
이를 위해 우리는 서버를 만들어 데이터를 저장해 여러 사람과 공유한다.
그러나 서버에 데이터를 무작정 담는 것은 아니다!
→ 이러한 규칙을 지정하는 것이 백엔드(서버) 프로그래밍이라 생각하면 좋을 듯!
21-1) Node.js 알아보기
Node.js는? 웹 브라우저뿐만 아니라 서버에서도 JS를 사용할 수 있는 런타임!
Node.js 환경에서 웹 서버를 구축할 때는 보통 Express, Hapi, Koa 등의 웹 프레임워크를 사용한다.
여기서는 Koa를 이용해볼 예정!
우리가 필요한 기능들만 붙여서 서버를 만들 수 있기 때문에 Express보다 훨씬 가볍다고 한다.
21-2) 작업 환경 준비
yarn init -y를 통해 패키지 정보 생성!
완료하면 package.json 파일이 만들어진다. cat package.json을 통해 확인 가능!
Koa 친구도 깔아주자.
21-3) Koa 기본 사용법
1. 서버 띄우기
package.json이 설치된 디렉터리에 blog-backend라는 폴더를 만들고, 그 안에 index.js 파일을 생성!
index.js
서버를 포트 4000번으로, 접속하면 텍스트를 반환하도록 설정해줬다.
서버 실행!
(원래 node를 통해 JS 파일을 실행할 때는 node src/index.js처럼 전체 경로를 입력해야 한다)
엄청난 페이지가 하나 뜨는 모습.
2. 미들웨어
Koa 애플리케이션은 미들웨어 배열로 구성되어 있다.
조금 전 사용한 app.use 함수는 미들웨어 함수를 애플리케이션에 등록한다는 뜻!
미들웨어 함수는
(ctx, next) => { } 와 같은 구조로 이루어져 있다.
즉 ctx와 next라는 두 개의 파라미터를 받는 건데...
ctx는 Context의 줄임말로 웹 요청과 응답에 관한 정보를 지니고 있다.
next는 현재 처리 중인 미들웨어의 다음 미들웨어를 호출하는 함수.
(미들웨어를 등록하고 next를 호출하지 않으면, 다음 미들웨어 실행 X)
→ (따라서 다음 미들웨어 처리할 필요 없으면 next를 생략해 설정하지 않음)
index.js
즉 이렇게 작성해주면...
터미널 창에(터미널에 뜨는 게 희한도 하다) 이렇게 뜨는 걸 확인할 수 있다!
맨 위 next()를 지워버리면? 당연히 1 하나만 찍힌다.
authorized=1이라는 쿼리 경로가 포함되어 있으면, 이후 미들웨어를 실행하도록 해줬다.
쿼리에 따라 작동되는 함수가 달라진 모습.
이때 쿼리 문자열은 문자열이므로 반드시 문자열 형태로 비교해줘야 한다.
(쿼리뿐만 아니라, 이후에는 쿠키나 헤더를 통해서도 처리가 가능!)
next 함수를 호출하면 Promise를 반환한다.
요 Promise는 다음에 처리해야 할 미들웨어가 끝나야 완료!
여기서는 next 함수 호출 이후 then을 사용해 Promise가 끝난 후 END를 기록해줬다.
즉 이런 식으로!
이런 식으로 async & await 친구들을 사용할 수도 있다.
21-4) nodemon 사용하기
지금까지는 코드를 고치는 족족 서버를 껐다가 재시작해줬다.
무지막지 귀찮으니까... nodemon이라는 녀석을 사용해보자.
개발용 의존 모듈로(--dev를 붙여줬다) 설치!
package.json
package.json에 들어가 다음과 같이 추가!
start 스크립트에는 서버를 시작하는 명령어를 넣고,
start:dev 스크립트에는 nodemon을 통해 서버를 실행해 주는 명령어를 넣었(단)다.
여기서 nodemon은 src 디렉터리를 주시하고 있다가...
해당 디렉터리 내부의 어떤 파일이 변경되면, 이를 감지해 src/index.js 파일을 재시작해 준다!
21-5) koa-router 사용하기
React에서는 웹 브라우저 라우팅을 돕는 react-router를 사용했었다.
Koa를 사용할 때는? koa-router를 사용!
역시 깔아주시면 된다.
index.js
쨘. 경로에 따라 다른 텍스트가 뜨도록 해줬다.
프로젝트가 커지면 커질수록 라우터도 방대해질 테다!
→ src 디렉터리에 api라는 폴더를 만들어 라우터를 따로 관리해주자.
api/index.js
이렇게 api란 이름의 라우터를 만든 후...
src/index.js
index.js에서 가져와 사용해주면 된다!
따라서 api/test 란 요청을 보내면 이렇게 뜰 테다.
다음으론 posts 라우트를 생성해보자.
api 디렉터리 내에 posts라는 폴더를 만들어주자.
posts/index.js
posts란 라우터에 여러 종류의 라우트를 설정한 후, 모두 printInfo란 함수를 호출하도록!
(현재 요청의 메서드, 경로, 파라미터를 JSON 타입으로 반환한다)
요 posts 라우터를 api 라우터에 적용해주자.
api/index.js
이런 식으로!
그럼 이렇게 뜬다. DRF가 생각나는 순간.
Postman을 사용하면 다른 HTTP 메서드도 확인이 가능하다!
2021.02.16 - [Django/Django REST Framework] - Django REST Framework _ Postman 사용하기
사용법은 이전 게시글을 참고.
라우트를 작성 시 특정 경로에 미들웨어를 등록할 때는...
아까처럼 두 번째 인자에 함수를 선언에 넣어줄 수 있었다.
만약 각 라우트 처리 함수의 코드가 무지막지하게 길어지면? 영 좋지는 않을 테다.
따라서 이 라우트 처리 함수들을 다른 파일로 따로 분리해서 관리할 수 있다.
→ 요 라우트 처리 함수만 모아 놓은 파일을 컨트롤러 라고 부른다!
API 기능을 본격적으로 구현하기 전...
koa-bodyparser 미들웨어를 적용해야 한다!
얘는 POST/PUT/PATCH 같은 메서드의 Request Body에 JSON 형식의 데이터를 넣어주면,
이를 파싱에 서버에서 사용할 수 있게 한다.
일단은 신나게 설치.
src/index.js
미들웨어를 불러와 적용하면 되는데...
중요한 점은 router를 적용하기 전에 해야 한다는 것!
다음은 posts 디렉터리에 posts.ctrl.js란 파일을 생성!
posts.ctrl.js
// id 초깃값
let postId = 1;
// posts 배열 초기 데이터
const posts = [
{
id: 1,
title: '제목',
body: '내용',
},
];
// 포스트 작성 : POST/api/posts
exports.write = (ctx) => {
// ctx.request.body에서 REST API의 Request Body 조회 o
const { title, body } = ctx.request.body;
postId += 1;
const post = { id: postId, title, body };
posts.push(post);
ctx.body = post;
};
// 포스트 목록 조회 : GET/api/posts
exports.list = (ctx) => {
ctx.body = posts;
};
// 특정 포스트 조회 : GET/api/:id
exports.read = (ctx) => {
const { id } = ctx.params;
// 주어진 id 값으로 포스트 찾음
// 파라미터로 받아 온 값은 문자열 형식 -> 얘를 숫자로 변환하거나 비교할 p.id 를 문자열로 바꿔줘야함
const post = posts.find((p) => p.id.toString() === id);
// 포스트가 없으면 오류 반환
if (!post) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다',
};
return;
}
ctx.body = post;
};
// 특정 포스트 제거 ; DELETE/api/posts/:id
exports.remove = (ctx) => {
const { id } = ctx.params;
// 해당 id의 post가 몇 번째인지 확인
const index = posts.findIndex((p) => p.id.toString() === id);
// 포스트 없으면 오류 반환
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트 존재 안함',
};
return;
}
// index 번째 아이템을 제거
posts.splice(index, 1);
ctx.status = 204; // no content 에러
};
// 포스트 수정(교체) : PUT/api/posts/:id
exports.replace = (ctx) => {
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트 존재 안함',
};
return;
}
// PUT -> 객체 전체를 덮어 씌운 후, 통째로 교채
// 따라서 id 제외한 기존 정보 모조리 날린 후, 객체 새로 만듬
posts[index] = {
id,
...ctx.request.body,
};
ctx.body = posts[index];
};
// 포스트 수정(변경) : PATCH/api/posts/id
exports.update = (ctx) => {
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트 존재 안함',
};
return;
}
// 기존 값에 정보 덮어 씌움
posts[index] = {
...posts[index],
...ctx.request.body,
};
ctx.body = posts[index];
};
우...와...
컨트롤러를 만들면서 exports.이름 을 통해 함수를 export 해줬다!
이렇게 내보낸 녀석들은...
const 모듈이름 = require('파일이름');
모듈이름.이름(); 형태로 사용 가능!
요 컨트롤러 함수들을 라우트에 연결!
posts/index.js
list, read, remove를 제외한 API들은 요청할 때 Request Body가 필요하다!
Postman을 통해 이렇게 넣어줄 수 있다! 응답도 잘 받아와 진다! 와우!
'source-code > React' 카테고리의 다른 글
API 요청으로 데이터 사용하기 (0) | 2021.03.26 |
---|---|
리액트를 다루는 기술 _ 22장 (0) | 2021.03.24 |
리액트를 다루는 기술 _ 19장, 20장 (0) | 2021.03.23 |
리액트를 다루는 기술 _ 17장 (0) | 2021.03.22 |
리액트를 다루는 기술 _ 16장 (0) | 2021.03.19 |