React API 연동과 TypeScript 타입 정의 패턴
React로 웹 서비스를 개발할 때 외부 데이터를 가져오는 API 연동은 피할 수 없는 핵심 과정입니다. 특히 요즘처럼 대규모 웹 애플리케이션을 안정적으로 운영해야 하는 환경에서는 TypeScript를 결합한 안전한 데이터 처리가 선택이 아닌 필수가 되었습니다. API가 보내주는 데이터가 어떤 형태인지 개발 단계에서 명확하게 알 수 없다면, 런타임 에러가 발생해 서비스가 갑자기 멈추는 불상사가 생길 수 있기 때문입니다.
서버에서 내려주는 응답 데이터를 타입스크립트로 엄격하게 규정하면, 자동 완성 기능을 통해 오타로 인한 실수를 방지할 수 있고 리팩토링도 한결 수월해집니다. 이번 글에서는 많은 개발자가 즐겨 사용하는 axios와 TypeScript를 조합하여 비동기 타입을 선언하는 실무적인 패턴들을 하나씩 살펴보겠습니다.
API 응답 데이터의 기초적인 TypeScript 타입 정의
가장 기본적이면서도 중요한 단계는 서버의 응답 구조를 인터페이스(Interface)나 타입(Type)으로 선언하는 것입니다. 일반적으로 axios TypeScript 연동을 진행할 때, axios.get 혹은 axios.post 메서드의 제네릭 매개변수로 서버가 반환할 데이터의 타입을 넘겨주게 됩니다.
예를 들어 사용자 정보를 조회하는 API라면 User 인터페이스를 먼저 정의하고, 이를 응답 타입으로 지정합니다. 이렇게 하면 response.data를 참조할 때 자동 완성의 도움을 받을 수 있으며 잘못된 속성에 접근하려는 시도를 컴파일 타임에 즉시 잡아낼 수 있습니다.
하지만 실제 서비스에서는 원시 데이터만 내려오기보다는 공통적인 봉투 구조에 싸여 오는 경우가 많습니다. 에러 코드나 성공 여부, 그리고 실제 데이터가 들어있는 data 필드로 구성된 공통 응답 규격이 대표적입니다. 이를 위해 제네릭을 활용한 ApiResponse<T> 형태의 공통 타입을 만들어 두면 중복 코드를 획기적으로 줄일 수 있습니다.
유지보수성을 높이는 API 요청 및 응답 타입 분리 패턴
실무에서 API를 호출할 때는 단순히 데이터를 가져오는 것뿐만 아니라, 쿼리 파라미터나 요청 바디를 함께 실어 보내야 합니다. 이때 요청에 들어가는 데이터와 서버에서 돌려주는 데이터의 타입을 명확하게 분리하여 정의하는 것이 유지보수에 유리합니다.
보통 Request와 Response 타입을 접미사로 붙여 구분하는 방식을 많이 사용합니다. 예를 들어 게시글을 생성할 때는 CreatePostRequest 타입을 정의하여 어떤 필드가 필수이고 어떤 필드가 선택인지 명시하고, 생성 완료 후 서버가 돌려주는 결과는 CreatePostResponse 타입으로 지정하는 식입니다.
이렇게 요청과 응답의 비동기 타입을 별도의 규격으로 격리해 두면, 서버 스펙이 변경되었을 때 어느 부분을 수정해야 하는지 직관적으로 파악할 수 있습니다. 프론트엔드와 백엔드 간의 소통 도구인 명세서를 코드 레벨로 고스란히 옮겨 놓은 듯한 효과를 얻을 수 있어 협업 효율도 극대화됩니다.
비동기 상태 관리를 위한 커스텀 훅과 제네릭 활용 패턴
React API 연동을 진행하다 보면 데이터를 불러오는 중, 실패함, 성공함과 같은 비동기 상태를 화면에 그려주어야 합니다. 이 세 가지 상태를 안전하게 타입으로 묶어서 관리하는 것도 매우 중요한 과제입니다.
단순히 loading 상태를 boolean으로 관리하는 것을 넘어, 데이터가 성공적으로 들어왔을 때만 해당 데이터에 접근할 수 있도록 차단하는 유니온 타입 패턴을 활용하면 더욱 안전합니다. 상태를 LOADING, SUCCESS, ERROR의 판별 가능한 유니온으로 정의하면 컴파일러가 타입 가드를 통해 조건에 맞는 데이터 접근을 확실하게 보장해 줍니다.
나아가 이를 커스텀 훅이나 React Query 같은 상태 관리 라이브러리와 묶을 때 제네릭을 적극적으로 활용하면 범용적인 비동기 처리 로직을 완성할 수 있습니다. 어떤 API를 호출하든 상관없이 로딩 상태와 에러 상태, 그리고 정확한 결과 타입까지 한 번에 추론해 주는 매끄러운 개발 경험을 누릴 수 있게 됩니다.
안정적인 데이터 변환을 돕는 Mapper 패턴
백엔드 API에서 사용하는 네이밍 규칙(예: 스네이크 케이스)과 프론트엔드에서 사용하는 규칙(예: 카멜 케이스)이 다를 때가 많습니다. 혹은 서버가 주는 날짜 문자열을 클라이언트에서 바로 Date 객체나 포맷팅된 텍스트로 변환해 사용해야 하는 경우도 빈번합니다.
이럴 때 유용하게 쓰이는 것이 바로 Mapper 패턴입니다. API 요청 및 응답 직후에 원본 데이터를 프론트엔드가 사용하기 편리한 가공된 타입으로 즉시 변환해 주는 전용 함수를 두는 방식입니다. 이렇게 하면 컴포넌트 내부가 서버 데이터의 원시적인 구조에 지나치게 종속되는 것을 막을 수 있습니다.
서버 타입과 클라이언트 모델 타입을 철저히 분리하고 그 사이를 Mapper 함수가 채우도록 설계해 보시기 바랍니다. 설령 서버의 API 스펙이 갑자기 변경되더라도 컴포넌트를 일일이 수정할 필요 없이 Mapper 함수 하나만 수정하면 되기 때문에 변화에 대단히 유연하게 대처할 수 있습니다.