

✏️ 데이터베이스란?
애플리케이션 개발 시 다양한 정보를 관리함.
이 정보들은 데이터베이스에 저장되며 데이터베이스는 크게 관계형 데이터베이스와 NoSQL 데이터베이스로 나뉜다.
JSON 형식 사용
클라우드 기반으로 데이터베이스를 생성할 수 있음.
계정 보안 강화: 이중 인증을 설정할 수 있으며, 초기 로그인 시 데이터베이스 설정을 위한 다양한 질문(사용 목적, 사용 언어, 데이터 모델 등)에 답해야 함.
서비스 선택:
VS 코드에서 몽고DB를 관리하기 위해 몽고DB 확장을 설치할 수 있음.
MongoDB 연동을 위한 환경 설정
.env 파일을 생성하여 DB 접속 정보를 저장하고, 중요한 정보를 보호하기 위해 .env 파일은 깃허브에 공유하지 않도록 설정함..env 파일에 저장하고 비밀번호를 실제 비밀번호로 수정함.mongoose 라이브러리 설치 및 설정
mongoose 라이브러리는 Node.js에서 MongoDB를 쉽게 사용할 수 있게 해줌.npm install mongoose dotenv 명령어를 통해 mongoose와 dotenv 라이브러리를 설치함.mongoose 라이브러리를 import함:const mongoose = require('mongoose');dotenv 모듈을 사용하여 .env 파일의 환경 변수를 로드함:require('dotenv').config();데이터베이스 연결 모듈 생성
config 폴더 안에 dbConnect.js 파일을 생성하고 비동기 처리를 위해 async/await 구문을 사용함:const connectDB = async () => {
try {
await mongoose.connect(process.env.DB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('DB Connected');
} catch (error) {
console.error('Error connecting to DB:', error);
}
};mongoose.connect() 함수를 사용하여 데이터베이스에 연결하며, 커넥션 스트링은 .env 파일에서 가져옴.module.exports = connectDB;app.js에서 데이터베이스 연결 모듈 사용
app.js 파일에서 데이터베이스 연결 모듈을 가져와 사용함:const connectDB = require('./config/dbConnect');connectDB() 함수를 호출하여 데이터베이스에 연결함:connectDB();1. 스키마(Schema)란?
스키마는 MongoDB에서 저장할 데이터의 구조를 정의함.
예: 연락처 앱-> 이름·번호·이메일 같은 필드를 어떻게 구성할지
스키마의 특징
- 필드 타입을 지정해 일관성 있는 데이터 저장 가능
- 필수/옵션 속성을 설정해 데이터 안정성 확보
- 자동 생성 시간, 수정 시간 기록 가능
📌 코드 예시:
const mongoose = require('mongoose');
const contactSchema = new mongoose.Schema({
name: {
type: String,
required: true, // 필수값
},
phone: {
type: String,
required: true,
},
email: {
type: String,
required: false,
},
}, {
timestamps: true, // createdAt, updatedAt 자동 생성
});
▪️역할
📌 코드 예시:
const Contact = mongoose.model('Contact', contactSchema);
이제 Contact 모델로 데이터를 생성/조회할 수 있음:
const newContact = new Contact({
name: '홍길동',
phone: '010-1234-5678',
});
newContact.save();
MongoDB 기본 단위는 도큐먼트, 컬렉션임. 다른 RDBMS에서는 각각 행(Row), 테이블(Table)에 해당됨.
▪️도큐먼트
▪️컬렉션
스키마 → 도큐먼트 구조 정의
도큐먼트 → 실제 데이터
컬렉션 → 도큐먼트 모음
모델 → 스키마 기반 도큐먼트 생성 및 DB 조작 도구
1️⃣ 스키마 정의하기
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
age: Number,
}, {
timestamps: true,
});
2️⃣ 모델 생성하기
const User = mongoose.model('User', userSchema);
3️⃣ 모델로 데이터 저장하기
const newUser = new User({
username: 'johndoe',
age: 25,
});
newUser.save().then(() => console.log('User saved!'));
모델명(User)이 자동으로 소문자 복수형(users) 컬렉션으로 매핑됨!
1. RESTful API란?
API(Application Programming Interface)는 애플리케이션 간 데이터를 주고받고 기능을 실행하기 위한 인터페이스임.
RESTful API는 HTTP 프로토콜을 기반으로 데이터를 주고받는 형식의 API를 의미함. HTTP 메서드(POST, GET, PUT, DELETE)와 URI(자원의 주소)를 조합해 자원에 접근함.
🌐 예시 URI
GET /contacts/10
→ ID가 10번인 연락처 정보를 요청함.
📌 주요 요청 방식 (CRUD)
| 요청 방식 | 역할 |
|---|---|
| POST | Create (생성) |
| GET | Read (조회) |
| PUT | Update (수정) |
| DELETE | Delete (삭제) |
MVC(Model-View-Controller) 패턴은 애플리케이션 기능을 역할별로 분리하여 구축하는 설계 방식임.
구성 요소
컨트롤러는 실제 API 동작을 처리하는 함수를 담고 있음. 라우터에서는 컨트롤러 함수만 호출함으로써 역할을 구분함.
✔️ 구조 예시
// routes/contactRoutes.js
const express = require('express');
const router = express.Router();
const {
getAllContacts,
createContact,
} = require('../controllers/contactController');
router.get('/', getAllContacts);
router.post('/', createContact);
module.exports = router;
// controllers/contactController.js
const asyncHandler = require('express-async-handler');
// GET /contacts
exports.getAllContacts = asyncHandler(async (req, res) => {
res.send('전체 연락처 조회 페이지');
});
// POST /contacts
exports.createContact = asyncHandler(async (req, res) => {
// 실제로는 DB에 데이터 저장 로직이 들어감
res.send('새 연락처 생성');
});
비동기 API에서는 try-catch를 일일이 작성해야 하는 번거로움이 있음. express-async-handler 모듈을 사용하면 자동으로 에러를 처리함.
설치
npm install express-async-handler
사용 예시
const asyncHandler = require('express-async-handler'); exports.getAllContacts = asyncHandler(async (req, res) => { // 에러 발생 시 자동으로 next(err) 호출됨 res.send('연락처 목록'); });
📌 GET - 연락처 전체 조회
contactController.js:
exports.getAllContacts = asyncHandler(async (req, res) => {
res.send('전체 연락처 목록');
});
📌 POST - 연락처 생성
contactController.js:
exports.createContact = asyncHandler(async (req, res) => {
// req.body에서 전달된 데이터 처리
res.send('새 연락처 생성됨');
});
✍ 라우터 구조 요약
routes/contactRoutes.js:
const express = require('express');
const router = express.Router();
const {
getAllContacts,
createContact,
} = require('../controllers/contactController');
router.get('/', getAllContacts); // GET /contacts
router.post('/', createContact); // POST /contacts
module.exports = router;
Node.js에서 MongoDB와 연동해 CRUD(Create, Read, Update, Delete) 기능을 구현, Mongoose 라이브러리를 사용해 데이터베이스에 접근하고 데이터를 조작함.
| 함수 | 설명 |
|---|---|
create() | 새 도큐먼트를 생성함 |
find() | 조건 없이 전체 조회 / 조건 있으면 해당 도큐먼트만 조회함 |
findOne() | 조건에 맞는 첫 번째 도큐먼트를 조회함 |
updateOne() | 조건에 맞는 첫 번째 도큐먼트를 수정함 |
updateMany() | 조건에 맞는 모든 도큐먼트를 수정함 |
deleteOne() | 조건에 맞는 첫 번째 도큐먼트를 삭제함 |
deleteMany() | 조건에 맞는 모든 도큐먼트를 삭제함 |
findById() | ID로 도큐먼트를 조회함 |
findByIdAndUpdate() | ID로 찾아 수정함 |
findByIdAndDelete() | ID로 찾아 삭제함 |
EJS는 템플릿 파일과 데이터를 연결해 동적 콘텐츠를 생성하는 템플릿 엔진임.
설치 및 설정 방법
npm install ejs로 설치함app.js에서app.set('view engine', 'ejs')로 설정- 템플릿 파일 저장용
views폴더를 생성함
res.render()로 EJS 파일을 렌더링함res.render('get_all.ejs', { heading: '유저 리스트' })| 태그 | 사용 예시 | 설명 |
|---|---|---|
<%= %> | <%= 변수명 %> | 변수 출력 |
<% %> | <% JS 코드 %> | JS 코드 실행 |
<%- %> | <%- HTML %> | HTML 삽입 |
<% users.forEach(user => { %>
<%= user.name %>
<% }); %>
public 폴더에 저장함 public 안에 css, js, images 폴더 등을 생성함 app.js에서 app.use(express.static('public'))으로 설정함 GET - 폼 표시 등 데이터 요청
POST - 새 연락처 저장
PUT - 연락처 수정
DELETE - 연락처 삭제
index.ejs: 전체 연락처 표시 add.ejs: 새 연락처 추가 폼 update.ejs: 기존 연락처 수정 폼 views/includes 폴더에 _header.ejs, _footer.ejs 파일 생성 <%- include('includes/_header') %>
...
<%- include('includes/_footer') %>물론이지! 아래처럼 "~한다", "~함" 말투로 간단하고 깔끔하게 정리해봤어 😊 contacts/add 경로를 만들어 연락처 추가 화면을 띄움
해당 경로로 GET 요청 시, 컨트롤러에서 add.ejs 파일을 렌더링함
routes 파일에 해당 경로를 연결하여 요청 처리함
사용자가 폼에 입력 후 저장 버튼 클릭 시 POST 요청 발생
같은 contacts/add 경로로 POST 요청이 들어오면, 컨트롤러에서 DB에 새 연락처를 저장함
EJS 폼에서 method="POST", action="/contacts/add"로 설정함
저장 후 연락처 목록으로 리다이렉트함
GET /contacts/:id 경로를 통해 특정 연락처 수정 화면을 보여줌
*컨트롤러에서 해당 ID에 맞는 연락처 정보를 DB에서 찾아 update.ejs로 전달함
update.ejs에서 각 입력 필드에 value로 기존 정보 표시
index.ejs에서 연필 아이콘을 클릭하면 /contacts/:id 경로로 이동하게 설정
method-override 모듈 설치 후, app.js에 등록
npm install method-override
update.ejs에서 폼 method는 POST, action은 ?_method=PUT으로 설정하여 PUT 요청으로 처리
수정 완료 시 /contacts로 리다이렉트
index.ejs에서 X 버튼 클릭 시 삭제 요청 보내도록 설정 form 태그 사용, method="POST", action="/contacts/:id?_method=DELETE" DELETE 방식으로 요청 보냄 findByIdAndDelete() 사용해 해당 ID의 연락처 삭제 /contacts로 리다이렉트 /) 경로 접속 시 로그인 화면이 나타나도록 home.ejs 생성 include 문으로 삽입하여 중복 제거 로그인용 컨트롤러 파일(
loginController.js) 생성
GET /요청 시home.ejs를 렌더링하는getLogin함수 작성
routes설정 파일에서 루트 경로와 연결함
app.js에서 로그인 라우터를 앱에 등록함
로그인 폼에서는
POST방식 사용,action="/"으로 설정
로그인 버튼 클릭 시loginUser함수가 호출되어 아이디/비밀번호 확인
아이디가admin, 비밀번호가1234이면 성공 메시지 출력
추후 DB에서 사용자 정보 비교하는 방식으로 변경 예정
오케이! 짧은 핵심 코드만 남기고 나머지 긴 코드는 다 빼고, 벨로그 스타일로 다시 깔끔하게 정리해줄게:
register.ejs 파일을 만들어 아이디, 비밀번호, 비밀번호 확인 필드를 포함함 views/ 폴더에 저장 form 태그는 POST 방식, action="/register"로 설정함<form action="/register" method="POST">
...
</form>
User 모델 생성 username과 password 필드 포함 username은 unique: true로 중복 방지함bcrypt 사용하여 비밀번호를 해시함 bcrypt.hash(password, 10);
jwt.sign(payload, secretKey, { expiresIn: '1h' });
jsonwebtoken: JWT 생성 및 검증 cookie-parser: 쿠키 파싱용npm install jsonwebtoken cookie-parser
/contacts로 이동res.cookie('token', token).redirect('/contacts');
❓질문 1: 사용자 인증 로직에서 JWT 토큰을 클라이언트 측 localStorage나 sessionStorage가 아닌 쿠키(cookie) 에 저장하는 이유
❓질문 2: 비밀번호를 bcrypt로 암호화해서 저장하는 이유
<-굳이 이렇게 복잡하게 하지 않고 그냥 사용자가 입력한 비밀번호를 그대로 데이터베이스에 저장하면 더 편하지 않을까?