개발 내역
첫 번째 MVP 개발
9월 27일 수요일부터 구현을 시작하고 거의 3일 만에 MVP 개발을 완료했다. 기능이랄 것도 없고 메일을 입력하면 DB에 저장했다가 특정 시간이 되면 서버가 학과 홈페이지에서 게시글을 가져와서 DB에 존재하는 유저 메일로 게시글을 전송한다.
- 유저가 form에 이메일을 입력
- 이메일이 서버API를 거쳐 DB에 저장된다.
- 특정 시간이 되면 cron.schedul()을 통해 메일 전송 작업이 시작된다.
- 정컴 학과 홈페이지 RSS url에서 게시글 목록을 가져와서 DB에 있는 유저들의 이메일로 전송한다.
내가 생각해봐도 그냥 라이브러리 쓰면 되는 간단한 프로젝트다. 그래서 좀 더 스케일을 키우기로 했다. 정보컴퓨터공학부 홈페이지 뿐만 아니라 타과, 타학교 학과 홈페이지의 정보도 크롤링할 수 있도록 구조를 바꾼 것이다.
스케일업
말이 스케일업이지, 이것도 사실 별 것 아니었다. 우리학교 학과 홈페이지들의 RSS url은 같은 형식을 공유했다.
https://{학과코드}.pusan.ac.kr/bbs/{학과코드}/{boardIndex}
- boardIndex: 학과 홈페이지별 게시판 번호
- 학과코드: 학과별로 고유한 코드
학과들이 같은 RSS url 형식을 공유한다면 매우 간단하다. DB에 학과별 학과코드만 저장해두고, 반복문을 돌면서 처리해버리면 바로 끝이다.
MongoDB
Node.js에서 MongoDB를 쓰기 위해서는 Mongoose 프레임워크를 사용해야 한다. DB는 친숙하지 못하지만.. Chat-GPT 3.5의 도움으로 어렵지 않게 처리했다.
// server/models/Department.js
import mongoose, { Schema } from "mongoose";
const DepartmentSchema = new Schema({
code: { // 학과코드
type: String,
required: true,
unique: true,
},
name: { // 학과이름
type: String,
required: true,
},
url: { // 학과홈페이지 RSS url
type: String,
required: true,
},
boards: { // 학과홈페이지 게시판 인덱스 목록
type: [Number],
default: [],
},
board_names: { // 학과홈페이지 게시판 이름 목록
type: [String],
default: [],
},
});
export default mongoose.model("Department", DepartmentSchema);
// server/models/User.js
import mongoose, { Schema } from "mongoose";
const UserSchema = new Schema({
email: { // 유저 이메일
type: String,
required: true,
unique: true,
},
latest_post_indexs: { // 게시판별 유저가 최근에 받은 게시물 인덱스
type: [Number],
default: -1,
},
department_code: { // 구독 중인 학과코드
type: String,
required: true,
default: "cse",
},
subscribe_time: { // 구독을 시작한 시간. 당장은 용도가 없다.
type: Date,
default: Date.now(),
},
});
export default mongoose.model("User", UserSchema);
"DB에 유저의 개인 정보인 email을 저장해도 괜찮을까?" 싶어서 구독 버튼을 누를 때, "개인정보제공동의" 체크란에 동의해야만 구독할 수 있도록 변경했다.
디자인
기능 구현은 사실 추석 포함 3~4일 만에 완료하긴 했다. 그러나 디자인이 내가 보기에도 너무 구려서 어떻게든 꾸며보기로 했다가 이틀이나 걸렸다. 그래도 react-particles와 sweatalert2 라이브러리를 사용해서 그래도 볼만하게는 만들 수 있었다. 알려준 동기친구에게 감사한다.
보안: 이메일 검증
로컬로 기능을 테스트하고 완전히 작동하는 것을 확인했지만 문득 악의적인 유저의 공격에 대해 어떻게 할지가 걱정되었다. 기존의 방식은 from에서 이메일을 입력하고 submit하면 서버API를 거쳐 바로 DB에 저장되는 방식이다. 이 때, 유저가 입력한 이메일을 검증하는 절차가 부족했다. 유저가 입력한 이메일이 "이메일의 형태를 가졌는지"는 체크했지만, 실제로 존재하는 이메일인지, 유저가 실제로 사용하는 이메일인지, 마구잡이로 생성된 일회성 이메일은 아닌지.. 이메일을 검증할 방식이 필요했다. 그래서 생각한 검증 방식은 다음과 같다. 은행 인증을 할 때 송금한 계좌의 이름 뒤에 붙은 숫자를 맞추면 인증하는 것처럼 한 스텝을 더 밟기로 했다.
1. 유저가 이메일을 입력하고 구독 버튼을 누르면, 서버의 대기리스트에 유저의 이메일을 추가하고 해당 이메일로 서버API를 날릴 수 있는 버튼 html 코드를 전송한다.
2. 서버의 대기리스트에 이메일이 들어온 지 10분 이내에 유저가 해당 버튼을 클릭하면 DB에 유저의 이메일을 등록하고 유저의 화면은 홈페이지의 validation 페이지로 리다이렉션시킨다.
3. 10분이 지났다면 해당 요청은 "권한없음" 처리되고 대기리스트에서 삭제된다. 유저는 1번 과정부터 다시 진행해야 한다.
4. 대기리스트에 쌓이고 만료기간이 지난 요청은 cron.schedule을 통해 정기적으로 삭제된다.
이제 악의적인 사용자가 마구잡이로 쓰레기 이메일을 집어넣어도 DB를 보호할 수 있게 되었다. 서버쪽 지식이 부족한만큼 다른 문제가 더 있겠지만... 보안 동아리 친구에게 자문을 구해볼 생각이다.
'프로젝트 > MailBadara' 카테고리의 다른 글
[토이프로젝트] MailBadara - (6) 서버 최적화 생각 중 (0) | 2023.10.16 |
---|---|
[토이프로젝트] MailBadara - (5) 트러블슈팅 (0) | 2023.10.13 |
[토이프로젝트] MailBadara - (4) 프론트 최적화 (0) | 2023.10.13 |
[토이프로젝트] MailBadara - (3) 베타테스트 및 피드백 (1) | 2023.10.08 |
[토이프로젝트] MailBadara - (1) 기획 (0) | 2023.10.02 |