고민하고 고민해 만들어낸 코드의 구조는 이러하다.
순서를 바꿔보는 건 추후 첨부해보겠다
1. 모듈 불러오기
const express = require('express');
const app = express();
const { MongoClient } = require('mongodb');
const multer = require('multer');
const path = require('path');
먼저 사용할 모듈은 이렇게 정했다.
const { MongoClient } = require('mongodb'); 는 MongoDB 클라이언트를 초기화하는 코드이다
multer 모듈은 파일 업로드를 처리하는 데 사용된다.
path 모듈은 파일 경로와 관련된 유틸리티 함수를 제공한가
즉, 위 코드는 express, MongoDB, multer, path 모듈을 초기화하고 해당 모듈들을 사용할 수 있도록 준비하는 역할을 한다.
요즘 공부하고 있는 모듈인 express 에 관하여 좀 적어보자면…
express 모듈 불러오기
const app = express();
이는 express 에플리케이션을 생성하는 코드
http 요청과 응답을 처리하고 라우팅, 미들웨어, 템플릿 엔진 등을 제공한다.
express() 는 express 애플리케이션 객체를 반환하는 함수이다. 이를 app 변수에 할당하여 애플리케이션 객체를 참조할 수 있도록 한다.
app 객체를 활용하여 서버의 동작을 정의하고 미들웨어를 추가하며, 라우팅을 설정할 수 있다
즉, const app = express(); 코드는 express 애플리케이션을 초기화하고 해당 애플리케이션의 객체에 대한 참조를 app 변수에 저장하는 역할을 한다.
이후 app 객체를 통해 서버의 동작을 설정하고 요청에 대한 응답을 처리할 수 있다
express란?
express는 Node.js로 작성된 웹 프레임워크이며, const app = express(); 코드는 express 애플리케이션을 초기화하고 해당 애플리케이션의 객체에 대한 참조를 app 변수에 저장하는 역할을 한다. 이후 app 객체를 통해 서버의 동작을 설정하고 요청에 대한 응답을 처리할 수 있다.
아래는 express를 사용한 간단한 예시 코드이다.
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello, World!');
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
위 코드에서 app.get은 라우팅을 설정하는 메서드로, '/' 경로로 GET 요청이 왔을 때 'Hello, World!' 문자열을 응답으로 보내주는 코드이다. app.listen은 서버를 실행시키는 메서드로, 3000번 포트로 서버를 시작한다.
2. 몽고디비 연결과 변수 설정하기
const client = new MongoClient('mongodb+srv://sparta:패스워드칸@sparta.rqx1qlk.mongodb.net/?retryWrites=true&w=majority');
let db;
위 코드를 해석해보겠다.
해당 코드는 MongoDB 서버와 연결하는 역할을 한다.
MongoClient는 MongoDB 클라이언트를 초기화하는 코드이며, 이 코드에서는 MongoDB Atlas를 사용하고 있다.
new MongoClient()를 호출하여 몽고DB에 연결하여 client 변수에 저장한다.
패스워드칸에는 사용자의 실제 패스워드를 입력해야 한다.내 db를 연결할때는 내 비밀번호를 사용하였다
이 패스워드를 사용하여 sparta 데이터베이스에 연결한 후에는 db 변수를 선언하여, 나중에 데이터베이스에서 데이터를 조회 및 조작할 때 사용할 수 있도록 한다.
db변수
db 변수는 몽고디비 연결 후에 클라이언트의 db() 메서드를 사용하여 데이터베이스에 접근하는데 사용된다.
에를 들어
db.collection(collectionname')
형식으로 접근하고 쿼리를 실행할 수 있다.
데이터베이스 연겷이 성공하면 databaseConnect() 함수가 호출되고, client.cd('dbsparta) 코드를 통해 ‘dbspartq’이름의 데이터베이스에 연결된 db 변수에 데이터베이스 클라이언트가 연결된다,
이를 사용하여 데이터베이스 조작을 수행할 수있다.
즉, db 변수는 MongoDB 데이터베이스 클라이언트에 액세스하고 조작하는데 사용되는 중요한 변수이다
3. 몽고디비 데이터베이스에 연결하고 연결 상태 확인
async function databaseConnect() {
try {
await client.connect();
db = client.db('dbsparta');
console.log('MongoDB에 연결되었습니다.');
} catch (error) {
console.error('MongoDB 연결 오류:', error);
}
}
위 코드의 내용을 설명해보자면,
- databaseConnect() 함수는 비동기 함수로 선언되어 있다.
- 함수 내부에서는 await client.connect(); 코드를 사용하여 client 객체가 MongoDB에 연결될 때까지 대기한다.
- 연결이 성공하면 client.db('dbsparta') 코드를 사용하여 'dbsparta'라는 이름의 데이터베이스에 대한 클라이언트 객체를 얻으며, 이 클라이언트 객체는 db 변수에 할당된다.
- 연결이 성공하면 **'MongoDB에 연결되었습니다.'*라는 메시지가 콘솔에 출력된다.
- 연결 중에 오류가 발생하면 catch 블록이 실행되고, 오류 메시지가 **'MongoDB 연결 오류:'*와 함께 콘솔에 출력된다.
await client.connect()
await client.connect()는 MongoDB 클라이언트가 MongoDB 서버에 연결하는 비동기 함수이다. 이 함수는 await 키워드를 사용하여 연결이 완료될 때까지 기다리고, 성공하면 클라이언트 객체를 반환한다. 이 객체는 이후 데이터베이스와의 상호 작용을 위해 사용된다.
아래는 await client.connect()를 사용하는 간단한 예시 코드이다.
async function connectToServer() {
try {
const client = await MongoClient.connect(url, { useNewUrlParser: true });
console.log("서버에 연결되었다.");
const db = client.db(dbName);
} catch (err) {
console.log(err.stack);
}
}
위 코드에서 await MongoClient.connect()는 MongoDB 클라이언트가 MongoDB 서버에 연결하는 비동기 함수이다. url은 MongoDB 서버의 URL 주소를 나타내고, dbName은 연결하려는 데이터베이스의 이름이다. 연결이 성공하면 클라이언트 객체가 반환되며, 이 객체를 사용하여 데이터베이스와 상호 작용할 수 있다.
동기 비동기를 모르겟다면…
https://insidepixce.tistory.com/102
4. 파일 업로드 처리하기
const upload = multer({
dest: 'uploads/',
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
const ext = path.extname(file.originalname).toLowerCase();
if (allowedExtensions.includes(ext)) {
cb(null, true);
} else {
cb(new Error('파일 업로드에 실패했습니다.'));
}
},
onError: (err, next) => {
next(err);
}
});
위 코드는 파일 업로드 처리를 위한 코드이다.
먼저 만들어둔 데이터베이스 연결 코드를 입력해준다.
위 코드는 파일 업로드를 위한 multer 모듈 설정이다.
- dest 옵션: 업로드된 파일이 저장될 경로를 지정할 수 있다.
- limits 옵션: 업로드되는 파일의 최대 크기를 제한할 수 있디.
- fileFilter 옵션: 업로드되는 파일의 확장자를 제한하는 필터링 함수를 구현할 수 있어. 이 함수를 사용하면 원하지 않는 파일 업로드를 방지할 수 있디.
- onError 옵션: 업로드 중 에러가 발생할 때 처리하는 함수를 구현할 수 있어. 이 함수를 사용하면 업로드 과정에서 발생할 수 있는 문제를 미리 예방할 수 있디.
위의 옵션들은 multer 모듈을 사용하여 파일 업로드를 구현할 때 매우 유용해. 이러한 설정을 사용하면 더 안정적이고 안전한 방식으로 파일을 업로드할 수 있디.
https://insidepixce.tistory.com/102
multer에 관해서 더 보고 싶다면 이 게시글 참고
5. express 애플리케이션 미들웨어 설정/ 루트 경로에 대한 get 요청
express에 대해서는 앞에서도 짧게 설명했지만, 이 부분에 대해서는 조금 깊게 다루고 싶어 추후 포스팅할 에정이다. 이러한 개념으로 작성했다… 정도 까지만 이해해주면 고마울듯.
app.set('views', path.join(__dirname, 'templates'));
app.set('view engine', 'html');
app.engine('html', require('ejs').renderFile);
app.use(express.static('uploads'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.render('index.html');
});
Express 애플리케이션의 설정을 다루며, 템플릿 파일이 위치한 디렉토리를 설정하고, HTML 템플릿 엔진을 지정하며, EJS 렌더링 엔진을 등록하고, 정적 파일을 만들고 JSON 및 URL 인코딩된 요청 본문을 파싱하고
루트 경로에 대한 GET 요청을 처리하여 'index.html' 파일을 렌더링하는 부분이다.
각각의 역할을 자세히 읽어보면 이해가 쉽다,
- app.set('views', path.join(__dirname, 'templates'));
- Express 애플리케이션에서 템플릿 파일이 위치한 디렉토리를 설정
- **__dirname**은 현재 스크립트가 실행 중인 디렉토리의 경로를 나타냄
- **'templates'**는 템플릿 파일이 위치한 디렉토리 이름
- app.set('view engine', 'html');
- Express 애플리케이션에서 사용할 템플릿 엔진을 설정
- **'html'**은 HTML 파일을 템플릿으로 사용한다는 것을 의미
- app.engine('html', require('ejs').renderFile);
- ejs 패키지를 사용하여 HTML 파일을 렌더링하는 엔진을 등록
- renderFile 함수는 ejs 모듈에서 제공하는 함수로, HTML 파일을 렌더링하여 결과를 생성
- app.use(express.static('uploads'));
- Express 애플리케이션에서 정적 파일을 제공하기 위한 미들웨어를 설정
- **'uploads'**는 정적 파일이 위치한 디렉토리 이름
- app.use(express.json());
- JSON 형식의 요청 본문을 파싱하기 위한 미들웨어를 설정
- app.use(express.urlencoded({ extended: true }));
- URL 인코딩된 요청 본문을 파싱하기 위한 미들웨어를 설정
- **extended: true**는 중첩된 객체를 파싱할 수 있도록 설정
- app.get('/', (req, res) => { ... });
- 루트 경로('/')에 대한 GET 요청을 처리하는 핸들러를 설정
- 요청이 들어오면 **'index.html'**을 렌더링하여 응답
6. 몽고디비에 업로드하기
app.post를 사용하여 몽고디비에 사용자가 작성한 내용을 업로드시켰다
app.post('/guestbook_post', upload.single('photo'), (req, res) => {
const name = req.body.name_give;
const comment = req.body.comment_give;
const file = req.file;
if (file) {
const filename = file.filename;
const doc = {
name: name,
comment: comment,
photo: filename
};
db.collection('fan').insertOne(doc)
.then(() => {
res.json({ msg: '응원 완료!' });
})
.catch(() => {
res.status(400).json({ msg: '파일 업로드에 실패했습니다.' });
});
} else {
res.status(400).json({ msg: '파일이 선택되지 않았습니다.' });
}
});
위의 코드는 Express 애플리케이션에서 /guestbook_post 경로에 대한 POST 요청을 처리하는 핸들러다.
- upload.single('photo') 미들웨어를 사용하여 photo 필드로 전송된 단일 파일을 업로드한다. 업로드된 파일은 req.file 객체로 접근할 수 있다.
- 요청 본문에서 **name_give**와 comment_give 필드 값을 추출하여 **name**과 comment 변수에 할
- 업로드된 파일(req.file)이 존재하는 경우, 파일 이름을 filename 변수에 할당
- name, comment, filename 값을 이용하여 doc 객체를 생성합니다. 이 객체는 MongoDB에 저장될 데이터를 나타냄
- **db.collection('fan').insertOne(doc)**를 사용하여 doc 객체를 'fan' 컬렉션에 삽입
- 삽입이 성공한 경우, 클라이언트에게 JSON 응답으로 **{ msg: '응원 완료!' }**를 전송
- 삽입이 실패한 경우, 클라이언트에게 상태 코드 400과 JSON 응답으로 **{ msg: '파일 업로드에 실패했습니다.' }**를 전송
- 만약 업로드된 파일이 없는 경우, 클라이언트에게 상태 코드 400과 JSON 응답으로 **{ msg: '파일이 선택되지 않았습니다.' }**를 전송
위의 코드는 /guestbook_post 경로로 POST 요청이 들어오면 파일 업로드를 처리하고 MongoDB에 데이터를 저장하는 역할을 한다. 이를 통해 게시판에 게시물을 작성하고 파일을 첨부할 수 있게 한다
7. 몽고디비로부터 이미 작성된 게시글 가지고 오기
app.get('/guestbook', (req, res) => {
db.collection('fan').find({}, { projection: { _id: 0 } }).toArray()
.then(allComments => {
allComments.forEach(comment => {
if (comment.photo) {
comment.photo = req.protocol + '://' + req.get('host') + '/uploads/' + comment.photo;
}
});
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.json({ result: allComments });
})
.catch(error => {
console.error(error);
res.status(500).json({ msg: '서버 오류' });
});
});
- **db.collection('fan').find({}, { projection: { _id: 0 } })**를 사용하여 'fan' 컬렉션의 모든 문서를 조회함. **{ projection: { _id: 0 } }**는 결과에서 _id 필드를 제외하고 조회하도록 지정한 것
- toArray() 메서드를 사용하여 조회된 결과를 배열로 변환
- Promise 체인을 이용하여 조회된 결과인 **allComments**를 처리합니다. then 블록에서는 각 댓글의 photo 필드가 존재하는 경우, 해당 경로를 조합하여 절대 URL로 변경함. 이는 클라이언트에서 댓글의 이미지를 로드할 수 있도록 함
- 응답에 Cache-Control 헤더를 설정하여 캐시를 사용하지 않도록 지정함
- **res.json({ result: allComments })**를 사용하여 JSON 형태로 **{ result: allComments }**를 응답 이는 클라이언트에게 조회된 댓글 목록을 전달
- 조회나 처리 중에 오류가 발생한 경우, catch 블록에서 오류를 콘솔에 기록하고 클라이언트에게 상태 코드 500과 JSON 응답으로 **{ msg: '서버 오류' }**를 전송
위의 코드는 /guestbook 경로로 GET 요청이 들어오면 MongoDB에서 'fan' 컬렉션의 댓글을 조회하여 클라이언트에게 응답하는 역할을 한다. 이를 통해 게시판의 댓글 목록을 클라이언트로 전달한다.
8. 서버 열기 (로컬호스트)
app.listen(8099, '0.0.0.0', () => {
console.log('서버가 시작되었습니다.');
});
app.listen으로 서버를 열어주고, 서버가 정상적으로 열렸다면 ‘서버가 시작되었다’라고 알려준다.
app.listen
app.listen은 Express.js에서 서버를 시작할 때 사용하는 함수이다다. 이 함수는 서버가 특정 포트에서 요청을 수신할 수 있도록 한다
다음은 app.listen을 사용하여 서버를 시작하는 예시 코드이다
const express = require('express');
const app = express();
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
위의 코드에서, app.listen 함수는 PORT 변수에 저장된 포트 번호에서 요청을 수신할 준비가 되었다는 메시지를 출력하고 서버를 시작한다.
const express = require('express');
const app = express();
const { MongoClient } = require('mongodb');
const multer = require('multer');
const path = require('path');
const client = new MongoClient('mongodb+srv://sparta:비밀번호@sparta.rqx1qlk.mongodb.net/?retryWrites=true&w=majority');
let db; // MongoDB 데이터베이스 클라이언트를 저장할 변수
async function databaseConnect() {
try {
await client.connect();
db = client.db('dbsparta');
console.log('MongoDB에 연결되었습니다.');
} catch (error) {
console.error('MongoDB 연결 오류:', error);
}
}
databaseConnect(); // 데이터베이스 연결 함수 호출
const upload = multer({
dest: 'uploads/',
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
const ext = path.extname(file.originalname).toLowerCase();
if (allowedExtensions.includes(ext)) {
cb(null, true);
} else {
cb(new Error('파일 업로드에 실패했습니다.'));
}
},
onError: (err, next) => {
next(err);
}
});
app.set('views', path.join(__dirname, 'templates'));
app.set('view engine', 'html');
app.engine('html', require('ejs').renderFile);
app.use(express.static('uploads'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.render('index.html');
});
app.post('/guestbook_post', upload.single('photo'), (req, res) => {
const name = req.body.name_give;
const comment = req.body.comment_give;
const file = req.file;
if (file) {
const filename = file.filename;
const doc = {
name: name,
comment: comment,
photo: filename
};
db.collection('fan').insertOne(doc)
.then(() => {
res.json({ msg: '응원 완료!' });
})
.catch(() => {
res.status(400).json({ msg: '파일 업로드에 실패했습니다.' });
});
} else {
res.status(400).json({ msg: '파일이 선택되지 않았습니다.' });
}
});
app.get('/guestbook', (req, res) => {
db.collection('fan').find({}, { projection: { _id: 0 } }).toArray()
.then(allComments => {
allComments.forEach(comment => {
if (comment.photo) {
comment.photo = req.protocol + '://' + req.get('host') + '/uploads/' + comment.photo;
}
});
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.json({ result: allComments });
})
.catch(error => {
console.error(error);
res.status(500).json({ msg: '서버 오류' });
});
});
app.listen(8099, '0.0.0.0', () => {
console.log('서버가 시작되었습니다.');
});
백앤드 코드는 끝났다. 오류가 생기면 다시 다듬어야겠지만, 클라이언트를 리엑트로 만들어 볼 예정인데, 문제가 생긴다면 다시 수정해야 할 것이다.
'2023 공부한것들' 카테고리의 다른 글
[자바스크립트 문법 뽀개기] 1-3강 : hello world (0) | 2023.06.23 |
---|---|
[TIL] 20230622 (0) | 2023.06.23 |
[Node.js] Multer 모듈 (0) | 2023.06.22 |
[node.js] 동기/비동기 한판 정리 (2) | 2023.06.22 |
[Node.js] An Introduction to Routing in Client/Server Applications (0) | 2023.06.19 |