ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,51 @@
+package christmas.domain.parser;
+
+import christmas.domain.Menu;
+import christmas.domain.validator.OrderValidator;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class OrderParser implements OrderParsable {
+    private final OrderValidator orderValidator;
+
+    public OrderParser() {
+        this.orderValidator = new OrderValidator();
+    }
+
+    @Override
+    public Map<Menu, Integer> getOrderDetail(String inputOrder) {
+        Map<Menu, Integer> orderDetail = convertOrderMenu(inputOrder);
+        orderValidator.validateOnlyDrink(Menu.isAllDrinks(orderDetail), orderDetail);
+        return orderDetail;
+    }
+
+    private Map<Menu, Integer> convertOrderMenu(String inputOrder) {
+        return decodeOrderMenu(inputOrder).entrySet().stream()
+                .collect(Collectors.toMap(
+                        entry -> Menu.matchingMenu(entry.getKey()),
+                        Map.Entry::getValue
+                ));
+    }
+
+    private Map<String, Integer> decodeOrderMenu(String inputOrder) {
+        String[] orderParts = inputOrder.split(",");
+        return parseInputOrder(orderParts);
+    }
+
+    private Map<String, Integer> parseInputOrder(String[] orderParts) {
+        Map<String, Integer> splitOrderMenu = new HashMap<>();
+
+        for (String orderPart : orderParts) {
+            String[] part = orderPart.split("-");
+            orderValidator.validateParse(part);
+            splitOrderMenu.put(part[0].trim(), Integer.parseInt(part[1].trim()));
+        }
+
+        orderValidator.validateOrderAmount(splitOrderMenu);
+
+        return splitOrderMenu;
+    }
+}
+

리뷰: validateOnlyDrink에 대한 로직이 노출되어 있어요. 검사는 validator 내부에서 진행해주세요,
```suggestion
    @Override
    public Map<Menu, Integer> getOrderDetail(String inputOrder) {
        Map<Menu, Integer> orderDetail = convertOrderMenu(inputOrder);
        orderValidator.validateOnlyDrink(orderDetail);
        return orderDetail;
    }
```
@@ -0,0 +1,22 @@
+package christmas.service;
+
+import christmas.domain.EventDate;
+
+public class EventDateService implements EventDatable {
+    @Override
+    public boolean isChristmasDDay(int todayDate) {
+        return todayDate == EventDate.CHRISTMAS_D_DAY_END.getDay();
+    }
+
+    @Override
+    public boolean isChristmasEventDay(int todayDate) {
+        return todayDate <= EventDate.CHRISTMAS_D_DAY_END.getDay();
+    }
+
+    @Override
+    public void validateDecemberDate(int inputDate) {
+        if (inputDate < EventDate.DECEMBER_DAY_START.getDay() || inputDate > EventDate.DECEMBER_DAY_END.getDay()) {
+            throw new IllegalArgumentException("[ERROR] 날짜는 1~31일 범위 내에서만 유효합니다.");
+        }
+    }
+}

리뷰: Today에서 인터페이스로 해당 클래스를 받아서 기능을 활용하던데, 그냥 Today 내부에서 구현해도 됐을 것 같아요.
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="" vcs="Git" />
   </component>
 </project>
\ No newline at end of file

리뷰: 이 파일은 버전관리에 들어가야 했던 이유가 있을까요? 

./idea 이하 폴더의 내용은 개발자 개인의 설정이 들어가는 경우가 많은데 팀 내에서 논의된 경우만 등록 부탁드립니다.
@@ -1,21 +1,54 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="kr.co.connect.boostcamp.livewhere">
-
+          xmlns:tools="http://schemas.android.com/tools" package="kr.co.connect.boostcamp.livewhere">
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <application
-            android:allowBackup="true"
-            android:icon="@mipmap/ic_launcher"
+            android:name=".LiveApplication"
+            android:allowBackup="false"
+            android:icon="@drawable/icon_logo"
+            android:roundIcon="@drawable/icon_logo"
             android:label="@string/app_name"
-            android:roundIcon="@mipmap/ic_launcher_round"
             android:supportsRtl="true"
             android:theme="@style/AppTheme">
-        <activity android:name=".MainActivity">
+
+        <uses-library android:name="org.apache.http.legacy"
+                      android:required="false"/>
+
+        <meta-data
+                android:name="com.google.android.geo.API_KEY"
+                android:value="@string/key_street_view"/>
+        <activity android:name=".ui.main.SplashActivity"
+                  android:theme="@style/SplashTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
 
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".ui.login.LoginActivity">
+
+        </activity>
+        <activity android:name=".ui.main.HomeActivity">
+        </activity>
+        <activity android:name=".ui.detail.DetailActivity"
+                  android:windowSoftInputMode="adjustResize">
+
+        </activity>
+        <activity android:name=".ui.map.MapActivity"
+                  android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW"/>
+
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".ui.map.StreetMapActivity"
+                  android:screenOrientation="portrait">
+        </activity>
+
     </application>
 
 </manifest>
\ No newline at end of file

리뷰: 호환성을 위해 추가한 라인으로 보이는데요, 중요한 변경일 경우 별도 주석으로 부탁드립니다. 
링크같은걸 커밋 메시지나 주석으로 붙여주시면 나중에 찾기도 쉬울거예요!
@@ -1,21 +1,54 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="kr.co.connect.boostcamp.livewhere">
-
+          xmlns:tools="http://schemas.android.com/tools" package="kr.co.connect.boostcamp.livewhere">
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <application
-            android:allowBackup="true"
-            android:icon="@mipmap/ic_launcher"
+            android:name=".LiveApplication"
+            android:allowBackup="false"
+            android:icon="@drawable/icon_logo"
+            android:roundIcon="@drawable/icon_logo"
             android:label="@string/app_name"
-            android:roundIcon="@mipmap/ic_launcher_round"
             android:supportsRtl="true"
             android:theme="@style/AppTheme">
-        <activity android:name=".MainActivity">
+
+        <uses-library android:name="org.apache.http.legacy"
+                      android:required="false"/>
+
+        <meta-data
+                android:name="com.google.android.geo.API_KEY"
+                android:value="@string/key_street_view"/>
+        <activity android:name=".ui.main.SplashActivity"
+                  android:theme="@style/SplashTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
 
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".ui.login.LoginActivity">
+
+        </activity>
+        <activity android:name=".ui.main.HomeActivity">
+        </activity>
+        <activity android:name=".ui.detail.DetailActivity"
+                  android:windowSoftInputMode="adjustResize">
+
+        </activity>
+        <activity android:name=".ui.map.MapActivity"
+                  android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW"/>
+
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".ui.map.StreetMapActivity"
+                  android:screenOrientation="portrait">
+        </activity>
+
     </application>
 
 </manifest>
\ No newline at end of file

리뷰: 액티비티 태그들은 코드 포맷을 한번 정리해주실 수 있을까요? 스튜디오 단축키 alt command L 을 누르면 한번에 정리됩니다. 
별거 아닌거 같지만 가독성이 나아지는 효과가 있습니다. 나중에 코드를 다시 볼 때 좋아요!
@@ -0,0 +1,45 @@
+package kr.co.connect.boostcamp.livewhere
+
+import android.content.Context
+import androidx.multidex.MultiDex
+import androidx.multidex.MultiDexApplication
+import com.bumptech.glide.Glide
+import com.crashlytics.android.Crashlytics
+import com.facebook.stetho.Stetho
+import com.naver.maps.map.NaverMapSdk
+import io.fabric.sdk.android.Fabric
+import kr.co.connect.boostcamp.livewhere.di.appModules
+import org.koin.android.ext.android.startKoin
+
+
+// FIXME MultiDex 이슈가 발생할 여지가 충분하기때문에 gradle과 Application클래스에서 Multidex 환경을 구성해주세요
+class LiveApplication : MultiDexApplication() {
+    override fun onCreate() {
+        super.onCreate()
+        //debug변수를 놓고 debug 인지 캐치함.
+        if (BuildConfig.isDebug) {
+            Stetho.initializeWithDefaults(this)
+        }
+        startKoin(this, appModules)
+        //Firebase Crashlytics
+        Fabric.with(this, Crashlytics())
+        NaverMapSdk.getInstance(this).client = NaverMapSdk.NaverCloudPlatformClient(BuildConfig.NaverClientId)
+    }
+
+    //application class에서 MultiDex 사용
+    override fun attachBaseContext(base: Context?) {
+        super.attachBaseContext(base)
+        MultiDex.install(this)
+    }
+
+
+    override fun onLowMemory() {
+        super.onLowMemory()
+        Glide.get(this).clearMemory()
+    }
+
+    override fun onTrimMemory(level: Int) {
+        super.onTrimMemory(level)
+        Glide.get(this).trimMemory(level)
+    }
+}
\ No newline at end of file

리뷰: 이 주석은 아래 코드에서 충분히 설명되므로, 삭제 제안드립니다.
@@ -0,0 +1,40 @@
+package kr.co.connect.boostcamp.livewhere.api
+
+import io.reactivex.Single
+import kr.co.connect.boostcamp.livewhere.model.HouseResponse
+import kr.co.connect.boostcamp.livewhere.model.PlaceResponse
+import retrofit2.Response
+import retrofit2.http.GET
+import retrofit2.http.POST
+import retrofit2.http.Query
+
+interface Api {
+    @GET("house/search/info")
+    fun getHouseDetail(
+        @Query("address") address: String
+    ): Single<Response<List<Any>>>
+
+    @GET("house/search/infos")
+    fun getDetail(
+        @Query("address") address: String
+    ): Single<Response<HouseResponse>>
+
+    @GET("place/search/infos")
+    fun getPlace(
+        @Query("lat") lat: String,
+        @Query("lng") lng: String,
+        @Query("radius") radius: String,
+        @Query("category") category: String
+    ): Single<Response<PlaceResponse>>
+
+    @GET("house/search/find/infos")
+    fun getDetailWithAddress(
+        @Query("address") address: String
+    ): Single<Response<HouseResponse>>
+
+    @POST("")
+    fun postReview(
+        @Query("nickname") nickname: String, @Query("id") id: String, @Query("contents") contents: String
+    ): Single<Response<Any>>
+
+}
\ No newline at end of file

리뷰: review를 추가하는 api는 엔드포인트 url 이 base와 같나요?
잘못 작성된 것은 아닌지 확인부탁드립니다.
@@ -0,0 +1,20 @@
+package kr.co.connect.boostcamp.livewhere.api
+
+import io.reactivex.Observable
+import kr.co.connect.boostcamp.livewhere.BuildConfig
+import kr.co.connect.boostcamp.livewhere.model.TmapResponse
+import retrofit2.Response
+import retrofit2.http.GET
+import retrofit2.http.Query
+
+interface TmapApi {
+    @GET("tmap/pois")
+    fun getAddress(
+        @Query("searchKeyword") searchKeyword: String,
+        @Query("appKey") appKey: String = BuildConfig.TmapApiKey,
+        @Query("count") count: String = "10",
+        @Query("areaLLCode") code: String = "11"
+    ): Observable<Response<TmapResponse>>
+
+
+}
\ No newline at end of file

리뷰: * 다른 api 처럼, 이 api의 리턴 값도 single 타입이 좋아보입니다. (일회성 요청이므로) 
   혹시 Observable이어야 하는 이슈가 있을까요?

* count, areaLLCode에 대한 설명이 추가되면 나중에 유지보수하기 편할듯 합니다. 값에 대한 예시나 상수 리스트를 볼 수 있는 링크를 첨부 부탁드립니다.
@@ -0,0 +1,38 @@
+package kr.co.connect.boostcamp.livewhere.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+interface SharedPreference {
+    var uuid: String?
+}
+
+class SharedPreferenceStorage(context: Context) : SharedPreference {
+    private val prefs = context.applicationContext.getSharedPreferences(PREF, Context.MODE_PRIVATE)
+
+    override var uuid: String? by UuidPreference(prefs, PREF_UUID, null)
+
+    companion object {
+        const val PREF = "pref"
+        const val PREF_UUID = "pref_uuid"
+    }
+}
+
+class UuidPreference(
+    private val pref: SharedPreferences,
+    private val name: String,
+    private val defaultValue: String?
+) : ReadWriteProperty<Any, String?> {
+
+
+    override fun getValue(thisRef: Any, property: KProperty<*>): String? {
+        return pref.getString(name, defaultValue)
+    }
+
+    override fun setValue(thisRef: Any, property: KProperty<*>, value: String?) {
+        pref.edit { putString(name, value) }
+    }
+}
\ No newline at end of file

리뷰: kotlin 에서는 String 의 경우 빈 문자열을 기본값으로 처리하면 사용하는 쪽에서 좀 더 편하게 사용 가능합니다. 
preference처럼 자주 사용하는 값을 nullable로 선언하면, safe call이 과도해지는 문제가 있습니다. 

물론 빈 문자열에 대한 처리도 확인 부탁드립니다.
@@ -0,0 +1,38 @@
+package kr.co.connect.boostcamp.livewhere.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+interface SharedPreference {
+    var uuid: String?
+}
+
+class SharedPreferenceStorage(context: Context) : SharedPreference {
+    private val prefs = context.applicationContext.getSharedPreferences(PREF, Context.MODE_PRIVATE)
+
+    override var uuid: String? by UuidPreference(prefs, PREF_UUID, null)
+
+    companion object {
+        const val PREF = "pref"
+        const val PREF_UUID = "pref_uuid"
+    }
+}
+
+class UuidPreference(
+    private val pref: SharedPreferences,
+    private val name: String,
+    private val defaultValue: String?
+) : ReadWriteProperty<Any, String?> {
+
+
+    override fun getValue(thisRef: Any, property: KProperty<*>): String? {
+        return pref.getString(name, defaultValue)
+    }
+
+    override fun setValue(thisRef: Any, property: KProperty<*>, value: String?) {
+        pref.edit { putString(name, value) }
+    }
+}
\ No newline at end of file

리뷰: 위와 같은 맥락이지만, default value가 non null 타입인것이 사용하는 쪽에서 더 보기 좋은 코드로 작성 가능합니다.
@@ -0,0 +1,25 @@
+package kr.co.connect.boostcamp.livewhere.data.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import kr.co.connect.boostcamp.livewhere.data.entity.BookmarkEntity
+
+@Dao
+interface BookmarkDAO {
+    @Query("SELECT * FROM bookmark")
+    fun getAll(): List<BookmarkEntity>
+
+    @Insert(onConflict = OnConflictStrategy.IGNORE)
+    fun insertBookmark(bookmark: BookmarkEntity): Long
+
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertBookmarkAll(bookmark: List<BookmarkEntity>): List<Long>
+
+    @Query("DELETE from Bookmark")
+    fun deleteAll(): Int
+
+    @Query("DELETE from Bookmark WHERE address IN (:address)")
+    fun deleteBookmark(address: String): Int
+}
\ No newline at end of file

리뷰: delete 연산에 대한 반환값은 꼭 Int 여야 하나요? 

쓰는 쪽에서는 boolean 이나 예외가 발생하는게 명확하기 때문에, 한번 확인 부탁드립니다. (Room 라이브러리 이슈라면 어쩔 수 없지만요 ㅠㅠ)
@@ -0,0 +1,14 @@
+package kr.co.connect.boostcamp.livewhere.data.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "bookmark")
+data class BookmarkEntity(
+    @PrimaryKey var address: String,
+    @ColumnInfo(name = "building_name") var building_name: String,
+    @ColumnInfo(name = "image_url") var img_url: String,
+    @ColumnInfo(name = "longitude") var longitude: String,
+    @ColumnInfo(name = "latitude") var latitude: String
+)
\ No newline at end of file

리뷰: * 보통의 convention으로 변수명으로 snake case (aaa_bb) 는 잘 사용하지 않습니다. img_url 보다는 imgUrl 등이 좋아보입니다. 
* 이 클래스의 필드들은 variable보다는, immutable한 값으로 작성해줘도 괜찮아보입니다. (db에서 있는 값을 가져오므로) 

entity 처럼 로직의 아래쪽에 있는 클래스에서 필드들이 필요이상으로 mutable(재할당 가능한) 하면, 나중에 값을 변하게 하는 로직이 있을 겨우 추적이 매우 어렵습니다. 추가적인 설명이 필요하다면 말씀주세요.
@@ -0,0 +1,14 @@
+package kr.co.connect.boostcamp.livewhere.data.entity
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "bookmark")
+data class BookmarkEntity(
+    @PrimaryKey var address: String,
+    @ColumnInfo(name = "building_name") var building_name: String,
+    @ColumnInfo(name = "image_url") var img_url: String,
+    @ColumnInfo(name = "longitude") var longitude: String,
+    @ColumnInfo(name = "latitude") var latitude: String
+)
\ No newline at end of file

리뷰: 다른 entity 클래스들도 그런 부분이 보이는데 한꺼번에 확인 부탁드립니다.
@@ -0,0 +1,50 @@
+package kr.co.connect.boostcamp.livewhere.di
+
+import io.reactivex.schedulers.Schedulers
+import kr.co.connect.boostcamp.livewhere.BuildConfig
+import kr.co.connect.boostcamp.livewhere.api.Api
+import kr.co.connect.boostcamp.livewhere.api.TmapApi
+import kr.co.connect.boostcamp.livewhere.util.TMAP_BASE_URL
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import org.koin.dsl.module.module
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+
+val apiModule = module {
+    single("api") {
+        Retrofit.Builder()
+            .client(
+                OkHttpClient.Builder()
+                    .connectTimeout(30, TimeUnit.SECONDS)
+                    .readTimeout(30, TimeUnit.SECONDS)
+                    .writeTimeout(30, TimeUnit.SECONDS)
+                    .addInterceptor(HttpLoggingInterceptor())//http log 확인
+                    .build()
+            )
+            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
+            .addConverterFactory(GsonConverterFactory.create())
+            .baseUrl(BuildConfig.BaseServerURL)
+            .build()
+            .create(Api::class.java)
+    }
+
+    single("tmapApi") {
+        Retrofit.Builder()
+            .client(
+                OkHttpClient.Builder()
+                    .connectTimeout(30, TimeUnit.SECONDS)
+                    .readTimeout(30, TimeUnit.SECONDS)
+                    .writeTimeout(30, TimeUnit.SECONDS)
+                    .addInterceptor(HttpLoggingInterceptor())
+                    .build()
+            )
+            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+            .addConverterFactory(GsonConverterFactory.create())
+            .baseUrl(TMAP_BASE_URL)
+            .build()
+            .create(TmapApi::class.java)
+    }
+}
\ No newline at end of file

리뷰: 이런 값들은 상수로 분리 가능할까요? 
나중에 다른 개발자가 볼 때 전부 같은 의미의 값으로 취급할 수도 있고, 이유 없이 변경할 수도 있습니다. 

* 왜 30초인지 간단한 설명이 추가되면 좋습니다.
@@ -0,0 +1,13 @@
+package kr.co.connect.boostcamp.livewhere.di
+
+
+val appModules = arrayListOf(
+    apiModule
+    , loginModule
+    , sharedModule
+    , databaseModule
+    , homeModule
+    , mapModule
+    , reverseGeoApiModule
+    , detailModule
+)

리뷰: 코드 포맷 한번 부탁드립니다. 일반적으로 
```
aaa,
bbb,
ccc,
```

이렇게 리스트를 작성합니다. 작은 부분이지만 위 코드를 잠깐보고 지나간다면 apiModule이 약간 달라보이는 단점이 있습니다. 
코딩 컨벤션 맥락에서 확인 부탁드립니다.
@@ -0,0 +1,27 @@
+package kr.co.connect.boostcamp.livewhere.di
+
+import kr.co.connect.boostcamp.livewhere.repository.BookmarkRepositoryImpl
+import kr.co.connect.boostcamp.livewhere.repository.BookmarkUserRepository
+import kr.co.connect.boostcamp.livewhere.repository.ReviewRepository
+import kr.co.connect.boostcamp.livewhere.ui.detail.DetailViewModel
+import org.koin.androidx.viewmodel.ext.koin.viewModel
+import org.koin.dsl.module.module
+
+
+val detailModule = module {
+
+    factory("reviewRepository") { ReviewRepository() }
+    factory("bookmarkRemoteRepository") { BookmarkUserRepository() }
+    factory("bookmarkLocalRepository") { BookmarkRepositoryImpl(get("bookmarkDAO")) }
+
+    viewModel {
+        DetailViewModel(
+            get("reviewRepository"),
+            get("bookmarkRemoteRepository"),
+            get("bookmarkLocalRepository"),
+            get()
+        )
+    }
+}
+
+

리뷰: koin의 경우 get() 만 작성해도 맞는 타입을 넣어줄 텐데요, 
위처럼 이름을 넣은 값들은 추후 사용하는 인스턴스가 바뀔 수 있기 때문인가요? 

추가로 그렇다고 해도, 다른 인스턴스가 추가 될 때 변경해주어도 될거같습니다.
@@ -0,0 +1,16 @@
+package kr.co.connect.boostcamp.livewhere.di
+
+import kr.co.connect.boostcamp.livewhere.repository.AutoCompleteRepositoryImpl
+import kr.co.connect.boostcamp.livewhere.repository.BookmarkRepositoryImpl
+import kr.co.connect.boostcamp.livewhere.repository.RecentSearchRepositoryImpl
+import kr.co.connect.boostcamp.livewhere.ui.main.HomeViewModel
+import org.koin.androidx.viewmodel.ext.koin.viewModel
+import org.koin.dsl.module.module
+
+val homeModule = module {
+    factory("bookmarkRepository") { BookmarkRepositoryImpl(get("bookmarkDAO")) }
+    factory("recentSearchRepository") { RecentSearchRepositoryImpl(get("recentSearchDAO")) }
+    factory("autoCompleteRepository") { AutoCompleteRepositoryImpl(get("tmapApi")) }
+
+    viewModel { HomeViewModel(get("bookmarkRepository"), get("recentSearchRepository"), get("autoCompleteRepository")) }
+}
\ No newline at end of file

리뷰: 위 리뷰와 같은 맥락으로 확인  부탁드립니다.
@@ -0,0 +1,29 @@
+import { NUMBERS } from "./numbers.js";
+import ERROR from "./error.js";
+import { PROMPT } from "./prompt.js";
+
+const { zero, presentAmount } = NUMBERS;
+const { prefix } = ERROR;
+const {month, preview, champagne, piece, won, none} = PROMPT;
+
+const messageFormat = {
+  errorMessage: (message) => `${prefix} ${message}`,
+
+  preview: (date) => `${month}${date}${preview}`,
+
+  menu: (menuNames, quantities) => `${menuNames} ${quantities}${piece}`,
+
+  preDiscount: (totalprice) => `${totalprice.toLocaleString()}${won}`,
+
+  free: (totalprice) => (totalprice < presentAmount ? `${none}` : `${champagne}`),
+
+  benefit: (item) => item !== zero ? `${item.type}${item.amount.toLocaleString()}${won}` : `${none}`,
+
+  totalBenefit: (totalDiscountPrice) => `${totalDiscountPrice.toLocaleString()}${won}`,
+
+  discountedAmount: (total) => `${total.toLocaleString()}${won}`,
+
+  badge: (selectedBadge) => (selectedBadge !== undefined ? selectedBadge.badge : `${none}`),
+};
+
+export default messageFormat;

리뷰: 출력 메세지 형식을 따로 지정해 주는 방법도 있군요 좋은 방법 배우고 갑니다