Skip to Content
Software Architecture카카오페이 v2 재구축과 스키마 기반 검증
🏗️ Software Architecture2023년 9월 19일

카카오페이 v2 재구축과 스키마 기반 검증

#e-commerce#payment-expansion#software-architecture#kakaopay
Payment Expansion · Series 3 · Rebuild & Quality6 / 14Software Architecture
기존 WebFlux·DynamoDB 환경을 유지하며 API 명세·모델·검증·로그의 공통 규칙을 다시 정리한 과정.

2023년 하반기에 카카오페이 연동 서비스를 새로 구성했습니다. 여기서 v2는 카카오페이의 공식 API 버전이 아니라 내부 연동 서비스를 구분하던 이름입니다.

기존 v1도 WebFlux 기반이었고 DynamoDB를 사용했습니다. 새 서비스를 만든 이유는 비동기로 바꾸거나 배포 단위를 처음 나누기 위해서가 아니었습니다. 저장결제와 환불 등 기능이 늘어나는 동안 쌓인 예외와 중복을 정리하고, 다른 결제 서비스에도 적용할 규칙을 맞추려는 작업이었습니다.

서비스별로 달라진 처리 규칙

벤더별 차이는 필요했지만 요청 모델과 입력 검증, 로그·에러 처리까지 서비스마다 달라지면 같은 문제를 반복해서 확인해야 했습니다. 기존 코드에 기능을 계속 추가하는 방식으로는 이런 차이를 정리하기 어려웠습니다.

그래서 공통 라이브러리와 v2 계열의 규칙을 함께 다시 구성하는 방향을 제안했습니다. 카카오페이부터 적용하되 이후 다른 결제 앱에서도 사용할 수 있도록 모델과 검증, 운영 기록의 기준을 맞추려고 했습니다.

API 명세와 구현의 연결

초기에는 프로젝트 구조와 API 명세를 정리하고, 이어서 컨트롤러와 서비스·벤더 통신·저장 처리를 구현했습니다. 기존 APIB 문서를 OpenAPI로 옮기고 Redocly로 빌드하는 작업도 함께 진행했습니다.

명세를 문서 출력에만 쓰려던 것은 아니었습니다. JSON Schema를 모델과 런타임 검증에서도 사용하고, OpenAPI에서 같은 파일을 참조하도록 연결했습니다. 이 설계는 Confluence에 정리해 두었고, 이후 다른 팀에서도 문의해 비슷한 기준을 논의했습니다.

OpenAPI 3.1과 JSON Schema

OpenAPI 3.1은 JSON Schema 2020-12와 호환되는 스키마 표현을 사용합니다. oneOf 자체는 3.0에도 있었지만, 3.1에서는 const와 조건부 스키마, null 타입 등을 같은 기준으로 다룰 수 있었습니다.

아래는 등록 결과의 상태에 따라 필수값을 다르게 검사하는 설명용 스키마입니다. 실제 벤더의 필드나 응답을 그대로 옮긴 예시는 아닙니다.

json
schema/registration-result.json
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "additionalProperties": false, "required": ["status"], "properties": { "status": { "enum": ["completed", "pending", "cancelled"] }, "registrationId": { "type": "string", "minLength": 1 }, "reason": { "type": ["string", "null"] } }, "if": { "properties": { "status": { "const": "completed" } }, "required": ["status"] }, "then": { "required": ["registrationId"] } }

completed이면 비어 있지 않은 registrationId가 필요합니다. 다른 상태에서는 이 필드가 필수는 아닙니다. 필드가 존재한다는 검사와 null·빈 문자열을 허용하는 검사는 구분해야 합니다.

스키마 조합도 Java 클래스 상속과 같지는 않습니다. allOf로 합친다고 앞 스키마의 additionalProperties: false가 다른 스키마의 필드를 자동으로 허용하지 않습니다. 사용할 검증기와 코드 생성기가 3.1의 어떤 기능을 지원하는지도 확인해야 했습니다.

공통 annotation과 검증 처리

공통 라이브러리에서는 직접 만든 @RequestJsonBody와 @SchemaValidate 같은 annotation을 사용했습니다. 요청에서는 원본 JSON 중 검증할 부분과 genericType을 지정하고, 검증을 통과하면 해당 타입으로 변환해 컨트롤러에 넘기는 구조였습니다. 응답 모델에도 연결할 스키마를 지정해 형식을 확인했습니다.

핵심은 annotation 이름이 아니라 스키마 선택·검증·타입 변환을 한곳에서 처리하는 부분이었습니다. 벤더마다 컨트롤러 안에 검증 코드를 반복하지 않고, 공통 에러 형식도 맞추기 위한 구성입니다.

이 구조를 재현할 때는 사용하는 웹 스택에 맞는 resolver와 응답 처리 지점이 필요합니다. Spring MVC용 resolver 메서드를 그대로 WebFlux에 붙일 수는 없습니다. JSONPath와 JSON Pointer도 표현식이 다르므로 선택한 라이브러리에 맞춰 처리해야 합니다.

문서 빌드와 품질 확인

같은 스키마를 OpenAPI의 $ref로 참조하면 문서와 런타임 검증에서 같은 정의를 사용할 수 있습니다. 다만 문서를 생성했다고 실제 요청에 검증이 적용되는 것은 아닙니다. 스키마 파일이 로드되는지, 유효하지 않은 요청이 거부되는지, 에러 응답이 맞는지를 따로 확인해야 합니다.

v2에서는 기존 WebFlux·DynamoDB 환경을 유지하면서 모델과 입력 검증, 로그·에러 처리의 공통 규칙을 다시 정리했습니다. 같은 스키마를 문서와 런타임 검증에서 함께 쓰도록 연결한 것이 이 단계의 결과였습니다. 저장 처리와 KMS 설정, 테스트 보강은 그 뒤에 이어졌습니다.

문서 빌드와 테스트 범위는 Redocly 문서 빌드와 커버리지 기준을 맞춘 과정에서 따로 다룹니다.

다음 글에서는 구축 중 확인한 DynamoDB 속성 이름과 KMS 설정을 설명합니다.

참고 자료

Last updated on