Feat/17 matching menu#28
Conversation
Feat/user tab
- Club Header / Overview / Members 탭 UI 추가 - 동아리/멤버 더미 모델 추가 - 멤버 탭 클릭 시 print 로그 출력 - 디자인 토큰(AppColors) 적용 및 8pt 여백 준수 Refs #13
- KakaoAuthService, KakaoAuthServiceImpl 구현 - POST /api/auth/kakao 엔드포인트 추가 - KakaoLoginRequest DTO, ErrorCode(KAKAO_*), LoginType.KAKAO 추가 - UserRepository findByKakaoId, existsByKakaoId 추가 - Flutter 카카오 로그인 연동 (auth_service, auth_repository, login_screen)
- POST /api/auth/kakao 엔드포인트 명세 추가 - 요청/응답 필드, 처리 흐름, 에러 코드 문서화 - User Entity에 kakaoId, LoginType.KAKAO 반영
- RootScreen에 initialIndex를 추가해 동아리 탭(인덱스 1) 기본 진입 지원 - root_screen 하단 네비게이션과 ClubScreen 연결 Refs #13
Feat/필수정보입력 api 개발
Feat/백오피스 구현
Feat(be)/kakao login
There was a problem hiding this comment.
Pull request overview
This is a large PR that significantly extends the PNU Basketball Platform across frontend, backend, and infrastructure. Despite the title suggesting primarily a "matching tab" addition, the PR contains extensive changes including Kakao social login integration, a complete schema redesign (nickname → realName, new club/match tables), an admin backoffice, and navigation restructuring.
Changes:
- Added matching tab to bottom navigation bar, created
MatchListScreen, restructuredroot_screen.dartnavigation items, and added club tab with dummy data - Integrated Kakao social login end-to-end (Flutter SDK, backend service, API endpoint), added post-login flows for profile completion and club selection, and renamed
nicknametorealNamethroughout the entire stack - Created admin backoffice (HTML/CSS/JS) with user management, club management, and match management features; added corresponding backend admin API endpoints and new domain entities (Club, ClubMember, Match, MatchParticipant)
Reviewed changes
Copilot reviewed 101 out of 102 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
frontend/lib/presentation/screens/root/root_screen.dart |
Restructured bottom nav bar: added matching & club tabs, styling, initialIndex support |
frontend/lib/presentation/screens/club/* |
New club screen with header, info section, and member list widgets |
frontend/lib/presentation/screens/auth/login_screen.dart |
Added Kakao login button, changed login/google handlers to return AuthResponseModel |
frontend/lib/presentation/screens/auth/signup_screen.dart |
Added realName, dateOfBirth, isPnuStudent, department, studentId fields |
frontend/lib/presentation/screens/auth/complete_profile_screen.dart |
New screen for social login users to complete profile |
frontend/lib/presentation/screens/auth/club_selection_screen.dart |
New screen for club selection after signup/login |
frontend/lib/data/models/user_model.dart |
Changed nickname → realName, added new fields |
frontend/lib/data/models/club_model.dart |
New ClubModel with JSON serialization and dummy data |
frontend/lib/data/models/auth_response_model.dart |
Added realName, needsClubSelection fields |
frontend/lib/data/repositories/auth_repository.dart |
Added Kakao login, club selection, profile completion methods |
frontend/lib/data/services/auth_service.dart |
Added Kakao login, completeProfile, club API calls |
frontend/lib/presentation/providers/auth_provider.dart |
Added kakaoLogin, completeProfile, getClubs, selectClub methods |
frontend/lib/main.dart |
Added Kakao SDK initialization |
frontend/lib/app.dart |
Added routes for complete-profile and club-selection, removed AuthGate |
frontend/lib/core/constants/app_colors.dart |
New AppColors constants (duplicates theme version) |
frontend/lib/core/constants/api_endpoints.dart |
Added kakaoLogin, completeProfile, club endpoints |
frontend/pubspec.yaml |
Added kakao_flutter_sdk dependencies |
frontend/dart_defines.json |
Contains leaked Kakao API key |
frontend/android/gradle.properties |
Contains leaked Kakao API key |
backend/src/main/java/com/pnu/basketball/domain/*.java |
New domain entities: Club, ClubMember, Match, MatchParticipant, enums |
backend/src/main/java/com/pnu/basketball/domain/User.java |
Major refactor: nickname→realName, added kakaoId, PNU student fields, stats |
backend/src/main/java/com/pnu/basketball/service/auth/KakaoAuthServiceImpl.java |
New Kakao authentication service |
backend/src/main/java/com/pnu/basketball/service/auth/AuthServiceImpl.java |
Added needsClubSelection to auth response, removed soft-delete checks |
backend/src/main/java/com/pnu/basketball/service/club/ClubServiceImpl.java |
New club service for listing, selecting, status checking |
backend/src/main/java/com/pnu/basketball/service/admin/AdminServiceImpl.java |
New admin service for users, clubs, matches management |
backend/src/main/java/com/pnu/basketball/service/user/UserServiceImpl.java |
Added completeProfile, changed withdraw to hard-delete |
backend/src/main/java/com/pnu/basketball/controller/auth/AuthController.java |
Added Kakao login, complete-profile, club-selection-status endpoints |
backend/src/main/java/com/pnu/basketball/controller/admin/AdminController.java |
New admin REST API controller |
backend/src/main/java/com/pnu/basketball/controller/club/ClubController.java |
New club controller |
backend/src/main/java/com/pnu/basketball/repository/*.java |
New repositories for Club, ClubMember, Match, MatchParticipant |
backend/src/main/java/com/pnu/basketball/config/SecurityConfig.java |
Simplified auth endpoint permits, added admin static files |
backend/src/main/resources/static/admin/* |
Complete admin backoffice (HTML/CSS/JS) |
backend/src/main/resources/application.yml |
New main config file |
backend/src/main/resources/application-local.yml |
Deleted local profile config |
docs/database/schema.sql |
Complete schema rewrite with clubs, matches, posts, comments |
docs/database/*.md |
New schema documentation |
scripts/migrate-basketball-tables.sql |
Migration script for basketball tables |
SPEC/AUTH_API_SPEC.md |
Added Kakao login API specification |
.github/workflows/deploy-backend.yml |
Simplified deployment step |
.gitignore |
Added Kakao key files to ignore list |
Comments suppressed due to low confidence (1)
backend/src/main/resources/application-local.yml:1
- Deleted
application-local.ymlwithout replacement: The local profile configuration (Redis-less mode with in-memory token storage) was deleted entirely. Theapplication.ymlsetsspring.profiles.active: local, but there's no longer anapplication-local.ymlto provide the local profile settings. This means theapp.token-storage: memorysetting and the Redis auto-configuration exclusion are lost, which could cause the application to fail to start locally if Redis is not available.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public List<ClubListResponse> getClubs() { | ||
| return clubRepository.findAll().stream() | ||
| .map(club -> ClubListResponse.builder() | ||
| .clubId(club.getId()) | ||
| .name(club.getName()) | ||
| .logoUrl(club.getLogoUrl()) | ||
| .memberCount(clubMemberRepository.countByClub_Id(club.getId())) | ||
| .build()) | ||
| .collect(Collectors.toList()); |
There was a problem hiding this comment.
N+1 query in club list: getClubs() iterates over all clubs and calls clubMemberRepository.countByClub_Id() for each club, resulting in N+1 queries. Similarly, toAdminClubListResponse calls countByClub_Id() per club. Consider using a single query with a COUNT aggregate or a @Query that joins clubs with a member count subquery.
| import 'package:flutter/material.dart'; | ||
|
|
There was a problem hiding this comment.
Inconsistent import path: The club widget files (club_member_list.dart, club_info_section.dart, club_header.dart) import AppColors from '../../../../core/constants/app_colors.dart', while root_screen.dart imports AppColors from '../../../core/theme/app_colors.dart'. There appear to be two separate AppColors classes in the project. The club widgets will use a different AppColors than the rest of the app (e.g., the club files reference AppColors.white and AppColors.headerGrey which are only defined in core/constants/app_colors.dart but not necessarily in core/theme/app_colors.dart). This duplication could lead to inconsistent styling. Consider consolidating into a single AppColors class.
| // 실제 기기: http://<PC IP>:8080 | ||
| static const String baseUrl = | ||
| String.fromEnvironment('API_BASE_URL', defaultValue: 'https://ddalba.duckdns.org'); | ||
| //String.fromEnvironment('API_BASE_URL', defaultValue: 'http://localhost:8080'); |
There was a problem hiding this comment.
Commented-out code: This commented-out localhost URL should be removed before merging to keep the codebase clean.
| const nativeAppKey = String.fromEnvironment( | ||
| 'KAKAO_NATIVE_APP_KEY', | ||
| defaultValue: '', | ||
| ); | ||
| KakaoSdk.init(nativeAppKey: nativeAppKey); |
There was a problem hiding this comment.
KakaoSdk initialized with potentially empty key: If KAKAO_NATIVE_APP_KEY is not provided via --dart-define, the defaultValue is an empty string, and KakaoSdk.init will be called with an empty nativeAppKey. This could cause runtime errors or silent failures when Kakao login is attempted. Consider adding a guard or logging a warning when the key is empty.
| "user": { | ||
| "userId": 10, | ||
| "email": "user@kakao.com", | ||
| "nickname": "카카오사용자", | ||
| "profileImageUrl": "https://k.kakaocdn.net/...", | ||
| "loginType": "KAKAO", | ||
| "isNewUser": true | ||
| } | ||
| }, | ||
| "message": "카카오 로그인 성공" | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Kakao login API spec uses nickname instead of realName: The Kakao login response example in the spec shows "nickname": "카카오사용자", but the backend AuthResponse.UserInfo class now uses realName instead of nickname. The spec should be updated to match the actual response format.
| if (authResponse != null && mounted) { | ||
| if (authResponse.user.needsClubSelection == true) { | ||
| Navigator.of(context).pushReplacementNamed('/club-selection'); | ||
| } else if (authResponse.user.isNewUser == true) { | ||
| Navigator.of(context).pushReplacementNamed('/complete-profile'); | ||
| } else { | ||
| Navigator.of(context).pushReplacementNamed('/home'); | ||
| } |
There was a problem hiding this comment.
Inconsistent login flow ordering: In _handleKakaoLogin, the check order is needsClubSelection → isNewUser → home. In _handleGoogleLogin, it's isNewUser → needsClubSelection → home. And in _handleLogin, it's needsClubSelection → home (no isNewUser check). The inconsistent ordering could lead to users being routed differently depending on login method. For example, a new Google user who also needs club selection would go to /complete-profile, while a new Kakao user who also needs club selection would go to /club-selection. Ensure the routing logic is consistent across all login methods.
| title: '딸바', | ||
| theme: AppTheme.theme, | ||
| home: const AuthGate(), | ||
| home: const LoginScreen(), |
There was a problem hiding this comment.
Removed AuthGate, always shows LoginScreen: The home property was changed from AuthGate() (which likely checked auth state) to always showing LoginScreen. This means users who are already logged in (have valid tokens) will always see the login screen when opening the app, breaking the auto-login experience. Consider restoring the AuthGate pattern or implementing similar token-check logic.
| | 401 | `KAKAO_TOKEN_INVALID` | 카카오 토큰 검증 실패 | | ||
| | 404 | `USER_NOT_FOUND` | 사용자를 찾을 수 없음 | | ||
| | 409 | `EMAIL_ALREADY_EXISTS` | 이미 존재하는 이메일 | | ||
| | 409 | `NICKNAME_ALREADY_EXISTS` | 이미 존재하는 닉네임 | |
There was a problem hiding this comment.
Stale API spec: The AUTH_API_SPEC.md still references NICKNAME_ALREADY_EXISTS error code (line 631), but this error code has been removed from the backend ErrorCode.java and replaced with STUDENT_ID_ALREADY_EXISTS. The response example at line 663 still shows nickname field. The spec should be updated to reflect the nickname → realName change throughout.
| | 409 | `NICKNAME_ALREADY_EXISTS` | 이미 존재하는 닉네임 | | |
| | 409 | `STUDENT_ID_ALREADY_EXISTS` | 이미 존재하는 학번 | |
| return userRepository.save(newUser); | ||
| })); | ||
|
|
||
| userRepository.save(user); |
There was a problem hiding this comment.
Duplicate redundant userRepository.save(user) call: On line 75, userRepository.save(user) is called after the user has already been saved in each branch of the orElseGet chain (lines 60, 72). Since JPA entities are managed within the transaction, this extra save is unnecessary and could cause confusion.
| CREATE TABLE matches ( | ||
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), | ||
| home_club_id UUID NOT NULL REFERENCES clubs(id) ON DELETE CASCADE, | ||
| away_club_id UUID NOT NULL REFERENCES clubs(id) ON DELETE CASCADE, | ||
| scheduled_at TIMESTAMP NOT NULL, | ||
| state match_state NOT NULL DEFAULT 'SCHEDULED', | ||
| home_score INTEGER, | ||
| away_score INTEGER, | ||
| created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| CONSTRAINT chk_match_clubs_different CHECK (home_club_id != away_club_id) | ||
| ); |
There was a problem hiding this comment.
Schema discrepancy between docs and migration: The docs/database/schema.sql defines matches with home_club_id and away_club_id as NOT NULL (lines 81-82), but scripts/migrate-basketball-tables.sql defines them as nullable (lines 46-47) to support QUICK matches. The migration script's approach is correct for the FORMAL/QUICK match design, but schema.sql will reject QUICK matches. These should be kept in sync.
[FEAT] 매칭 탭 신규 추가 및 네비게이션 바 수정
💡 작업 내용
root_screen.dart내부bottomNavigationBar이름 수정match_list_screen.dart신규 생성 및 매칭 탭 추가root_screen의bottomNavigationBar동작 확인을 위해 임시로club-tab과 병합을 진행했습니다.club-tab관련 파일들이 불완전할 수 있으니 리뷰 및 테스트 시 참고 부탁드립니다.📸 스크린샷
매칭탭 관련
root_screen 내부 bottomNavigationBar 관련