Service
GitHub - Mineru98/MiniFlix
Mineru98
2025.04.27
·GitHub·by Anonymous#Web Service#Streaming#Database#API#System Design
핵심 포인트
- 1MiniFlix는 넷플릭스의 핵심 기능을 간소화하여 구현한 웹 기반 스트리밍 서비스로, 콘텐츠 탐색, 재생, 회원 관리 및 찜 목록 기능을 제공합니다.
- 2이 시스템은 사용자, 콘텐츠, 장르, 찜 목록, 시청 기록 등을 관리하는 ERD와 함께, 회원가입, 로그인, 콘텐츠 재생 등의 핵심 시나리오를 Client, API, DB 간의 상호작용으로 상세하게 설계하였습니다.
- 3기술 스택으로는 프론트엔드에 Next.js와 React를, 백엔드에 Golang과 Gin 프레임워크를 활용했으며, 데이터베이스는 MySQL을 사용하고 Docker Compose를 통해 쉽게 배포할 수 있습니다.
이 문서는 웹 기반 스트리밍 서비스인 "MiniFlix"의 시스템 설계 및 구현에 대한 기획을 설명합니다. 넷플릭스의 핵심 기능을 간소화하여 구현한 프로젝트입니다.
1. 시스템 설계 기획
- 화면 흐름도 (Flowchart):
랜딩 페이지에서 시작하여 로그인 페이지 또는 회원가입 페이지로 이동할 수 있습니다. 성공적인 인증 후에는 홈 화면으로 이동합니다. 홈 화면에서는 검색 결과 화면, 장르별 필터링 화면, 콘텐츠 상세 페이지, 마이페이지로 접근할 수 있습니다. 검색 결과 화면과 장르별 필터링 화면은 콘텐츠 상세 페이지로 연결되며, 콘텐츠 상세 페이지에서는 비디오 플레이어로 이동하거나 찜하기/취소 기능을 수행할 수 있습니다. 마이페이지는 찜 목록과 계정 정보 관리 페이지로 나뉘며, 찜 목록에서는 다시 콘텐츠 상세 페이지로 이동 가능합니다. 모든 하위 페이지 (검색, 장르, 상세, 플레이어, 찜 목록, 계정)에서 홈 화면으로 돌아갈 수 있는 구조입니다. 각 페이지는 기능별로 색상(landingPage: #3E2723, authPage: #0D47A1, mainPage: #B71C1C, contentPage: #2E7D32, playerPage: #4A148C, userPage: #E65100)으로 구분되어 시각적으로 명확하게 표현됩니다.- ERD (개체-관계 다이어그램):
- Users: 사용자 정보를 저장합니다.
id(bigint PK): 사용자 고유 식별자.email(varchar(100)): 사용자 이메일 (로그인 ID).password_hash(varchar(255)): 암호화된 비밀번호.name(varchar(50)): 사용자 이름.created_at(timestamp): 가입 일시.updated_at(timestamp): 정보 수정 일시.is_active(bit(1)): 계정 활성화 상태.
- Contents: 콘텐츠 정보를 저장합니다.
id(bigint PK): 콘텐츠 고유 식별자.title(varchar(200)): 콘텐츠 제목.description(text): 콘텐츠 설명.thumbnail_url(varchar(255)): 썸네일 이미지 경로.video_url(varchar(255)): 비디오 파일 경로.duration(int): 영상 길이(초).release_year(int): 출시 연도.created_at(timestamp): 등록 일시.updated_at(timestamp): 수정 일시.
- Genres: 장르 정보를 저장합니다.
id(bigint PK): 장르 고유 식별자.name(varchar(50)): 장르명.description(varchar(200)): 장르 설명.
- ContentGenres: 콘텐츠와 장르 간의 다대다 관계를 매핑합니다.
id(bigint PK): 매핑 고유 식별자.content_id(bigint FK): 콘텐츠 ID.genre_id(bigint FK): 장르 ID.
- Wishlists: 사용자의 찜 목록을 저장합니다.
id(bigint PK): 찜 목록 고유 식별자.user_id(bigint FK): 사용자 ID (Users 테이블 참조).content_id(bigint FK): 콘텐츠 ID (Contents 테이블 참조).created_at(timestamp): 찜한 일시.
- ViewingHistories: 사용자의 시청 기록을 저장합니다.
id(bigint PK): 시청 기록 고유 식별자.user_id(bigint FK): 사용자 ID (Users 테이블 참조).content_id(bigint FK): 콘텐츠 ID (Contents 테이블 참조).watch_duration(int): 총 시청 시간(초).last_position(int): 마지막 시청 위치(초).watched_at(timestamp): 시청 일시.is_completed(bit(1)): 시청 완료 여부.
Users가 Wishlists와 ViewingHistories를 "저장"하고 "시청"하며, Contents는 Wishlists와 ViewingHistories에 "저장됨" 또는 "시청됨"으로 연결되고, Contents와 Genres는 ContentGenres를 통해 "분류"됩니다.2. 핵심 기능별 시나리오 (Sequence Diagrams)
각 기능은 클라이언트 (브라우저), 백엔드 API 서버, 데이터베이스 (DB), 그리고 미디어 스토리지 간의 상호작용을 상세히 보여줍니다.
- 회원가입 시나리오:
- 클라이언트가 이메일, 비밀번호, 이름을 포함한 회원가입 요청을 API 서버로 전송합니다.
- API 서버는 요청 데이터의 유효성을 검증(이메일 형식, 비밀번호 강도)하고, DB에 이메일 중복 확인 쿼리를 보냅니다.
- 이메일이 중복되면
409 Conflict응답을, 유효성 검증 실패 시400 Bad Request응답을 클라이언트에 반환합니다. - 가입 가능한 경우, API 서버는 비밀번호를 해싱한 후,
Users테이블에 사용자 정보를 저장합니다. - 저장 성공 시, API 서버는
201 Created응답과 함께 회원가입 성공 메시지를 반환하고, 클라이언트는 로그인 페이지로 리다이렉트됩니다.
- 로그인 시나리오:
- 클라이언트가 이메일과 비밀번호로 로그인 요청을 API 서버에 전송합니다.
- API 서버는
Users테이블에서 해당 이메일로 사용자 정보를 조회합니다. - 사용자가 없으면
401 Unauthorized를, 계정이 비활성화 상태면403 Forbidden을 반환합니다. - 사용자가 존재하면, API 서버는 저장된
password_hash와 입력된 비밀번호를 비교하여 검증합니다. - 비밀번호가 일치하지 않으면
401 Unauthorized를 반환합니다. - 로그인 성공 시, API 서버는 사용자 ID, 이름 등의 정보를 포함하는 JWT(JSON Web Token)를 생성하여
200 OK응답과 함께 클라이언트에 반환합니다. - 클라이언트는 토큰을
localStorage또는 쿠키에 저장하고 홈 화면으로 리다이렉트됩니다.
- 홈 화면 콘텐츠 로드 시나리오:
- 클라이언트가 홈 화면에 접근합니다.
- 로그인 상태인 경우: JWT 토큰을 포함하여 인증된 콘텐츠 목록을 요청합니다. API 서버는 토큰을 검증하고 유효한 경우,
Contents테이블에서 기본 정보를 조회합니다. 추가적으로Wishlists테이블에서 사용자의 찜 목록을 조회하여 콘텐츠에 찜 상태 정보를 추가한 후200 OK응답으로 클라이언트에 반환합니다. 토큰이 무효하면401 Unauthorized를 반환하고 로그인 페이지로 리다이렉트됩니다. - 비로그인 상태인 경우: 비인증 콘텐츠 목록을 요청합니다. API 서버는
Contents테이블에서 콘텐츠 기본 정보만 조회하여 찜 상태 없이200 OK응답으로 클라이언트에 반환합니다. - 클라이언트는 수신된 콘텐츠를 그리드 형태로 표시합니다.
- 콘텐츠 검색 시나리오:
- 클라이언트가 검색어를 입력하고 API 서버에 검색 요청(검색어, 선택적으로 JWT 토큰)을 전송합니다.
- API 서버는 로그인 상태인 경우 JWT 토큰을 검증합니다.
- API 서버는
Contents테이블에서title LIKE '%검색어%'쿼리를 사용하여 제목 기반으로 콘텐츠를 검색합니다. - 로그인 상태인 경우,
Wishlists테이블에서 사용자의 찜 목록을 조회하여 검색 결과에 찜 상태 정보를 추가합니다. - 검색 결과가 있으면
200 OK(검색 결과 콘텐츠 목록)를, 없으면200 OK(빈 배열)를 반환합니다. - 클라이언트는 검색 결과를 표시합니다.
- 장르별 필터링 시나리오:
- 클라이언트가 API 서버에 장르 목록을 요청하고, API 서버는
Genres테이블에서 장르 정보를 조회하여 반환합니다. 클라이언트는 이를 장르 필터 UI로 표시합니다. - 클라이언트가 특정 장르를 선택하고 API 서버에 해당 장르의 콘텐츠를 요청(genre\_id, 선택적으로 JWT 토큰)합니다.
- API 서버는 로그인 상태인 경우 JWT 토큰을 검증합니다.
- API 서버는
Contents테이블과ContentGenres테이블을 JOIN하여 선택된genre_id에 해당하는 콘텐츠를 조회합니다. - 로그인 상태인 경우,
Wishlists테이블에서 사용자의 찜 목록을 조회하여 필터링된 콘텐츠에 찜 상태 정보를 추가합니다. - API 서버는
200 OK응답과 함께 필터링된 콘텐츠 목록을 반환하고, 클라이언트는 이를 표시합니다.
- 클라이언트가 API 서버에 장르 목록을 요청하고, API 서버는
- 콘텐츠 상세 정보 조회 시나리오:
- 클라이언트가 콘텐츠 썸네일을 클릭하여 API 서버에 콘텐츠 상세 정보 요청(content\_id, 선택적으로 JWT 토큰)을 전송합니다.
- API 서버는 로그인 상태인 경우 JWT 토큰을 검증합니다.
- API 서버는
Contents,ContentGenres,Genres테이블을 JOIN하여 콘텐츠의 상세 정보(제목, 설명, URL, 길이, 출시 연도, 장르 목록 등)를 조회합니다. - 로그인 상태인 경우,
Wishlists테이블에서 찜 여부를,ViewingHistories테이블에서 최근 시청 정보(last_position)를 추가로 조회합니다. - API 서버는
200 OK응답과 함께 콘텐츠 상세 정보, 찜 상태, 시청 위치(선택적)를 반환합니다. - 클라이언트는 콘텐츠 상세 페이지를 렌더링(찜하기 버튼, 재생 버튼 등 포함)합니다.
- 콘텐츠 재생 시나리오:
- 클라이언트가 재생 버튼을 클릭합니다.
- 비로그인 상태인 경우: 클라이언트는 로그인 페이지로 리다이렉트되고 시청 불가 알림을 받습니다.
- 로그인 상태인 경우: 클라이언트가
content_id와 JWT 토큰을 포함한 재생 요청을 API 서버에 보냅니다. - API 서버는 JWT 토큰을 검증하고, 유효한 경우
Contents테이블에서video_url을,ViewingHistories테이블에서last_position을 조회합니다. - API 서버는
200 OK응답과 함께 스트리밍 URL과 마지막 시청 위치를 클라이언트에 반환합니다. - 클라이언트는 미디어 스토리지에 비디오 스트림을 요청하여 수신하고, 비디오 플레이어를 마지막 시청 위치부터 초기화하여 재생합니다.
- 클라이언트는 약 30초마다 현재 재생 위치를 API 서버에 업데이트 요청(
content_id,current_position, JWT 토큰)합니다. API 서버는ViewingHistories테이블의last_position과watch_duration을 업데이트/생성합니다. - 재생 종료 시(일시정지, 창 닫기 등), 클라이언트는 최종 재생 위치(
final_position), 총 시청 시간(watch_duration), 시청 완료 여부(is_completed)를 포함하여 최종 업데이트 요청을 API 서버에 보냅니다. API 서버는ViewingHistories테이블을 업데이트합니다.
- 콘텐츠 찜하기/취소 시나리오:
- 클라이언트가 찜하기 버튼을 클릭합니다.
- 비로그인 상태인 경우: 클라이언트는 로그인 페이지로 리다이렉트됩니다.
- 로그인 상태인 경우: 클라이언트가
content_id와 JWT 토큰을 포함한 찜하기 토글 요청을 API 서버에 전송합니다. - API 서버는 JWT 토큰을 검증하고,
Wishlists테이블에서 현재 찜 상태를 확인합니다. - 이미 찜한 상태인 경우,
Wishlists테이블에서 해당 항목을 삭제하고200 OK(찜 취소 성공, 새로운 상태: false)를 반환합니다. - 찜하지 않은 상태인 경우,
Wishlists테이블에 새 항목을 추가하고201 Created(찜하기 성공, 새로운 상태: true)를 반환합니다. - 클라이언트는 UI(찜하기 버튼 상태)를 업데이트합니다.
- 찜 목록 조회 시나리오:
- 클라이언트가 마이페이지에서 찜 목록을 클릭하고, JWT 토큰을 포함한 찜 목록 요청을 API 서버에 보냅니다.
- API 서버는 JWT 토큰을 검증하고,
Wishlists테이블과Contents테이블을 JOIN하여 사용자가 찜한 콘텐츠 목록을 조회합니다. - 찜 목록이 있으면
200 OK(찜한 콘텐츠 목록)를, 없으면200 OK(빈 배열)를 반환합니다. - 클라이언트는 찜 목록 화면을 렌더링합니다.
- 계정 정보 조회 시나리오:
- 클라이언트가 마이페이지에서 계정 정보를 클릭하고, JWT 토큰을 포함한 계정 정보 요청을 API 서버에 보냅니다.
- API 서버는 JWT 토큰을 검증하고,
Users테이블에서 사용자 정보(이메일, 이름, 가입일시)를 조회합니다. - API 서버는
200 OK응답과 함께 사용자 정보를 반환하고, 클라이언트는 계정 정보 화면을 렌더링합니다.
- 계정 정보 수정 시나리오:
- 클라이언트가 계정 정보 수정 폼(이름 또는 비밀번호)을 작성하고, 변경할 정보, 현재 비밀번호, JWT 토큰을 포함한 수정 요청을 API 서버에 보냅니다.
- API 서버는 JWT 토큰을 검증하고, DB에서 현재 사용자의
password_hash를 조회하여 입력된 현재 비밀번호와 비교합니다. - 현재 비밀번호가 불일치하면
403 Forbidden을 반환하고 클라이언트는 오류 메시지를 표시합니다. - 비밀번호가 일치하면, API 서버는 새 정보의 유효성을 검증하고, 새 비밀번호 변경 요청이 포함된 경우 새 비밀번호를 해싱합니다.
- API 서버는
Users테이블을 업데이트(name,password_hash(선택적),updated_at)하고,200 OK(수정 성공 메시지)를 반환합니다. - 클라이언트는 성공 알림을 표시하고 업데이트된 정보를 반영합니다.
3. 주요 기능
- 콘텐츠 스트리밍: 영화 및 드라마 클립을 웹에서 시청할 수 있습니다.
- 콘텐츠 탐색: 홈 화면에서 콘텐츠 목록 확인, 검색 및 장르별 필터링을 제공합니다.
- 회원 기능: 회원가입, 로그인, 마이페이지 기능을 포함합니다.
- 찜 목록: 관심 있는 콘텐츠를 저장하고 관리할 수 있습니다.
- 반응형 디자인: PC와 모바일 환경 모두에서 사용 가능한 UI를 제공합니다.
4. 기술 스택
- 프론트엔드:
- 언어: TypeScript
- 프레임워크/라이브러리: React, Next.js
- 상태 관리: Zustand
- 서버 상태 관리: React Query
- 스타일링: TailwindCSS
- HTTP 클라이언트: Axios
- 백엔드:
- 언어: Golang
- 웹 프레임워크: Gin
- 데이터베이스: MySQL
- 인증: JWT (JSON Web Token)
- API 문서: Swagger
5. 실행 방법
- Docker Compose:
docker-compose up또는docker-compose up -d명령어를 통해 전체 서비스를 실행할 수 있으며,docker-compose down으로 중지합니다. - 개발 환경 설정:
- 프론트엔드:
cd frontend,yarn,yarn dev명령어로 개발 서버를 실행합니다. - 백엔드:
cd backend,go mod tidy,./swag init && go run main.go명령어로 개발 서버를 실행합니다.
- 프론트엔드:
- 접속 정보:
- 프론트엔드:
http://localhost:3000 - 백엔드 API:
http://localhost:8080 - Swagger API 문서:
http://localhost:8080/swagger/index.html
- 프론트엔드: