Keyboard Arena는 경쟁 기반 타자 연습을 위한 회원제 커뮤니티 서비스입니다.
ESTsoft 백엔드 개발자 과정 '오르미' 4기 Java/Spring 프로젝트의 일환으로 제작되었습니다.
시연 동영상
주간 랭크전 타자연습 아레나 참전
- 유저는 관리자가 개설한 랭크전 타자연습에 참전해 서비스 내 랭킹을 산정받을 수 있습니다.
일반 타자연습 아레나 개설과 참전
- 유저는 타자연습 아레나를 개장하고, 120초 내에 해당 아레나를 직접 클리어하여 서비스 내의 아레나로 정식 등록될 수 있도록 자격을 획득합니다.
- 유저는 자격 검증을 마치고 정식으로 등록된 아레나를 자유로이 즐기고, 좋아요 및 댓글을 남길 수 있습니다.
유저 랭킹 기반 권한 부여 시스템
- 아레나 참전 및 자유게시판 게시글 조회에 있어 권한이 차등 부여됩니다.
랭크 아레나 기록을 기반으로 유저 랭크를 일정 시간마다 산정해주는 기능이다.
랭크 산정 알고리즘 후보군
- 랭크 아레나 4개의 등수의 평균값을 내고, 그 값을 기반으로 1/5씩 나눠 랭크 1~5까지 산정해주는 시스템
- 문제점: 랭크 아레나 하나만 1등으로 클리어한 사람이 랭크 아레나 4개를 모두 2등으로 클리어한 사람보다 높은 랭크를 차지하게돼 불공정해진다.
- 그럼 모든 랭크 아레나를 클리어한 사람들을 기준으로 랭크를 산정하면 안되나?
- 랭크 아레나를 모두 플레이 하지 않았어도 랭크 산정에 포함하고 싶었음
-
유저의 랭크는 가장 낮은 1부터 가장 높은 5까지 존재한다.
-
경쟁 아레나는 총 4개이다.
-
한명의 유저가 하나의 랭크 아레나를 클리어 해서 얻을 수 있는 가중치는 최대 1, 최소 0이다.
-
가중치 랭크 산정 알고리즘의 장점
- 랭크 아레나를 클리어한 사람의 수, 유저의 등수에 따라 평등하게 랭크를 산정해줄 수 있다.
- 랭크 아레나를 모두 클리어하지 않아도 랭킹 산정 시스템에 포함이 되며, 랭크 아레나를 많이 클리어 할수록 더 높은 랭크를 가져갈 수 있다.
-
이 가중치 알고리즘은 SQL문 하나만으로 해결해 RDS와 EC2 인스턴스의 불필요한 네트워크 비용을 절감할 수 있었다.
UPDATE user u
JOIN (
SELECT
uc.id,
SUM((uc.total_users - uc.board_rank) / (uc.total_users-1) )+1 AS rank_score
FROM (
SELECT
id,
board_id,
RANK() OVER (PARTITION BY board_id ORDER BY clear_time Asc) AS board_rank,
COUNT(*) OVER (PARTITION BY board_id) AS total_users
FROM user_cleared_board
WHERE board_id IN (SELECT board_id FROM board WHERE board_type = 2)
) AS uc
GROUP BY uc.id
) AS scores
ON u.id = scores.id
SET u.user_rank = ROUND(scores.rank_score)
where u.user_rank!=10;
- 서창덕 (팀장, Github)
- 아레나 CRUD, 참전 api 및 UI 구현
- 아레나 검증 api 및 UI 구현
- 유저 랭킹 산정 api 및 UI 구현
- RDS 생성 및 발표 담당
- 송찬혁 (Github)
- 자유게시판 CRUD api 및 UI 구현
- 댓글 관련 api 및 UI 구현
- 좋아요 관련 api 및 UI 구현
- 지윤호 (Github)
- 비밀번호 변경 api 및 UI 구현
- 회원탈퇴 api 및 UI 구현
- flow chart 작성
- 장한빛 (Github)
- 로그인/회원가입 관련 api 및 UI 구현
- 마이페이지 관련 api 및 UI 구현
- EC2 배포 및 발표자료 제작 담당
- Language :
Java 17
- Framework :
Spring Boot 3.2.4
- Database :
MySQL
,JDBC
,Spring Data JPA
- Frontend :
Thymeleaf
,HTML/CSS
,JavaScript
- 배포 :
AWS EC2/RDS
Project Tree
src └─ main ├─ java │ └─ com │ └─ example │ └─ KeyboardArenaProject │ ├─ KeyboardArenaApplication.java │ ├─ config │ │ ├─ InterceptorConfiguration.java │ │ ├─ LogoutListener.java │ │ ├─ SchedulerConfig.java │ │ ├─ SwaggerConfig.java │ │ └─ WebSecurityConfig.java │ ├─ controller │ │ ├─ AOPAspect.java │ │ ├─ GlobalExceptionHandler.java │ │ ├─ LikeCommentController.java │ │ ├─ arena │ │ │ └─ ArenaController.java │ │ ├─ freeBoard │ │ │ └─ FreeBoardController.java │ │ └─ user │ │ ├─ MyPageController.java │ │ ├─ UserController.java │ │ └─ UserViewController.java │ ├─ dto │ │ ├─ CommentResponse.java │ │ ├─ arena │ │ │ ├─ ArenaBestUserResponse.java │ │ │ ├─ ArenaDashBoardResponse.java │ │ │ ├─ ArenaReceiveForm.java │ │ │ ├─ ArenaResponse.java │ │ │ ├─ ArenaResultResponse.java │ │ │ ├─ ArenaStartTimeResponse.java │ │ │ ├─ ArenaVerifyResponse.java │ │ │ ├─ ArenaVerifyResultResponse.java │ │ │ └─ BoardDetailResponse.java │ │ ├─ freeBoard │ │ │ ├─ FreeBoardRecieveForm.java │ │ │ ├─ FreeBoardResponse.java │ │ │ └─ FreeBoardWriteRequest.java │ │ ├─ mypage │ │ │ ├─ MyArenaResponse.java │ │ │ ├─ MyCommentedBoardsResponse.java │ │ │ └─ MyLikedBoardsResponse.java │ │ └─ user │ │ ├─ AddUserRequest.java │ │ ├─ AnonymousUser.java │ │ ├─ ChangePwRequest.java │ │ ├─ DeleteUserRequest.java │ │ ├─ MyPageInformation.java │ │ ├─ ResetPwRequest.java │ │ ├─ UserResponse.java │ │ └─ UserTopBarInfo.java │ ├─ entity │ │ ├─ Board.java │ │ ├─ Cleared.java │ │ ├─ Comment.java │ │ ├─ IP.java │ │ ├─ Like.java │ │ ├─ User.java │ │ └─ compositeKey │ │ ├─ IpCompositeKey.java │ │ └─ UserBoardCompositeKey.java │ ├─ interceptor │ │ └─ Interceptor.java │ ├─ repository │ │ ├─ ClearedRepository.java │ │ ├─ CommentRepository.java │ │ ├─ CommonBoardRepository.java │ │ ├─ IpRepository.java │ │ ├─ LikeRepository.java │ │ ├─ MyPageRepository.java │ │ └─ UserRepository.java │ ├─ service │ │ ├─ CommentService.java │ │ ├─ LikeService.java │ │ ├─ board │ │ │ └─ CommonBoardService.java │ │ └─ user │ │ ├─ ClearedService.java │ │ ├─ MyPageService.java │ │ ├─ UserDetailService.java │ │ └─ UserService.java │ └─ utils │ └─ user │ ├─ GenerateIdUtils.java │ ├─ GeneratePwUtils.java │ └─ UserTopBarInfoUtil.java └─ resources ├─ static │ ├─ css │ │ ├─ arena │ │ ├─ common │ │ ├─ freeboard │ │ ├─ mypage │ │ └─ user │ ├─ js │ │ ├─ arena │ │ ├─ common │ │ ├─ freeboard │ │ ├─ mypage │ │ └─ user │ └─ media └─ templates └─ error
기능 | HTTP method | url |
---|---|---|
회원가입 | POST | /user |
로그인 | POST | /login |
로그아웃 | GET | /logout |
아이디 중복 확인 | GET | /user/find/id |
이메일 중복 확인 | GET | /user/find/email |
아이디 찾기 | POST | /user/find/id |
비밀번호 찾기 | POST | /user/find/pw |
기능 | HTTP method | url |
---|---|---|
내 정보 조회 | GET | /mypage |
비밀번호 수정 | POST | /mypage/changePassword |
회원탈퇴 | POST | /mypage/signout |
작성한 게시물 전체 조회 | GET | /mypage/boards |
좋아요를 누른 게시물 전체 조회 | GET | /mypage/boards/liked |
댓글을 작성한 게시물 전체 조회 | GET | /mypage/boards/commented |
내가 참전한 아레나 전체 조회 | GET | /mypage/arenas |
기능 | HTTP method | url |
---|---|---|
아레나 전체 조회 | GET | /arenas |
아레나 개별 조회 | GET | /arenas/{boardId} |
아레나 참전 시작 시간 기록 | GET | /arenas/{boardId}/start |
아레나 참전 | POST | /arenas/{boardId} |
아레나 제작 | POST | /newArena |
아레나 제작 후 활성화 전 검증 | POST | /arena/{boardId}/verify |
아레나 검증 시작 시간 기록 | GET | /arenas/{boardId}/verify/start |
아레나 삭제 | DELETE | /arenas/{boardId} |
기능 | HTTP method | url |
---|---|---|
자유게시판 게시글 전체 조회 | GET | /board |
자유게시판 게시글 전체 조회(작성일자 순 정렬) | GET | /board/sort=2 |
자유게시판 게시글 개별 조회 | GET | /board/{boardId} |
자유게시판 게시글 작성 | POST | /board |
자유게시판 게시글 수정 | PUT | /board/{board_id} |
자유게시판 게시글 삭제 | DELETE | /board/{board_id} |
자유게시판 게시글 좋아요 누르기 | POST | /api/like |
자유게시판 게시글 댓글 작성 | POST | /comments/{board_id} |
자유게시판 게시글 댓글 삭제 | DELETE | /comments/{board_id} |