Published on

3월 2주 있었던 일 정리

Authors
  • avatar
    Name
    이건창
    Twitter

정렬

데이터를 정렬 할 필요가 생겼다. 백오피스에서 정렬 기능이 커스텀으로 구현되어 있어서 pageable 을 사용하도록 수정할지 고민했다. 수정 이유는 data 라이브러리에서 pageable 이라는 친구를 사용하면 api 부터 sql 까지 일사천리로 진행 가능하기 때문이다.

동작을 간단하게 이야기 해보면 pageable 은 sort 의 값을 읽어 Sort 내부에 저장한다.

GET /api/v1/boards?sort=createdAt,desc

그리고 데이터 계층에 던져주면 알아서 잘 매핑되도록 구성되어 있다.

fun findById(pageable: Pageable): Page<Board>

아쉬운 점은 sort 에 저장된 파라미터가 올바른지 검증하는 로직이 없고 sql 예외를 통해 확인해야 해서 아쉬웠다. 그리고 일부 데이터 정렬만 제한하기 위해서도 추가 설정이 필요했다.

즉, 정렬 로직으로 인해 QueryDsl을 활용하고 있기 때문에 데이터 계층에서 25% 정도의 코드를 줄일 수 있어서 복잡도도 줄일 수 있다.

고민 시작

API가 변경 가능성

가장 위험한건 API가 변경된다는 점이다. 다음처럼 변경해야 해서 프론트에서도 변경이 필요했다. 버저닝이 없어서 더더욱 고민이 들었다.

AS-IS

GET /api/boards?column=CREATE_AT&order=desc

TO-BE

GET /api/boards?sort=createdAt,desc

우선 두 개의 API를 지원할 수 있도록 수정한 다음 UI 계층에서 Pageable을 직접 구현해 유연하게 동작할 필요가 있었다. 그럼 의도한 코드 복잡도를 줄일 수 있다.

images

검증 정책이 없는 점

조회가 핵심 비즈니스는 아니라 검증 정책이 추가되지 않은 상태이다. 인풋 아웃풋이 동일하지만 내부 동작은 변경되는 일이라 테스트 코드가 필요하다.

전체 코드 라인이 줄어들지 않아 목표와는 부합하지 않지만 복잡도를 줄이고 서비스 안정성을 높일 수 있어보인다.

업무 우선순위가 낮다.

당장 발견한 개선 포인트이지만 진행하는 업무와 병행하기 어렵다. 그리고 잘 동작하는 코드에 리소스를 투입하는 일이 비효율적이라는 생각도 들었다.

여유 시간에는 이미 모니터링 작업을 계획하고 있기 때문에 아마 4월 부터 진행할 수 있어 보인다.

결론

수정한 코드에 검증 로직을 추가하고 소스 코드를 20 라인 가량 줄일 수 있었다. 그러나 아쉽게도 전체 프로젝트를 개선할 여력은 없었다.

이번 기회에 개발 방향성을 제시하면서 앞으로의 팀 개발 방향을 고민해보는 시간을 가졌고 어떻게 관리하면 좋을지 이야기를 나눌 수 있었다. 코드를 관리할 수 있는 여유와 능력이 필요함을 깨달았고 팀 스터디를 통해 정진해보려 한다.

API 요청에 대한 응답 처리

특정 식별자의 상태를 확인하기 위해 외부 API를 호출해야 하는 상황이 생겼다. 제공자는 식별자를 가지는 도메인 전체 정보를 노출하고 있었다.

image

소비자 계약은 제공자가 제공하는 값은 비즈니스 이외의 값을 의미(incomplete)하며 비즈니스외적인 모든 값을 제공(open)한다.

이런 경우 소비자 계약을 의미한다. 즉, 제공자는 전체 정보를 제공하고 소비자는 필요한 정보만 가져가는 방식이다. 소비자 계약의 단점은 제공자가 전체 정보를 노출하기 떄문에 서비스 간 의존도가 높아질 수 있다는 점이다. 해당 도메인을 조회해야 하는 경우 해당 API를 공통적으로 사용하기 때문에 제공자 API 변경에 부담이 생긴다.

그래서 응답 값으로 접근할 수 있는 데이터를 제한해 공통 API를 제거했다.

@GetMapping("/정보")
fun getMembershipPrice(): ApiResult<MembershipPriceResponse>

내가 알고 싶은건 지불했는지 여부이고 isPaid() 메서드 외에는 접근할 수 없게 막았다.

data class MembershipPriceResponse(
    private val price: Int
) {
    @JsonCreator
    constructor(
        @JsonProperty("information_data")
        information: Map<Any, Any>
    ) : this(information[price] as Int)

    fun isPaid(): Boolean {
        return price > 0
    }
}