[TIL]1210
Express.js와 MongoDB로 웹서비스 만들기 2
회원가입 구현하기 - 비밀번호 저장 방법 - Hash
-
Hash는 문자열을 되돌릴 수 없는 방식으로 암호화하는 방법
-
-> hash 출력값을 이용해 사용자의 비밀번호를 알아낼 수 없음
-
비밀번호의 Hash 값을 데이터베이스에 저장하고,
-
로그인 시 전달된 비밀번호를 Hash 하여 저장된 값과 비교해 로그인을 처리
SHA1 - 사용방법
const hash = crypto.createHash("sha1");
hash.update(password);
hash.digest("hex");
- Node.js의 기본제공 모듈인 crypto 모듈을 사용하여 hash값을 얻을 수 있음
- 간단하게 sha1 알고리즘을 사용하거나 보다 강력한 sha224, sha256등의 알고리즘도 사용할 수 있음
Passport.js 란?
- Express.js 어플리케이션에 간단하게 사용자 인증 기능을 구현하게 도와주는 패키지
- 유저 세션 관리 및 다양한 로그인 방식 추가 가능
passport-local
- passport는 다양한 로그인 방식을 구현하기 위해 strategy라는 인터페이스를 제공
- strategy 인터페이스에 맞게 설계된 다양한 구현체들이 있다(facebook,google,...)
- passport-local은 username, password를 사용하는 로그인의 구현체
로그인 기능 구현하기 - passport-local strategy
const config = {
usernameField: "email",
passwordField: "password",
};
// 아이디 패스워드 필드 설정 필수!
const local = new LocalStrategy(config, async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
throw new Error("회원을 찾을 수 없습니다.");
}
// user에 저장한 password는 hash 값이기 때문에 hash(pass)를 사용해야함
if (user.password !== password) {
throw new Error("비밀번호가 일치하지 않습니다.");
}
// 세션에 저장되는 유저 정보의 최소화
done(null, {
shortId: user.shortId,
email: user.email,
name: user.name,
});
} catch (err) {
done(err, null);
}
});
- passport strategy를 구성할때 config값과 Authenticator를 사용한다
- passport strategy안에서 로그인처리가 되어야 하고 되었을 경우 어떤 데이터를 보낼지
로그인 기능 구현하기 - passport.js 설정하기
const local = require("./strategies/local");
passport.use(local);
- 작성한 strategy를 passport.use를 이용해 사용하도록 선언 해야 함
- passport.use를 이용해 strategy를 사용하도록 선언한 후
- passport.authenticate를 사용해 해당 strategyt를 이용해 요청을 처리할 수 있음
로그인 기능 구현하기 - passport.js로 post요청 처리하기
// routes/auth.js
router.post("/", passport.authenticate("local"));
// app.js
const session = require("express-session");
app.use(
session({
secret: "secret",
resave: false,
saveUninitialized: true,
})
);
app.use(passprot.initialize());
app.use(passport.session());
app.use("/auth", authRouter);
-
passport.authenticate 함수를 http 라우팅에 연결하면 passport가
-
자동으로 해당하는 strategy를 사용하는 request handler를 자동생성
-
express-session과 passport.session()을 사용하면 passport가 로그인 시
-
유저 정보를 세션에 저장하고 가져오는 동작을 자동으로 수행해 줌
로그인 기능 구현하기 - session 유저 활용하기
passport.serializeUser((user, callback) => {
callback(null, user);
});
passport.deserializeUser((obj, callback) => {
callback(null, obj);
});
- session을 이용해 user를 사용할 때에는 serializeUser와 deserializeUser 를 설정해 주어야 함
- 이는 세션에 user 정보를 변환하여 저장하고 가져오는 기능을 제공
- ex) 회원 id만 세션에 저장하고, 사용시 회원정보를 디비에서 찾아서 사용
- 세션 사용 시 위 두 함수를 작성하지 않으면 passport 로그인이 동작하지 않음
로그아웃
router.get('/logout', ... {
req.logout();
res.redirect('/');
});
- passport는 req.logout 함수를 통해
- 세션의 로그인 정보를 삭제하여, 로그아웃 기능을 구현할 수 있음
로그인 확인 미들웨어
function loginRequired(req, res, next) {
if (!req.user) {
res.redirect("/");
return;
}
next();
}
app.use("/posts", loginRequired, postsRouter);
- 로그인을 필수로 설정하고 싶을 경우, 미들웨어를 사용하여 체크할 수 있음.
Session Store
Session 이란?
-
웹 서버가 클라이언트의 정보를 클라이언트별로 구분하여 서버에 저장하고
-
클라이언트 요청 시 Session Id를 사용하여 클라이언트의 정보를 다시 확인하는 기술
-
클라이언트가 정보를 저장하고, 요청 시 정보를 보내는 Cookie와 대조됨
Session 작동 방식
- 서버는 세션을 생성하여 세션의 구분자인 Session ID를 클라이언트에 전달함
- 클라이언트는 요청 시 session id를 함께 요청에 담아서 전송
- 서버는 전달받은 session id로 해당하는 세션을 찾아 클라이언트 정보를 확인
Express.js의 session
- express-session 패키지를 사용하여 간단하게 session 동작을 구현할 수 있음
- 특별한 설정 없이, 자동으로 session 동작을 구현해 줌
- -> 자동으로 session id를 클라이언트에 전달, session id로 클라이언트 정보 확인
Session Store를 사용하는 이유
- express-session 패키지는 session을 기본적으로 메모리에 저장함
- 따라서 현재 구현된 어플리케이션을 종료 후 다시 실행하면, 모든 유저의 로그인이 해제됨
- 혹은 서버가 여러 대가 있을 경우, 서버 간 세션 정보 공유할 수 없음
Session Store 구성
Hash는 문자열을 되돌릴 수 없는 방식으로 암호화하는 방법
-> hash 출력값을 이용해 사용자의 비밀번호를 알아낼 수 없음
비밀번호의 Hash 값을 데이터베이스에 저장하고,
로그인 시 전달된 비밀번호를 Hash 하여 저장된 값과 비교해 로그인을 처리
const hash = crypto.createHash("sha1");
hash.update(password);
hash.digest("hex");
const config = {
usernameField: "email",
passwordField: "password",
};
// 아이디 패스워드 필드 설정 필수!
const local = new LocalStrategy(config, async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
throw new Error("회원을 찾을 수 없습니다.");
}
// user에 저장한 password는 hash 값이기 때문에 hash(pass)를 사용해야함
if (user.password !== password) {
throw new Error("비밀번호가 일치하지 않습니다.");
}
// 세션에 저장되는 유저 정보의 최소화
done(null, {
shortId: user.shortId,
email: user.email,
name: user.name,
});
} catch (err) {
done(err, null);
}
});
const local = require("./strategies/local");
passport.use(local);
// routes/auth.js
router.post("/", passport.authenticate("local"));
// app.js
const session = require("express-session");
app.use(
session({
secret: "secret",
resave: false,
saveUninitialized: true,
})
);
app.use(passprot.initialize());
app.use(passport.session());
app.use("/auth", authRouter);
passport.authenticate 함수를 http 라우팅에 연결하면 passport가
자동으로 해당하는 strategy를 사용하는 request handler를 자동생성
express-session과 passport.session()을 사용하면 passport가 로그인 시
유저 정보를 세션에 저장하고 가져오는 동작을 자동으로 수행해 줌
passport.serializeUser((user, callback) => {
callback(null, user);
});
passport.deserializeUser((obj, callback) => {
callback(null, obj);
});
router.get('/logout', ... {
req.logout();
res.redirect('/');
});
function loginRequired(req, res, next) {
if (!req.user) {
res.redirect("/");
return;
}
next();
}
app.use("/posts", loginRequired, postsRouter);
웹 서버가 클라이언트의 정보를 클라이언트별로 구분하여 서버에 저장하고
클라이언트 요청 시 Session Id를 사용하여 클라이언트의 정보를 다시 확인하는 기술
클라이언트가 정보를 저장하고, 요청 시 정보를 보내는 Cookie와 대조됨
- 왼쪽의 경우 Express Appilcation을 만들었을 때 session을 그냥 사용하게 되면 session이 Express Application에 포함되어있다, 이는 만약 Express Application이 종료가 된다면 Session도 같이 없어지게 된다
- 오른쪽 Session Store를 사용한 경우 Session Strore가 따로 있고, 각각의 Express Application은 Session Strore에 연결이 되어 있다. 하나의 Session Strore를 여러 Express Application이 공유하고 있기 때문에 Express Application이 종료가 되더라도 Session Strore는 종료가 되지 않았기 때문에 다시 Express Application을 Session Strore에 붙였을 때 로그인 정보나 클라이언트 정보들이 날아가지 않고 다시 확인 할 수 있다.
MongoDB를 Session Store로 사용하기
- connect-mongo 패키지를 이용해, MongoDB를 session store로 사용 할 수 있음
- connect-mongo 패키지는 express-session 패키지의 옵션으로 전달가능
- 자동으로 session 값이 변경될 때 update되고, session이 호출될 때 find함
connect-mongo
const MongoStore = require("connect-mongo");
app.use(
session({
secret: "SeCrEt",
resave: false,
saveUninitialized: true,
store: MongoStore.create({
mongoUrl: "mongoUrl",
}),
})
);
- connect-mongo 패키지를 사용해 express-session 설정 시
- store 옵션에 전달하고, mongoUrl을 설정
- 세션데이터를 몽고디비에 저장하고 관리하는 기능을 자동으로 수행해줌
회원과 게시글의 연동 - Index
- MongoDB는 검색을 위해 Document를 정렬하여 저장하는 기능을 제공함
- Index를 설정하면 주어진 쿼리를 효율적으로 수행하여 성능을 향상시킬 수 있음
- 다중 키, 좌표, 텍스트 등의 특별한 값으로 정리되는 인덱스도 제공
author에 index 설정하기
author: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true,
}
-
PostSchema의 author 속성에 index:true 옵션을 사용하면
-
mongoose가 자동으로 MongoDB에 인덱스를 생성해 줌
-
이미 데이터가 많은 상탱에서 인덱스를 추가할 시 작업 시간이 길어져, MongoDB가 응답하지 않을 수 있음
-
-> 예상되는 인덱스를 미리 추가하는 것이 좋음
CSR을 구현하는 방법
- 페이지 로드 시 필요한 리소스를 클라이언트에 선언
- 클라이언트에서 필요한 데이터를 비동기 호출
- 클라이언트가 전달받은 데이터를 가공, 리소스를 사용하여 화면에 표시
클라이언트에 리소스 선언 - HTML Template
- 클라이언트에 리소스를 선언하기 위한 다양한 방법이 존재.(React.js, Vue.js 등)
- HTML Template은 브라우저에 표시되지 않는 HTML Element를 작성해 두고,
- JavaScript로 이를 화면에 반복적으로 그릴 수 있게 하는 기술
MongoDB Aggregation
Aggregation이란?
- MongoDB에서 Document 들을 가공하고, 연산하는 기능
- RDBMS에서 SQL로 수행할 수 있는 기능들을 유사하게 구현할 수 있음
- ex) SQL의 GROUP BY, DISTINCT, COUNT, JOIN등
Aggregation을 사용하는 이유
- MongoDB의 find는 검색 필터링과 정렬 이외의 기능을 제공하지 않음
- 다른 Collection에서 데이터를 가져오거나,
- 검색된 데이터를 그룹화하는 등의 작업이 필요한 경우
- Aggregation을 통해 이를 수행할 수 있음.
Aggregation 예제
db.posts.aggregate([
{ $group: { _id: '$author', count: { $sum: 1 } } },
{ $match: { sum: { $gt: 10}}},
{ $lookup: { from: 'users', localField: '_id', foreignField: '_id', as: 'users'} },
]);
- aggregation은 Stage들의 배열로 이루어지고 각 Stage는 순차적으로 수행됨
- 작성자별 게시글 수를 취합하고
- 게시글 수가 10개보다 많은 작성자를 찾아서
- 해당 작성자를 회원 collection에서 검색 함
Aggregation Reference
- Aggregation의 종류는 너무 다양하고 복잡하기 때문에 전부 외워서 사용할 필요는 없다.
- MongoDB 홈페이지에 Stage들의 설명과 예제 코드까지 잘 정리되어 있다.
- https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#stages
Author And Source
이 문제에 관하여([TIL]1210), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sza0203/TIL1210저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)