#0 Post로 백엔드에 데이터 보내기
Post는 Get처럼 페이지를 Request하는 또 다른 메소드 중 하나이다.
Get이라는 Method를 사용할 때, 특정 URL에 Method 정보를 보내서 페이지 HTML을 불러오도록 Server에 요청했듯이,
Post 또한 특정 URL을 통해 서버로 Method 정보를 보내서 페이지의 res를 기다린다.
이 둘의 차이는 간단하게 URL에 정보가 표시되냐, 그렇지 않냐의 차이이다.
사이트를 이동할 때 결국 URL창에 모든 경로가 표시되는 이유도 다 이와 일맥 상통한다.
결국 POST는 DB의 값을 변경하는 등의 작업에 사용하여 정보를 숨기도록 할 때 이용한다.
POST는 이미 Get이 사용중인 URL에 가는 건 전혀 상관 없으나, res를 구분해서 잘 해줘야 오류가 발생하지 않는다.
아래가 예시
docRouter.get("/:id(\\d+)/edit", getEdit);
docRouter.post("/:id(\\d+)/edit", postEdit);
위 두줄은 아래처럼 줄여서 쓸 수 있다.
docRouter.route("/:id(\\d+)/edit").get(getEdit).post(postEdit);
+ 추가
res.redirect() 는 어떤 기능일까?
브라우저가 자동으로 이동하게 하는 기능으로, 특정 URL로 보낼 때 사용한다.
예를들어 게시글을 작성하는 POST req를 처리하고 나서 res로서 게시글 목록 페이지로 이동시킬 때 사용하면 된다.
++ 추가2
form의 body란 무엇일까?
form태그 내부에서 작성되어 서버로 전송되는 HTTP req의 데이터들을 body라고 칭한다.
express에서는 POST로 전송되는 form의 body를 다루기 위해서 추가적인 설정을 해주어야 하는데, 아래와 같다.
//미들웨어들은 모두 라우팅 전에 사용해야함을 명심.
app.use(express.urlencoded({extended: true}));
//Controller에서 출력해보면 object로 form의 데이터들이 나오는 걸 알 수 있다.
export function PostDocumentUpload(req, res){
console.log(req.body);
return res.redirect("/documents/upload");
}
#1 MongoDB 설정하기
링크 - https://code-magic.tistory.com/112
#1 Mongoose 설정하기
Mogoose는 MongoDB를 JS코드로 사용하기 위한 패키지이다.
npm i mongoose
이제 db와 연동해줄 js파일을 만들어서 아래와 같이 기본 설정을 해준다.
import mongoose from "mongoose";
const dbName = "";
mongoose.connect(`mongodb://127.0.0.1:27017/${dbName}`);
이후 처음 시작되는 js파일(server.js)에 파일 자체를 import하여 같이 동작하도록 한다.
import "./db";
+추가
파일 자체를 import하는 행위란?
js가 실행시켜주나 늦게 실행됨.
다시 db 파일에서 db의 여러가지 상태를 출력해주는 함수를 추가해준다. 에러나 접속 등의 행동을 수행할 때 출력됨.
이때, on method는 event listenr처럼 여러번 상태를 감시하고 출력하지만. once는 한번만 감시한다.
function dbOpenEvent(){
console.log("DB Connected!");
}
dbState.on("error", (error) => console.log("DB Error", error));
dbState.once("open", dbOpenEvent);
#2 DB 구조에 따라 schema 및 model 선언하기
공식 문서 - https://mongoosejs.com/docs/models.html
프로젝트 내에서는 src/models 폴더 하위에 선언하는게 일반적이다.
먼저 type을 명시한 schema를 선언하고, 이를 모델로 만들어주는 과정을 거친다.
1. schma 선언
//src/models/Document.js
import mongoose from "mongoose";
const document = new mongoose.Schema({
title: String,
description: String,
createdAt: Date,
wordFrequencies:[
{
word: String,
}
],
});
위는 모델의 자료형을 미리 선언해주는 과정이다.
2. 이 아래에 model 선언. 첫번째 인자에는 만들고 싶은 모델의 사용자 지정 이름이,
두번째 인자에는 생성한 스키마를 입력한다.
const documentModel = mongoose.model("Document", documentSchema);
//다른 파일에서 사용할 수 있도록.
export default documentModel;
.model()은 선언한 schema를 복사해주는 과정과 같기 때문에 schma를 잘 확인한다.
*첫번째 인자로 넣은 "Document"는 mongoose가 자동으로 소문자화, 복수화하여 documents 라는 Collection을 찾게 된다.
따라서 const로 선언하는 변수는 js에서 쓰기 위한 코드이고, 인자로 들어가는 이름은 모델명임과 동시에 collection명과도 관련이 되게 된다.
3. 위의 const documentModel 안에 저장된 model에 DB와 연동하여 instance를 할당하면 용어적으로 'document'라고 부른다. 아래는 instance를 할당하기까지 과정이다.
(현재 명명한 변수 이름이 똑같아서 헷갈릴 수 있음. .save()가 되기 전까지는 아님.)
import Document from "../models/Document";
const doc = new Document({
title: title,
categories: categories,
description: document,
createdAt: Date.now(),
annotations: annotations,
})
위와 같이 doc 변수 안에 저장된 "Document"는 model을 통해 만들어진 js Object이다.
이는 요약하자면 mongoose가 만들어내는 똑똑한 object라고 할 수 있다.
미리 schema에서 선언해둔 자료형과 최대한 맞춰주고, 맞춰주기 어려울 만큼 데이터 유효성 검사에서 어긋난 데이터는 제외하고 object를 생성하기도 한다.
또, id와 같은 요소들을 자동으로 object에 추가하여 document를 만들기도 한다.
어쨋든 이렇게 생성된 document들은 자체 method로 DB에 저장할 수 있는데, .save() 가 대표적이다.
일단 이들은 promise를 반환하기 때문에 async await을 사용해주어야 한다.
위 코드로 설명하자면 아래와 같이 된다.
//async 함수 내부
await doc.save()
#4에서 이어서 설명...
#3 DB와 소통하는 방법 두가지: callback과 promise
이전에 정리해둔 비동기 개념들: https://code-magic.tistory.com/95
결국 비동기적으로 작동하기 때문에 SSR을 적용시키려면 비동기 처리를 해줘야 한다.
상세하게 풀어서 설명하기 전에 간단히 요약하면, Controller에서 비동기 처리를 해준 후, render를 작성하여 마무리하면, DB에서 데이터 처리가 완료된 이후에 res처리가 시작된다.
async await을 사용해서 비동기 처리를 해주면 아래와 같다.
xport async function wordInfo(req, res){
const word = req.params.w;
//word를 기반으로 DB에서 변환되지 않은 문서 html을 불러온다.
//const data = "<h1 class='word__title'/> 글라골문자 </h1>"
const document = await Document.find({});
//이렇게 불러온 document 정보 기반으로 data, title, annotation을 만들어주는 코드.
return res.render("word", {tab: `${word} - 러시아위키`, data, title, annotations});
}
다만 error처리는 try catch문으로 감싸서 처리한다.
#4 DB에 저장하고, 반환값으로 Document를 받고, validate 속성 추가까지.
export async function postDocumentUpload(req, res){
//console.log(req.body);
const {title, category, document, annotation_title, annotation} = req.body;
let categories = [];
categories.push(category);
let annotations = [];
annotations.push({
annotation_title: annotation_title,
annotation_content: annotation
});
const doc = new Document({
title: title,
categories: categories,
description: document,
createdAt: Date.now(),
annotations: annotations,
})
await doc.save();
return res.redirect("/documents/upload");
}
위 코드에서 object 생성과 .save()로 저장하는 과정을 합쳐서 아래와 같이 .create() 로 사용할 수 있다.
await Document.create({
title: title,
categories: categories,
description: document,
createdAt: Date.now(),
annotations: annotations,
})
여기서도 반환 값으로 document(변수명 아님. mongoose용어)를 받게 된다.
+ 이제 try catch와 required 속성을 추가할 것이다.
먼저 required 속성을 추가하려면 schema 단계에서 진행해야한다.
const documentSchema = new mongoose.Schema({
title: {type: String, required: true},
description: {type: String, required: true, default: Date.now},
categories: [{type: String}],
createdAt: {type: Date, required: true},
idfValue: Number,
wordFrequencies:[
{
String: Number,
}
],
annotations:[{
annotation_title: String,
annotation_content: String
}],
});
+추가로 default에는 callback함수를 써준다. 해당 스키마를 사용해 model을 만들 떄, 함수를 실행하게 된다.
이후 await으로 DB에 접속하는 함수는 항상 오류가 나면 해당 라인에서 멈춰버리기 때문에 try catch로 해결해준다.
이때, 오류 message를 출력할 건데 catch의 error 인자에서 error._message로 접근하면 짧은 오류 코드를 볼 수 있다. 이를 통해 error 페이지까지 하나 만들어준다.
try{
await Document.create({
title: title,
categories: categories,
description: document,
createdAt: Date.now(),
annotations: annotations,
})
return res.redirect("/documents/upload");
}
catch(error){
return res.render("errorPage", {error:error});
}
'프로젝트 기록 > 러시아어 사전 및 검색 웹' 카테고리의 다른 글
개발 과정 리뷰 -7- ejs와 html escape 개념 (0) | 2023.12.14 |
---|---|
개발 과정 리뷰 -5- Webpack 적용하기 (0) | 2023.12.09 |
개발 과정 리뷰 -3- (0) | 2023.12.07 |
개발 과정 리뷰 -2- 라우터와 정규표현식 (0) | 2023.12.04 |
개발 과정 리뷰 -1- (0) | 2023.12.03 |