안녕하세요! 결과물을 보고나니 저도 가시오갈피의 멤버가 되고 싶군요!
멋진 데뷔 축하드립니다.
과제 요구 사항에만 그치지 않고 몰입해서 코딩했다는게 결과물에 보입니다.
이제 앞으로는 example package 명은 유의미한 이름으로 변경해주세요.
data/di/network/presentation/useCase(package 명은 소문자) 기능에 맞게 package도 잘 나누어서 코드가 많은데도 가독성이 좋네요.
조금만 더 정리하면 완벽해지겠어요!
model:
data class들 잘 구성했어요. 기능별로 package 분리를 한 김에 사용처 별로 좀 더 분리되어도 보기 좋을 것 같아요.
repository:
project가 커지면서 이제 presentation / domain(optional)/data 영역으로 구분하며 domain 영역을 도입하게 되실거예요. 그럼 repository interface는 domain 영역으로 이동하시면 됩니다.
repository 도 기능별로 조금더 package 분리가 되면 좋겠어요. 지금은 분리를 잘한 기능과 안한 기능이 혼재되어 있네요.
이제부터는 안정성과 성능을 높여보아요!
di : DI 시작을 축하합니다!
presentation : 여러 명이 개발한 티가나요! 좀더 깔끔하게 가독성을 높여보아요!
전반적으로 잘 해냈고. 처음 1주차때가 생각이 나며 눈물이 핑 도네요!
이 경험을 바탕으로 멋진 실전 프로젝트 완성해 보아요!
지금은 favorite data가 많지 않지만 추후 대량의 data가 쌓이다보면 최초 진입시 load를 하게되면 진입 성능이 떨어지게 됩니다. 앱의 최초 진입성능은 사용자를 끌어들이기 위해 무척 중요한 요소인데요. 지연 초기화 (by lazy) 등을 활용해 실제 사용시점에 load 되도록 개선해 보세요.
이제부턴 안정성과 성능을 높여야할 때죠. Json은 외부에서 받는 data인 경우가 많기 때문에 항상 exception이 발생할 가능성을 내재하고 있어요. runCaching, try -catch 등의 예외 처리 사용을 생활화 하세요. 에러시 꼭 Log.e 도 찍어두어야 겠죠? 코드가 더러워진다 싶으면 json이나 sharePreference 관련 확장함수를 미리 만들어 두고 사용하는 것도 좋은 방법이예요.
sharedPreference의 get put 정도는 괜찮지만 commit, json으로 파싱한 대량의 data load 등을 할 때는 main thread에서 사용을 지양해야해요. 추후 대량의 data를 다루게 될 경우에는 withContext(Dispachers.IO)를 참고해보세요!
FavoriteRepositoryImpl와 FavoriteTravelSpotRepositoryImpl 겹치는 기능이 많아보여요! 재사용 할 수 있는 부분도 있겠네요!
custom 'Date' 포멧터를 사용하면 날짜를 String이 아닌 Date로 바로 받아 올 수 있어요.
매번 변환하는 루틴의 호출이 매우 잦다면 미리 Date로 변환해서 받는게 효율적이예요.
val gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") // 서버에서 사용하는 날짜 포맷에 맞게 설정
.create()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.~~")
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
data class VideoSnippet(
@SerializedName("publishedAt")
val publishedAt: Date?
)
이제는 통신의 안정성도 챙길 때죠? api 서버에 붙다보면 여러가지 예상치 못한 error가 발생하게 될 수 있어요. 그에 따라 api 문서에서는 에러 코드에 대한 설명도 추가되어 있답니다. (400, 404, 200 등등)
각 에러 상황에 맞게 에러 메세지나 추후 동작이 연결되도록 좀 더 구체적으로 에러 핸들링을 하면 안정성을 높일 수 있답니다.
https://developers.google.com/youtube/v3/docs/errors
try {
val response = apiService.getData()
if (response.isSuccessful) {
// 성공 처리
} else {
when (response.code()) {
400 -> handleError("Bad Request: 잘못된 요청입니다.")
401 -> handleError("Unauthorized: 인증 오류입니다.")
403 -> handleError("Forbidden: 접근 권한이 없습니다.")
404 -> handleError("Not Found: 요청한 리소스를 찾을 수 없습니다.")
500 -> handleError("Internal Server Error: 서버 오류가 발생했습니다.")
503 -> handleError("Service Unavailable: 서버가 일시적으로 사용 불가능합니다.")
else -> handleError("Unknown Error: ${response.message()}")
}
}
} catch (e: Exception) {
handleError("An unexpected error occurred: ${e.message}")
}
총평
안녕하세요! 결과물을 보고나니 저도 가시오갈피의 멤버가 되고 싶군요! 멋진 데뷔 축하드립니다. 과제 요구 사항에만 그치지 않고 몰입해서 코딩했다는게 결과물에 보입니다. 이제 앞으로는 example package 명은 유의미한 이름으로 변경해주세요. data/di/network/presentation/useCase(package 명은 소문자) 기능에 맞게 package도 잘 나누어서 코드가 많은데도 가독성이 좋네요. 조금만 더 정리하면 완벽해지겠어요!
model: data class들 잘 구성했어요. 기능별로 package 분리를 한 김에 사용처 별로 좀 더 분리되어도 보기 좋을 것 같아요.
repository: project가 커지면서 이제 presentation / domain(optional)/data 영역으로 구분하며 domain 영역을 도입하게 되실거예요. 그럼 repository interface는 domain 영역으로 이동하시면 됩니다. repository 도 기능별로 조금더 package 분리가 되면 좋겠어요. 지금은 분리를 잘한 기능과 안한 기능이 혼재되어 있네요. 이제부터는 안정성과 성능을 높여보아요!
di : DI 시작을 축하합니다!
presentation : 여러 명이 개발한 티가나요! 좀더 깔끔하게 가독성을 높여보아요! 전반적으로 잘 해냈고. 처음 1주차때가 생각이 나며 눈물이 핑 도네요! 이 경험을 바탕으로 멋진 실전 프로젝트 완성해 보아요!
코드 피드백
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/repository/favoritetravelspot/FavoriteTravelSpotRepositoryImpl.kt#L13
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/repository/favoritetravelspot/FavoriteTravelSpotRepositoryImpl.kt#L41 https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/repository/FavoriteRepositoryImpl.kt#L44-L48
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/repository/favoritetravelspot/FavoriteTravelSpotRepositoryImpl.kt#L46
FavoriteRepositoryImpl와 FavoriteTravelSpotRepositoryImpl 겹치는 기능이 많아보여요! 재사용 할 수 있는 부분도 있겠네요!
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/repository/FavoriteTravelSpotDeserializer.kt#L17-L22
Repository가 많다보니 각 Repository가 어떤 부분을 담당하는지 확실치 않네요. 이름을 좀 더 기능과 관련해서 명확하게 지어보세요.!
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/source/local/room/VideoDao.kt#L10-L13
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/data/source/remote/YoutubeApi.kt#L28-L48
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/network/AuthorizationInterceptor.kt#L5-L12
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/network/RetrofitClient.kt#L12
여러 방식의 정리가 가능할 거예요! 막바지에 급하게 넣은 기능은 코드에서 티가 난답니다 : D
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/extensions/TextViewExtensions.kt#L18
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/home/adapters/Best10ListAdapter.kt#L15-L17 매 adapter마다 유사패턴의 onclick interface를 선언하는 대신 generic을 활용할 수 있어요!
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/extensions/TextViewExtensions.kt#L13-L20
custom 'Date' 포멧터를 사용하면 날짜를 String이 아닌 Date로 바로 받아 올 수 있어요. 매번 변환하는 루틴의 호출이 매우 잦다면 미리 Date로 변환해서 받는게 효율적이예요.
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/home/HomeFragment.kt#L84-L87
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/home/HomeViewModel.kt#L45-L78
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/presentation/videodetail/VideoDetailFragment.kt#L49-L52 성공했나요? : D
https://github.com/GGo-ging/riding_balloon/blob/a355b377ce165d5e98acee9bae2b7d71f87051f6/app/src/main/java/com/example/riding_balloon/useCases/ChannelUseCase.kt#L10
이제는 통신의 안정성도 챙길 때죠? api 서버에 붙다보면 여러가지 예상치 못한 error가 발생하게 될 수 있어요. 그에 따라 api 문서에서는 에러 코드에 대한 설명도 추가되어 있답니다. (400, 404, 200 등등) 각 에러 상황에 맞게 에러 메세지나 추후 동작이 연결되도록 좀 더 구체적으로 에러 핸들링을 하면 안정성을 높일 수 있답니다. https://developers.google.com/youtube/v3/docs/errors