본문 바로가기
작디 작은 나만의 라이브러리/BridgeApi

[신입 개발자의 '작디 작고 작디 작고 자그만' 첫 라이브러리 제작기] Fin. Bridge-Api의 실제 적용과 개선 효과

by 시니성 2025. 1. 5.

안녕하세요!
드디어 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);

주요 개선점:

  1. 코드량 대폭 감소 (수백 줄 → 수십 줄)
  2. 함수 이름을 일일이 정의할 필요 없음
  3. REST API 스타일의 직관적이고 구조화 된 인터페이스
  4. 타입 안전성 확보

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())
    )

주요 개선점:

  1. 코드의 가독성 대폭 향상
  2. 비즈니스 로직과 인프라 로직의 명확한 분리
  3. 자동화된 직렬화/역직렬화
  4. 통일된 에러 처리
  5. 명확한 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를 실제 프로젝트에 적용하면서 얻은 주요 교훈들은 다음과 같습니다:

  1. 구조적 설계의 중요성
    • 초기 설계에 투자한 시간이 후반부에 큰 효과를 발휘
    • 확장성과 유지보수성이 크게 향상
  2. 관심사의 분리
    • 비즈니스 로직과 인프라 로직의 분리가 코드 품질을 크게 개선
    • 테스트와 디버깅이 훨씬 용이해짐
  3. 표준화의 가치
    • REST API 스타일 채택으로 학습 곡선 감소
    • 코드의 일관성이 크게 향상

이런 개선 효과들을 보면서, 작은 라이브러리 하나가 전체 프로젝트의 품질에 얼마나 큰 영향을 미칠 수 있는지 실감할 수 있었습니다.

앞으로도 이러한 경험을 바탕으로 더 나은 개발자가 되도록 노력하겠습니다. 그동안 긴 시리즈 글 읽어주셔서 감사합니다!

728x90