Conversation
…rt-screen # Conflicts: # frontend/ongi/lib/screens/start_screen.dart # frontend/ongi/pubspec.yaml
Walkthrough이 변경사항은 온기 앱의 회원가입 및 로그인 플로우를 대대적으로 리팩토링하고, 가족 코드 생성 및 가입, 토큰 저장 등 인증 관련 서비스와 화면을 새롭게 도입합니다. 기존의 인증 로직은 분리된 서비스 클래스로 이동되었으며, 각 화면의 UI와 동작이 명확히 분리 및 개선되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant LoginPwScreen
participant LoginService
participant TokenStorage
participant LoginReadyScreen
participant HomeScreen
User->>LoginPwScreen: 비밀번호 입력 및 로그인 버튼 클릭
LoginPwScreen->>LoginService: login(email, password)
LoginService->>TokenStorage: saveAccessToken(token)
LoginService-->>LoginPwScreen: 로그인 결과 반환(성공/실패)
alt 로그인 성공
LoginPwScreen->>LoginReadyScreen: username 전달하며 이동
LoginReadyScreen->>HomeScreen: 2초 후 자동 이동
else 로그인 실패
LoginPwScreen->>User: 에러 스낵바 표시
end
sequenceDiagram
participant User
participant FamilycodeCreateScreen
participant CodeService
User->>FamilycodeCreateScreen: "생성하기" 클릭
FamilycodeCreateScreen->>CodeService: familyCreate(familyName)
CodeService-->>FamilycodeCreateScreen: familyCode 반환
FamilycodeCreateScreen->>User: 코드 표시 및 "함께하기" 버튼 활성화
Possibly related PRs
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
frontend/ongi/lib/services/family_join_service.dart (1)
6-6: 하드코딩된 베이스 URL을 환경 설정으로 분리하세요.다른 서비스들과 마찬가지로 베이스 URL이 하드코딩되어 있습니다. 중앙 집중식 설정 관리가 필요합니다.
🧹 Nitpick comments (4)
frontend/ongi/lib/utils/token_storage.dart (1)
1-20: 전체적으로 견고한 에러 처리 추가를 고려해보세요.현재 구현은 기본적인 기능을 잘 제공하지만, SharedPreferences 작업에 대한 에러 처리를 추가하면 더 견고한 코드가 될 것입니다.
예를 들어, 다음과 같은 에러 처리를 추가할 수 있습니다:
static Future<void> saveAccessToken(String token) async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setString(_accessTokenKey, token); } catch (e) { // 로그 또는 에러 처리 rethrow; } }frontend/ongi/lib/screens/start_screen.dart (1)
200-216: 한국어 문자 검증 로직이 잘 구현되었습니다.정규표현식을 사용한 한국어 검증이 올바르게 구현되어 있고, 일관된 스낵바 스타일이 적용되었습니다.
// 성능 개선을 위해 정규표현식을 static final로 선언하는 것을 고려해보세요: static final RegExp _koreanRegex = RegExp(r'[ㄱ-ㅎㅏ-ㅣ가-힣]');frontend/ongi/lib/screens/login/login_pw_screen.dart (1)
83-106: 비밀번호 표시/숨기기 토글 기능을 추가하세요사용자 경험 향상을 위해 비밀번호를 표시하거나 숨길 수 있는 토글 버튼을 추가하는 것이 좋습니다.
상태 변수를 추가하고:
bool _isPasswordVisible = false;TextField를 수정하세요:
child: TextField( controller: _passwordCtrl, - keyboardType: TextInputType.visiblePassword, + obscureText: !_isPasswordVisible, + keyboardType: TextInputType.text, style: const TextStyle(fontSize: 25, color: AppColors.ongiOrange), decoration: InputDecoration( hintText: 'PASSWORD', hintStyle: TextStyle(color: Colors.grey), + suffixIcon: IconButton( + icon: Icon( + _isPasswordVisible ? Icons.visibility : Icons.visibility_off, + color: AppColors.ongiOrange, + ), + onPressed: () { + setState(() { + _isPasswordVisible = !_isPasswordVisible; + }); + }, + ),frontend/ongi/lib/screens/signup/mode_screen.dart (1)
39-55: 이메일이 비어있을 때의 처리를 개선하세요현재는 모든 필드가 비어있을 때만 에러를 표시하지만, 각 필드별로 구체적인 에러 메시지를 제공하는 것이 좋습니다.
- if (email.isEmpty || password.isEmpty || name.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text( - '회원가입 정보가 올바르지 않아요.', - style: TextStyle(color: AppColors.ongiOrange), - ), + if (email.isEmpty) { + _showErrorSnackBar('이메일 정보가 없습니다.'); + return; + } + if (password.isEmpty) { + _showErrorSnackBar('비밀번호 정보가 없습니다.'); + return; + } + if (name.isEmpty) { + _showErrorSnackBar('이름 정보가 없습니다.'); + return; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
frontend/ongi/lib/main.dart(2 hunks)frontend/ongi/lib/screens/login/login_pw_screen.dart(1 hunks)frontend/ongi/lib/screens/login/login_ready_screen.dart(1 hunks)frontend/ongi/lib/screens/signup/familycode_create_screen.dart(4 hunks)frontend/ongi/lib/screens/signup/familycode_screen.dart(3 hunks)frontend/ongi/lib/screens/signup/familyname_screen.dart(4 hunks)frontend/ongi/lib/screens/signup/mode_screen.dart(3 hunks)frontend/ongi/lib/screens/signup/ready_screen.dart(1 hunks)frontend/ongi/lib/screens/signup/username_screen.dart(1 hunks)frontend/ongi/lib/screens/start_screen.dart(1 hunks)frontend/ongi/lib/services/code_service.dart(1 hunks)frontend/ongi/lib/services/email_service.dart(1 hunks)frontend/ongi/lib/services/family_join_service.dart(1 hunks)frontend/ongi/lib/services/login_service.dart(1 hunks)frontend/ongi/lib/services/signup_service.dart(1 hunks)frontend/ongi/lib/utils/token_storage.dart(1 hunks)
🔇 Additional comments (24)
frontend/ongi/lib/services/email_service.dart (1)
1-2: import 순서 변경 확인 – 문제 없습니다
dart:SDK import 뒤에 외부 패키지 import를 배치한 것은 Dart 스타일 가이드에 부합합니다. 다른 로직 변경이 없으므로 승인합니다.frontend/ongi/lib/screens/signup/username_screen.dart (1)
118-129: SnackBar 스타일링 개선이 잘 되었습니다.흰색 배경에 오렌지색 텍스트, 플로팅 동작, 둥근 모서리 등의 스타일링이 추가되어 사용자 경험이 향상되었습니다. 2초 지속 시간도 적절합니다.
frontend/ongi/lib/services/signup_service.dart (1)
4-4: 클래스명 변경 검증 완료코드베이스 전체에서 이전 클래스명
AuthService에 대한 참조가 존재하지 않음을 확인했습니다.
AuthService→SignupService로의 변경은 의미적으로 더 명확하며 안전하게 반영되었습니다.frontend/ongi/lib/main.dart (2)
2-2: 새로운 로그인 화면 import가 적절합니다.
LoginPwScreen을 위한 새로운 import가 추가되었습니다.
26-26: 로그인 라우트 업데이트가 올바릅니다.
LoginPwScreen으로 라우트가 업데이트되어 새로운 로그인 플로우와 일치합니다.frontend/ongi/lib/screens/signup/ready_screen.dart (2)
33-33: 텍스트 폰트 굵기 변경이 적절합니다.
FontWeight.w800에서FontWeight.w200으로 변경되어 더 가벼운 텍스트 스타일을 제공합니다.
38-43: 버튼 레이아웃 개선이 잘 되었습니다.Padding 위젯으로 감싸고 minimumSize 제약 조건을 추가하여 버튼 레이아웃이 개선되었습니다.
frontend/ongi/lib/utils/token_storage.dart (4)
3-4: TokenStorage 클래스 설계가 적절합니다.토큰 관리를 위한 유틸리티 클래스가 잘 설계되었습니다. private 상수를 사용한 키 관리가 좋습니다.
6-9: 토큰 저장 메서드가 올바르게 구현되었습니다.SharedPreferences를 사용한 토큰 저장 로직이 적절합니다.
11-14: 토큰 조회 메서드가 올바르게 구현되었습니다.토큰 조회 로직이 적절하며, null 반환 처리도 올바릅니다.
16-19: 토큰 삭제 메서드가 올바르게 구현되었습니다.토큰 삭제 로직이 적절합니다.
frontend/ongi/lib/screens/start_screen.dart (1)
187-199: 이메일 형식 검증 메시지가 일관되게 스타일링되었습니다.기존 검증 로직에 일관된 스낵바 스타일링이 적용되어 사용자 경험이 개선되었습니다.
frontend/ongi/lib/screens/login/login_ready_screen.dart (2)
21-27: 타이머 처리와 네비게이션 로직이 잘 구현되었습니다.
mounted체크를 통해 메모리 누수를 방지하고, 적절한 타이머 관리가 이루어져 있습니다.
29-33: 리소스 정리가 올바르게 구현되었습니다.dispose 메서드에서 타이머를 적절히 취소하여 메모리 누수를 방지하고 있습니다.
frontend/ongi/lib/services/login_service.dart (1)
26-29: 토큰 저장 로직이 올바르게 구현되었습니다.응답 처리와 토큰 저장 로직이 적절히 구현되어 있습니다.
frontend/ongi/lib/screens/signup/familycode_create_screen.dart (3)
19-25: 상태 기반 네비게이션 로직이 잘 구현되었습니다.코드 생성 후 버튼 클릭 시 바로 다음 화면으로 이동하는 로직이 명확하게 구현되어 있습니다.
31-44: CodeService를 사용한 코드 생성 로직이 올바르게 구현되었습니다.서비스 분리가 잘 이루어졌고, 응답 처리도 적절합니다.
157-159: 동적 버튼 텍스트 변경이 사용자 경험을 개선합니다.상태에 따라 버튼 텍스트가 변경되어 사용자에게 명확한 피드백을 제공합니다.
frontend/ongi/lib/services/family_join_service.dart (3)
10-13: 토큰 검증 로직이 올바르게 구현되었습니다.액세스 토큰 존재 여부를 확인하고 적절한 에러 메시지를 제공합니다.
18-21: Authorization 헤더가 올바르게 구현되었습니다.Bearer 토큰을 사용한 인증 헤더가 적절히 설정되어 있습니다.
27-31: 상태 코드별 에러 처리가 적절합니다.200과 201 상태 코드를 모두 성공으로 처리하고, 실패 시 구체적인 에러 메시지를 제공합니다.
frontend/ongi/lib/screens/signup/familyname_screen.dart (1)
18-54: 코드 변경사항이 적절합니다!가족 이름 저장 로직이 단순화되고 에러 처리가 개선되었습니다.
frontend/ongi/lib/screens/signup/familycode_screen.dart (2)
14-33: 에러 처리가 잘 구현되었습니다!로딩 상태 관리와 에러 메시지 표시를 위한 헬퍼 메서드가 깔끔하게 구현되었습니다.
35-59: 비동기 처리와 상태 관리가 적절합니다!로딩 인디케이터와 에러 처리가 사용자 경험을 고려하여 잘 구현되었습니다.
| import '../utils/token_storage.dart'; | ||
|
|
||
| class LoginService { | ||
| static const String baseUrl = 'https://ongi-1049536928483.asia-northeast1.run.app'; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
하드코딩된 베이스 URL을 환경 설정으로 분리하세요.
베이스 URL이 하드코딩되어 있어 개발/운영 환경 분리가 어렵습니다.
// 환경 설정 파일로 분리하는 것을 고려해보세요:
import 'package:ongi/config/api_config.dart';
class LoginService {
static const String baseUrl = ApiConfig.baseUrl;
// ...
}🤖 Prompt for AI Agents
In frontend/ongi/lib/services/login_service.dart at line 7, the baseUrl is
hardcoded, which hinders environment separation. To fix this, remove the
hardcoded URL and instead import a configuration file (e.g., api_config.dart)
that defines the baseUrl as a constant. Then assign baseUrl from that
configuration to enable easy switching between development and production
environments.
| } else { | ||
| throw Exception('로그인 실패: ${response.statusCode} ${response.body}'); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
에러 메시지를 더 사용자 친화적으로 개선하세요.
현재 에러 메시지가 기술적인 정보를 포함하고 있어 사용자에게 혼란을 줄 수 있습니다.
} else {
// 상태 코드별로 구체적인 에러 메시지 제공
final errorMessage = response.statusCode == 401
? '이메일 또는 비밀번호가 잘못되었습니다.'
: '로그인에 실패했습니다. 잠시 후 다시 시도해주세요.';
throw Exception(errorMessage);
}🤖 Prompt for AI Agents
In frontend/ongi/lib/services/login_service.dart around lines 30 to 32, the
current error message includes technical details like status code and response
body, which can confuse users. Replace this with user-friendly messages by
checking the status code: if it is 401, throw an exception with '이메일 또는 비밀번호가
잘못되었습니다.'; otherwise, throw '로그인에 실패했습니다. 잠시 후 다시 시도해주세요.' to provide clearer,
more helpful feedback.
| if (response.statusCode == 200 || response.statusCode == 201) { | ||
| return jsonDecode(response.body); | ||
| } else { | ||
| throw Exception('회원가입에 실패했습니다. 상태 코드: ${response.statusCode}'); | ||
| } |
There was a problem hiding this comment.
오류 메시지를 수정하세요
가족 생성 API인데 오류 메시지가 "회원가입에 실패했습니다"로 되어 있습니다.
- throw Exception('회원가입에 실패했습니다. 상태 코드: ${response.statusCode}');
+ throw Exception('가족 생성에 실패했습니다. 상태 코드: ${response.statusCode}');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (response.statusCode == 200 || response.statusCode == 201) { | |
| return jsonDecode(response.body); | |
| } else { | |
| throw Exception('회원가입에 실패했습니다. 상태 코드: ${response.statusCode}'); | |
| } | |
| if (response.statusCode == 200 || response.statusCode == 201) { | |
| return jsonDecode(response.body); | |
| } else { | |
| throw Exception('가족 생성에 실패했습니다. 상태 코드: ${response.statusCode}'); | |
| } |
🤖 Prompt for AI Agents
In frontend/ongi/lib/services/code_service.dart between lines 28 and 32, the
error message incorrectly states "회원가입에 실패했습니다" which means "Failed to sign up"
but this is for the family creation API. Update the exception message to
accurately reflect the family creation failure, such as "가족 생성에 실패했습니다" and keep
the status code included.
| final response = await http.post( | ||
| Uri.parse('$baseUrl/family'), | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': 'Bearer $accessToken', | ||
| }, | ||
| body: jsonEncode({ | ||
| 'name': name, | ||
| }), | ||
| ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
HTTP 요청에 타임아웃을 추가하세요
네트워크 요청이 무한정 대기하는 것을 방지하기 위해 타임아웃 설정을 추가하는 것이 좋습니다.
try {
final response = await http.post(
Uri.parse('$baseUrl/family'),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'name': name,
}),
- );
+ ).timeout(const Duration(seconds: 10));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| final response = await http.post( | |
| Uri.parse('$baseUrl/family'), | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer $accessToken', | |
| }, | |
| body: jsonEncode({ | |
| 'name': name, | |
| }), | |
| ); | |
| final response = await http.post( | |
| Uri.parse('$baseUrl/family'), | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': 'Bearer $accessToken', | |
| }, | |
| body: jsonEncode({ | |
| 'name': name, | |
| }), | |
| ).timeout(const Duration(seconds: 10)); |
🤖 Prompt for AI Agents
In frontend/ongi/lib/services/code_service.dart around lines 17 to 26, the HTTP
POST request does not have a timeout set, which can cause the app to wait
indefinitely if the network is slow or unresponsive. Add a timeout duration to
the http.post call by chaining a timeout method with a reasonable duration
(e.g., 10 seconds) to ensure the request fails gracefully if it takes too long.
| final prefs = await SharedPreferences.getInstance(); | ||
| final email = prefs.getString('signup_email') ?? ''; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
SharedPreferences 키 이름을 명확하게 변경하세요
로그인 화면에서 'signup_email'이라는 키를 사용하는 것은 혼란을 줄 수 있습니다.
final prefs = await SharedPreferences.getInstance();
- final email = prefs.getString('signup_email') ?? '';
+ final email = prefs.getString('user_email') ?? '';또한 이메일이 없을 경우의 처리도 추가하는 것이 좋습니다:
if (email.isEmpty) {
_showErrorSnackBar('이메일 정보가 없습니다. 다시 로그인해주세요.');
return;
}🤖 Prompt for AI Agents
In frontend/ongi/lib/screens/login/login_pw_screen.dart at lines 133-134, rename
the SharedPreferences key from 'signup_email' to a clearer name like
'login_email' to avoid confusion. Additionally, add a check after retrieving the
email to handle the case when it is empty by showing an error snackbar with a
message like '이메일 정보가 없습니다. 다시 로그인해주세요.' and return early to prevent further
processing.
| child: TextField( | ||
| controller: _passwordCtrl, | ||
| keyboardType: TextInputType.visiblePassword, | ||
| style: const TextStyle(fontSize: 25, color: AppColors.ongiOrange), | ||
| decoration: InputDecoration( | ||
| hintText: 'PASSWORD', | ||
| hintStyle: | ||
| TextStyle(color: Colors.grey), | ||
| contentPadding: const EdgeInsets.symmetric( | ||
| horizontal: 24, vertical: 13), | ||
| filled: true, | ||
| fillColor: Colors.transparent, | ||
| enabledBorder: OutlineInputBorder( | ||
| borderSide: | ||
| const BorderSide(color: AppColors.ongiOrange, width: 1), | ||
| borderRadius: BorderRadius.circular(20), | ||
| ), | ||
| focusedBorder: OutlineInputBorder( | ||
| borderSide: | ||
| const BorderSide(color: AppColors.ongiOrange, width: 1), | ||
| borderRadius: BorderRadius.circular(20), | ||
| ), | ||
| ), | ||
| ), |
There was a problem hiding this comment.
비밀번호 필드에 보안 설정을 추가하세요
비밀번호 입력 필드가 평문으로 표시되고 있습니다. 보안을 위해 obscureText를 사용해야 합니다.
child: TextField(
controller: _passwordCtrl,
- keyboardType: TextInputType.visiblePassword,
+ obscureText: true,
+ keyboardType: TextInputType.text,
style: const TextStyle(fontSize: 25, color: AppColors.ongiOrange),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| child: TextField( | |
| controller: _passwordCtrl, | |
| keyboardType: TextInputType.visiblePassword, | |
| style: const TextStyle(fontSize: 25, color: AppColors.ongiOrange), | |
| decoration: InputDecoration( | |
| hintText: 'PASSWORD', | |
| hintStyle: | |
| TextStyle(color: Colors.grey), | |
| contentPadding: const EdgeInsets.symmetric( | |
| horizontal: 24, vertical: 13), | |
| filled: true, | |
| fillColor: Colors.transparent, | |
| enabledBorder: OutlineInputBorder( | |
| borderSide: | |
| const BorderSide(color: AppColors.ongiOrange, width: 1), | |
| borderRadius: BorderRadius.circular(20), | |
| ), | |
| focusedBorder: OutlineInputBorder( | |
| borderSide: | |
| const BorderSide(color: AppColors.ongiOrange, width: 1), | |
| borderRadius: BorderRadius.circular(20), | |
| ), | |
| ), | |
| ), | |
| child: TextField( | |
| controller: _passwordCtrl, | |
| + obscureText: true, | |
| + keyboardType: TextInputType.text, | |
| style: const TextStyle(fontSize: 25, color: AppColors.ongiOrange), | |
| decoration: InputDecoration( | |
| hintText: 'PASSWORD', | |
| hintStyle: TextStyle(color: Colors.grey), | |
| contentPadding: const EdgeInsets.symmetric( | |
| horizontal: 24, vertical: 13), | |
| filled: true, | |
| fillColor: Colors.transparent, | |
| enabledBorder: OutlineInputBorder( | |
| borderSide: | |
| const BorderSide(color: AppColors.ongiOrange, width: 1), | |
| borderRadius: BorderRadius.circular(20), | |
| ), | |
| focusedBorder: OutlineInputBorder( | |
| borderSide: | |
| const BorderSide(color: AppColors.ongiOrange, width: 1), | |
| borderRadius: BorderRadius.circular(20), | |
| ), | |
| ), | |
| ), |
🤖 Prompt for AI Agents
In frontend/ongi/lib/screens/login/login_pw_screen.dart between lines 83 and
106, the password input field currently displays the entered text in plain view,
which is a security risk. To fix this, add the property obscureText: true to the
TextField widget to mask the password input and enhance security.
| Future<void> _setModeAndRegister(BuildContext context, String mode) async { | ||
| final scaffoldMessenger = ScaffoldMessenger.of(context); |
There was a problem hiding this comment.
사용하지 않는 변수를 제거하세요
scaffoldMessenger 변수가 선언되었지만 사용되지 않습니다.
Future<void> _setModeAndRegister(BuildContext context, String mode) async {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
ScaffoldMessenger.of(context).showSnackBar(🤖 Prompt for AI Agents
In frontend/ongi/lib/screens/signup/mode_screen.dart around lines 13 to 14, the
variable scaffoldMessenger is declared but never used. Remove the declaration of
scaffoldMessenger to clean up the code and avoid unused variable warnings.
* fix: pretendard 파일 수정 * feat: 시작 화면 구현 및 이메일 유효성 검사 구현 완료 * fix: emailScreen 배경 수정 * feat: signup 기능 구현 * fix: familyname_screen.dart 디자인 수정 * feat: 가족 생성 및 기존 가족 참가 api 연결, snackbar 추가, 로딩 인디케이터 수정, 회원가입 이메일 한글 예외 처리 추가 * fix: 필요 없는 코드 수정 * feat: login 화면 구현 및 api 연결 완료
로그인 플로우 화면들을 구현하였고,
로그인 api를 연결하여 로그인 기능을 완성하였습니다.
Summary by CodeRabbit
신규 기능
버그 수정 및 개선
스타일