현재 우리의 프로젝트의 서버 디렉토리는 다음과 같은 구조로 되어있다.
server
api
datingUsers
meetingUsers
json
lib
models
- selfDating.js
- selfMeeting.js
- index.js
- socket.js
먼저 우리가 docker를 통해 생성한 mongoDB 컨테이너를 우리 프로젝트와 연결하는 작업이 필요하다.
다음은 server/index.js 의 일부이다.
mongoose .connect(process.env.MONGO_URI, { useNewUrlParser: true, useFindAndModify: false, }) .then(() => { console.log('Connected to MongoDB'); mongoose.connection.db.dropCollection('datingusers'); mongoose.connection.db.dropCollection('meetingusers'); DatingUser.insertMany(daters, function (err, result) { if (err) { console.error(e); }; }); MeetingUser.insertMany(meeters, function (err, result) { if (err) { console.error(e); }; }); }) .catch(e => { console.error(e); });
여기서 사용하는 mongoose란 DB에 있는 데이터를 자바스크립트 객체로 만들어주는 MongoDB ODM(Object Document Mapping)의 한 종류이다.
mongoose를 사용하기 전 npm이나 yarn과 같은 패키지 관리자를 통해 다운로드 받아주도록 한다.
먼저 mongoDB 서버와의 연결을 해야한다.
mongoose.connect(uri, option)
다음은 mongoose.connect의 문법이다. 우리는 환경 변수 파일에 있는
MONGO_URI
변수를 활용하여 uri를 설정해주었다.MONGO_URI = mongodb://localhost:27017/hongaeting
option으로는 useNewUrlParser와 useFindAndModify를 설정해주었다.
useNewUrlParser
는 mongoDB 드라이버에는 최신과 옛날 두가지의 URL Parser가 존재한다. 따라서 골라서 쓸 수 있는데 우리는 최신 버전을 사용하기 위해 useNewUrlParser
를 true로 설정해 주었다.
또한 useFindAndModify
는 true가 기본값인데, false로 설정하게 되면 findAndModify()
함수 대신 findOneAndUpdate()
함수를 사용하게 한다.그 외 옵션들은 다음 페이지를 참고하자.
위 코드는 연결되는 즉시 dropCollection() 함수를 사용하여 collection들을 삭제하게 만들었다. 이는 수동으로 database를 업데이트 하기 위해 일시적으로 활용했던 코드임으로 무시해도 무관하다.
따라서 다시 유의미한 코드들로만 추려보았다.
mongoose .connect(process.env.MONGO_URI, { useNewUrlParser: true, useFindAndModify: false, }) .then(() => { console.log('Connected to MongoDB'); }) .catch(e => { console.error(e); });
이제 서버를 구동했을 때
Connected to MongoDB
라고 콘솔에 뜬다면 정상적으로 mongoDB 서버와 연결되었음을 의미한다.mongoDB schema 생성
우리는 mongoDB 서버와의 연결을 끝냈다. 이미 환경변수에서
mongodb://localhost:27017/hongaeting
로 설정된 uri와 연결하였기 때문에 mongoDB 서버 안에 hongaeting이라는 database도 생성이 완료된 상태이다.이제 우리는 collection을 프로그래밍을 통해 만들어주어야 한다.
다음 코드는 server/models/selfDating.js 코드이다.
import mongoose, { Schema } from 'mongoose'; const SelfDatingSchema = new Schema({ id: String, time: String, email: String, gender: String, age: String, status: String, collage: String, address: String, self: Number, same: Number, appearance: String, personality: String, hobby: String, idealtype: String, religion: String, smoke: String, chatlink: String, tag: String, keysentence: String, appearance2: String, personality2: String, hobby2: String, idealtype2: String, firstkey: String, secondkey: String, favorite2: String, avoidance2: String, religion2: String, smoke2: String, }); const DatingUser = mongoose.model('datinguser', SelfDatingSchema); export default DatingUser;
{ Schema }라는 클래스를 mongoose 패키지에서 불러와서 새로운 스키마 인스턴스를 생성해주었다.
그리고 mongoose.model() 함수를 이용하여 DatingUser라는 변수에 datingusers 라는 collection의 인터페이스를 정의해준다.
mongoose.model(CollectionName, Schema)
여기서 CollectionName은 단수형으로 표현된 문자열이 들어가지만 실제로 생성되는 collection의 이름은 복수형이 된다.
따라서
mongoose.model('datinguser', SelfDatingSchema)
함수를 실행해도 datingusers라는 collection이 생성된다.api 생성
먼저 server/index.js에 다음 코드가 존재한다.
import api from './api'; router.use('/api', api.routes());
이는 서버에 라우터를 설정해준 것인데
이를 통해
(server address)/api
라는 주소를 사용할 수 있게 된다.이제 server/api/index.js 코드를 살펴보자
import Router from 'koa-router'; import auth from './auth'; import chat from './chat'; import datingUser from './datingUsers'; import meetingUser from './meetingUsers'; const api = new Router(); api.use('/auth', auth.routes()); api.use('/chat', chat.routes()); api.use('/datingusers', datingUser.routes()); api.use('/meetingusers', meetingUser.routes()); export default api;
위 코드에서는 api라는 라우터를 또 만들어서 api에서 또 여러 주소들로 갈라지는 것을 볼 수 있다. (아직 부족한 전문성으로 설명이 추상적인점은 차차 개선해나가도록 하자..)
따라서 우리는 이제
(server address)/api/datingusers
라는 주소(meetingusers도 마찬가지)로 요청을 보낼 수 있다.이제 server/api/datingUsers/index.js 코드를 보자
import Router from 'koa-router'; import * as controller from './controller'; const datingUser = new Router(); datingUser.post('/', controller.create); datingUser.get('/pageNumber/:pageNumber/gender/:gender', controller.list); datingUser.get('/gender/:gender', controller.count); //datingUser.get('/:id', controller.selected); export default datingUser;
다시 우리는 datingUser라는 라우터를 새로 만들어주었다. 이는 당연히 api라는 라우터에 종속될 것이다.
한 줄씩 살펴보자
datingUser.post('/', controller.create);
(server address)/api/datingusers/
라는 주소로 post 요청을 보냈을 때 server/api/datingUsers/controller.js 파일에 있는 create 함수를 실행함을 의미한다.datingUser.get('/pageNumber/:pageNumber/gender/:gender', controller.list);
처음에 위 코드는
datingUser.get('/:pageNumber/:gender', controller.list);
와 같이 작성되었으나, 많이 붙을 수록 어떤 문자가 어떤 변수를 의미하는지 알 수 없으므로 위 코드와 같이 작성하였다.마지막으로 controller를 살펴보자
import DatingUser from '../../models/selfDating'; export const create = async (ctx) => { let {id} = ctx.request.body; const { //id, time, email, address, gender, age, collage, self, same, appearance, personality, hobby, religion, smoke, idealtype, chatlink, tag, keysentence, appearance2, personality2, hobby2, idealtype2, religion2, smoke2 } = ctx.request.body; if(email === '' || address === '' || gender === '' || age === '' || collage === ''){ id = id.toString(); try{ if (self == 1) { DatingUser.update({id : id},{self : self}) } else if(self == 0){ DatingUser.update({id : id}, {self : self}) } if (same == 1) { DatingUser.update({id : id},{same : same}); } else if(same == 0){ DatingUser.update({id : id},{same : same}); } if (appearance) { DatingUser.update({id : id},{appearance : appearance}); } if (personality) { DatingUser.update({id : id},{personality : personality}); } if (hobby) { DatingUser.update({id : id},{hobby : hobby}); } if (religion) { DatingUser.update({id : id},{religion : religion}); } if (smoke) { DatingUser.update({id : id},{smoke : smoke}); } if (idealtype) { DatingUser.update({id : id},{idealtype : idealtype}); } if (chatlink) { DatingUser.update({id : id},{chatlink : chatlink}); } if (tag) { DatingUser.update({id : id},{tag : tag}); } if (keysentence) { DatingUser.update({id : id},{keysentence : keysentence}); } if (appearance2) { DatingUser.update({id : id},{appearance2 : appearance2}); } if (personality2) { DatingUser.update({id : id},{personality2 : personality2}); } if (hobby2) { DatingUser.update({id : id},{hobby2 : hobby2}); } if (idealtype2) { DatingUser.update({id : id},{idealtype2 : idealtype2}); } if (religion2) { DatingUser.update({id : id},{religion2 : religion2}); } if (smoke2) { DatingUser.update({id : id},{smoke2 : smoke2}); } console.log(`${id} 수정완료`); } catch (e){ return ctx.throw(500, e); } } else{ const datingUser = new DatingUser({ id, time, email, address, gender, age, collage, self, same, appearance, personality, hobby, religion, smoke, idealtype, chatlink, tag, keysentence, appearance2, personality2, hobby2, idealtype2, religion2, smoke2 }); try { await datingUser.save(); await console.log("request come well"); } catch (e) { return ctx.throw(500, e); } ctx.body = datingUser; } } export const list = async (ctx) => { const { pageNumber, gender} = await ctx.params; let list ={}; if(gender == 0){ try{ list = await DatingUser.find({gender: "남학우", self: 1}) .sort({_id: -1}) // .skip((pageNumber-1) * 1000) // .limit(1000) .exec(); } catch (e){ return ctx.throw(500, e); } }else if(gender == 1){ try{ list = await DatingUser.find({gender: "여학우", self:1}) .sort({_id: -1}) // .skip((pageNumber-1) * 1000) // .limit(1000) .exec(); } catch (e){ return ctx.throw(500, e); } }else if(gender == 2){ try{ list = await DatingUser.find({self: 1}) .sort({_id: -1}) // .skip((pageNumber-1) * 1000) // .limit(1000) .exec(); } catch (e){ return ctx.throw(500, e); } } ctx.body = list; } export const count = async (ctx) => { const {gender} = ctx.params; let count; if(gender == 0){ try{ count = await DatingUser.find({gender: "남학우", self: 1}).count().exec(); } catch (e){ return ctx.throw(500, e); } }else if(gender == 1){ try{ count = await DatingUser.find({gender: "여학우", self: 1}).count().exec(); } catch (e){ return ctx.throw(500, e); } }else if(gender == 2){ try{ count = await DatingUser.find({self: 1}).count().exec(); } catch (e){ return ctx.throw(500, e); } } ctx.body = count; } export const selected = async (ctx) => { const {id} = ctx.params; let user; try{ user = await DatingUser.findOne({id:id}).exec(); } catch (e){ return ctx.throw(500, e); } if(!user){ ctx.status = 404; ctx.body = { message: 'user not found'}; return; } ctx.body = user; }
- create 함수
create 함수는 post 요청과 함께 온 datinguser의 속성들을 하나하나 변수에 저장한다. 여기서 주의점은 우리는 id 값을 서버로 보낼 때 number 형태로 보내게 되는데 model에서 살펴보게 되면 서버에서는 id 값을 string 형태로 처리하게 설정해 놓았다. 그렇기 때문에 id 값만 따로 let으로 변수를 선언하였고, 나머지는 const로 변수를 선언하였다.
ctx란 context의 약자인데 koa에서 요청을 보낼때 사용하는 객체이다. 우리는 요청 받은 객체의 body에 있는 속성값들을 사용한다.
이미 존재하는 값일 경우를 대비하여 update라는 Mongoose에 있는 함수를 사용하였다.
DatingUser.update(condition object, doc object);
- list 함수
list 함수가 실행되기 위한 조건은
datingUser.get('/pageNumber/:pageNumber/gender/:gender', controller.list);
함수가 실행되었을 때이고, (server address)/pageNumber/1/gender/1
과 같은 주소로 get 요청이 들어온다면,
const { pageNumber, gender } = await ctx.params;
와 같이 params 속성을 통해 질의에 필요한 데이터를 받아올 수 있다.