diff --git a/.fvmrc b/.fvmrc
new file mode 100644
index 0000000..c300356
--- /dev/null
+++ b/.fvmrc
@@ -0,0 +1,3 @@
+{
+ "flutter": "stable"
+}
\ No newline at end of file
diff --git a/.github/workflows/jusicool_cd.yml b/.github/workflows/jusicool_cd.yml
index b86dcd2..b1008b8 100644
--- a/.github/workflows/jusicool_cd.yml
+++ b/.github/workflows/jusicool_cd.yml
@@ -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
diff --git a/.github/workflows/jusicool_ci.yml b/.github/workflows/jusicool_ci.yml
index 93fc108..d6b92f9 100644
--- a/.github/workflows/jusicool_ci.yml
+++ b/.github/workflows/jusicool_ci.yml
@@ -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
diff --git a/.gitignore b/.gitignore
index 8fd601c..59e0586 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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*
@@ -39,7 +42,6 @@ doc/api/
### Flutter ###
# Flutter/Dart/Pub related
**/doc/api/
-.fvm/flutter_sdk
.pub-cache/
.pub/
coverage/
@@ -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
\ No newline at end of file
+# End of https://www.toptal.com/developers/gitignore/api/dotenv,flutter,xcode,swift,intellij,dart,git
+
+# FVM Version Cache
+.fvm/
\ No newline at end of file
diff --git a/.idea/Jusicool-iOS.iml b/.idea/Jusicool-iOS.iml
index c55092e..d9e12e4 100644
--- a/.idea/Jusicool-iOS.iml
+++ b/.idea/Jusicool-iOS.iml
@@ -18,6 +18,795 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..1374fca
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,6 @@
+analyzer:
+ exclude:
+ - "**/*.g.dart"
+ - "**/*.freezed.dart"
+ errors:
+ invalid_annotation_target: ignore
\ No newline at end of file
diff --git a/lib/core/config/di/dependencies.dart b/lib/core/config/di/dependencies.dart
index 5c80a11..fbc7ff4 100644
--- a/lib/core/config/di/dependencies.dart
+++ b/lib/core/config/di/dependencies.dart
@@ -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());
di.registerSingletonAsync(() async => await cookieJar());
+ di.registerLazySingleton(() => neis(), instanceName: 'neis');
log('✅ :: DIO DI 성공');
} catch (e) {
log("⛔ :: DIO DI 실패 \n$e");
}
}
+
+void _setApi() {
+ try {
+ di.registerLazySingleton(() => UserApi(di.get()));
+ di.registerLazySingleton(
+ () => NeisApi(di.get(instanceName: 'neis')),
+ );
+ log('✅ :: API DI 성공');
+ } catch (e) {
+ log("⛔ :: API DI 실패 \n$e");
+ }
+}
+
+void _setDataSources() {
+ try {
+ di.registerLazySingleton(
+ () => UserDataSourceImpl(di.get(), di.get()),
+ );
+ log('✅ :: DataSources DI 성공');
+ } catch (e) {
+ log("⛔ :: DataSources DI 실패 \n$e");
+ }
+}
+
+void _setRepository() {
+ try {
+ di.registerLazySingleton(
+ () => UserRepositoryImpl(di.get()),
+ );
+ di.registerLazySingleton(
+ () => UserRepositoryImpl(di.get()),
+ );
+ log('✅ :: Repository DI 성공');
+ } catch (e) {
+ log("⛔ :: Repository DI 실패 \n$e");
+ }
+}
+
+void _setUseCase() {
+ try {
+ di.registerLazySingleton(
+ () => SignInUseCaseImpl(di.get()),
+ );
+ di.registerLazySingleton(
+ () => SignUpUseCaseImpl(di.get()),
+ );
+ 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");
+ }
+}
diff --git a/lib/router.dart b/lib/core/config/router/router.dart
similarity index 80%
rename from lib/router.dart
rename to lib/core/config/router/router.dart
index 283bd9d..f0c4ab8 100644
--- a/lib/router.dart
+++ b/lib/core/config/router/router.dart
@@ -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';
@@ -45,7 +44,7 @@ class AppRouter {
),
GoRoute(
path: RoutePaths.login,
- builder: (context, state) => const LoginScreen(),
+ builder: (context, state) => LoginScreen(),
),
GoRoute(
path: RoutePaths.main,
@@ -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?;
- 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?;
- return FindSchoolScreen(
- username: extra?['username'] ?? '',
- email: extra?['email'] ?? '',
- password: extra?['password'] ?? '',
- );
- },
+ builder: (context, state) => FindSchoolScreen(),
),
GoRoute(
path: RoutePaths.mainCapital,
diff --git a/lib/menu_bottom.dart b/lib/core/config/widget/menu_bottom.dart
similarity index 100%
rename from lib/menu_bottom.dart
rename to lib/core/config/widget/menu_bottom.dart
diff --git a/lib/core/network/api/api_client.dart b/lib/core/network/api/api_client.dart
index 4256899..170a641 100644
--- a/lib/core/network/api/api_client.dart
+++ b/lib/core/network/api/api_client.dart
@@ -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';
@@ -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),
),
@@ -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;
}
diff --git a/lib/core/network/interceptor/dio_error_interceptor.dart b/lib/core/network/interceptor/dio_error_interceptor.dart
index 6dee083..aa20744 100644
--- a/lib/core/network/interceptor/dio_error_interceptor.dart
+++ b/lib/core/network/interceptor/dio_error_interceptor.dart
@@ -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);
}
diff --git a/lib/core/network/interceptor/dio_request_interceptor.dart b/lib/core/network/interceptor/dio_request_interceptor.dart
new file mode 100644
index 0000000..6683c5b
--- /dev/null
+++ b/lib/core/network/interceptor/dio_request_interceptor.dart
@@ -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);
+ }
+}
diff --git a/lib/data/user/data_sources/user_data_source.dart b/lib/data/user/data_sources/user_data_source.dart
new file mode 100644
index 0000000..c90cd7c
--- /dev/null
+++ b/lib/data/user/data_sources/user_data_source.dart
@@ -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 signIn(SignInRequestDto body);
+
+ Future signUp(SignUpRequestDto body);
+
+ Future sendEmail(SignUpSendEmailRequestDto body);
+
+ Future verifyEmail(SignUpVerifyEmailRequestDto body);
+
+ Future searchSchools(
+ SignUpSearchSchoolRequestDto schoolName,
+ );
+}
diff --git a/lib/data/user/data_sources/user_data_source_impl.dart b/lib/data/user/data_sources/user_data_source_impl.dart
new file mode 100644
index 0000000..0113c13
--- /dev/null
+++ b/lib/data/user/data_sources/user_data_source_impl.dart
@@ -0,0 +1,43 @@
+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/service/neis_api.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/data/user/dto/remote/request/sign_in_request_dto.dart';
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_request_dto.dart';
+import '../dto/remote/response/sign_up_search_school_response_dto.dart';
+
+class UserDataSourceImpl extends UserDataSource {
+ final UserApi _userApi;
+ final NeisApi _neisClient;
+
+ UserDataSourceImpl(this._userApi, this._neisClient);
+
+ @override
+ Future signIn(SignInRequestDto body) async {
+ return await _userApi.signIn(body);
+ }
+
+ @override
+ Future signUp(SignUpRequestDto body) async {
+ return await _userApi.signUp(body);
+ }
+
+ @override
+ Future sendEmail(SignUpSendEmailRequestDto body) async {
+ return await _userApi.sendEmail(body);
+ }
+
+ @override
+ Future verifyEmail(SignUpVerifyEmailRequestDto body) async {
+ return await _userApi.verifyEmail(body);
+ }
+
+ @override
+ Future searchSchools(
+ SignUpSearchSchoolRequestDto school,
+ ) async {
+ return await _neisClient.fetchSchools(school.schoolName);
+ }
+}
diff --git a/lib/data/user/dto/local/request/.gitkeep b/lib/data/user/dto/local/request/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/data/user/dto/local/response/.gitkeep b/lib/data/user/dto/local/response/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/data/user/dto/remote/request/sign_in_request_dto.dart b/lib/data/user/dto/remote/request/sign_in_request_dto.dart
new file mode 100644
index 0000000..64c2a05
--- /dev/null
+++ b/lib/data/user/dto/remote/request/sign_in_request_dto.dart
@@ -0,0 +1,16 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_in_request_dto.g.dart';
+
+part 'sign_in_request_dto.freezed.dart';
+
+@freezed
+abstract class SignInRequestDto with _$SignInRequestDto {
+ const factory SignInRequestDto({
+ required String email,
+ required String password,
+ }) = _SignInRequestDto;
+
+ factory SignInRequestDto.fromJson(Map json) =>
+ _$SignInRequestDtoFromJson(json);
+}
diff --git a/lib/data/user/dto/remote/request/sign_up_request_dto.dart b/lib/data/user/dto/remote/request/sign_up_request_dto.dart
new file mode 100644
index 0000000..523e840
--- /dev/null
+++ b/lib/data/user/dto/remote/request/sign_up_request_dto.dart
@@ -0,0 +1,18 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_request_dto.g.dart';
+
+part 'sign_up_request_dto.freezed.dart';
+
+@freezed
+abstract class SignUpRequestDto with _$SignUpRequestDto {
+ factory SignUpRequestDto({
+ required String email,
+ required String password,
+ required String username,
+ required String school,
+ }) = _SignUpRequestDto;
+
+ factory SignUpRequestDto.fromJson(Map json) =>
+ _$SignUpRequestDtoFromJson(json);
+}
diff --git a/lib/data/user/dto/remote/request/sign_up_search_school_request_dto.dart b/lib/data/user/dto/remote/request/sign_up_search_school_request_dto.dart
new file mode 100644
index 0000000..d4bc32e
--- /dev/null
+++ b/lib/data/user/dto/remote/request/sign_up_search_school_request_dto.dart
@@ -0,0 +1,15 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_search_school_request_dto.freezed.dart';
+
+part 'sign_up_search_school_request_dto.g.dart';
+
+@freezed
+abstract class SignUpSearchSchoolRequestDto
+ with _$SignUpSearchSchoolRequestDto {
+ factory SignUpSearchSchoolRequestDto({@Default("") String schoolName}) =
+ _SignUpSearchSchoolRequestDto;
+
+ factory SignUpSearchSchoolRequestDto.fromJson(Map json) =>
+ _$SignUpSearchSchoolRequestDtoFromJson(json);
+}
diff --git a/lib/data/user/dto/remote/request/sign_up_send_email_request_dto.dart b/lib/data/user/dto/remote/request/sign_up_send_email_request_dto.dart
new file mode 100644
index 0000000..47d5811
--- /dev/null
+++ b/lib/data/user/dto/remote/request/sign_up_send_email_request_dto.dart
@@ -0,0 +1,14 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_send_email_request_dto.freezed.dart';
+
+part 'sign_up_send_email_request_dto.g.dart';
+
+@freezed
+abstract class SignUpSendEmailRequestDto with _$SignUpSendEmailRequestDto {
+ factory SignUpSendEmailRequestDto({required String email}) =
+ _SignUpSendEmailRequestDto;
+
+ factory SignUpSendEmailRequestDto.fromJson(Map json) =>
+ _$SignUpSendEmailRequestDtoFromJson(json);
+}
diff --git a/lib/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart b/lib/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart
new file mode 100644
index 0000000..5352509
--- /dev/null
+++ b/lib/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart
@@ -0,0 +1,16 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_verify_email_request_dto.freezed.dart';
+
+part 'sign_up_verify_email_request_dto.g.dart';
+
+@freezed
+abstract class SignUpVerifyEmailRequestDto with _$SignUpVerifyEmailRequestDto {
+ factory SignUpVerifyEmailRequestDto({
+ required String email,
+ required String code,
+ }) = _SignUpVerifyEmailRequestDto;
+
+ factory SignUpVerifyEmailRequestDto.fromJson(Map json) =>
+ _$SignUpVerifyEmailRequestDtoFromJson(json);
+}
diff --git a/lib/data/user/dto/remote/response/sign_up_search_school_response_dto.dart b/lib/data/user/dto/remote/response/sign_up_search_school_response_dto.dart
new file mode 100644
index 0000000..bc4bf9b
--- /dev/null
+++ b/lib/data/user/dto/remote/response/sign_up_search_school_response_dto.dart
@@ -0,0 +1,37 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_search_school_response_dto.freezed.dart';
+
+part 'sign_up_search_school_response_dto.g.dart';
+
+@freezed
+abstract class SignUpSearchSchoolResponseDto
+ with _$SignUpSearchSchoolResponseDto {
+ const factory SignUpSearchSchoolResponseDto({
+ @JsonKey(name: 'schoolInfo') required List schoolInfo,
+ }) = _SignUpSearchSchoolResponseDto;
+
+ factory SignUpSearchSchoolResponseDto.fromJson(
+ Map json,
+ ) => _$SignUpSearchSchoolResponseDtoFromJson(json);
+}
+
+@freezed
+abstract class SchoolInfoDto with _$SchoolInfoDto {
+ const factory SchoolInfoDto({List? head, List? row}) =
+ _SchoolInfoDto;
+
+ factory SchoolInfoDto.fromJson(Map json) =>
+ _$SchoolInfoDtoFromJson(json);
+}
+
+@freezed
+abstract class SchoolRowDto with _$SchoolRowDto {
+ const factory SchoolRowDto({
+ @JsonKey(name: 'SCHUL_NM') required String schoolName,
+ @JsonKey(name: 'ORG_RDNMA') required String schoolAddress,
+ }) = _SchoolRowDto;
+
+ factory SchoolRowDto.fromJson(Map json) =>
+ _$SchoolRowDtoFromJson(json);
+}
diff --git a/lib/data/user/mappers/local/request/.gitkeep b/lib/data/user/mappers/local/request/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/data/user/mappers/local/response/.gitkeep b/lib/data/user/mappers/local/response/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/data/user/mappers/remote/request/sign_in_request_mapper.dart b/lib/data/user/mappers/remote/request/sign_in_request_mapper.dart
new file mode 100644
index 0000000..485e922
--- /dev/null
+++ b/lib/data/user/mappers/remote/request/sign_in_request_mapper.dart
@@ -0,0 +1,10 @@
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_in_request_dto.dart';
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+
+class SignInRequestMapper {
+ static SignInRequestDto toDto(SignInEntity entity) =>
+ SignInRequestDto(email: entity.email, password: entity.password);
+
+ static SignInEntity toEntity(SignInRequestDto dto) =>
+ SignInEntity(email: dto.email, password: dto.password);
+}
diff --git a/lib/data/user/mappers/remote/request/sign_up_request_mapper.dart b/lib/data/user/mappers/remote/request/sign_up_request_mapper.dart
new file mode 100644
index 0000000..1dec46a
--- /dev/null
+++ b/lib/data/user/mappers/remote/request/sign_up_request_mapper.dart
@@ -0,0 +1,18 @@
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_request_dto.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_entity.dart';
+
+class SignUpRequestMapper {
+ static SignUpRequestDto toDto(SignUpEntity entity) => SignUpRequestDto(
+ email: entity.email,
+ password: entity.password,
+ username: entity.name,
+ school: entity.school,
+ );
+
+ static SignUpEntity toEntity(SignUpRequestDto dto) => SignUpEntity(
+ email: dto.email,
+ password: dto.password,
+ name: dto.username,
+ school: dto.school,
+ );
+}
diff --git a/lib/data/user/mappers/remote/request/sign_up_search_school_request_mapper.dart b/lib/data/user/mappers/remote/request/sign_up_search_school_request_mapper.dart
new file mode 100644
index 0000000..e165842
--- /dev/null
+++ b/lib/data/user/mappers/remote/request/sign_up_search_school_request_mapper.dart
@@ -0,0 +1,7 @@
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_search_school_request_dto.dart';
+
+class SignUpSearchSchoolRequestMapper {
+ static SignUpSearchSchoolRequestDto toDto(String entity) {
+ return SignUpSearchSchoolRequestDto(schoolName: entity);
+ }
+}
diff --git a/lib/data/user/mappers/remote/request/sign_up_send_email_request_mapper.dart b/lib/data/user/mappers/remote/request/sign_up_send_email_request_mapper.dart
new file mode 100644
index 0000000..8cd512e
--- /dev/null
+++ b/lib/data/user/mappers/remote/request/sign_up_send_email_request_mapper.dart
@@ -0,0 +1,8 @@
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_email_entity.dart';
+
+import '../../../dto/remote/request/sign_up_send_email_request_dto.dart';
+
+class SignUpSendEmailRequestMapper {
+ static SignUpSendEmailRequestDto toDto(SignUpEmailEntity entity) =>
+ SignUpSendEmailRequestDto(email: entity.email);
+}
diff --git a/lib/data/user/mappers/remote/request/sign_up_verify_email_request_mapper.dart b/lib/data/user/mappers/remote/request/sign_up_verify_email_request_mapper.dart
new file mode 100644
index 0000000..8231896
--- /dev/null
+++ b/lib/data/user/mappers/remote/request/sign_up_verify_email_request_mapper.dart
@@ -0,0 +1,8 @@
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart';
+
+import '../../../../../domain/sign_up/entity/sign_up_email_entity.dart';
+
+class SignUpVerifyEmailRequestMapper {
+ static SignUpVerifyEmailRequestDto toDto(SignUpEmailEntity entity) =>
+ SignUpVerifyEmailRequestDto(email: entity.email, code: entity.verifyCode);
+}
diff --git a/lib/data/user/mappers/remote/response/sign_up_search_school_response_mapper.dart b/lib/data/user/mappers/remote/response/sign_up_search_school_response_mapper.dart
new file mode 100644
index 0000000..ec6456c
--- /dev/null
+++ b/lib/data/user/mappers/remote/response/sign_up_search_school_response_mapper.dart
@@ -0,0 +1,24 @@
+import 'package:jusicool_ios/data/user/dto/remote/response/sign_up_search_school_response_dto.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_search_school_entity.dart';
+
+class SignUpSearchSchoolResponseMapper {
+ static SignUpSearchSchoolResponseEntity toEntity(
+ SignUpSearchSchoolResponseDto dto,
+ ) {
+ return SignUpSearchSchoolResponseEntity(
+ schoolInfo:
+ dto.schoolInfo.map((infoDto) {
+ return SchoolInfoEntity(
+ head: infoDto.head,
+ row:
+ infoDto.row?.map((rowDto) {
+ return SchoolRowEntity(
+ schoolName: rowDto.schoolName,
+ schoolAddress: rowDto.schoolAddress,
+ );
+ }).toList(),
+ );
+ }).toList(),
+ );
+ }
+}
diff --git a/lib/data/user/repositories/user_repository_impl.dart b/lib/data/user/repositories/user_repository_impl.dart
new file mode 100644
index 0000000..8368023
--- /dev/null
+++ b/lib/data/user/repositories/user_repository_impl.dart
@@ -0,0 +1,60 @@
+import 'package:jusicool_ios/data/user/data_sources/user_data_source.dart';
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_in_request_dto.dart';
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_request_dto.dart';
+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_verify_email_request_dto.dart';
+import 'package:jusicool_ios/data/user/mappers/remote/request/sign_in_request_mapper.dart';
+import 'package:jusicool_ios/data/user/mappers/remote/request/sign_up_request_mapper.dart';
+import 'package:jusicool_ios/data/user/mappers/remote/request/sign_up_send_email_request_mapper.dart';
+import 'package:jusicool_ios/data/user/mappers/remote/request/sign_up_verify_email_request_mapper.dart';
+import 'package:jusicool_ios/data/user/mappers/remote/response/sign_up_search_school_response_mapper.dart';
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+import 'package:jusicool_ios/domain/sign_in/repositories/sign_in_repository.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_email_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_search_school_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/repositories/sign_up_repository.dart';
+import '../dto/remote/request/sign_up_send_email_request_dto.dart';
+import '../mappers/remote/request/sign_up_search_school_request_mapper.dart';
+
+class UserRepositoryImpl implements SignInRepository, SignUpRepository {
+ final UserDataSource _userDataSource;
+
+ UserRepositoryImpl(this._userDataSource);
+
+ @override
+ Future signIn(SignInEntity body) async {
+ final SignInRequestDto request = SignInRequestMapper.toDto(body);
+ return await _userDataSource.signIn(request);
+ }
+
+ @override
+ Future signUp(SignUpEntity body) async {
+ final SignUpRequestDto request = SignUpRequestMapper.toDto(body);
+ return await _userDataSource.signUp(request);
+ }
+
+ @override
+ Future sendEmail(SignUpEmailEntity body) async {
+ final SignUpSendEmailRequestDto request =
+ SignUpSendEmailRequestMapper.toDto(body);
+ return await _userDataSource.sendEmail(request);
+ }
+
+ @override
+ Future verifyEmail(SignUpEmailEntity entity) async {
+ final SignUpVerifyEmailRequestDto request =
+ SignUpVerifyEmailRequestMapper.toDto(entity);
+ return await _userDataSource.verifyEmail(request);
+ }
+
+ @override
+ Future searchSchool(
+ String schoolName,
+ ) async {
+ final SignUpSearchSchoolRequestDto request =
+ SignUpSearchSchoolRequestMapper.toDto(schoolName);
+ final response = await _userDataSource.searchSchools(request);
+ return SignUpSearchSchoolResponseMapper.toEntity(response);
+ }
+}
diff --git a/lib/data/user/service/neis_api.dart b/lib/data/user/service/neis_api.dart
new file mode 100644
index 0000000..e034596
--- /dev/null
+++ b/lib/data/user/service/neis_api.dart
@@ -0,0 +1,15 @@
+import 'package:dio/dio.dart';
+import 'package:retrofit/retrofit.dart';
+import '../dto/remote/response/sign_up_search_school_response_dto.dart';
+
+part 'neis_api.g.dart';
+
+@RestApi()
+abstract class NeisApi {
+ factory NeisApi(Dio dio, {String baseUrl}) = _NeisApi;
+
+ @GET('/')
+ Future fetchSchools(
+ @Query('SCHUL_NM') String schoolName,
+ );
+}
diff --git a/lib/data/user/service/user_api.dart b/lib/data/user/service/user_api.dart
new file mode 100644
index 0000000..f2dece3
--- /dev/null
+++ b/lib/data/user/service/user_api.dart
@@ -0,0 +1,25 @@
+import 'package:dio/dio.dart';
+import 'package:jusicool_ios/data/user/dto/remote/request/sign_up_verify_email_request_dto.dart';
+import 'package:retrofit/retrofit.dart';
+import '../dto/remote/request/sign_in_request_dto.dart';
+import '../dto/remote/request/sign_up_request_dto.dart';
+import '../dto/remote/request/sign_up_send_email_request_dto.dart';
+
+part 'user_api.g.dart';
+
+@RestApi()
+abstract class UserApi {
+ factory UserApi(Dio dio, {String baseUrl}) = _UserApi;
+
+ @POST('/user/signin')
+ Future signIn(@Body() SignInRequestDto body);
+
+ @POST('/user/signup')
+ Future signUp(@Body() SignUpRequestDto body);
+
+ @POST('/user/email/send')
+ Future sendEmail(@Body() SignUpSendEmailRequestDto body);
+
+ @POST('/user/email/verify')
+ Future verifyEmail(@Body() SignUpVerifyEmailRequestDto body);
+}
diff --git a/lib/data/models/my_assets.dart b/lib/domain/my_capital/entities/my_assets.dart
similarity index 100%
rename from lib/data/models/my_assets.dart
rename to lib/domain/my_capital/entities/my_assets.dart
diff --git a/lib/domain/my_capital/repositories/.gitkeep b/lib/domain/my_capital/repositories/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/domain/my_capital/usecase/.gitkeep b/lib/domain/my_capital/usecase/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/domain/sign_in/entity/sign_in_entity.dart b/lib/domain/sign_in/entity/sign_in_entity.dart
new file mode 100644
index 0000000..e544e9f
--- /dev/null
+++ b/lib/domain/sign_in/entity/sign_in_entity.dart
@@ -0,0 +1,14 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_in_entity.freezed.dart';
+
+part 'sign_in_entity.g.dart';
+
+@freezed
+abstract class SignInEntity with _$SignInEntity {
+ factory SignInEntity({required String email, required String password}) =
+ _SignInEntity;
+
+ factory SignInEntity.fromJson(Map json) =>
+ _$SignInEntityFromJson(json);
+}
diff --git a/lib/domain/sign_in/repositories/sign_in_repository.dart b/lib/domain/sign_in/repositories/sign_in_repository.dart
new file mode 100644
index 0000000..0c64680
--- /dev/null
+++ b/lib/domain/sign_in/repositories/sign_in_repository.dart
@@ -0,0 +1,5 @@
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+
+abstract class SignInRepository {
+ Future signIn(SignInEntity entity);
+}
diff --git a/lib/domain/sign_in/usecase/sign_in_usecase.dart b/lib/domain/sign_in/usecase/sign_in_usecase.dart
new file mode 100644
index 0000000..46fbbcd
--- /dev/null
+++ b/lib/domain/sign_in/usecase/sign_in_usecase.dart
@@ -0,0 +1,5 @@
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+
+abstract class SignInUseCase {
+ Future signIn(SignInEntity entity);
+}
diff --git a/lib/domain/sign_in/usecase/sign_in_usecase_impl.dart b/lib/domain/sign_in/usecase/sign_in_usecase_impl.dart
new file mode 100644
index 0000000..7efb2aa
--- /dev/null
+++ b/lib/domain/sign_in/usecase/sign_in_usecase_impl.dart
@@ -0,0 +1,14 @@
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+import 'package:jusicool_ios/domain/sign_in/usecase/sign_in_usecase.dart';
+import '../repositories/sign_in_repository.dart';
+
+class SignInUseCaseImpl extends SignInUseCase {
+ final SignInRepository _signInRepository;
+
+ SignInUseCaseImpl(this._signInRepository);
+
+ @override
+ Future signIn(SignInEntity entity) async {
+ return await _signInRepository.signIn(entity);
+ }
+}
diff --git a/lib/domain/sign_up/entity/sign_up_email_entity.dart b/lib/domain/sign_up/entity/sign_up_email_entity.dart
new file mode 100644
index 0000000..38ebbd9
--- /dev/null
+++ b/lib/domain/sign_up/entity/sign_up_email_entity.dart
@@ -0,0 +1,16 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_email_entity.freezed.dart';
+
+part 'sign_up_email_entity.g.dart';
+
+@freezed
+abstract class SignUpEmailEntity with _$SignUpEmailEntity {
+ const factory SignUpEmailEntity({
+ required String email,
+ required String verifyCode,
+ }) = _SignUpEmailEntity;
+
+ factory SignUpEmailEntity.fromJson(Map json) =>
+ _$SignUpEmailEntityFromJson(json);
+}
diff --git a/lib/domain/sign_up/entity/sign_up_entity.dart b/lib/domain/sign_up/entity/sign_up_entity.dart
new file mode 100644
index 0000000..b65a9aa
--- /dev/null
+++ b/lib/domain/sign_up/entity/sign_up_entity.dart
@@ -0,0 +1,18 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_entity.freezed.dart';
+
+part 'sign_up_entity.g.dart';
+
+@freezed
+abstract class SignUpEntity with _$SignUpEntity {
+ const factory SignUpEntity({
+ required String email,
+ required String password,
+ required String name,
+ required String school,
+ }) = _SignUpEntity;
+
+ factory SignUpEntity.fromJson(Map json) =>
+ _$SignUpEntityFromJson(json);
+}
diff --git a/lib/domain/sign_up/entity/sign_up_search_school_entity.dart b/lib/domain/sign_up/entity/sign_up_search_school_entity.dart
new file mode 100644
index 0000000..45ed788
--- /dev/null
+++ b/lib/domain/sign_up/entity/sign_up_search_school_entity.dart
@@ -0,0 +1,39 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'sign_up_search_school_entity.freezed.dart';
+
+part 'sign_up_search_school_entity.g.dart';
+
+@freezed
+abstract class SignUpSearchSchoolResponseEntity
+ with _$SignUpSearchSchoolResponseEntity {
+ const factory SignUpSearchSchoolResponseEntity({
+ @JsonKey(name: 'schoolInfo') required List schoolInfo,
+ }) = _SignUpSearchSchoolResponseEntity;
+
+ factory SignUpSearchSchoolResponseEntity.fromJson(
+ Map json,
+ ) => _$SignUpSearchSchoolResponseEntityFromJson(json);
+}
+
+@freezed
+abstract class SchoolInfoEntity with _$SchoolInfoEntity {
+ const factory SchoolInfoEntity({
+ List? head,
+ List? row,
+ }) = _SchoolInfoEntity;
+
+ factory SchoolInfoEntity.fromJson(Map json) =>
+ _$SchoolInfoEntityFromJson(json);
+}
+
+@freezed
+abstract class SchoolRowEntity with _$SchoolRowEntity {
+ const factory SchoolRowEntity({
+ @JsonKey(name: 'SCHUL_NM') required String schoolName,
+ @JsonKey(name: 'ORG_RDNMA') required String schoolAddress,
+ }) = _SchoolRowEntity;
+
+ factory SchoolRowEntity.fromJson(Map json) =>
+ _$SchoolRowEntityFromJson(json);
+}
diff --git a/lib/domain/sign_up/repositories/sign_up_repository.dart b/lib/domain/sign_up/repositories/sign_up_repository.dart
new file mode 100644
index 0000000..a60c975
--- /dev/null
+++ b/lib/domain/sign_up/repositories/sign_up_repository.dart
@@ -0,0 +1,14 @@
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_email_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_entity.dart';
+
+import '../entity/sign_up_search_school_entity.dart';
+
+abstract class SignUpRepository {
+ Future signUp(SignUpEntity entity);
+
+ Future sendEmail(SignUpEmailEntity entity);
+
+ Future verifyEmail(SignUpEmailEntity entity);
+
+ Future searchSchool(String schoolName);
+}
diff --git a/lib/domain/sign_up/usecase/sign_up_usecase.dart b/lib/domain/sign_up/usecase/sign_up_usecase.dart
new file mode 100644
index 0000000..475f9e8
--- /dev/null
+++ b/lib/domain/sign_up/usecase/sign_up_usecase.dart
@@ -0,0 +1,13 @@
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_email_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_search_school_entity.dart';
+
+abstract class SignUpUseCase {
+ Future signUp(SignUpEntity entity);
+
+ Future verifyEmail(SignUpEmailEntity entity);
+
+ Future sendEmail(SignUpEmailEntity entity);
+
+ Future searchSchool(String schoolName);
+}
diff --git a/lib/domain/sign_up/usecase/sign_up_usecase_impl.dart b/lib/domain/sign_up/usecase/sign_up_usecase_impl.dart
new file mode 100644
index 0000000..0eaa2aa
--- /dev/null
+++ b/lib/domain/sign_up/usecase/sign_up_usecase_impl.dart
@@ -0,0 +1,32 @@
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_email_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/entity/sign_up_search_school_entity.dart';
+import 'package:jusicool_ios/domain/sign_up/usecase/sign_up_usecase.dart';
+
+import '../repositories/sign_up_repository.dart';
+
+class SignUpUseCaseImpl extends SignUpUseCase {
+ final SignUpRepository _signUpRepository;
+
+ SignUpUseCaseImpl(this._signUpRepository);
+
+ @override
+ Future signUp(SignUpEntity entity) {
+ return _signUpRepository.signUp(entity);
+ }
+
+ @override
+ Future sendEmail(SignUpEmailEntity entity) {
+ return _signUpRepository.sendEmail(entity);
+ }
+
+ @override
+ Future verifyEmail(SignUpEmailEntity entity) {
+ return _signUpRepository.verifyEmail(entity);
+ }
+
+ @override
+ Future searchSchool(String schoolName) {
+ return _signUpRepository.searchSchool(schoolName);
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index f03c36f..dbe5db4 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,20 +1,20 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:jusicool_design_system/src/core/theme/colors/color_palette.dart';
import 'package:jusicool_ios/core/config/di/dependencies.dart';
-import 'package:jusicool_ios/menu_bottom.dart';
-import 'package:jusicool_ios/router.dart';
-
+import 'core/config/router/router.dart';
import 'core/config/theme/app_theme.dart';
+import 'core/config/widget/menu_bottom.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: '.env');
- setDio();
+ setDependencies();
await di.allReady();
SystemChrome.setSystemUIOverlayStyle(
@@ -26,7 +26,7 @@ void main() async {
),
);
- runApp(const MyApp());
+ runApp(ProviderScope(child: const MyApp()));
}
class MyApp extends StatelessWidget {
diff --git a/lib/presentation/my_capital/screens/my_assets_screen.dart b/lib/presentation/my_capital/screens/my_assets_screen.dart
index 6ba4dc5..3fbfc57 100644
--- a/lib/presentation/my_capital/screens/my_assets_screen.dart
+++ b/lib/presentation/my_capital/screens/my_assets_screen.dart
@@ -6,7 +6,7 @@ import 'package:intl/intl.dart';
import 'package:jusicool_design_system/src/core/theme/texts/typography.dart';
import 'package:jusicool_design_system/src/core/theme/colors/color_palette.dart';
import 'package:jusicool_ios/presentation/my_capital/widgets/my_asset_tile.dart';
-import '../../../data/models/my_assets.dart';
+import '../../../domain/my_capital/entities/my_assets.dart';
class MyAssetsScreen extends StatefulWidget {
const MyAssetsScreen({super.key});
diff --git a/lib/presentation/sign_in/controller/sign_in_controller.dart b/lib/presentation/sign_in/controller/sign_in_controller.dart
new file mode 100644
index 0000000..d9223f1
--- /dev/null
+++ b/lib/presentation/sign_in/controller/sign_in_controller.dart
@@ -0,0 +1,112 @@
+import 'package:email_validator/email_validator.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:go_router/go_router.dart';
+import 'package:jusicool_ios/core/config/router/router.dart';
+import 'package:jusicool_ios/domain/sign_in/usecase/sign_in_usecase.dart';
+import '../../../core/config/di/dependencies.dart';
+import '../mapper/sign_in_mapper.dart';
+import '../state/sign_in_state.dart';
+
+final signInControllerProvider =
+ StateNotifierProvider(
+ (ref) => UserSignInController(di.get()),
+ );
+
+class UserSignInController extends StateNotifier {
+ final SignInUseCase _signInUseCase;
+
+ UserSignInController(this._signInUseCase) : super(SignInState()) {
+ _emailController.addListener(() {
+ _setEmail(_emailController.text);
+ });
+ _passwordController.addListener(() {
+ _setPassword(_passwordController.text);
+ });
+ }
+
+ bool _isValidPassword(String password) {
+ if (password.length < 8 || password.length > 13) return false;
+
+ final hasLetter = RegExp(r'[A-Za-z]').hasMatch(password);
+ final hasNumber = RegExp(r'\d').hasMatch(password);
+ final hasSpecial = RegExp(r'[@$!%*?&]').hasMatch(password);
+
+ int satisfiedConditions =
+ [hasLetter, hasNumber, hasSpecial].where((e) => e).length;
+
+ return satisfiedConditions >= 2;
+ }
+
+ final TextEditingController _emailController = TextEditingController();
+ final TextEditingController _passwordController = TextEditingController();
+
+ TextEditingController get emailController => _emailController;
+
+ TextEditingController get passwordController => _passwordController;
+
+ void _setEmail(String email) {
+ state = state.copyWith(email: email);
+ if (email.isNotEmpty && EmailValidator.validate(email)) {
+ _clearError();
+ } else {
+ _setEnableButton(false);
+ }
+ }
+
+ void _setPassword(String password) {
+ state = state.copyWith(password: password);
+ if (password.isNotEmpty && _isValidPassword(password)) {
+ _clearError();
+ } else {
+ _setEnableButton(false);
+ }
+ }
+
+ void _setError(String message) {
+ state = state.copyWith(hasError: true, errorMessage: message);
+ }
+
+ void _clearError() {
+ state = state.copyWith(hasError: false, errorMessage: "");
+ _setEnableButton(true);
+ }
+
+ void _setEnableButton(bool enable) {
+ state = state.copyWith(enableButton: enable);
+ }
+
+ void signIn(BuildContext context) {
+ if (state.email.isEmpty || state.password.isEmpty) {
+ _setError("이메일과 비밀번호를 입력해주세요.");
+ return;
+ }
+ if (!EmailValidator.validate(state.email)) {
+ _setError("유효한 이메일을 입력해주세요.");
+ return;
+ }
+ if (!_isValidPassword(state.password)) {
+ _setError("비밀번호는 8~13자이며, 문자, 숫자, 특수문자 중 2가지 이상 포함해야 합니다.");
+ return;
+ }
+
+ _clearError();
+
+ final request = SignInMapper.toEntity(state);
+ _signInUseCase
+ .signIn(request)
+ .then((_) {
+ context.pushReplacement(RoutePaths.main);
+ })
+ .catchError((error) {
+ _setError("아이디와 비밀번호를 다시 한 번 확인해주세요");
+ });
+ }
+
+ @override
+ void dispose() {
+ _emailController.dispose();
+ _passwordController.dispose();
+ super.dispose();
+ }
+}
diff --git a/lib/presentation/sign_in/mapper/sign_in_mapper.dart b/lib/presentation/sign_in/mapper/sign_in_mapper.dart
new file mode 100644
index 0000000..69f868c
--- /dev/null
+++ b/lib/presentation/sign_in/mapper/sign_in_mapper.dart
@@ -0,0 +1,7 @@
+import 'package:jusicool_ios/domain/sign_in/entity/sign_in_entity.dart';
+import 'package:jusicool_ios/presentation/sign_in/state/sign_in_state.dart';
+
+class SignInMapper {
+ static SignInEntity toEntity(SignInState state) =>
+ SignInEntity(email: state.email, password: state.password);
+}
diff --git a/lib/presentation/sign_in/screens/login_screen.dart b/lib/presentation/sign_in/screens/login_screen.dart
index b10dc56..b114c1d 100644
--- a/lib/presentation/sign_in/screens/login_screen.dart
+++ b/lib/presentation/sign_in/screens/login_screen.dart
@@ -1,195 +1,20 @@
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
-import 'package:go_router/go_router.dart';
import 'package:jusicool_design_system/jusicool_design_system.dart';
-import 'package:email_validator/email_validator.dart';
-import 'package:jusicool_ios/presentation/sign_in/widgets/input_field.dart';
-import 'package:jusicool_ios/router.dart';
+import 'package:jusicool_ios/presentation/sign_in/screens/widgets/input_field.dart';
import '../../sign_up/screens/name_input_screen.dart';
+import '../controller/sign_in_controller.dart';
-class LoginScreen extends StatefulWidget {
- const LoginScreen({super.key});
+class LoginScreen extends ConsumerWidget {
+ LoginScreen({super.key});
@override
- State createState() => _LoginScreenState();
-}
-
-class _LoginScreenState extends State {
- final TextEditingController _emailController = TextEditingController();
- final TextEditingController _passwordController = TextEditingController();
-
- bool showEmailError = false;
- String emailErrorMessage = '';
- bool showPasswordError = false;
- String passwordErrorMessage = '';
- bool showLoginError = false;
- String loginErrorMessage = '';
-
- static const double FIELD_HEIGHT = 56.0;
- static const double FORM_WIDTH = 312.0;
-
- /// ====================================
- final List