Vlog는 동영상을 녹화, 업로드, 시청, 검색할 수 있는 사이트입니다.
JavaScript와 Node.js를 배우며 동영상을 녹화/업로드/시청/검색할 수 있는 사이트를 구현하였습니다.
클라이언트와 서버 간의 사용자 요청 및 서버 응답 원리를 이해하고, 데이터를 처리하는 연습을 위해 프로젝트를 진행하였습니다.
개인 프로젝트입니다.
2021.09.13 ~ 2021.10.25(6주)
ID: test123
PW: qwe123!!!
- 레포지토리 복제 및 의존성 설치
$ git clone https://github.com/summereuna/vlog.git
$ cd vlog
$ npm install
- 개발 서버 가동
$ npm start
- 브라우저에서 실행
http://localhost:4000/
분류 | 기술 | 기술 선택 이유 |
---|---|---|
패키지 관리 | npm | - 자바스크립트 생태계에서 가장 널리 사용되는 npm을 사용했습니다. 개발 경험이 많지 않아서, 가장 일반적으로 사용되는 패키지 관리 도구를 선택했습니다. |
FE 번들링 및 빌드 툴 | Webpack | - Webpack은 다양한 플러그인과 로더를 통해 파일을 통합하고 번들링할 수 있는 강력한 도구입니다. 프론트엔드 번들링의 기초를 공부하기 위해 Webpack을 선택했습니다. |
FE 언어 | JavaScript | - Node.js와 함께 풀스택 개발을 하기 위해 JavaScript를 선택했습니다. 마침 JavaScript를 배우고 있어서 이를 사용해 개발을 진행하였습니다. |
스타일링 | SCSS | - CSS보다 효율적이고 유지보수가 쉬운 SCSS를 사용했습니다. 재사용 가능한 스타일을 작성할 수 있고, Webpack과 함께 CSS 로더 설정을 통해 간편하게 사용할 수 있었습니다. |
템플릿 엔진 | Pug | - Pug는 Express와의 통합이 용이하고 HTML보다 간결한 코드 작성이 가능해 생산성을 높일 수 있어서 선택했습니다. |
BE 언어 | Node.js | - JavaScript 기반의 서버 개발을 위해 Node.js를 사용했습니다. 프론트엔드와 동일한 언어를 사용해 접근성과 학습 효율성이 뛰어나 초보자가 사용하기 적합했습니다. |
BE 프레임워크 | Express | - Express는 Node.js의 가벼운 프레임워크로, API 설계와 미들웨어 기반의 백엔드 구축을 손쉽게 할 수 있어 선택했습니다. |
DB ODM | Mongoose | - 스키마 기반으로 데이터를 구조화하고, 데이터 검증이 용이한 Mongoose를 사용했습니다. MongoDB와 쉽게 연동할 수 있어 사용했습니다. |
클라우드 DB | MongoDB Atlas | - MongoDB Atlas는 유연한 NoSQL 데이터베이스로, JSON 기반 구조 덕분에 데이터 모델이 직관적이고, 필드 확장과 수정이 용이해 선택했습니다. 무료 플랜으로 접근성이 높았습니다. |
이미지/동영상 클라우드 스토리지 | AWS S3 | 프리 티어로 무료로 사용할 수 있었고, 대기업의 안정적인 클라우드 서비스를 경험해보고 싶어 선택했습니다. |
배포 및 호스팅 | Flyio | Flyio는 무료 플랜을 제공하며, 애플리케이션을 여러 지역에 자동으로 배포해 빠른 응답 시간을 제공할 수 있어 선택했습니다. Heroku의 무료 플랜 종료 이후 대안으로 Flyio를 사용했습니다. |
📦 vlog
┣ 📂src
┃ ┣ 📂client // 클라이언트
┃ ┃ ┣ 📂js
┃ ┃ ┃ ┣ 📜commentSection.js
┃ ┃ ┃ ┣ 📜main.js
┃ ┃ ┃ ┣ 📜recorder.js
┃ ┃ ┃ ┗ 📜videoPlayer.js
┃ ┃ ┗ 📂scss
┃ ┃ ┣ 📂components
┃ ┃ ┣ 📂config
┃ ┃ ┃ ┣ 📜_reset.scss
┃ ┃ ┃ ┗ 📜_variables.scss
┃ ┃ ┣ 📂screens
┃ ┃ ┗ 📜styles.scss
┃ ┃ // 서버
┃ ┣ 📂controllers
┃ ┃ ┣ 📜userController.js
┃ ┃ ┗ 📜videoController.js
┃ ┣ 📂models
┃ ┃ ┣ 📜Comment.js
┃ ┃ ┣ 📜User.js
┃ ┃ ┗ 📜Video.js
┃ ┣ 📂routers
┃ ┃ ┣ 📜apiRouter.js
┃ ┃ ┣ 📜rootRouter.js
┃ ┃ ┣ 📜userRouter.js
┃ ┃ ┗ 📜videoRouter.js
┃ ┣ 📂views
┃ ┃ ┣ 📂mixins
┃ ┃ ┃ ┣ 📜comment.pug
┃ ┃ ┃ ┣ 📜message.pug
┃ ┃ ┃ ┣ 📜video-aside.pug
┃ ┃ ┃ ┣ 📜video.pug
┃ ┃ ┃ ┗ 📜watch-comment.pug
┃ ┃ ┣ 📂partials
┃ ┃ ┃ ┣ 📜footer.pug
┃ ┃ ┃ ┣ 📜header.pug
┃ ┃ ┃ ┣ 📜sidenav.pug
┃ ┃ ┃ ┗ 📜social-login.pug
┃ ┃ ┣ 📂users
┃ ┃ ┃ ┣ 📜change-password.pug
┃ ┃ ┃ ┣ 📜edit-profile.pug
┃ ┃ ┃ ┗ 📜profile.pug
┃ ┃ ┣ 📂videos
┃ ┃ ┃ ┣ 📜edit.pug
┃ ┃ ┃ ┗ 📜upload.pug
┃ ┃ ┣ 📜404.pug
┃ ┃ ┣ 📜base.pug
┃ ┃ ┣ 📜home.pug
┃ ┃ ┣ 📜join.pug
┃ ┃ ┣ 📜login.pug
┃ ┃ ┣ 📜search.pug
┃ ┃ ┗ 📜watch.pug
┃ ┣ 📜db.js
┃ ┣ 📜init.js
┃ ┣ 📜middlewares.js
┃ ┗ 📜server.js
┃ //로컬에서 이미지/비디오 저장
┣ 📂uploads
┃ ┣ 📂avatars
┃ ┗ 📂videos
┃
┣ 📜.dockerignore
┣ 📜.env
┣ 📜.gitignore
┣ 📜Dockerfile
┣ 📜README.md
┣ 📜babel.config.json
┣ 📜fly.toml
┣ 📜nodemon.json
┣ 📜package-lock.json
┣ 📜package.json
┗ 📜webpack.config.js
폴더명 | 내용 |
---|---|
/src |
클라이언트와 서버 파일들을 포함한 루트 디렉토리 |
/client/js |
(클라이언트) 클라이언트에서 실행되는 JavaScript 파일 관리 |
/client/scss |
(클라이언트) 스타일시트 파일 관리 |
/controllers |
(서버/컨트롤러) 비즈니스 로직 처리하는 컨트롤러 파일 관리 |
/models |
(서버/모델) 데이터베이스 모델 정의 파일 관리 |
/routers |
(서버/라우터) 라우팅 파일 관리 |
/views |
(서버/뷰) 주요 페이지 템플릿 관리 |
/views/mixins |
(서버/뷰) 재사용 가능한 템플릿 관리 |
/views/partials |
(서버/뷰) 공통 레이아웃 템플릿 관리 |
/views/users |
(서버/뷰) 사용자 관련 페이지 템플릿 관리 |
/views/videos |
(서버/뷰) 비디오 관련 페이지 템플릿 관리 |
/uploads |
(로컬 저장소) 로컬에서 업로드된 이미지, 동영상 파일 저장 |
.dockerignore | Docker 빌드 시 제외할 파일/디렉토리 정의 |
.env | 환경 변수 정의 |
.gitignore | Git에서 추적하지 않을 파일/디렉토리 정의 |
Dockerfile | Docker 이미지를 빌드하기 위한 설정 파일 |
README.md | 리드미 파일 |
babel.config.json | ES6+ 코드를 변환하기 위한 Babel 설정 파일 |
fly.toml | Fly.io 배포 관련 설정 파일 |
nodemon.json | 개발 중 서버를 자동으로 재시작하는 데 사용하는 Nodemon 설정 파일 |
package-lock.json | npm 의존성 트리를 잠그기 위한 파일 |
package.json | 프로젝트의 의존성과 스크립트 등 관리 |
webpack.config.js | 클라이언트 자바스크립트 파일을 번들링하기 위한 웹팩 설정 파일 |
메인 페이지/ |
동영상 페이지/videos/:id |
---|---|
![]() |
![]() |
로그인 페이지/login |
회원가입 페이지/join |
![]() |
![]() |
검색 페이지/search?keyword= |
동영상 업로드 페이지/videos/upload |
![]() |
![]() |
유저 별 페이지/users/:id |
프로필 변경 페이지/users/edit |
![]() |
![]() ![]() |
- ID/PW 통한 회원 가입 및 로그인 기능
- Github OAuth 소셜 로그인 지원
- 폼 유효성 검증
- 로그인/로그아웃 시 알림창 기능
- 상단 검색바에서 동영상 제목 키워드 검색 기능
- 반응형 웹 디자인 통해 다양한 화면 크기에 맞는 UI 제공
- 최근 업로드된 동영상 기준으로 전체 동영상 조회
- 동영상 상세 페이지에서 비디오 및 댓글 조회 기능
- 동영상 조회수 증가 기능
- HTMLMediaElement API 활용한 비디오 컨트롤러 기능 제공
- KeyboardEvent 활용한 키보드 숏컷 기능 제공 (재생, 일시정지, 앞으로, 뒤로, 뮤트, 언뮤트, 전체화면 등)
- 웹캠 실시간 녹화 및 다운로드 기능 제공
- multer 사용해 AWS S3에 동영상 및 썸네일 업로드
- 유저 프로필 사진 및 닉네임 변경 기능
- 프로필 변경 시 알림창 기능
-
사용자가 비디오를 업로드 하고 나면, 사용자의 비밀번호가 또 해싱되기 때문에 다음 번에 로그인을 못하는 상황이 발생했습니다.
userSchema.pre("save", async function () {
this.password = await bcrypt.hash(this.password, 5);
});
비디오 업로드시, 유저 정보에 있는 비디오 어레이에 비디오 아이디 업데이트 하며 `user.save()`를 사용하고 있었습니다. 이를 실행할 때 마다 비밀번호 해싱코드가 실행되고 있었기 때문에 사용자의 해시된 비밀번호가 또 해시되는 문제가 발생했습니다.
userSchema.pre("save", async function () {
if (this.isModified("password")) {
this.password = await bcrypt.hash(this.password, 5);
}
});
비밀번호가 수정될 때만 새로 해시될 수 있도록 미들웨어에 조건문을 추가하여 문제를 해결하였습니다.