Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
88b6d7c
feat: React-router-dom 설치 및 라우터 설정
ShipFriend0516 Nov 3, 2024
c1ad2b9
feat: 간단한 비디오 켜기 기능 구현
ShipFriend0516 Nov 3, 2024
a4d37cd
feat: 협업을 위한 routes 설정
ShipFriend0516 Nov 5, 2024
9d815bd
feat: Signaling server 구현
twalla26 Nov 5, 2024
059f4e9
Merge pull request #53 from ShipFriend0516/feature/web-rtc
ShipFriend0516 Nov 5, 2024
ccbca57
Merge pull request #52 from boostcampwm-2024/feature/web-rtc
ShipFriend0516 Nov 5, 2024
5433f4d
feat: Tailwind config에 foundation 설정
ShipFriend0516 Nov 5, 2024
6213aca
feat: 세션 리스트 페이지 라우팅 설정
ShipFriend0516 Nov 5, 2024
e16de49
feat: 세션 리스트 카드 컴포넌트 레이아웃 지정
ShipFriend0516 Nov 5, 2024
738a1a2
Merge pull request #54 from twalla26/feature/signaling-server
twalla26 Nov 5, 2024
44a5c30
chore: React-icons 라이브러리 설치,
ShipFriend0516 Nov 5, 2024
d9f8d70
style: 테일윈드 config 설정
ShipFriend0516 Nov 5, 2024
259a12b
chore: 폴더구조 변경
ShipFriend0516 Nov 5, 2024
c2e1562
feat: 세션 리스트 페이지와 세션 카드 컴포넌트 구현
ShipFriend0516 Nov 5, 2024
8960dfd
Merge pull request #55 from ShipFriend0516/feature/session-list-page
ShipFriend0516 Nov 6, 2024
c1edff1
feat: WebRTC를 이용한 화상회의 기능 추가
yiseungyun Nov 6, 2024
a82fe5b
Merge remote-tracking branch 'upstream/feature/signaling-server' into…
ShipFriend0516 Nov 6, 2024
2e99067
feat: 비디오 컴포넌트 구현
ShipFriend0516 Nov 6, 2024
e6da349
feat: 비디오, 마이크 토글 버튼 구현
ShipFriend0516 Nov 6, 2024
177efb4
refactor: 세션 리스트 코드 중복 제거
ShipFriend0516 Nov 6, 2024
ce41c79
fix: Eslint 규칙에 맞게 수정
ShipFriend0516 Nov 7, 2024
40e1f38
fix: commitlint 규칙 수정
ShipFriend0516 Nov 7, 2024
f6bf9bf
chore: 시그널링 서버 주소 환경변수 관리
yiseungyun Nov 7, 2024
a0aae77
fix: lint warning 수정
yiseungyun Nov 7, 2024
08238e8
Merge pull request #56 from boostcampwm-2024/feature/web-rtc-pair
blu3fishez Nov 7, 2024
e7e51cc
fix: webrtc 통신 과정에서 오디오가 송출되지 않던 문제 해결
ShipFriend0516 Nov 7, 2024
ca60eca
fix: 방이 가득차면 방에 들어가지지 않고, 미디어만 켜지던 오류 해결
ShipFriend0516 Nov 7, 2024
b4893e6
feat: index 페이지로 접근시 sessions 페이지로 리다이렉션
ShipFriend0516 Nov 7, 2024
d8e910d
feat: 세션리스트에서 세션 페이지로 이동 가능하도록 구현
ShipFriend0516 Nov 7, 2024
6757453
fix: useEffect 의존성 배열 lint 수정
ShipFriend0516 Nov 7, 2024
5db9864
fix: 미디어 장치 권한 혹은 없는 경우에 대해 사용자에게 안내하도록 수정
ShipFriend0516 Nov 7, 2024
e13b04c
feat: 세션 입장 전 미디어 장치를 선택할 수 있는 기능 구현
ShipFriend0516 Nov 7, 2024
f9c8654
feat: 소켓 이벤트 정리 클린업함수에 room_full 이벤트에도 대응하도록 추가
ShipFriend0516 Nov 7, 2024
5c5878c
Merge pull request #57 from ShipFriend0516/fix/webrtc
ShipFriend0516 Nov 7, 2024
89d8432
feat: vite-plugin-remove-console 설치 및 배포판에 콘솔로그 제거하도록 구현
ShipFriend0516 Nov 7, 2024
614c298
refactor: videoRoom 컴포넌트를 sessionPage 페이지 컴포넌트로 변경
ShipFriend0516 Nov 7, 2024
41ee9ef
feat: session 페이지 UI 구현
ShipFriend0516 Nov 7, 2024
523bcda
style: 각 비디오가 같은 공간을 차지하도록 수정
ShipFriend0516 Nov 7, 2024
efdc216
style: 세로 스크롤 생기지 않도록 수정
ShipFriend0516 Nov 7, 2024
1326429
refactor: getUserDevices 함수로 코드 중복 제거
ShipFriend0516 Nov 7, 2024
ef0ced4
Merge pull request #58 from ShipFriend0516/feature/media
ShipFriend0516 Nov 7, 2024
3780182
feat: 배포 워크플로우 추가
blu3fishez Nov 8, 2024
25d7eba
style: 색상 시스템 변경 및 폰트 수정
yiseungyun Nov 8, 2024
4155b62
refactor: 소켓 연결 실패시 에러처리 핸들러 등록
ShipFriend0516 Nov 9, 2024
7e30a10
style: borderRadius, boxShadow 값 설정 및 폰트 단위 px에서 rem으로 수정
yiseungyun Nov 9, 2024
926f676
style: 세션 카드 컴포넌트 디자인
yiseungyun Nov 9, 2024
afd9ee2
refactor: useSocket로 소켓 초기화하는 코드 커스텀 hook으로 분리
ShipFriend0516 Nov 9, 2024
8c1b7bb
refactor: 세션 페이지의 사이드바 컴포넌트를 분리
ShipFriend0516 Nov 9, 2024
2c143f1
refactor: 세션 페이지의 Footer 툴바 컴포넌트를 분리
ShipFriend0516 Nov 9, 2024
bfc3ff1
feat: 공감 버튼 UI 구현
ShipFriend0516 Nov 9, 2024
71a2c7c
refactor: useSocket 매개변수에 따라 재실행되도록 수정
ShipFriend0516 Nov 9, 2024
08c36d7
feat: 질문 넘기기 버튼을 aria-label로 접근성 향상
ShipFriend0516 Nov 9, 2024
2707dee
refactor: useMediaDevices 훅으로 미디어 장치와 미디어 스트림 관리를 하나로 분리
ShipFriend0516 Nov 9, 2024
fcdeb35
style: 세션 리스트 페이지 디자인
yiseungyun Nov 9, 2024
066c6cf
feat: 화상회의 도중 미디어 장치를 바꾸면 바로 스트림에 적용되도록 구현
ShipFriend0516 Nov 10, 2024
168fef0
fix: 방 참가를 안해도 미디어 장치가 켜지던 오류 해결
ShipFriend0516 Nov 10, 2024
b669619
feat: 발견된 미디어 장치가 없을 때 없다고 표시하도록 구현
ShipFriend0516 Nov 10, 2024
d43232a
style: 전체 레이아웃 최대 너비 증가 및 각 비디오 컴포넌트 최대크기 지정
ShipFriend0516 Nov 10, 2024
d2ae18e
style: toolbar 버튼 스타일 수정
ShipFriend0516 Nov 10, 2024
e9c8416
style: 미디어 장치 선택 select 너비 증가
ShipFriend0516 Nov 10, 2024
37e9547
style: pretendard font 추가 및 root font 설정
ShipFriend0516 Nov 10, 2024
93d433e
feat: 참가자 목록을 사이드바에서 볼 수 있도록 구현
ShipFriend0516 Nov 10, 2024
cd77996
refactor: 오류메시지 수정
ShipFriend0516 Nov 10, 2024
7db4fc3
docs: Preview 배너 이미지 추가
yiseungyun Nov 11, 2024
6a62147
Merge pull request #60 from boostcampwm-2024/feature/design
blu3fishez Nov 11, 2024
0db053f
Merge pull request #65 from boostcampwm-2024/feature/github-action
blu3fishez Nov 11, 2024
8742d46
Merge pull request #67 from boostcampwm-2024/feature/readme
blu3fishez Nov 11, 2024
ee147b0
Merge branch 'dev' into refactor/session-page
ShipFriend0516 Nov 11, 2024
c3ad1a4
Merge pull request #63 from ShipFriend0516/refactor/session-page
ShipFriend0516 Nov 11, 2024
19f9d3d
Merge branch 'main' into dev
ShipFriend0516 Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Deploy to Ncloud Server
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.3.0 # workflow 내에서 repo의 최신 코드를 checkout
- name: Deploy to Ncloud via SSH
uses: appleboy/ssh-action@master # GitHub Actions에서 SSH로 서버에 접근
with:
host: ${{ secrets.REMOTE_SSH_HOST }}
username: ${{ secrets.REMOTE_SSH_USERNAME }}
key: ${{ secrets.REMOTE_SSH_KEY }}
passphrase: ${{ secrets.REMOTE_SSH_PASSPHRASE }}
password: ${{ secrets.REMOTE_SSH_PASSWORD }}
port: ${{ secrets.REMOTE_SSH_PORT }}
script: |
echo ${{ secrets.REMOTE_SSH_PASSWORD }} | sudo -S echo deploy start!
./deploy.sh # 배포 스크립트 실행
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# web27-Preview
# ✨ Preview
<img width="100%" src="https://github.com/user-attachments/assets/4894268d-c31d-44d6-9e6c-9c37b86a4a99" />

실시간 화상회의 기반 면접 스터디 플랫폼
<br/>

<div align="center">면접 연습을 하고 싶은데.. 🧐</div>
<div align="center">다른 사람과 함께 할 수 없을까? 👥</div>

<h3 align="center">✨ Preview에서 면접 연습 시작하자! ✨</h3>

### 배포
[배포 링크](https://boostcamp-preview.kro.kr/)
[**Preview 바로가기**](https://boostcamp-preview.kro.kr)

## 👋 팀원 소개

Expand Down Expand Up @@ -95,6 +101,7 @@
<br>

## 📁 문서

### 팀 노션 워크스페이스
- [노션 링크](https://alpine-tiglon-9f0.notion.site/PREVIEW-HOME-12d696f85d1f805b9787e26374b3d209?pvs=4)

Expand All @@ -114,5 +121,4 @@

### 개발 일지
- [공통 개발 일지](https://alpine-tiglon-9f0.notion.site/12d696f85d1f80c89569dcfe55b62b44?v=12f696f85d1f802db6af000cf32dfa28&pvs=4)
- [문제 해결 일지](https://alpine-tiglon-9f0.notion.site/87b7f1ce19564eda8127eca29d567d0f?v=f2df7d634605464d876ccf43c9197db4&pvs=4)
-
- [문제 해결 일지](https://alpine-tiglon-9f0.notion.site/87b7f1ce19564eda8127eca29d567d0f?v=f2df7d634605464d876ccf43c9197db4&pvs=4)
5 changes: 4 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-socket.io": "^10.4.6",
"@nestjs/websockets": "^10.4.6",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"socket.io": "^4.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
32 changes: 16 additions & 16 deletions backend/src/app.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Test, TestingModule } from "@nestjs/testing";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

describe('AppController', () => {
let appController: AppController;
describe("AppController", () => {
let appController: AppController;

beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();

appController = app.get<AppController>(AppController);
});
appController = app.get<AppController>(AppController);
});

describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
describe("root", () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe("Hello World!");
});
});
});
});
14 changes: 7 additions & 7 deletions backend/src/app.controller.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { Controller, Get } from "@nestjs/common";
import { AppService } from "./app.service";

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
constructor(private readonly appService: AppService) {}

@Get()
getHello(): string {
return this.appService.getHello();
}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
13 changes: 7 additions & 6 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { SocketModule } from "./socket/socket.module";

@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
imports: [SocketModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
8 changes: 4 additions & 4 deletions backend/src/app.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
getHello(): string {
return "Hello World!";
}
}
8 changes: 4 additions & 4 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
124 changes: 124 additions & 0 deletions backend/src/socket/socket.gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
WebSocketGateway,
WebSocketServer,
OnGatewayConnection,
OnGatewayDisconnect,
SubscribeMessage,
MessageBody,
} from "@nestjs/websockets";
import { Server } from "socket.io";

interface User {
id: string;
nickname: string;
}

@WebSocketGateway({
cors: {
origin: "*", // CORS 설정
},
})
export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;

private users: { [key: string]: User[] } = {};
private socketToRoom: { [key: string]: string } = {};
private maximum = 5;

handleConnection(socket: any) {
console.log(`Client connected: ${socket.id}`);
}

handleDisconnect(socket: any) {
console.log(`Client disconnected: ${socket.id}`);
const roomID = this.socketToRoom[socket.id];
if (roomID) {
const room = this.users[roomID];
if (room) {
this.users[roomID] = room.filter(
(user) => user.id !== socket.id
);
if (this.users[roomID].length === 0) {
delete this.users[roomID];
} else {
this.server.to(roomID).emit("user_exit", { id: socket.id });
}
}
}
}

@SubscribeMessage("join_room")
handleJoinRoom(socket: any, data: { room: string; nickname: string }) {
if (this.users[data.room]) {
if (this.users[data.room].length === this.maximum) {
socket.emit("room_full");
return;
}
this.users[data.room].push({
id: socket.id,
nickname: data.nickname,
});
} else {
this.users[data.room] = [
{ id: socket.id, nickname: data.nickname },
];
}

this.socketToRoom[socket.id] = data.room;
socket.join(data.room);
console.log(`[${data.room}]: ${socket.id} enter`);

const usersInThisRoom = this.users[data.room].filter(
(user) => user.id !== socket.id
);
socket.emit("all_users", usersInThisRoom);
}

@SubscribeMessage("offer")
handleOffer(
@MessageBody()
data: {
offerReceiveID: string;
sdp: any;
offerSendID: string;
offerSendNickname: string;
}
) {
this.server.to(data.offerReceiveID).emit("getOffer", {
sdp: data.sdp,
offerSendID: data.offerSendID,
offerSendNickname: data.offerSendNickname,
});
}

@SubscribeMessage("answer")
handleAnswer(
@MessageBody()
data: {
answerReceiveID: string;
sdp: any;
answerSendID: string;
}
) {
this.server.to(data.answerReceiveID).emit("getAnswer", {
sdp: data.sdp,
answerSendID: data.answerSendID,
});
}

@SubscribeMessage("candidate")
handleCandidate(
@MessageBody()
data: {
candidateReceiveID: string;
candidate: any;
candidateSendID: string;
}
) {
this.server.to(data.candidateReceiveID).emit("getCandidate", {
candidate: data.candidate,
candidateSendID: data.candidateSendID,
});
}
}
7 changes: 7 additions & 0 deletions backend/src/socket/socket.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Module } from "@nestjs/common";
import { SocketGateway } from "./socket.gateway";

@Module({
providers: [SocketGateway],
})
export class SocketModule {}
21 changes: 10 additions & 11 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'],
],
'subject-case': [2, 'always', 'sentence-case'],
'subject-max-length': [2, 'always', 70],
},
};
extends: ["@commitlint/config-conventional"],
rules: {
"type-enum": [
2,
"always",
["feat", "fix", "docs", "style", "refactor", "test", "chore"],
],
"subject-max-length": [2, "always", 70],
},
};
2 changes: 2 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.env
12 changes: 9 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
"build": "tsc -b && vite build",
"lint": "eslint .",
"format": "prettier --write \"**/*.{ts,tsx}\"",
"preview": "vite preview"
"preview": "vite preview",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@types/socket.io-client": "^3.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.27.0",
"socket.io-client": "^4.8.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
Expand All @@ -28,6 +33,7 @@
"tailwindcss": "^3.4.14",
"typescript": "~5.6.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
"vite": "^5.4.10",
"vite-plugin-remove-console": "^2.2.0"
}
}
Loading