-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/#74 사용자 등록 기능 #95
The head ref may contain hidden characters: "Feature/#74-\uC0AC\uC6A9\uC790_\uB4F1\uB85D_\uAE30\uB2A5"
Changes from 71 commits
1ae318e
3d49927
255b058
35d8708
c6a5e92
c0cabfa
439e459
d006ba8
46e32a1
744ec05
cca6236
c481a4d
ec9959b
64a0714
b6dafc3
5300669
653a288
8b6528e
6bf0f26
668f6d9
9e553a2
2ef4e61
3b392ab
701365b
48a0985
4795f19
cbcac8f
e5953af
9552a03
ef37ffb
de30bbd
700bd28
10cbefa
b6ee58e
39fd809
264b9d8
8e1bd46
f371e97
a30f126
76f6c4e
4bc1267
d00395e
44d64b0
c51345e
14e912b
ebb4622
fe4b74c
3dccbad
d3928ff
ff99068
226ce9e
7a78248
7f4a349
4a688d0
1db80da
c6c7ec4
75c7455
6562aa5
4aa24c0
a5276c9
ed2ee3e
ea38f20
0bbddf9
3e5fee1
75ad06c
d51dd02
5572ed1
62a8a22
7035d1a
01bb9f0
a8d32e6
4c2f926
697be4c
c2057ee
15487d6
ca8c42d
32f983a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: Kerdy Backend Dev Server CI/CD | ||
|
||
on: | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build: | ||
runs-on: self-hosted | ||
steps: | ||
- name: deploy | ||
run: ~/deploy.sh |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
name: Kerdy Backend Build Test | ||
|
||
on: | ||
pull_request: | ||
types: [ opened, synchronize, reopened ] | ||
branches: | ||
- backend-main | ||
|
||
jobs: | ||
build: | ||
name: Build with Gradle | ||
runs-on: ubuntu-latest | ||
services: | ||
mysql: | ||
image: mysql:8.0.28 | ||
env: | ||
MYSQL_USER: user | ||
MYSQL_PASSWORD: password | ||
MYSQL_ROOT_PASSWORD: 1234 | ||
MYSQL_DATABASE: kerdy | ||
ports: | ||
- 13306:3306 | ||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
with: | ||
ref: backend-main | ||
|
||
- name: Set up JDK 11 | ||
uses: actions/setup-java@v2 | ||
with: | ||
java-version: 11 | ||
distribution: 'temurin' | ||
|
||
- name: cd backend directory | ||
run: cd backend/emm-sale | ||
|
||
- name: Give permission for Gradle | ||
run: chmod +x gradlew | ||
|
||
- name: Build with Gradle | ||
run: gradlew clean build |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#Wed Jul 19 21:44:15 KST 2023 | ||
gradle.version=8.0.2 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.emmsale.data.activity | ||
|
||
import com.emmsale.data.activity.dto.ActivitiesApiModel | ||
import com.emmsale.data.activity.dto.ActivityApiModel | ||
|
||
data class Activities( | ||
val category: String, | ||
val activities: List<Activity> | ||
) { | ||
companion object { | ||
fun from(activitiesApiModels: List<ActivitiesApiModel>): List<Activities> = | ||
activitiesApiModels.map(::from) | ||
|
||
private fun from(activitiesApiModel: ActivitiesApiModel): Activities = Activities( | ||
category = activitiesApiModel.category, | ||
activities = Activity.from(activitiesApiModel.activities) | ||
) | ||
} | ||
} | ||
|
||
data class Activity( | ||
val id: Int, | ||
val name: String, | ||
) { | ||
companion object { | ||
fun from(activitiesApiModel: List<ActivityApiModel>): List<Activity> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위의 ApiModel 및 정적 팩토리 메서드 리뷰와 마찬가지입니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 동일하게 반영 완료하였습니다 : ) |
||
activitiesApiModel.map(::from) | ||
|
||
private fun from(activityApiModel: ActivityApiModel): Activity = | ||
Activity( | ||
id = activityApiModel.id, | ||
name = activityApiModel.name | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.emmsale.data.activity | ||
|
||
import com.emmsale.data.common.ApiResult | ||
|
||
interface ActivityRepository { | ||
suspend fun getActivities(): ApiResult<List<Activities>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.emmsale.data.activity | ||
|
||
import com.emmsale.data.common.ApiError | ||
import com.emmsale.data.common.ApiException | ||
import com.emmsale.data.common.ApiResult | ||
import com.emmsale.data.common.ApiSuccess | ||
import com.emmsale.data.common.handleApi | ||
import kotlinx.coroutines.CoroutineDispatcher | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.withContext | ||
|
||
class ActivityRepositoryImpl( | ||
private val dispatcher: CoroutineDispatcher = Dispatchers.IO, | ||
private val activityService: ActivityService, | ||
) : ActivityRepository { | ||
|
||
override suspend fun getActivities(): ApiResult<List<Activities>> = withContext(dispatcher) { | ||
when (val activitiesApiResult = handleApi { activityService.getActivities() }) { | ||
is ApiSuccess -> ApiSuccess(Activities.from(activitiesApiResult.data)) | ||
is ApiError -> ApiError(activitiesApiResult.code, activitiesApiResult.message) | ||
is ApiException -> ApiException(activitiesApiResult.e) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.emmsale.data.activity | ||
|
||
import com.emmsale.data.activity.dto.ActivitiesApiModel | ||
import retrofit2.Response | ||
import retrofit2.http.GET | ||
|
||
interface ActivityService { | ||
@GET("/careers") | ||
suspend fun getActivities(): Response<List<ActivitiesApiModel>> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.emmsale.data.activity.dto | ||
|
||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
|
||
@Serializable | ||
data class ActivitiesApiModel( | ||
@SerialName("activityName") | ||
val category: String = "-", | ||
@SerialName("activityResponses") | ||
val activities: List<ActivityApiModel> = emptyList() | ||
) | ||
|
||
@Serializable | ||
data class ActivityApiModel( | ||
@SerialName("id") | ||
val id: Int, | ||
@SerialName("name") | ||
val name: String, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.emmsale.data.common | ||
|
||
import retrofit2.HttpException | ||
import retrofit2.Response | ||
|
||
sealed interface ApiResult<T : Any> | ||
class ApiSuccess<T : Any>(val data: T) : ApiResult<T> | ||
class ApiError<T : Any>(val code: Int, val message: String?) : ApiResult<T> | ||
class ApiException<T : Any>(val e: Throwable) : ApiResult<T> | ||
|
||
suspend fun <T : Any> handleApi( | ||
execute: suspend () -> Response<T>, | ||
): ApiResult<T> { | ||
return try { | ||
val response = execute() | ||
val body = response.body() | ||
|
||
when { | ||
response.isSuccessful && body != null -> ApiSuccess(body) | ||
else -> ApiError(code = response.code(), message = response.message()) | ||
} | ||
} catch (e: HttpException) { | ||
ApiError(code = e.code(), message = e.message()) | ||
} catch (e: Throwable) { | ||
ApiException(e) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.emmsale.data.common | ||
|
||
import com.emmsale.presentation.KerdyApplication | ||
import okhttp3.Interceptor | ||
import okhttp3.Response | ||
|
||
class AuthInterceptor : Interceptor { | ||
override fun intercept(chain: Interceptor.Chain): Response { | ||
val token = KerdyApplication.repositoryContainer.tokenRepository.getToken() | ||
?: return chain.proceed(chain.request()) | ||
|
||
val newRequest = chain.request().newBuilder() | ||
.addHeader(ACCESS_TOKEN_HEADER, token.accessToken) | ||
.build() | ||
return chain.proceed(newRequest) | ||
} | ||
|
||
companion object { | ||
private const val ACCESS_TOKEN_HEADER = "accessToken" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.emmsale.data.common | ||
|
||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory | ||
import kotlinx.serialization.json.Json | ||
import okhttp3.MediaType.Companion.toMediaType | ||
import okhttp3.OkHttpClient | ||
import retrofit2.Retrofit | ||
import java.util.concurrent.TimeUnit | ||
|
||
class ServiceFactory { | ||
private val jsonMediaType = "application/json".toMediaType() | ||
private val json = Json { | ||
coerceInputValues = true | ||
encodeDefaults = true | ||
isLenient = true | ||
} | ||
private val jsonConverterFactory = json.asConverterFactory(jsonMediaType) | ||
|
||
private val okhttpClient = OkHttpClient.Builder() | ||
.addInterceptor(AuthInterceptor()) | ||
.connectTimeout(120, TimeUnit.SECONDS) | ||
.readTimeout(120, TimeUnit.SECONDS) | ||
.writeTimeout(120, TimeUnit.SECONDS) | ||
.build() | ||
|
||
private val retrofit: Retrofit = Retrofit.Builder() | ||
.baseUrl(BASE_URL) | ||
.addConverterFactory(jsonConverterFactory) | ||
.client(okhttpClient) | ||
.build() | ||
|
||
fun <T> create(service: Class<T>): T = retrofit.create(service) | ||
|
||
companion object { | ||
private const val BASE_URL = "http://13.125.212.56/" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
데이터 모델이 ApiModel에 의존해서는 안된다고 생각합니다. apiModel.toExternal() 혹은 apiModel.toDomain()이런 식으로 바꾸는 게 좋을 것 같습니다. 만약 로컬 데이터 저장소에서 로그인 데이터를 불러온다면 Login.from(entity: LoginEntity) 이렇게 새로 만들어줘야 하기 때문입니다. 즉, 데이터 소스 모델에 의해 데이터 모델이 변경되면 안된다고 생각합니다.
정적 팩토리 메서드는 해당 클래스 혹은 그 하위 타입을 반환하는 것이 좋다고 생각합니다! 리스트처럼 다른 타입으로 변환하는 일은 다른 Activities 외부에서 하는 것이 좋지 않을까요? 이처럼 다른 타입으로 변환하는 일을 Activities에 포함한다면 Activities 클래스가 하는 일이 많아질 거라고 생각합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정리하자면,
이후에 같은 데이터 모델에 대해 여러 타입으로 변환될 일이 생길 수 있기 때문에,
변환을 모두 데이터 모델에 맡기면 한 클래스가 하는 일이 많아질 수 있고, 기존 클래스의 변경을 최소화하자는 말씀으로 이해하고 리뷰 반영하겠습니다.