안녕하세요!
드디어 Bridge-Api 시리즈의 마지막 글을 작성하게 되었습니다.
이번 글에서는 Bridge-Api를 실제 프로젝트에 적용하면서 경험한 개선 효과들을 공유해보려고 합니다.
1. 레거시 코드와의 비교
1.1 클라이언트 측면
레거시 코드:
const FUNCTION_NAME = {
//... 수십 개의 함수 이름 나열
processCardePayment: "processCardPayment",
// ... 수십 개의 함수 이름 나열
};
const processCardPayment = async () => {
return callAndroidFunction(interfaceName, FUNCTION_NAME.processCardePayment);
};
// ... 모든 함수에 대해 반복적인 래퍼 함수 정의
Bridge-Api 적용 후:
const bridgeClient = BridgeApi.create({
headers: {"Authorization": "Bearer token"},
timeout: 5000,
});
// REST API 스타일로 호출
const response = await bridgeClient.post<ApiCommonResponse<OrderResponse>>("/api/v1/orders", order);
주요 개선점:
- 코드량 대폭 감소 (수백 줄 → 수십 줄)
- 함수 이름을 일일이 정의할 필요 없음
- REST API 스타일의 직관적이고 구조화 된 인터페이스
- 타입 안전성 확보
1.2 백엔드 측면
레거시 코드:
@JavascriptInterface
fun paymentCardRequest(cardRequest: String): String = try {
LogHelper.info("paymentCardRequest : $cardRequest")
val jsonData: CreditPaymentRequestDto = SerializeUtil.Json.deserialize(cardRequest)
var retryCount = 0
Log.e(TAG, "[encodeRequestCardData] $jsonData")
// 비즈니스 로직과 직렬화/역직렬화가 뒤섞여 있음
val paymentFuture: Future<CreditPaymentResponseDto> = Payment.instance.runPayment(jsonData)
// ... 복잡한 에러 처리 로직
} catch (e: Exception) {
LogHelper.error("paymentCardRequest error:$e")
ObjectBoxUtil.responseError(ApiResponseCode.from(-1), "unknown error")
}
Bridge-Api 적용 후:
@Post("/credits")
suspend fun processCardPayment(
@JsonBody creditPaymentCommand: CreditPaymentCommandReqDto
): ApiCommonResponse<PaymentHeader> =
ApiCommonResponse.success(
paymentService.processCardPayment(creditPaymentCommand.toDomain())
)
주요 개선점:
- 코드의 가독성 대폭 향상
- 비즈니스 로직과 인프라 로직의 명확한 분리
- 자동화된 직렬화/역직렬화
- 통일된 에러 처리
- 명확한 API 엔드포인트 구조
2. 아키텍처 개선
2.1 관심사의 분리
Before:
- 직렬화/역직렬화, 비즈니스 로직, 에러 처리가 한 함수에 뒤섞여 있음
- 함수별로 중복되는 코드가 많음
After:
// 컨트롤러: API 엔드포인트 정의
class PaymentController(private val paymentService: PaymentUseCase) {
@Post("/credits")
suspend fun processPayment(@JsonBody command: PaymentCommandDto) =
ApiCommonResponse.success(paymentService.process(command))
}
// 서비스: 비즈니스 로직
class PaymentService {
suspend fun process(command: PaymentCommand) = // 순수 비즈니스 로직
}
// 인터셉터: 공통 관심사 처리
class LoggingInterceptor : ServiceDecorator() {
override suspend fun serve(ctx: RequestContext) = // 로깅 처리
}
2.2 다중 플랫폼 지원 강화
Bridge-Api의 가장 큰 장점 중 하나는 여러 플랫폼에서 동일한 코드를 재사용할 수 있다는 점입니다.
웹뷰 환경:
요청 메소드와 URI는 그대로 Client만 BridgeApiClient를 Axios등 웹 JS 클라이언트로 교체하여 그대로 사용 가능.
서버 환경 (컨트롤러만 웹 프레임워크의 컨트롤러로 교체. 동일한 비즈니스 로직 재사용):
// Spring 컨트롤러
@RestController
class UserController(private val userService: UserService) {
@GetMapping("/api/v1/users/{id}")
fun getUser(@PathVariable id: Long) = userService.getUser(id)
}
// Ktor 라우팅
routing {
get("/api/v1/users/{id}") {
call.respond(userService.getUser(id))
}
}
3. 코드 품질 개선
3.1 코드량 감소
- 레거시 코드: ~5,000 lines
- Bridge-Api 적용 후: ~2,000 lines
- 감소율: 약 60%
3.2 유지보수성 향상
- 새로운 API 추가 시간 감소
- 버그 수정 시간 감소
- 테스트 용이성 대폭 향상
5. 마치며
Bridge-Api를 실제 프로젝트에 적용하면서 얻은 주요 교훈들은 다음과 같습니다:
- 구조적 설계의 중요성
- 초기 설계에 투자한 시간이 후반부에 큰 효과를 발휘
- 확장성과 유지보수성이 크게 향상
- 관심사의 분리
- 비즈니스 로직과 인프라 로직의 분리가 코드 품질을 크게 개선
- 테스트와 디버깅이 훨씬 용이해짐
- 표준화의 가치
- REST API 스타일 채택으로 학습 곡선 감소
- 코드의 일관성이 크게 향상
이런 개선 효과들을 보면서, 작은 라이브러리 하나가 전체 프로젝트의 품질에 얼마나 큰 영향을 미칠 수 있는지 실감할 수 있었습니다.
앞으로도 이러한 경험을 바탕으로 더 나은 개발자가 되도록 노력하겠습니다. 그동안 긴 시리즈 글 읽어주셔서 감사합니다!
- 라이브러리 레포지토리: https://github.com/shiniseong/bridge-api
728x90