Skip to content

그룹원들과 홈트 시간을 공유하며 쉽고 꾸준한 운동 습관을 길러주는 '홈트라이'

Notifications You must be signed in to change notification settings

kakao-tech-campus-2nd-step3/Team17_BE

Repository files navigation

홈트라이

그룹원들과 홈트 시간을 공유하며 쉽고 꾸준한 운동 습관을 길러주는 홈트 플랫폼

홈트라이 소개 이미지

📄 목차


🚀팀 내 배포 링크

[FE] https://hometry.vercel.app/

[BE] https://home-try.13.125.102.156.sslip.io


🎯개발 동기 및 목적

홈트라이는 혼자 운동하기 어려운 이들을 위해, 함께 운동하는 느낌과 동기를 제공하는 홈트 플랫폼입니다. 코로나 이후 홈트 붐이 일어났지만 집에서 혼자 운동하다 보니 동기부여가 어렵고, 쉽게 포기할 수 있습니다. 홈트라이는 이러한 문제점을 해결하고자 시작되었습니다.

  • 꾸준한 운동 습관 형성
    • 운동 시간을 시각화하고 기록하여 매일 운동을 실천하도록 돕고, 그룹 내 랭킹 시스템과 채팅 기능을 통해 그룹원들과 소통하며 운동의 재미를 더합니다. 👏
  • 건강한 커뮤니티 형성
    • 그룹을 통해 운동 지식을 공유하고, 서로의 운동 성과를 인증하며 응원함으로써 건강한 커뮤니티 문화를 만들어갑니다. 💬
  • 쉬운 접근성
    • 모바일로 간편하게 운동 기록과 통계를 확인하고 실시간으로 운동에 참여할 수 있어 언제 어디서든 운동 습관을 기를 수 있습니다. 🌟

✨서비스 소개

그룹원들과 홈트 시간을 공유하며 쉽고 꾸준한 운동 습관을 길러주는 홈트 플랫폼, 홈트라이

  • 실시간으로 운동 시간을 측정하여 시각화를 통해, 혼자 집에서도 꾸준히 홈트를 진행하도록 동기 부여 🔥
  • 매일 반복되는 운동 사이클 속 재미와 운동 습관 형성 ✨
  • 그룹 내 랭킹 및 채팅 기능을 통해 그룹원들과 커뮤니케이션이 가능하며, 새로운 운동 지식과 오운완 인증까지! 💪🏻

🖥️팀원 소개

FE

정서윤(FE 테크리더) 이도현
로그인, 메인, 랭킹, 마켓, 마이페이지 로그인, 나의 그룹, 그룹 탐색, 채팅페이지

BE

정우재(팀장) 김수랑(BE 테크리더) 박준형 조현서
나의 그룹, 그룹탐색 회원, 인증, 채팅 일기, 태그 운동, 마켓

📆개발 기간

2024년 9월 ~ 11월


📚기술 스택

서버 사이드

Spring Spring Data JPA

데이터베이스

MySQL

실시간 채팅

WebSocket STOMP.js

인증 및 보안

JWT

서버 사이드 렌더링

HTML CSS JavaScript jQuery

빌드 도구 및 배포

Gradle AWS Nginx


📝개발 문서

ERD

✨주요 기능

  • 회원, 인증
  • 운동
  • 일기
  • 그룹
  • 채팅
  • 마켓
  • 관리자

💡개발 주안점

🙆‍♂️회원, 인증

  • 회원가입 / 로그인

    • 사용자가 카카오 계정으로 로그인 할 수 있는 기능입니다.
    • 회원가입 시 사용자의 역할은 USER로 저장됩니다.
    • JPA의 @PrePersist를 이용하여 처음 회원 정보를 DB에 insert 할 때 운동 출석일수를 default로 0으로 지정합니다.
    • 회원가입 시 사용자의 닉네임은 무작위로 지정되며, 추후에 마이페이지에서 닉네임을 변경할 수 있습니다.
    • 카카오 Biz 앱 애플리케이션을 사용하여 회원의 이메일을 저장합니다.
    • 카카오 로그인은 해당 시퀀스 다이어그램을 따릅니다.
      카카오 로그인 시퀀스 다이어그램 보기 시퀀스 다이어그램
  • 마이페이지

    • 마이페이지를 조회합니다. 사용자는 자신의 닉네임, 이메일, 출석일 수, 운동 월별/주간 통계를 볼 수 있습니다.
    • 닉네임 변경
      • 사용자의 닉네임을 최대 32자로 변경할 수 있습니다.
    • 사용자는 마이페이지에서 회원 탈퇴 요청을 보낼 수 있습니다.
  • 회원 탈퇴

    • 사용자가 로그인한 회원을 탈퇴하는 기능입니다.

    • 회원 탈퇴는 해당 시퀀스 다이어그램을 따릅니다.

      회원 탈퇴 시퀀스 다이어그램 보기

      회원 탈퇴 시퀀스 다이어그램

💪운동

  • 운동 생성
    • 사용자가 새로운 운동을 생성할 수 있는 기능입니다. 사용자가 운동 이름을 입력하여 생성 요청을 보내면 운동이 생성됩니다.
    • 운동 생성 시 이벤트 기반 아키텍처를 사용하여 관련된 부가 작업(ExerciseTime 생성 및 초기화)을 별도로 처리합니다. 이를 통해 운동 생성의 주요 비즈니스 로직과 부가 작업을 분리하여 코드의 가독성과 유지보수성을 높였습니다.
  • 운동 삭제
    • 특정 운동 기록을 삭제하는 기능입니다. 사용자가 잘못 기록한 운동이나 불필요한 운동을 삭제할 수 있습니다.
    • 운동 삭제는 Soft Delete를 이용하여 처리되기 때문에, 데이터베이스에서 삭제되지 않고 isDeprecated 값을 통해 비활성화됩니다. 삭제 요청 시, 운동이 실행 중인지 확인하여 실행 중인 운동은 삭제할 수 없도록 제한합니다.
  • 운동 시작
    • 사용자가 운동을 시작할 수 있는 기능입니다.
    • 사용자가 이미 다른 운동을 실행 중인 경우, 새로운 운동을 시작할 수 없습니다.
    • 운동 시작 시, 현재 시간 기준으로 운동 시작 시간을 기록합니다. 운동이 시작되면 상태가 isActive = true로 변경됩니다.
  • 운동 종료
    • 사용자가 진행 중인 운동을 종료하는 기능입니다.
    • 한 번에 운동할 수 있는 최대 시간은 8시간 미만이며, 하루 총 운동 가능 시간은 12시간 미만입니다.
      • 한 번에 8시간 이상 진행한 운동은 시간이 기록되지 않고 운동이 종료됩니다.
      • 하루 총 운동 시간이 12시간을 초과하는 경우 11시간 59분 59초까지만 시간이 기록되고 운동이 종료됩니다.
    • 운동 종료 시, 시작 시간부터 현재 시간까지의 운동 시간을 계산해 exerciseTime에 저장하고, isActive = false로 설정합니다.
  • 운동 기록 저장
    • @Scheduled의 cron을 이용하여 매일 새벽 3시에 사용자의 운동 기록을 히스토리에 저장하고, 하루 운동 시간을 초기화하는 스케줄러 기능이 있습니다.

    • 스케줄러가 실행되면 모든 진행 중인 운동을 강제 종료하고, 운동 시간을 저장한 후 ExerciseHistoryExerciseTime의 기록을 이동시킵니다. 그리고 운동 시간을 기록한 사용자에 대해 출석일을 증가시킵니다.

      스케줄러 시퀀스 다이어그램

      스케줄러 시퀀스 다이어그램

📔일기

  • 일기 조회
    • 메인 페이지에서 일기 조회가 가능하며 내림차순 정렬의 페이지네이션으로 구현되었습니다.
  • 일기 생성
    • 사용자가 일기를 작성할 수 있는 기능입니다. 사용자가 메모를 입력하여 생성 요청을 보내면 일기가 생성됩니다.
  • 일기 삭제
    • 일기는 아이디값을 기반으로 삭제가 이루어지며, 삭제는 hard delete 방식이 사용됩니다.

👫그룹

  • 그룹 생성
    • 사용자가 운동을 같이 할 그룹을 만드는 기능입니다. 팀 이름, 팀 설명, 패스워드, 적용할 태그리스트를 보내주면 이에 맞춰서 팀이 생성됩니다.
  • 그룹 삭제
    • 사용자가 만든 그룹을 삭제하는 기능입니다.
    • 사용자는 여러개의 그룹에 가입이 가능하기에 N:M 관계가 성립됩니다. 따라서 그룹과 사용자간의 가입 정보를 나타내는 TeamMemberMapping 테이블이 존재하며, 그룹이 삭제되는 경우 해당 팀의 TeamMemberMapping 데이터가 hard delete 됩니다.
    • 팀장만이 그룹을 삭제할 수 있으며, 그룹 자체와 그룹 내에서 팀원들끼리 주고받았던 채팅과 hard delete 방식으로 삭제됩니다.
  • 그룹 조회
    • 그룹 탐색 페이지에서 사용자가 현재 가입할 수 있는 그룹을 조회하는 기능입니다. 각 그룹마다 가지고 있는 Id값을 기준으로 오름차순으로 정렬되어 보여지며 페이지네이션으로 구현되었습니다.
    • 사용자는 그룹 탐색 페이지에서 그룹 이름과 태그를 통해서 그룹을 필터링 할 수 있으며, 사용자가 적용한 필터링을 통해 해당되는 팀을 찾아 반환합니다.
    • 팀 이름의 경우 해당 단어가 들어가는 팀은 모두 찾을 수 있습니다.
  • 모든 그룹 태그 조회
    • 저희 서비스에서 적용이 가능한 모든 그룹 태그들을 반환해주는 기능입니다.
    • 기본적으로 관리자가 그룹 태그 삭제 시 soft delete 가 적용되어있어서, 해당 요청을 받으면 soft delete 되어있지 않은 그룹 태그들을 조회합니다. 그 후 각 그룹 태그들의 속성으로 묶어서 반환하게 됩니다.
  • 랭킹 조회
    • 사용자가 해당 팀에 속해있는 그룹원들간의 랭킹을 조회할 수 있는 기능입니다
    • 요청으로 들어오는 날짜값에 맞춰서 해당 요일의 그룹원들의 운동시간을 기준으로 내림차순 으로 정렬되어 보여지며 페이지네이션으로 구현되었습니다.
    • 또한 본인의 랭킹을 한눈에 찾을 수 있도록 별도로 본인의 랭킹도 응답으로 반환됩니다.
  • 그룹 가입
    • 사용자가 가입하고 싶은 그룹에 가입을 할 수 있는 기능입니다.
    • 사용자가 가입하려는 팀이 최초로 가입하는 팀인 경우 TeamMemberMapping 테이블에 새로운 데이터가 추가됩니다
    • 만약에 이전에 탈퇴했던 팀인 경우 soft delete 되어있던 TeamMemberMappingisDeprecated값을 false로 바꾸어주는 방식으로 가입이 진행됩니다
  • 그룹 탈퇴
    • 사용자가 가입했던 그룹에서 탙퇴할 수 있는 기능입니다.
    • 사용자가 그룹을 탈퇴 한 후에 재가입이 가능하므로TeamMemberMapping 테이블에서 그룹과 사용자를 바탕으로 데이터를 찾은 후, TeamMemberMapping 데이터를 soft delete 하는 방식으로 그룹에서 탈퇴됩니다.
  • 그룹 비밀번호 일치확인
    • 사용자가 비공개 그룹에 가입 시 그룹에서 설정해 놓은 비밀번호와 일치한지 확인하는 기능입니다.
    • 요청으로 들어온 비밀번호를 내부적으로 검사 후 일치한 경우는 Ok 응답을, 틀린 경우는 BadRequest 응답을 보내게 됩니다
  • 내가 가입한 그룹 조회
    • 나의 그룹 페이지에서 사용자가 가입한 그룹들을 보여ㅈ주는 기능입니다. 각 그룹마다 가지고 있는 Id값을 기준으로 오름차순으로 정렬되어 보여지며 페이지네이션으로 구현되었습니다.

💬채팅

  • 채팅 조회
    • 채팅방을 보기 전에 생성된 채팅 조회
      • HTTP 프로토콜을 이용하며, 채팅 생성 시간 기준으로 내림차순 정렬의 페이지네이션으로 채팅 조회를 할 수 있습니다.
    • 채팅방을 보는 중에 생성되는 채팅 조회
      • WebSocket/Stomp 프로토콜을 이용하며, 채팅방에 연결하고, 해당 채팅방을 구독 했기 때문에 자신/상대방이 발행한(보낸) 채팅을 실시간으로 볼 수 있습니다.
  • 채팅 메세지 보내기
    • WebSocket/Stomp 프로토콜을 이용하며, 채팅방에 연결하고, 해당 채팅방에서 채팅을 발행합니다.
  • 채팅 기능은 이처럼 발행/구독 패턴을 따르며 해당 시퀀스 다이어그램 방식으로 작동됩니다.
    채팅 시퀀스 다이어그램 보기 Frame 3

🏬마켓

  • 상품 조회
    • 사용자가 마켓의 상품을 조회하는 기능입니다.
    • 마켓에서 원하는 태그를 기반으로 상품을 필터링할 수 있으며, 선택된 태그가 없을 경우 전체 상품 목록을 볼 수 있습니다.
    • 모든 상품은 조회수 내림차순가격 오름차순으로 정렬됩니다.
  • 상품 조회수 증가
    • 특정 상품을 선택하면 해당 상품의 조회수가 증가하여 인기도를 반영합니다.
    • @Modifying과 커스텀 JPQL 쿼리를 사용하여 조회수 증가 연산을 원자적(atomic)으로 수행하여, 동시성 문제 없이 일관된 데이터를 유지할 수 있도록 했습니다.

🔧관리자

  • 상품 관리

    • 상품의 CRUD 기능을 제공합니다.
    • REST APISSR endpoint를 분리하여 구현하였습니다. REST API를 이용하여 상품의 생성, 수정, 삭제와 같은 관리 작업을 수행하고, SSR endpoint를 통해 상품 관리 UI를 지원하며, 상품 목록 페이지, 추가/수정 폼을 제공합니다.
    • 상품 등록
      • 새로운 상품을 등록하며, 상품 태그는 필수 항목입니다.
    • 상품 수정
      • 상품의 이미지, URL, 이름, 가격 등의 정보와 태그를 수정할 수 있습니다.
    • 상품 삭제
      • 필요없는 상품은 삭제할 수 있습니다. 상품은 Soft Delete를 이용하여 처리하므로, 데이터의 추적이 가능합니다.
    • 상품 조회
      • 페이지네이션을 이용해 전체 상품 목록을 조회하며, 각 상품의 태그 정보를 함께 제공합니다.
  • 태그 관리

    • 태그는 상품 태그와 그룹 태그로 분류됩니다.
    • REST API 와 SSR endpoint를 분리하여 구현하였습니다. REST API를 이용하여 태그의 생성, 삭제와 같은 관리 작업을 수행하고, SSR endpoint를 통해 태그 관리 UI를 지원하며, 태그 목록 페이지, 추가 폼을 제공합니다.
    • 태그 조회
      • 전체 태그 정보를 조회하며, 태그 목록 페이지를 통해 태그의 추가 및 삭제 작업을 수행할 수 있습니다.
    • 태그 생성
      • 태그별로 정보를 입력하여 생성 요청을 보내면 태그가 생성됩니다.
        • 상품 태그: 이름
        • 팀태그: 이름, 속성
    • 태그 삭제
      • 필요 없는 태그는 삭제할 수 있으며, 삭제는 Soft Delete 방식으로 처리되어 데이터 추적이 가능합니다.

✨페이지별 화면 소개

  • 아래 화면은 모바일에서 실제 웹을 캡쳐한 사진입니다.
메인 페이지 나의그룹 페이지 그룹탐색 페이지 마켓 페이지 마이 페이지
hometry1_main1 hometry3_mygroup hometry6_searchgroup hometry8_market hometry9_mypage
hometry2_main2 hometry4_ranking hometry7_addgroup
hometry5_chat

✨페이지별 기능

분류 기능1 기능2 기능3 기능4 기능5
로그인 페이지 카카오 소셜 로그인
메인 페이지 날짜별 총 운동시간, 상세운동기록, 일기 조회 운동 추가, 삭제 운동 시작, 종료 일기 작성, 삭제
나의 그룹 페이지 사용자가 가입하거나 만든 전체 그룹 조회 사용자가 가입한 그룹과 만든 그룹에 대한 필터링 그룹원들의 날짜별 운동시간에 대한 랭킹 조회 그룹 탈퇴
그룹 탐색 페이지 전체 그룹 조회 그룹 이름에 기반한 검색 그룹 태그를 이용한 필터링 비밀번호가 있으면 입력 후 그룹 가입, 없으면 바로 가입 그룹 생성
마켓 페이지 마켓 상품 조회 상품 태그를 이용한 필터링
마이 페이지 닉네임 변경 출석 일수, 운동 통계 조회 회원 탈퇴
관리자 로그인페이지 카카오 소셜 로그인
관리자 상품 관리 페이지 상품 추가, 수정, 삭제
관리자 상품 태그 관리 페이지 상품 태그 추가, 삭제
관리자 팀 그룹 태그 관리 페이지 그룹 태그 추가, 삭제

📁폴더 구조

폴더 구조 보기
src
├── main
│   ├── java
│   │   └── homeTry
│   │       ├── Application.java
│   │       ├── admin
│   │       │   ├── controller
│   │       │   │   ├── rest
│   │       │   │   │   └── AdminPageRestController.java
│   │       │   │   └── view
│   │       │   │       ├── AdminAuthorityController.java
│   │       │   │       ├── AdminMainPageController.java
│   │       │   │       └── AdminPageViewController.java
│   │       │   ├── dto
│   │       │   │   └── request
│   │       │   │       └── AdminCodeRequest.java
│   │       │   ├── exception
│   │       │   │   ├── AdminErrorType.java
│   │       │   │   └── badReqeustException
│   │       │   │       └── InvalidAdminCodeException.java
│   │       │   └── service
│   │       │       └── AdminPageService.java
│   │       ├── chatting
│   │       │   ├── config
│   │       │   │   └── ChattingConfig.java
│   │       │   ├── dto
│   │       │   │   ├── request
│   │       │   │   │   └── ChattingMessageRequest.java
│   │       │   │   └── response
│   │       │   │       └── ChattingMessageResponse.java
│   │       │   ├── endpointHandler
│   │       │   │   ├── async
│   │       │   │   │   └── ChattingMessageListener.java
│   │       │   │   └── rest
│   │       │   │       └── ChattingController.java
│   │       │   ├── exception
│   │       │   │   ├── ChattingErrorType.java
│   │       │   │   ├── badRequestException
│   │       │   │   │   ├── InactivatedMemberWithValidTokenException.java
│   │       │   │   │   ├── InvalidChattingTokenException.java
│   │       │   │   │   ├── InvalidTeamIdException.java
│   │       │   │   │   └── NoSuchMemberInDbWithValidTokenException.java
│   │       │   │   └── handler
│   │       │   │       ├── ChattingExceptionHandler.java
│   │       │   │       └── StompInterceptorErrorHandler.java
│   │       │   ├── interceptor
│   │       │   │   └── StompInterceptor.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   └── Chatting.java
│   │       │   │   └── vo
│   │       │   │       └── Message.java
│   │       │   ├── repository
│   │       │   │   └── ChattingRepository.java
│   │       │   └── service
│   │       │       └── ChattingService.java
│   │       ├── common
│   │       │   ├── annotation
│   │       │   │   ├── DateValid.java
│   │       │   │   ├── LoginMember.java
│   │       │   │   ├── PasswordValid.java
│   │       │   │   └── validator
│   │       │   │       ├── DateValidator.java
│   │       │   │       └── PasswordValidator.java
│   │       │   ├── auth
│   │       │   │   ├── LoginMemberArgumentResolver.java
│   │       │   │   ├── exception
│   │       │   │   │   ├── AuthErrorType.java
│   │       │   │   │   ├── badRequestException
│   │       │   │   │   │   ├── InvalidAuthCodeException.java
│   │       │   │   │   │   └── InvalidTokenException.java
│   │       │   │   │   └── internalServerException
│   │       │   │   │       ├── HomeTryServerException.java
│   │       │   │   │       └── KakaoAuthServerException.java
│   │       │   │   ├── jwt
│   │       │   │   │   ├── JwtAuth.java
│   │       │   │   │   └── JwtUtil.java
│   │       │   │   └── kakaoAuth
│   │       │   │       ├── client
│   │       │   │       │   └── KakaoApiClient.java
│   │       │   │       ├── config
│   │       │   │       │   ├── DevKakaoAuthConfigRegistrar.java
│   │       │   │       │   ├── KakaoAuthConfig.java
│   │       │   │       │   └── ProdKakaoAuthConfigRegistrar.java
│   │       │   │       ├── controller
│   │       │   │       │   ├── rest
│   │       │   │       │   │   └── KakaoAuthRestController.java
│   │       │   │       │   └── view
│   │       │   │       │       └── KakaoAuthSSRController.java
│   │       │   │       ├── dto
│   │       │   │       │   ├── KakaoMemberInfoDTO.java
│   │       │   │       │   ├── KakaoMemberWithdrawDTO.java
│   │       │   │       │   └── response
│   │       │   │       │       ├── KakaoErrorResponse.java
│   │       │   │       │       ├── KakaoMemberInfoResponse.java
│   │       │   │       │       └── TokenResponse.java
│   │       │   │       └── service
│   │       │   │           ├── KakaoAuthService.java
│   │       │   │           └── KakaoClientService.java
│   │       │   ├── config
│   │       │   │   ├── SwaggerConfig.java
│   │       │   │   └── WebMvcConfig.java
│   │       │   ├── constants
│   │       │   │   └── DateTimeUtil.java
│   │       │   ├── converter
│   │       │   │   └── DurationToLongConverter.java
│   │       │   ├── entity
│   │       │   │   ├── BaseEntity.java
│   │       │   │   └── SoftDeletableEntity.java
│   │       │   ├── exception
│   │       │   │   ├── BadRequestException.java
│   │       │   │   ├── CommonErrorType.java
│   │       │   │   ├── ErrorType.java
│   │       │   │   ├── InternalServerException.java
│   │       │   │   ├── dto
│   │       │   │   │   └── response
│   │       │   │   │       └── ErrorResponse.java
│   │       │   │   └── handler
│   │       │   │       └── GlobalExceptionHandler.java
│   │       │   └── interceptor
│   │       │       ├── AdminInterceptor.java
│   │       │       └── JwtInterceptor.java
│   │       ├── diary
│   │       │   ├── controller
│   │       │   │   └── DiaryController.java
│   │       │   ├── dto
│   │       │   │   ├── DiaryDto.java
│   │       │   │   └── request
│   │       │   │       └── DiaryRequest.java
│   │       │   ├── exception
│   │       │   │   ├── DiaryErrorType.java
│   │       │   │   └── badRequestException
│   │       │   │       └── DiaryNotFoundException.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   └── Diary.java
│   │       │   │   └── vo
│   │       │   │       └── Memo.java
│   │       │   ├── repository
│   │       │   │   └── DiaryRepository.java
│   │       │   └── service
│   │       │       └── DiaryService.java
│   │       ├── docs
│   │       │   └── SwaggerRedirectController.java
│   │       ├── exerciseList
│   │       │   ├── controller
│   │       │   │   └── ExerciseController.java
│   │       │   ├── dto
│   │       │   │   ├── request
│   │       │   │   │   └── ExerciseRequest.java
│   │       │   │   └── response
│   │       │   │       └── ExerciseResponse.java
│   │       │   ├── event
│   │       │   │   ├── ExerciseCreationEvent.java
│   │       │   │   ├── ExerciseCreationEventListener.java
│   │       │   │   └── ExerciseEventPublisher.java
│   │       │   ├── exception
│   │       │   │   ├── ExerciseErrorType.java
│   │       │   │   └── badRequestException
│   │       │   │       ├── ExerciseAlreadyStartedException.java
│   │       │   │       ├── ExerciseDeprecatedException.java
│   │       │   │       ├── ExerciseInProgressException.java
│   │       │   │       ├── ExerciseNotFoundException.java
│   │       │   │       ├── ExerciseNotStartedException.java
│   │       │   │       └── NoExercisePermissionException.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   ├── Exercise.java
│   │       │   │   │   ├── ExerciseHistory.java
│   │       │   │   │   └── ExerciseTime.java
│   │       │   │   └── vo
│   │       │   │       └── ExerciseName.java
│   │       │   ├── repository
│   │       │   │   ├── ExerciseHistoryRepository.java
│   │       │   │   ├── ExerciseRepository.java
│   │       │   │   └── ExerciseTimeRepository.java
│   │       │   └── service
│   │       │       ├── ExerciseHistoryService.java
│   │       │       ├── ExerciseSchedulerService.java
│   │       │       ├── ExerciseService.java
│   │       │       ├── ExerciseTimeHelper.java
│   │       │       └── ExerciseTimeService.java
│   │       ├── mainPage
│   │       │   ├── controller
│   │       │   │   └── MainPageController.java
│   │       │   ├── dto
│   │       │   │   └── response
│   │       │   │       └── MainPageResponse.java
│   │       │   └── service
│   │       │       └── MainPageService.java
│   │       ├── member
│   │       │   ├── controller
│   │       │   │   └── MemberController.java
│   │       │   ├── dto
│   │       │   │   ├── MemberDTO.java
│   │       │   │   ├── request
│   │       │   │   │   └── ChangeNicknameRequest.java
│   │       │   │   └── response
│   │       │   │       └── MyPageResponse.java
│   │       │   ├── exception
│   │       │   │   ├── MemberErrorType.java
│   │       │   │   ├── badRequestException
│   │       │   │   │   ├── InactivatedMemberException.java
│   │       │   │   │   ├── LoginFailedException.java
│   │       │   │   │   ├── MemberNotFoundException.java
│   │       │   │   │   └── RegisterEmailConflictException.java
│   │       │   │   └── internalServerException
│   │       │   │       └── UniqueKeyViolatonException.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   └── Member.java
│   │       │   │   ├── enums
│   │       │   │   │   └── Role.java
│   │       │   │   └── vo
│   │       │   │       ├── Email.java
│   │       │   │       └── Nickname.java
│   │       │   ├── repository
│   │       │   │   └── MemberRepository.java
│   │       │   ├── service
│   │       │   │   ├── MemberService.java
│   │       │   │   ├── MemberTeamWithdrawService.java
│   │       │   │   └── MemberWithdrawService.java
│   │       │   └── utils
│   │       │       └── RandomNicknameGenerator.java
│   │       ├── product
│   │       │   ├── controller
│   │       │   │   ├── rest
│   │       │   │   │   ├── AdminProductRestController.java
│   │       │   │   │   └── MarketController.java
│   │       │   │   └── view
│   │       │   │       └── AdminProductViewController.java
│   │       │   ├── dto
│   │       │   │   ├── request
│   │       │   │   │   └── ProductRequest.java
│   │       │   │   └── response
│   │       │   │       ├── ProductAdminResponse.java
│   │       │   │       └── ProductResponse.java
│   │       │   ├── exception
│   │       │   │   ├── ProductErrorType.java
│   │       │   │   └── badRequestException
│   │       │   │       ├── MissingProductTagException.java
│   │       │   │       └── ProductNotFoundException.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   ├── Product.java
│   │       │   │   │   └── ProductTagMapping.java
│   │       │   │   └── vo
│   │       │   │       ├── ProductImageUrl.java
│   │       │   │       ├── ProductName.java
│   │       │   │       ├── ProductPrice.java
│   │       │   │       ├── ProductUrl.java
│   │       │   │       └── StoreName.java
│   │       │   ├── repository
│   │       │   │   ├── ProductRepository.java
│   │       │   │   └── ProductTagMappingRepository.java
│   │       │   └── service
│   │       │       ├── AdminProductService.java
│   │       │       ├── ProductService.java
│   │       │       └── ProductTagMappingService.java
│   │       ├── tag
│   │       │   ├── controller
│   │       │   │   ├── rest
│   │       │   │   │   └── TagRestController.java
│   │       │   │   └── view
│   │       │   │       └── AdminTagViewController.java
│   │       │   ├── exception
│   │       │   │   ├── TagErrorType.java
│   │       │   │   └── badRequestException
│   │       │   │       └── ForbiddenTagAccessException.java
│   │       │   ├── model
│   │       │   │   ├── entity
│   │       │   │   │   └── Tag.java
│   │       │   │   └── vo
│   │       │   │       └── TagName.java
│   │       │   ├── productTag
│   │       │   │   ├── dto
│   │       │   │   │   ├── ProductTagDto.java
│   │       │   │   │   ├── request
│   │       │   │   │   │   └── ProductTagRequest.java
│   │       │   │   │   └── response
│   │       │   │   │       └── ProductTagResponse.java
│   │       │   │   ├── exception
│   │       │   │   │   ├── ProductTagErrorType.java
│   │       │   │   │   └── badRequestException
│   │       │   │   │       ├── ProductTagAlreadyExistsException.java
│   │       │   │   │       └── ProductTagNotFoundException.java
│   │       │   │   ├── model
│   │       │   │   │   └── entity
│   │       │   │   │       └── ProductTag.java
│   │       │   │   ├── repository
│   │       │   │   │   └── ProductTagRepository.java
│   │       │   │   └── service
│   │       │   │       └── ProductTagService.java
│   │       │   └── teamTag
│   │       │       ├── dto
│   │       │       │   ├── AllTeamTagDTO.java
│   │       │       │   ├── TeamTagDTO.java
│   │       │       │   ├── request
│   │       │       │   │   └── TeamTagRequest.java
│   │       │       │   └── response
│   │       │       │       └── TeamTagResponse.java
│   │       │       ├── exception
│   │       │       │   ├── TeamTagErrorType.java
│   │       │       │   └── badRequestException
│   │       │       │       ├── TeamTagAlreadyExistsException.java
│   │       │       │       └── TeamTagNotFoundException.java
│   │       │       ├── model
│   │       │       │   ├── entity
│   │       │       │   │   └── TeamTag.java
│   │       │       │   └── vo
│   │       │       │       └── TeamTagAttribute.java
│   │       │       ├── repository
│   │       │       │   └── TeamTagRepository.java
│   │       │       └── service
│   │       │           └── TeamTagService.java
│   │       └── team
│   │           ├── controller
│   │           │   └── TeamController.java
│   │           ├── dto
│   │           │   ├── RankingDTO.java
│   │           │   ├── request
│   │           │   │   ├── CheckingPasswordRequest.java
│   │           │   │   └── TeamCreateRequest.java
│   │           │   └── response
│   │           │       ├── RankingResponse.java
│   │           │       ├── TagListResponse.java
│   │           │       └── TeamResponse.java
│   │           ├── exception
│   │           │   ├── TeamErrorType.java
│   │           │   └── badRequestException
│   │           │       ├── AlreadyJoinedTeamException.java
│   │           │       ├── InvalidPasswordException.java
│   │           │       ├── MyRankingNotFoundException.java
│   │           │       ├── NotTeamLeaderException.java
│   │           │       ├── TeamHasNotPasswordException.java
│   │           │       ├── TeamLeaderCannotWithdrawException.java
│   │           │       ├── TeamMemberNotFoundException.java
│   │           │       ├── TeamNameAlreadyExistsException.java
│   │           │       ├── TeamNotFoundException.java
│   │           │       └── TeamParticipantsFullException.java
│   │           ├── model
│   │           │   ├── entity
│   │           │   │   ├── Team.java
│   │           │   │   ├── TeamMemberMapping.java
│   │           │   │   └── TeamTagMapping.java
│   │           │   └── vo
│   │           │       ├── Description.java
│   │           │       ├── Name.java
│   │           │       ├── Participant.java
│   │           │       └── Password.java
│   │           ├── repository
│   │           │   ├── TeamMemberMappingRepository.java
│   │           │   ├── TeamRepository.java
│   │           │   └── TeamTagMappingRepository.java
│   │           └── service
│   │               ├── TeamJoinAndWithdrawService.java
│   │               ├── TeamMemberMappingService.java
│   │               ├── TeamService.java
│   │               └── TeamTagMappingService.java
│   └── resources
│       ├── application-dev-kakao-login.properties
│       ├── application-dev.properties
│       ├── application-prod-kakao-login.properties
│       ├── application-prod.properties
│       ├── application-secret.properties
│       ├── application.properties
│       ├── data-dev.sql
│       ├── data.sql
│       ├── static
│       │   ├── css
│       │   │   ├── admin
│       │   │   │   ├── adminAuthority.css
│       │   │   │   ├── adminMainPage.css
│       │   │   │   └── adminPromote.css
│       │   │   ├── product
│       │   │   │   ├── productAdd.css
│       │   │   │   ├── productEdit.css
│       │   │   │   └── productList.css
│       │   │   ├── styles.css
│       │   │   └── tag
│       │   │       ├── addProductTag.css
│       │   │       ├── addTeamTag.css
│       │   │       ├── productTags.css
│       │   │       └── teamTags.css
│       │   ├── image
│       │   │   ├── add.png
│       │   │   ├── edit.png
│       │   │   ├── kakao_login_medium_narrow.png
│       │   │   ├── previous.png
│       │   │   ├── remove.png
│       │   │   └── save.png
│       │   └── js
│       │       ├── admin
│       │       │   ├── adminAuthority.js
│       │       │   ├── adminMainMage.js
│       │       │   └── adminPromote.js
│       │       ├── main.js
│       │       ├── product
│       │       │   ├── productAdd.js
│       │       │   ├── productEdit.js
│       │       │   └── productList.js
│       │       └── tag
│       │           ├── productTagAdd.js
│       │           ├── productTagDelete.js
│       │           ├── teamTagAdd.js
│       │           └── teamTagDelete.js
│       └── templates
│           ├── admin
│           │   ├── adminAuthority.html
│           │   ├── adminKakaoLoginResult.html
│           │   ├── adminMain.html
│           │   ├── adminPage.html
│           │   └── adminPromote.html
│           ├── product
│           │   ├── productAdd.html
│           │   ├── productEdit.html
│           │   └── productList.html
│           └── tag
│               ├── addProductTag.html
│               ├── addTeamTag.html
│               ├── productTags.html
│               └── teamTags.html
└── test
    └── java
        └── homeTry
            ├── chatting
            │   └── ChattingTest.java
            ├── common
            │   └── config
            │       └── CorsTest.java
            ├── diary
            │   └── DiaryTest.java
            ├── exerciseList
            │   ├── ExerciseTest.java
            │   └── service
            │       ├── ExerciseSchedulerServiceTest.java
            │       └── ExerciseTimeServiceTest.java
            ├── mainPage
            │   └── MainPageTest.java
            ├── member
            │   └── MemberTest.java
            ├── product
            │   ├── AdminProductViewTest.java
            │   └── MarketTest.java
            ├── tag
            │   └── TagPageTest.java
            └── team
                └── TeamTest.java

About

그룹원들과 홈트 시간을 공유하며 쉽고 꾸준한 운동 습관을 길러주는 '홈트라이'

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published