Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
dd74700
๐Ÿ™ˆ :: Adds generated Dart files to .gitignore
iloveuhyeon Jun 30, 2025
7c28b9f
โž• :: Sets up dependency injection
iloveuhyeon Jun 30, 2025
39d1ad3
:recycle: :: Handles missing base URL in .env
iloveuhyeon Jun 30, 2025
4317539
๐Ÿšš :: Moves router and menu bottom
iloveuhyeon Jun 30, 2025
44db5c1
๐Ÿšš :: Updates data model import paths
iloveuhyeon Jun 30, 2025
9e42a83
:recycle: :: Refactors project structure and dependencies
iloveuhyeon Jun 30, 2025
c2bc6ae
โž• :: Updates dependencies and adds code generation
iloveuhyeon Jun 30, 2025
e99b572
:sparkles: :: Adds user API for authentication
iloveuhyeon Jun 30, 2025
5845add
:sparkles: :: Adds user authentication DTOs
iloveuhyeon Jun 30, 2025
2cd0cd4
:sparkles: :: Adds user data source abstraction
iloveuhyeon Jun 30, 2025
73a70b2
:sparkles: :: Adds user repository interface and implementation
iloveuhyeon Jun 30, 2025
6109dde
:truck: :: Moves asset entity to domain layer
iloveuhyeon Jun 30, 2025
3552062
Refactors and adds new screens for user flow
iloveuhyeon Jul 1, 2025
af7aadd
โž• :: Updates dependencies
iloveuhyeon Jul 8, 2025
e68b6a0
โž• :: Configures analyzer options
iloveuhyeon Jul 8, 2025
767b53e
:truck: :: Moves router and widget to config
iloveuhyeon Jul 8, 2025
4863d4a
:recycle: :: Replaces log with print for Dio error output
iloveuhyeon Jul 8, 2025
66ad32f
:sparkles: :: Adds request body logging interceptor
iloveuhyeon Jul 8, 2025
dd2892b
:sparkles: :: Configures DI for user authentication
iloveuhyeon Jul 8, 2025
7dd094d
:sparkles: :: Adds user data source implementation
iloveuhyeon Jul 8, 2025
3d548ea
:recycle: :: Updates user sign-up and sign-in DTOs
iloveuhyeon Jul 8, 2025
2f485e8
:sparkles: :: Adds sign up request/response DTOs
iloveuhyeon Jul 8, 2025
48a8a32
:sparkles: :: Adds remote request mappers for user sign up
iloveuhyeon Jul 8, 2025
0915de4
:fire: :: Removes UserRepository abstraction
iloveuhyeon Jul 8, 2025
74be830
:sparkles: :: Adds user authentication services
iloveuhyeon Jul 8, 2025
19df2aa
:recycle: :: Implements user repository
iloveuhyeon Jul 8, 2025
33a608e
:sparkles: :: Adds sign-in entity for data handling.
iloveuhyeon Jul 8, 2025
f9c3266
:sparkles: :: Creates sign-in repository abstraction
iloveuhyeon Jul 8, 2025
1a6c93b
:sparkles: :: Defines the sign-in use case abstraction.
iloveuhyeon Jul 8, 2025
9485a97
:sparkles: :: Implements sign-in use case.
iloveuhyeon Jul 8, 2025
9b6e430
:sparkles: :: Creates sign-up email entity
iloveuhyeon Jul 8, 2025
1f77aec
:sparkles: :: Creates sign up entity
iloveuhyeon Jul 8, 2025
363e8c3
:sparkles: :: Adds school search entity for sign-up
iloveuhyeon Jul 8, 2025
149b58e
:sparkles: :: Creates sign up repository
iloveuhyeon Jul 8, 2025
aa31c8c
:sparkles: :: Defines sign up use case
iloveuhyeon Jul 8, 2025
2394061
I:sparkles: :: mplements sign up use case
iloveuhyeon Jul 8, 2025
75666a3
:sparkles: :: Adds sign-in controller logic
iloveuhyeon Jul 8, 2025
0f68e52
:sparkles: :: Adds Riverpod for state management
iloveuhyeon Jul 8, 2025
ed32f9e
:sparkles: :: Creates sign-in mapper
iloveuhyeon Jul 8, 2025
7bd3240
:sparkles: :: Implements the login screen UI
iloveuhyeon Jul 8, 2025
6e46941
:fire: :: Removes dedicated input field widget
iloveuhyeon Jul 8, 2025
23559ce
:sparkles: :: Creates reusable input field component
iloveuhyeon Jul 8, 2025
e15dc6c
:sparkles: :: Creates sign in state
iloveuhyeon Jul 8, 2025
12341d5
:sparkles: :: Adds email signup controller
iloveuhyeon Jul 8, 2025
24b5af6
:sparkles: :: Adds sign-up name input controller
iloveuhyeon Jul 8, 2025
59badd6
:sparkles: :: Adds signup password controller
iloveuhyeon Jul 8, 2025
2c4ff48
:sparkles: :: Adds school signup controller
iloveuhyeon Jul 8, 2025
9501123
:sparkles: :: Adds data mappers for sign up feature
iloveuhyeon Jul 8, 2025
71f7a3e
:sparkles: :: Adds sign up states using freezed
iloveuhyeon Jul 8, 2025
4869195
:sparkles: :: Implements password creation screen
iloveuhyeon Jul 8, 2025
9171d35
:fire: :: Removes unnecessary imports
iloveuhyeon Jul 8, 2025
52e954b
:green_heart: :: Adds code generation to CI/CD workflows
iloveuhyeon Jul 8, 2025
1ac8210
Update lib/presentation/sign_up/screens/find_school_screen.dart
iloveuhyeon Jul 8, 2025
be59042
:memo: :: Updates .gitignore and project settings
iloveuhyeon Jul 8, 2025
0e3083c
:heavy_plus_sign: :: .fvmrc ์ถ”๊ฐ€
iloveuhyeon Jul 8, 2025
afaa6da
Merge branch 'feature/#22-connect-user-api' of https://github.com/Jusโ€ฆ
iloveuhyeon Jul 8, 2025
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
3 changes: 3 additions & 0 deletions .fvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"flutter": "stable"
}
3 changes: 3 additions & 0 deletions .github/workflows/jusicool_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
- name: Install Dependencies
run: flutter pub get

- name: Run Code Generation
run: flutter pub run build_runner build --delete-conflicting-outputs

- name: Analyze project source
run: flutter analyze

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/jusicool_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
- name: Install Dependencies
run: flutter pub get

- name: Run Code Generation
run: flutter pub run build_runner build --delete-conflicting-outputs

- name: Analyze project source
run: flutter analyze

Expand Down
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pubspec.lock
# If you don't generate documentation locally you can remove this line.
doc/api/

# .g.dart files are generated by the Dart build system.
*.g.dart
*.freezed.dart
# dotenv environment variables file
.env*

Expand All @@ -39,7 +42,6 @@ doc/api/
### Flutter ###
# Flutter/Dart/Pub related
**/doc/api/
.fvm/flutter_sdk
.pub-cache/
.pub/
coverage/
Expand Down Expand Up @@ -319,4 +321,7 @@ iOSInjectionProject/
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings

# End of https://www.toptal.com/developers/gitignore/api/dotenv,flutter,xcode,swift,intellij,dart,git
# End of https://www.toptal.com/developers/gitignore/api/dotenv,flutter,xcode,swift,intellij,dart,git

# FVM Version Cache
.fvm/
789 changes: 789 additions & 0 deletions .idea/Jusicool-iOS.iml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
analyzer:
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
errors:
invalid_annotation_target: ignore
78 changes: 77 additions & 1 deletion lib/core/config/di/dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,92 @@ import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:jusicool_ios/core/network/api/api_client.dart';
import 'package:jusicool_ios/data/user/service/user_api.dart';
import 'package:jusicool_ios/data/user/data_sources/user_data_source.dart';
import 'package:jusicool_ios/domain/sign_in/repositories/sign_in_repository.dart';
import 'package:jusicool_ios/domain/sign_in/usecase/sign_in_usecase.dart';
import 'package:jusicool_ios/domain/sign_in/usecase/sign_in_usecase_impl.dart';
import 'package:jusicool_ios/domain/sign_up/repositories/sign_up_repository.dart';
import 'package:jusicool_ios/domain/sign_up/usecase/sign_up_usecase.dart';
import 'package:jusicool_ios/domain/sign_up/usecase/sign_up_usecase_impl.dart';
import '../../../data/user/data_sources/user_data_source_impl.dart';
import '../../../data/user/repositories/user_repository_impl.dart';
import '../../../data/user/service/neis_api.dart';
import '../../network/interceptor/cookie_interceptor.dart';

final di = GetIt.instance;

void setDio() {
void _setDio() {
try {
di.registerLazySingleton<Dio>(() => dio());
di.registerSingletonAsync<CookieJar>(() async => await cookieJar());
di.registerLazySingleton<Dio>(() => neis(), instanceName: 'neis');
log('โœ… :: DIO DI ์„ฑ๊ณต');
} catch (e) {
log("โ›” :: DIO DI ์‹คํŒจ \n$e");
}
}

void _setApi() {
try {
di.registerLazySingleton<UserApi>(() => UserApi(di.get<Dio>()));
di.registerLazySingleton<NeisApi>(
() => NeisApi(di.get<Dio>(instanceName: 'neis')),
);
log('โœ… :: API DI ์„ฑ๊ณต');
} catch (e) {
log("โ›” :: API DI ์‹คํŒจ \n$e");
}
}

void _setDataSources() {
try {
di.registerLazySingleton<UserDataSource>(
() => UserDataSourceImpl(di.get<UserApi>(), di.get<NeisApi>()),
);
log('โœ… :: DataSources DI ์„ฑ๊ณต');
} catch (e) {
log("โ›” :: DataSources DI ์‹คํŒจ \n$e");
}
}

void _setRepository() {
try {
di.registerLazySingleton<SignInRepository>(
() => UserRepositoryImpl(di.get<UserDataSource>()),
);
di.registerLazySingleton<SignUpRepository>(
() => UserRepositoryImpl(di.get<UserDataSource>()),
);
log('โœ… :: Repository DI ์„ฑ๊ณต');
} catch (e) {
log("โ›” :: Repository DI ์‹คํŒจ \n$e");
}
}

void _setUseCase() {
try {
di.registerLazySingleton<SignInUseCase>(
() => SignInUseCaseImpl(di.get<SignInRepository>()),
);
di.registerLazySingleton<SignUpUseCase>(
() => SignUpUseCaseImpl(di.get<SignUpRepository>()),
);
log('โœ… :: UseCase DI ์„ฑ๊ณต');
} catch (e) {
log("โ›” :: UseCase DI ์‹คํŒจ \n$e");
}
}

void setDependencies() {
try {
_setDio();
_setApi();
_setDataSources();
_setRepository();
_setUseCase();
log('โœ… :: DI ์„ค์ • ์™„๋ฃŒ');
} catch (e) {
log("โ›” :: DI ์‹คํŒจ \n$e");
}
}
27 changes: 5 additions & 22 deletions lib/router.dart โ†’ lib/core/config/router/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import 'package:jusicool_ios/presentation/sign_up/screens/name_input_screen.dart
import 'package:jusicool_ios/presentation/sign_up/screens/password_create_screen.dart';
import 'package:jusicool_ios/presentation/splash/screens/splash_screen.dart';
import 'package:jusicool_ios/presentation/community/screens/community_post_list_screen.dart';

import 'main.dart';
import '../../../main.dart';

class RoutePaths {
static const String splash = '/splash';
Expand Down Expand Up @@ -45,7 +44,7 @@ class AppRouter {
),
GoRoute(
path: RoutePaths.login,
builder: (context, state) => const LoginScreen(),
builder: (context, state) => LoginScreen(),
),
GoRoute(
path: RoutePaths.main,
Expand All @@ -57,31 +56,15 @@ class AppRouter {
),
GoRoute(
path: RoutePaths.emailAuth,
builder: (context, state) {
final username = state.extra as String?;
return EmailAuthScreen(username: username ?? '');
},
builder: (context, state) => EmailAuthScreen(),
),
GoRoute(
path: RoutePaths.passwordCreate,
builder: (context, state) {
final extra = state.extra as Map<String, String>?;
return PasswordCreateScreen(
username: extra?['username'] ?? '',
email: extra?['email'] ?? '',
);
},
builder: (context, state) => PasswordCreateScreen(),
),
GoRoute(
path: RoutePaths.findSchool,
builder: (context, state) {
final extra = state.extra as Map<String, String>?;
return FindSchoolScreen(
username: extra?['username'] ?? '',
email: extra?['email'] ?? '',
password: extra?['password'] ?? '',
);
},
builder: (context, state) => FindSchoolScreen(),
),
GoRoute(
path: RoutePaths.mainCapital,
Expand Down
File renamed without changes.
34 changes: 29 additions & 5 deletions lib/core/network/api/api_client.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'dart:developer';
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get_it/get_it.dart';
import 'package:jusicool_ios/core/network/interceptor/dio_request_interceptor.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import '../interceptor/dio_error_interceptor.dart';

Expand All @@ -13,15 +15,19 @@ Dio dio() {
bool _homeDebugMode = dotenv.env['HOME_DEBUG_MODE'] == 'true';

if (_baseUrlDev == null || _baseUrlProd == null) {
throw Exception('BASE_URL_DEV or BASE_URL_PROD is not set in .env file');
log('โ›” :: BASE_URL_DEV or BASE_URL_PROD is not set in .env file');
}

String _baseUrl =
String? _baseUrl =
(kReleaseMode || _homeDebugMode) ? _baseUrlProd : _baseUrlDev;

Dio dio = Dio(
BaseOptions(
baseUrl: _baseUrl,
baseUrl: _baseUrl ?? "",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
connectTimeout: Duration(seconds: 30),
receiveTimeout: Duration(seconds: 30),
),
Expand All @@ -31,15 +37,33 @@ Dio dio() {
dio.interceptors.add(
PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseHeader: true,
responseBody: true,
error: false,
compact: true,
maxWidth: 90,
enabled: kDebugMode,
),
);
dio.interceptors.add(DioErrorInterceptor());
dio.interceptors.add(DioRequestInterceptor());
return dio;
}

Dio neis() {
String? _neisApiKey = dotenv.env['NEIS_API_KEY'];
if (_neisApiKey == null) {
log('โ›” :: NEIS_API_KEY is not set in .env file');
}

Dio dio = Dio(
BaseOptions(
baseUrl: 'https://open.neis.go.kr/hub/schoolInfo',
connectTimeout: Duration(seconds: 30),
receiveTimeout: Duration(seconds: 30),
queryParameters: {'KEY': _neisApiKey ?? '', 'Type': 'json'},
),
);
dio.interceptors.add(DioErrorInterceptor());
dio.interceptors.add(PrettyDioLogger(responseBody: true));
return dio;
}
28 changes: 13 additions & 15 deletions lib/core/network/interceptor/dio_error_interceptor.dart
Original file line number Diff line number Diff line change
@@ -1,51 +1,49 @@
import 'dart:developer';

import 'package:dio/dio.dart';

class DioErrorInterceptor extends InterceptorsWrapper {
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
log('โ›” DIO ์—๋Ÿฌ :: ${err.type}');
print('โ›” DIO ์—๋Ÿฌ :: ${err.type}');
switch (err.type) {
case DioExceptionType.connectionTimeout:
log('โณ ์—ฐ๊ฒฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ');
print('โณ ์—ฐ๊ฒฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ');
break;
case DioExceptionType.sendTimeout:
log('โณ ์ „์†ก ์‹œ๊ฐ„ ์ดˆ๊ณผ');
print('โณ ์ „์†ก ์‹œ๊ฐ„ ์ดˆ๊ณผ');
break;
case DioExceptionType.receiveTimeout:
log('โณ ์ˆ˜์‹  ์‹œ๊ฐ„ ์ดˆ๊ณผ');
print('โณ ์ˆ˜์‹  ์‹œ๊ฐ„ ์ดˆ๊ณผ');
break;
case DioExceptionType.badResponse:
switch (err.response?.statusCode) {
case 400:
log('๐Ÿšซ ์ž˜๋ชป๋œ ์š”์ฒญ: ${err.response?.data}');
print('๐Ÿšซ ์ž˜๋ชป๋œ ์š”์ฒญ: ${err.response?.data}');
break;
case 401:
log('๐Ÿšซ ์ธ์ฆ ์‹คํŒจ: ${err.response?.data}');
print('๐Ÿšซ ์ธ์ฆ ์‹คํŒจ: ${err.response?.data}');
break;
case 403:
log('๐Ÿšซ ๊ถŒํ•œ ์—†์Œ: ${err.response?.data}');
print('๐Ÿšซ ๊ถŒํ•œ ์—†์Œ: ${err.response?.data}');
break;
case 404:
log('๐Ÿšซ ๋ฆฌ์†Œ์Šค ์—†์Œ: ${err.response?.data}');
print('๐Ÿšซ ๋ฆฌ์†Œ์Šค ์—†์Œ: ${err.response?.data}');
break;
case 500:
log('๐Ÿšซ ์„œ๋ฒ„ ์˜ค๋ฅ˜: ${err.response?.data}');
print('๐Ÿšซ ์„œ๋ฒ„ ์˜ค๋ฅ˜: ${err.response?.data}');
break;
default:
log('๐Ÿšซ ๊ธฐํƒ€ ์˜ค๋ฅ˜: ${err.response?.data}');
print('๐Ÿšซ ๊ธฐํƒ€ ์˜ค๋ฅ˜: ${err.response?.data}');
break;
}
case DioExceptionType.cancel:
log('โŒ ์š”์ฒญ ์ทจ์†Œ๋จ');
print('โŒ ์š”์ฒญ ์ทจ์†Œ๋จ');
break;
case DioExceptionType.connectionError:
log('๐Ÿšซ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ ์˜ค๋ฅ˜: ${err.message}');
print('๐Ÿšซ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ ์˜ค๋ฅ˜: ${err.message}');
break;
case DioExceptionType.unknown:
default:
log('โ“ ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ: ${err.message}');
print('โ“ ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ: ${err.message}');
}
handler.next(err);
}
Expand Down
13 changes: 13 additions & 0 deletions lib/core/network/interceptor/dio_request_interceptor.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'dart:convert';

import 'package:dio/dio.dart';

class DioRequestInterceptor extends InterceptorsWrapper {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
print("โ•” Body");
print("โ•‘ ${jsonEncode(options.data)}");
print("โ•š${'โ•' * 90}โ•");
super.onRequest(options, handler);
}
}
20 changes: 20 additions & 0 deletions lib/data/user/data_sources/user_data_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_search_school_request_dto.dart';
import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_send_email_request_dto.dart';
import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart';
import 'package:jusicool_ios/data/user/dto/remote/response/sign_up_search_school_response_dto.dart';
import '../dto/remote/request/sign_in_request_dto.dart';
import '../dto/remote/request/sign_up_request_dto.dart';

abstract class UserDataSource {
Future<void> signIn(SignInRequestDto body);

Future<void> signUp(SignUpRequestDto body);

Future<void> sendEmail(SignUpSendEmailRequestDto body);

Future<void> verifyEmail(SignUpVerifyEmailRequestDto body);

Future<SignUpSearchSchoolResponseDto> searchSchools(
SignUpSearchSchoolRequestDto schoolName,
);
}
Loading