๋ณธ ํ๋ก์ ํธ๋ ์ฌ์ฉ์๋ค์ด ์์ ๋ง์ ์กฐํฉ(๋ ์ํผ)์ ๋ฑ๋กํ๊ณ ๊ณต์ ํ ์ ์๋
Android ๊ธฐ๋ฐ ์กฐํฉ ๊ณต์ ์ ํ๋ฆฌ์ผ์ด์
์ด๋ค.
์ฌ์ฉ์๋ ์กฐํฉ์ ๊ฒ์ํ๊ณ ์นดํ
๊ณ ๋ฆฌ๋ณ๋ก ํ์ํ ์ ์์ผ๋ฉฐ,
๋ง์์ ๋๋ ์กฐํฉ์ ์ข์์๋ฅผ ๋๋ฅด๊ฑฐ๋ ์ง์ ์๋ก์ด ์กฐํฉ์ ๋ฑ๋กํ ์ ์๋ค.
๋ํ ๋ง์ด ํ์ด์ง๋ฅผ ํตํด ์์ ์ด ์์ฑํ ์กฐํฉ๊ณผ ์ข์์ํ ์กฐํฉ์ ํ๋์ ๊ด๋ฆฌํ ์ ์๋ค.
- โ ๋ก๊ทธ์ธ/๋ก๊ทธ์์: ์ผ๋ฐ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๋ฐ ๋ก๊ทธ์์ ๊ธฐ๋ฅ
- โ ํ ํ๋ฉด: ์กฐํฉ ๋ชฉ๋ก ์กฐํ, ์นดํ ๊ณ ๋ฆฌ ํํฐ, ๊ฒ์ ๊ธฐ๋ฅ
- โ ์กฐํฉ ์์ธ: ์กฐํฉ ์์ธ ์ ๋ณด ๋ณด๊ธฐ, ์ข์์ ๊ธฐ๋ฅ
- โ ์กฐํฉ ๋ฑ๋ก: ์ ์กฐํฉ ๋ฑ๋ก (์ด๋ฏธ์ง, ์ ๋ชฉ, ์ฌ๋ฃ, ๋ง๋๋ ๋ฐฉ๋ฒ)
- โ ๋ง์ด ํ์ด์ง: ๋์ ๋ ์ํผ, ์ข์์ํ ์กฐํฉ ๋ชจ์ ๋ณด๊ธฐ ๊ธฐ๋ฅ
- ์ฌ์ฉ์ ๋ก๊ทธ์ธ ๋ฐ ํ์๊ฐ์
- ํ ํฐ ๊ธฐ๋ฐ ์ธ์ฆ ๊ด๋ฆฌ
- ๋ก๊ทธ์์ ๋ฐ ์ธ์ฆ ์ํ ์ด๊ธฐํ
presentation/screen/auth/LoginScreen.ktpresentation/screen/auth/RegistrationScreen.ktpresentation/screen/auth/RegistrationSuccessScreen.kt
presentation/viewmodel/LoginViewModel.ktpresentation/viewmodel/RegistrationViewModel.kt
domain/repository/AuthRepository.ktdata/repositoryimpl/AuthRepositoryImpl.kt
data/service/AuthService.ktdata/dto/request/LoginRequest.ktdata/dto/request/SignupRequest.ktdata/dto/response/LoginResponse.ktdata/local/TokenManager.ktdata/network/AuthInterceptor.kt
- ์ธ์ฆ ๋ก์ง์ Repository๋ก ๋ถ๋ฆฌํ์ฌ UI์ ๋ถ๋ฆฌ
- Interceptor๋ฅผ ํตํด ๋ชจ๋ ์์ฒญ์ ํ ํฐ ์๋ ์ฝ์
- TokenManager๋ก ๋ก๊ทธ์ธ ์ํ ์ผ๊ด์ฑ ์ ์ง
AuthInterceptor๋ ๋ชจ๋ ๋คํธ์ํฌ ์์ฒญ์ ๋ํด
Access Token์ ์๋์ผ๋ก ์ถ๊ฐํ๋ ์ญํ ์ ๋ด๋นํ๋ ํต์ฌ ์ธ์ฆ ์ปดํฌ๋ํธ์ด๋ค.
- ์ธ์ฆ์ด ํ์ํ API ์์ฒญ์
Authorization: Bearer <token>ํค๋ ์๋ ์ฝ์ - ๋ก๊ทธ์ธ / ํ์๊ฐ์ / ํ ํฐ ์ฌ๋ฐ๊ธ ์์ฒญ์ ์ธ์ฆ ์์ด ํต๊ณผ
- UI๋ Repository์์ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์ง์ ํ์ง ์๋๋ก ๋ถ๋ฆฌ
- ๋ชจ๋ ๋คํธ์ํฌ ์์ฒญ์ด ์ ์ก๋๊ธฐ ์ Interceptor๋ฅผ ๊ฑฐ์นจ
- ์์ฒญ URL์ ๊ธฐ์ค์ผ๋ก ์ธ์ฆ ํ์ ์ฌ๋ถ ํ๋จ
- ์ธ์ฆ์ด ํ์ํ ๊ฒฝ์ฐ:
TokenManager์์ Access Token ์กฐํ- ํ ํฐ์ด ์กด์ฌํ๋ฉด Authorization ํค๋ ์ถ๊ฐ
- ์ธ์ฆ์ด ํ์ ์๋ ๊ฒฝ์ฐ:
- ์๋ณธ ์์ฒญ ๊ทธ๋๋ก ์๋ฒ๋ก ์ ๋ฌ
- ์กฐํฉ(๋ ์ํผ) ๋ชฉ๋ก ์กฐํ
- ์นดํ ๊ณ ๋ฆฌ ๊ธฐ๋ฐ ํํฐ๋ง
- ํค์๋ ๊ฒ์ ๊ธฐ๋ฅ ์ ๊ณต
presentation/screen/home/HomeScreen.ktpresentation/screen/home/HomeContent.ktpresentation/screen/home/HomeCombinationList.ktpresentation/screen/home/HomeTopBar.ktpresentation/screen/home/SearchScreen.kt
presentation/viewmodel/HomeViewModel.ktpresentation/viewmodel/SearchViewModel.kt
domain/repository/CombinationRepository.ktdata/repositoryimpl/CombinationRepositoryImpl.kt
data/service/HomeService.ktdata/dto/response/CombinationResponse.ktdata/mapper/CombinationMapper.kt
core/component/CombinationCard.ktcore/component/CategoryChip.ktcore/component/SearchBar.kt
- ํ ํ๋ฉด์ ์ํ ๊ด๋ฆฌ ๋ฐ ๋น์ฆ๋์ค ๋ก์ง์ ViewModel์์ ์ฒ๋ฆฌ
- ์นดํ ๊ณ ๋ฆฌ ์ ํ ๋ฐ ๊ฒ์์ด ์ ๋ ฅ์ ๋ฐ๋ผ ์กฐํฉ ๋ชฉ๋ก์ ๋์ ์ผ๋ก ๊ฐฑ์
- ๊ณตํต UI ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฆฌํ์ฌ ์ฌ์ฌ์ฉ์ฑ๊ณผ UI ์ผ๊ด์ฑ ํ๋ณด
HomeViewModel์ ํ ํ๋ฉด์์ ๋ฐ์ํ๋
์กฐํฉ ๋ชฉ๋ก ์กฐํ, ์นดํ
๊ณ ๋ฆฌ ํํฐ๋ง, ๊ฒ์ ์ํ๋ฅผ ํตํฉ ๊ด๋ฆฌํ๋ ํต์ฌ ViewModel์ด๋ค.
- ํ ํ๋ฉด ์ง์ ์ ์กฐํฉ ๋ชฉ๋ก์ ์๋ฒ๋ก๋ถํฐ ์กฐํ
- ์ ํ๋ ์นดํ ๊ณ ๋ฆฌ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ชฉ๋ก ํํฐ๋ง
- ๊ฒ์์ด ์ ๋ ฅ ์ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ์ํ๋ก ๊ด๋ฆฌ
- UI๋ ViewModel์ด ์ ๊ณตํ๋ ์ํ(StateFlow)๋ฅผ ๊ด์ฐฐํ์ฌ ๋ ๋๋ง
- ํ๋ฉด ์ง์ ๋๋ ์ฌ์ฉ์ ์ก์ ๋ฐ์
- ViewModel์์ Repository๋ฅผ ํตํด ๋ฐ์ดํฐ ์์ฒญ
- ์๋ฒ ์๋ต์ Domain Entity๋ก ๋ณํ
- ์ํ(State) ๊ฐฑ์ โ UI ์๋ ๋ฆฌ๋ ๋๋ง
CombinationCard๋ ์กฐํฉ(๋ ์ํผ)์ ํํํ๋
ํ ํ๋ฉด์ ๊ธฐ๋ณธ UI ๋จ์ ์ปดํฌ๋ํธ์ด๋ค.
-
์กฐํฉ ์ด๋ฏธ์ง, ์ ๋ชฉ, ๊ฐ๋จํ ์ ๋ณด ํ์
-
ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ธ๋ถ์์ ์ฃผ์ ๋ฐ์ ํ๋ฉด ์ด๋ ์ฒ๋ฆฌ
-
ํ / ๊ฒ์ / ๋ง์ด ํ์ด์ง ๋ฑ ์ฌ๋ฌ ํ๋ฉด์์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅ
-
UI ์ค๋ณต ์ ๊ฑฐ
-
์ปดํฌ๋ํธ ๋จ์ ์ค๊ณ๋ก ์ ์ง๋ณด์์ฑ ํฅ์
-
ํ๋ฉด ๊ฐ ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ ์ ๊ณต
- ์ ํํ ์กฐํฉ์ ์์ธ ์ ๋ณด ์กฐํ
- ์ข์์(์ฐ) ๊ธฐ๋ฅ ์ ๊ณต
presentation/screen/combination/DetailScreen.kt
presentation/viewmodel/DetailViewModel.kt
domain/repository/RecipeRepository.ktdata/repositoryimpl/RecipeRepositoryImpl.kt
data/service/RecipeService.ktdata/dto/response/RecipeDetailResponse.ktdata/mapper/RecipeMapper.ktdomain/entity/RecipeDetail.kt
- ๋จ์ผ ์กฐํฉ ์ํ๋ง ๊ด๋ฆฌํ๋๋ก ViewModel ๊ตฌ์ฑ
- ์๋ฒ ์๋ต DTO๋ฅผ Mapper๋ฅผ ํตํด Domain Entity๋ก ๋ณํ
- UI๋ ์ํ ๋ณํ์ ๋ฐ๋ผ ์๋ ๋ ๋๋ง
- ์กฐํฉ ์์ธ ํ๋ฉด์ ๋จ์ผ ์กฐํฉ ๋ฐ์ดํฐ ์ํ๋ง ๊ด๋ฆฌํ๋๋ก ๊ตฌ์ฑ
- ์์ธ ์กฐํ ๋ฐ ์ข์์ ์ํ ๋ณ๊ฒฝ ๋ก์ง์ ViewModel์์ ์ฒ๋ฆฌ
- ์๋ฒ ์๋ต DTO๋ Mapper๋ฅผ ํตํด Domain Entity๋ก ๋ณํํ์ฌ ์ฌ์ฉ
- UI๋ ViewModel์ด ์ ๊ณตํ๋ ์ํ(State)๋ฅผ ๊ด์ฐฐํ์ฌ ์๋ ๋ ๋๋ง
DetailViewModel์ ์ ํ๋ ์กฐํฉ ํ๋์ ๋ํ
์์ธ ์ ๋ณด ์กฐํ ๋ฐ ์ข์์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ํต์ฌ ViewModel์ด๋ค.
- ์กฐํฉ ID๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ธ ์ ๋ณด ์์ฒญ
- ์๋ฒ๋ก๋ถํฐ ๋ฐ์ ์กฐํฉ ๋ฐ์ดํฐ๋ฅผ ์ํ๋ก ๊ด๋ฆฌ
- ์ข์์ ๋ฒํผ ํด๋ฆญ ์ ์ข์์ ์ํ ๊ฐฑ์
- UI๋ ์ํ ๋ณํ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ ๋ฐ์ดํธ๋จ
- ์์ธ ํ๋ฉด ์ง์ ์ ์กฐํฉ ID ์ ๋ฌ
- ViewModel์์ Repository๋ฅผ ํตํด ์์ธ ์ ๋ณด ์์ฒญ
- ์๋ต ๋ฐ์ดํฐ๋ฅผ ์ํ(State)๋ก ์ ์ฅ
- ์ข์์ ๋ฒํผ ํด๋ฆญ ์ ์ํ ์ ๋ฐ์ดํธ
- ์ํ ๋ณ๊ฒฝ โ UI ์๋ ๋ฆฌ๋ ๋๋ง
RecipeMapper๋ ์๋ฒ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์
RecipeDetailResponse๋ฅผ ์ฑ ๋ด๋ถ์์ ์ฌ์ฉํ๋
RecipeDetail Domain Entity๋ก ๋ณํํ๋ ์ญํ ์ ๋ด๋นํ๋ค.
-
๋คํธ์ํฌ ๋ชจ๋ธ(DTO)๊ณผ ์ฑ ๋ด๋ถ ๋ชจ๋ธ(Entity) ๋ถ๋ฆฌ
-
๋ฐ์ดํฐ ๊ณ์ธต๊ณผ ๋๋ฉ์ธ ๊ณ์ธต ๊ฐ ์์กด์ฑ ์ ๊ฑฐ
-
์๋ฒ ์๋ต ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ ์ํฅ ๋ฒ์๋ฅผ Mapper๋ก ํ์
-
Clean Architecture ๊ธฐ๋ฐ ๊ณ์ธต ๋ถ๋ฆฌ ๊ฐํ
-
ViewModel๊ณผ UI์์ ์๋ฒ ๋ชจ๋ธ์ ์ง์ ์์กดํ์ง ์๋๋ก ์ค๊ณ
- ์๋ก์ด ์กฐํฉ(๋ ์ํผ) ๋ฑ๋ก
- ์ด๋ฏธ์ง, ์ฌ๋ฃ, ๋ง๋๋ ๋ฐฉ๋ฒ ์ ๋ ฅ
presentation/screen/combination/CreateCombinationScreen.kt
presentation/viewmodel/CreateCombinationViewModel.kt
data/dto/request/CreateCombinationRequest.ktdata/service/CombinationService.kt
- ์กฐํฉ ๋ฑ๋ก ํ๋ฉด์ ์ฌ์ฉ์ ์ ๋ ฅ์ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ ํผ(Form) ํํ์ ํ๋ฉด
- ์ ๋ ฅ ๊ฐ(์ ๋ชฉ, ์ฌ๋ฃ, ๋ง๋๋ ๋ฐฉ๋ฒ, ์ด๋ฏธ์ง ๋ฑ)์ ViewModel์์ ์ํ๋ก ๊ด๋ฆฌ
- ๋ฑ๋ก ์์ฒญ์ Request DTO๋ก ๋ฌถ์ด ์๋ฒ์ ์ ๋ฌ
- ๋ฑ๋ก ์ฑ๊ณต ์ ํ ํ๋ฉด์ผ๋ก ์ด๋ํ๋๋ก ํ๋ฆ ๊ตฌ์ฑ
CreateCombinationViewModel์ ์กฐํฉ ๋ฑ๋ก ํ๋ฉด์์ ๋ฐ์ํ๋
๋ชจ๋ ์
๋ ฅ ์ํ์ ๋ฑ๋ก ์ด๋ฒคํธ๋ฅผ ๊ด๋ฆฌํ๋ ํต์ฌ ViewModel์ด๋ค.
- ์ ๋ชฉ, ์ฌ๋ฃ, ๋ง๋๋ ๋ฐฉ๋ฒ ๋ฑ ์ ๋ ฅ ๊ฐ ์ํ ๊ด๋ฆฌ
- ์ด๋ฏธ์ง ์ ํ ๊ฒฐ๊ณผ๋ฅผ ์ํ๋ก ์ ์ฅ
- ๋ฑ๋ก ๋ฒํผ ํด๋ฆญ ์ ์ ๋ ฅ๊ฐ์ Request DTO๋ก ๋ณํํ์ฌ ์๋ฒ ์์ฒญ
- ๋ฑ๋ก ์ฑ๊ณต/์คํจ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ UI ์ํ ๊ฐฑ์
- ์ฌ์ฉ์ ์ ๋ ฅ ๋ฐ์ (ํ ์คํธ / ์ด๋ฏธ์ง ์ ํ)
- ViewModel์์ ์ ๋ ฅ ์ํ ์ ๋ฐ์ดํธ
- ๋ฑ๋ก ๋ฒํผ ํด๋ฆญ ์
CreateCombinationRequest์์ฑ - Repository(Service)๋ฅผ ํตํด ์๋ฒ์ ๋ฑ๋ก ์์ฒญ
- ์ฑ๊ณต ์ ํ๋ฉด ์ด๋, ์คํจ ์ ์๋ฌ ์ํ ์ฒ๋ฆฌ
CreateCombinationRequest๋ ์กฐํฉ ๋ฑ๋ก ์ ์๋ฒ๋ก ์ ๋ฌ๋๋
๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ์ํ๋ Request DTO์ด๋ค.
-
์ ๋ชฉ, ์ฌ๋ฃ ๋ชฉ๋ก, ์กฐ๋ฆฌ ๋ฐฉ๋ฒ, ์ด๋ฏธ์ง ์ ๋ณด ํฌํจ
-
์๋ฒ API ์คํ๊ณผ 1:1๋ก ๋งคํ
-
UI ๋ฐ ViewModel์ด ์๋ฒ ๊ตฌ์กฐ์ ์ง์ ์์กดํ์ง ์๋๋ก ๋ถ๋ฆฌ
-
๋คํธ์ํฌ ๊ณ์ธต๊ณผ UI ๊ณ์ธต ๊ฐ ๋ช ํํ ์ญํ ๋ถ๋ฆฌ
-
์์ฒญ ๋ฐ์ดํฐ ๊ตฌ์กฐ ๋ณ๊ฒฝ ์ ์ํฅ ๋ฒ์๋ฅผ DTO๋ก ํ์
- ์ฌ์ฉ์ ํ๋กํ ์กฐํ
- ๋ด๊ฐ ์์ฑํ ์กฐํฉ ๋ชฉ๋ก ์กฐํ
- ์ข์์ํ ์กฐํฉ ๋ชจ์๋ณด๊ธฐ
- ํ๋กํ ์์ ๊ธฐ๋ฅ
presentation/screen/profile/MyScreen.ktpresentation/screen/profile/EditProfileScreen.kt
presentation/viewmodel/MyPageViewModel.ktpresentation/viewmodel/EditProfileViewModel.kt
domain/repository/UserRepository.ktdata/repositoryimpl/UserRepositoryImpl.kt
data/service/UserService.ktdata/dto/request/UpdateProfileRequest.ktdata/dto/response/UserResponse.kt
- ์ฌ์ฉ์ ๊ด๋ จ ๊ธฐ๋ฅ์ UserRepository๋ก ํตํฉ ๊ด๋ฆฌ
- ํ๋กํ ์์ ๊ณผ ์กฐํ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ์ฑ ์ ๋ช ํํ
- ๋ง์ด ํ์ด์ง๋ ์ฌ์ฉ์ ๊ธฐ์ค ๊ธฐ๋ฅ์ ๋ด๋นํ๋ ํ๋ฉด์ผ๋ก ๊ตฌ์ฑ
- ์ฌ์ฉ์ ํ๋กํ ์ ๋ณด, ์์ฑํ ์กฐํฉ, ์ข์์ํ ์กฐํฉ์ ํ ํ๋ฉด์์ ์ ๊ณต
- ์ฌ์ฉ์ ๊ด๋ จ ๋ฐ์ดํฐ ์ ๊ทผ์ UserRepository๋ฅผ ํตํด ์ผ๊ด ๊ด๋ฆฌ
- ํ๋กํ ์กฐํ์ ์์ ๋ก์ง์ ViewModel ๋จ์๋ก ๋ถ๋ฆฌํ์ฌ ์ฑ ์ ๋ช ํํ
MyPageViewModel์ ๋ง์ด ํ์ด์ง์์ ํ์ํ
์ฌ์ฉ์ ์ ๋ณด ๋ฐ ์ฌ์ฉ์ ๊ด๋ จ ์กฐํฉ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ํต์ฌ ViewModel์ด๋ค.
- ์ฌ์ฉ์ ํ๋กํ ์ ๋ณด ์กฐํ
- ๋ด๊ฐ ์์ฑํ ์กฐํฉ ๋ชฉ๋ก ์กฐํ
- ์ข์์ํ ์กฐํฉ ๋ชฉ๋ก ์กฐํ
- ์ ํ๋ ํญ ๋๋ ํ๋ฉด ์ํ์ ๋ฐ๋ฅธ ๋ฐ์ดํฐ ๊ด๋ฆฌ
- ๋ง์ด ํ์ด์ง ์ง์ ์ ์ฌ์ฉ์ ์ ๋ณด ์์ฒญ
- Repository๋ฅผ ํตํด ์ฌ์ฉ์ ๊ด๋ จ ๋ฐ์ดํฐ ์กฐํ
- ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ์ํ(State)๋ก ์ ์ฅ
- UI๋ ์ํ ๋ณํ๋ฅผ ๊ด์ฐฐํ์ฌ ํ๋ฉด ๊ฐฑ์
- Language: Kotlin
- UI: Jetpack Compose (Material 3)
- Architecture: MVVM + Clean Architecture
- DI: Hilt
- Network: Retrofit + OkHttp + Kotlinx Serialization
- Image Loading: Coil 3
- Navigation: Navigation Compose
์ด ํ๋ก์ ํธ๋ ์๋ํ๋ ์ฝ๋ ํ์ง ๊ฒ์ฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ปค๋ฐ ์ ์๋์ผ๋ก ์ฝ๋ ํ์ง ๊ฒ์ฌ๊ฐ ์คํ๋ฉ๋๋ค.
# Git Bash์์ ์ปค๋ฐ ์ ์๋ ์คํ
git commit -m "your message"
# ์๋์ผ๋ก Ktlint + Detekt ์คํ
# PowerShell/CMD์์๋ ์๋ ์คํ ํ์
.\scripts\check-code-quality.ps1
```
#### 2. Gradle Task (๋น๋ ์ ์๋ ํฌ๋งทํ
)
๋น๋ ์ ์๋์ผ๋ก ์ฝ๋ ํฌ๋งทํ
์ด ์คํ๋ฉ๋๋ค (๊ฒ์ฌ๋ Git Hook์์ ์ฒ๋ฆฌ).
`````bash
./gradlew assembleDebug
# ์๋์ผ๋ก Ktlint ํฌ๋งทํ
์คํ (๊ฒ์ฌ๋ Git Hook์์ ์ฒ๋ฆฌ)
```
#### 3. ์๋ ์คํ
**Windows (PowerShell)**:
`````powershell
.\scripts\check-code-quality.ps1
```
**Git Bash / Linux / Mac**:
`````bash
./scripts/check-code-quality.sh
```
**Gradle ์ง์ ์คํ**:
`````bash
# ์ฝ๋ ํ์ง ๊ฒ์ฌ ํตํฉ ์คํ
./gradlew codeQualityCheck
# ๊ฐ๋ณ ์คํ
./gradlew ktlintFormat # ์ฝ๋ ํฌ๋งทํ
./gradlew ktlintCheck # ์ฝ๋ ์คํ์ผ ๊ฒ์ฌ
./gradlew detekt # ์ฝ๋ ํ์ง ๊ฒ์ฌ
### ํ
์คํธ ์คํ
**Windows (PowerShell)**:
```powershell
# ๋ชจ๋ ํ
์คํธ ์คํ
.\scripts\run-tests.ps1
# ํน์ ํ
์คํธ ํด๋์ค๋ง ์คํ
.\scripts\run-tests.ps1 "CreateCombinationViewModelTest"
```
**Git Bash / Linux / Mac**:
```bash
# ๋ชจ๋ ํ
์คํธ ์คํ
./scripts/run-tests.sh
# ํน์ ํ
์คํธ ํด๋์ค๋ง ์คํ
./scripts/run-tests.sh "CreateCombinationViewModelTest"
```
**Gradle ์ง์ ์คํ**:
```bash
# ๋ชจ๋ ํ
์คํธ ์คํ
./gradlew test
# ํน์ ํ
์คํธ ํด๋์ค๋ง ์คํ
./gradlew test --tests "CreateCombinationViewModelTest"
```
```
## ๐ ํ๋ก์ ํธ ๊ตฌ์กฐ
```
com.example.hackathon/
โโโ data/ # ๋ฐ์ดํฐ ๋ ์ด์ด
โ โโโ dto/ # Request/Response ๋ชจ๋ธ
โ โโโ mapper/ # Entity โ DTO ๋ณํ
โ โโโ repositoryimpl/ # Repository ๊ตฌํ
โ โโโ service/ # API ์ธํฐํ์ด์ค
โโโ domain/ # ๋๋ฉ์ธ ๋ ์ด์ด
โ โโโ entity/ # ๋น์ฆ๋์ค ์ํฐํฐ
โ โโโ repository/ # Repository ์ธํฐํ์ด์ค
โโโ presentation/ # ํ๋ ์ ํ
์ด์
๋ ์ด์ด
โ โโโ navigation/ # ๋ค๋น๊ฒ์ด์
โ โโโ route/ # ๋ผ์ฐํธ ์ ์
โ โโโ screen/ # ํ๋ฉด ์ปดํฌ์ ๋ธ
โ โโโ viewmodel/ # ViewModel
โโโ core/ # ๊ณตํต
โ โโโ component/ # ๊ณตํต ์ปดํฌ๋ํธ
โโโ di/ # ์์กด์ฑ ์ฃผ์
โโโ ui/theme/ # ํ
๋ง ์ค์
```
## ๐ ์์ํ๊ธฐ
### ํ๊ฒฝ ์๊ตฌ์ฌํญ
- Android Studio Hedgehog (2023.1.1) ์ด์
- JDK 17 ์ด์
- Android SDK 36 (compileSdk, targetSdk)
- Min SDK 36
### ์คํ ๋ฐฉ๋ฒ
1. ํ๋ก์ ํธ ํด๋ก
`````bash
git clone https://github.com/1hyok/Hackathon-FE.git
cd Hackathon-FE
```
2. Android Studio์์ ์ด๊ธฐ
- `File` โ `Open` โ ํ๋ก์ ํธ ํด๋ ์ ํ
3. Gradle ๋๊ธฐํ
- Android Studio๊ฐ ์๋์ผ๋ก ๋๊ธฐํํ๊ฑฐ๋
- `File` โ `Sync Project with Gradle Files`
4. ์คํ!
- ์๋ฎฌ๋ ์ดํฐ ๋๋ ์ค์ ๊ธฐ๊ธฐ ์ฐ๊ฒฐ
- `Run` ๋ฒํผ ํด๋ฆญ ๋๋ `Shift + F10`
## ๐ฅ ํ ๊ตฌ์ฑ
- **์ผํ**: ๋ก๊ทธ์ธ ํ๋ฉด, ํ์๊ฐ์
ํ๋ฉด, ์จ๋ณด๋ฉ ํ๋ฉด, ์กฐํฉ ๋ฑ๋ก ํ๋ฉด, ๊ฒ์ ํ๋ฉด
- **์์**: ํ ํ๋ฉด, ์กฐํฉ ์์ธ ํ๋ฉด
## ๐ฟ ๋ธ๋์น ์ ๋ต
- `main`: ๋ฉ์ธ ๋ธ๋์น (์์ ์ ์ธ ์ฝ๋)
- `ilhyuk`: ์ผํ ์์
๋ธ๋์น
- `yewon`: ์์ ์์
๋ธ๋์น
๊ฐ์ ๋ธ๋์น์์ ์์
ํ `main`์ ๋ณํฉํฉ๋๋ค.
## ๐ ์ฐธ๊ณ ๋ฌธ์
- [PROJECT_MEMO.md](./PROJECT_MEMO.md) - ํ๋ก์ ํธ ๋ฉ๋ชจ ๋ฐ ์์ธ ์ ๋ณด
- [CHECKLIST.md](./CHECKLIST.md) - ์์
์ฒดํฌ๋ฆฌ์คํธ
- [CONVENTION.md](./CONVENTION.md) - ์ฝ๋ฉ ์ปจ๋ฒค์
- [GIT_STRATEGY.md](./GIT_STRATEGY.md) - Git ์ ๋ต
- [.cursorrules](./.cursorrules) - Cursor AI ๊ท์น
## ๐จ ๋์์ธ
- Figma ๋์์ธ ํ์ผ ์ฐธ๊ณ
- Figma AI ํ๋กฌํํธ:
- [๋ก๊ทธ์ธ ํ๋ฉด](./FIGMA_AI_PROMPT_LOGIN.md)
- [์กฐํฉ ๋ฑ๋ก ํ๋ฉด](./FIGMA_AI_PROMPT_CREATE_COMBINATION.md)
## ๐ ์ฃผ์ ํน์ง
- **๋คํฌ ๋ชจ๋ ๋ฏธ์ง์**: ํญ์ ๋ผ์ดํธ ๋ชจ๋๋ง ์ฌ์ฉ
- **Material 3**: ์ต์ Material Design 3 ์ ์ฉ
- **Clean Architecture**: ๋ ์ด์ด ๋ถ๋ฆฌ๋ก ์ ์ง๋ณด์์ฑ ํฅ์
- **์ฝ๋ ํ์ง**: Ktlint + Detekt๋ก ์๋ ์ฝ๋ ํ์ง ๊ฒ์ฌ
## ๐ APK ์๋ ๋ฐฐํฌ
๋ฉ์ธ ๋ธ๋์น์ ํธ์๋ ๋๋ง๋ค APK๊ฐ ์๋์ผ๋ก ๋น๋๋๊ณ ํ์๋ค์๊ฒ ๋ฐฐํฌ๋ฉ๋๋ค.
### ์ค์ ๋ฐฉ๋ฒ
#### ์ต์
1: Firebase App Distribution (์ถ์ฒ)
1. **Firebase ํ๋ก์ ํธ ์์ฑ**
- [Firebase Console](https://console.firebase.google.com)์์ ํ๋ก์ ํธ ์์ฑ
- ์๋๋ก์ด๋ ์ฑ ์ถ๊ฐ (ํจํค์ง๋ช
: `com.example.hackathon`)
2. **google-services.json ํ์ผ ๋ค์ด๋ก๋ ๋ฐ ๋ณด์ ์ฒ๋ฆฌ**
- Firebase ์ฝ์์์ `google-services.json` ํ์ผ ๋ค์ด๋ก๋
- **โ ๏ธ ๋ณด์ ์ฃผ์**: ์ด ํ์ผ์ ๋ฏผ๊ฐํ ์ ๋ณด๋ฅผ ํฌํจํ๋ฏ๋ก GitHub ์ ์ฅ์์ ์ง์ ์ฌ๋ฆฌ์ง ๋ง์ธ์!
- ํ์ผ์ Base64๋ก ์ธ์ฝ๋ฉ:
```powershell
# Windows (PowerShell)
[Convert]::ToBase64String([IO.File]::ReadAllBytes("google-services.json")) | Out-File google-services-base64.txt
```
```bash
# Mac/Linux
base64 -i google-services.json > google-services-base64.txt
```
- `google-services-base64.txt` ํ์ผ์ ์ ์ฒด ๋ด์ฉ์ ๋ณต์ฌ
3. **Firebase CLI ํ ํฐ ๋ฐ๊ธ**
```bash
firebase login:ci
```
๋ฐ๊ธ๋ ํ ํฐ์ ๋ณต์ฌ
4. **Firebase App ID ํ์ธ**
- Firebase ์ฝ์ โ ํ๋ก์ ํธ ์ค์ โ ์ผ๋ฐ ํญ
- App ID ๋ณต์ฌ
5. **GitHub Secrets ์ค์ **
- GitHub ์ ์ฅ์ โ Settings โ Secrets and variables โ Actions
- ๋ค์ Secrets ์ถ๊ฐ:
- `GOOGLE_SERVICES_JSON`: Base64๋ก ์ธ์ฝ๋ฉ๋ `google-services.json` ๋ด์ฉ (2๋จ๊ณ์์ ๋ณต์ฌํ ์ ์ฒด ๋ฌธ์์ด)
- `FIREBASE_APP_ID`: Firebase App ID
- `FIREBASE_TOKEN`: Firebase CLI ํ ํฐ
- `DISCORD_WEBHOOK`: Discord ์นํ
URL (์ ํ์ฌํญ)
6. **Firebase ํ
์คํฐ ๊ทธ๋ฃน ์์ฑ**
- Firebase ์ฝ์ โ App Distribution โ ํ
์คํฐ ๋ฐ ๊ทธ๋ฃน
- "testers" ๊ทธ๋ฃน ์์ฑ ๋ฐ ํ์ ์ด๋ฉ์ผ ์ถ๊ฐ
7. **์ํฌํ๋ก์ฐ ํ์ฑํ**
- `.github/workflows/deploy-apk.yml` ํ์ผ์ด ์๋์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค
- ์ํฌํ๋ก์ฐ๋ GitHub Secret์์ `google-services.json` ํ์ผ์ ์๋์ผ๋ก ์์ฑํฉ๋๋ค
#### ์ต์
2: GitHub Releases (Firebase ์์ด)
1. **GitHub Secrets ์ค์ **
- `DISCORD_WEBHOOK`: Discord ์นํ
URL (์ ํ์ฌํญ)
2. **์ํฌํ๋ก์ฐ ํ์ฑํ**
- `.github/workflows/deploy-apk-github-releases.yml` ํ์ผ ์ฌ์ฉ
- `deploy-apk.yml` ํ์ผ์ ๋นํ์ฑํํ๊ฑฐ๋ ์ญ์
### Discord ์นํ
์ค์ (์ ํ์ฌํญ)
1. Discord ์ฑ๋ ์ค์ โ ์ฐ๋ โ ์นํ
์์ฑ
2. ์นํ
URL์ GitHub Secrets์ `DISCORD_WEBHOOK`์ผ๋ก ์ ์ฅ
### ํ์ ์ฌ์ฉ ๋ฐฉ๋ฒ
#### Firebase App Distribution ์ฌ์ฉ ์:
1. ์ฒ์ ํ ๋ฒ๋ง [Firebase App Tester ์ฑ](https://play.google.com/store/apps/details?id=com.google.firebase.appdistribution) ์ค์น
2. ๋ฉ์ธ ๋ธ๋์น ์
๋ฐ์ดํธ ์ ์๋์ผ๋ก ์ด๋ฉ์ผ ๋ฐ ์ฑ ํธ์ ์๋ฆผ ์์
3. ์๋ฆผ ํด๋ฆญ โ ์ํฐ์น ์ค์น
#### GitHub Releases ์ฌ์ฉ ์:
1. Discord ์๋ฆผ์์ ๋งํฌ ํด๋ฆญ
2. GitHub Releases ํ์ด์ง์์ APK ๋ค์ด๋ก๋
3. ์๋๋ก์ด๋ ๊ธฐ๊ธฐ์์ "์ถ์ฒ๋ฅผ ์ ์ ์๋ ์ฑ" ์ค์น ๊ถํ ํ์ฉ
4. APK ์ค์น