Retrofit은 REST API 통신을 쉽게 처리할 수 있도록 해주는 HTTP 클라이언트 라이브러리입니다.
아래 코드는 Provider와 Retrofit, 그리고 Dio를 활용해 HTTP 요청을 간단히 구성하는 방식입니다.
Retrofit이 메소드 호출 시 내부적으로 HTTP 요청을 자동으로 만들어 주므로 개발자는 직접적인 HTTP 통신 코드를 작성하지 않아도 됩니다.
코드 간결화 : 간단한 메소드 정의로 HTTP 요청 가능
비동기 처리 : 메인 스레드를 차단하지 않고 데이터를 받아올 수 있음
HTTP 메소드 어노테이션 : GET, POST, PUT, DELETE 등의 요청을 직관적으로 정의 가능
자동 JSON 파싱 : JSON 응답을 모델 객체로 자동 변환
에러 처리 : 에러 응답을 쉽게 핸들링
Retrofit은 API 호출을 위한 HTTP 요청 코드를 추상화하여, 간단하게 작성할 수 있습니다.
('/user')
Future<UserModel> getUser();
@annotation을 사용하여
getUser()를 호출할 때 자동으로 /user 경로로 GET 요청이 전송됩니다.
필요한 경로만 정의하여 코드가 간결해지고, 각 요청 메소드의 역할이 명확해집니다.
Retrofit은 비동기 방식으로 HTTP 요청을 처리하여, 응답이 올 때까지 메인 스레드를 블록하지 않습니다. Flutter에서 비동기 처리는 Future를 통해 이루어지며, Retrofit을 통해 반환된 응답을 쉽게 다룰 수 있습니다.
Future<void> fetchUserData() async {
try {
UserModel user = await userRepository.getUser();
print('User Data: $user');
} catch (e) {
print('Error: $e');
}
}
await 키워드를 통해 getUser() 메소드가 완료될 때까지 기다리고,
완료되면 응답 데이터를 user 변수에 저장합니다.
Retrofit은 HTTP 메소드를 annotation으로 지정할 수 있습니다.
각각의 annotation이 HTTP 요청 방식을 결정해주며,
코드 위에 어떤 요청이 들어가는지 쉽게 이해할 수 있습니다.
('/user') // GET 요청
('/user') // POST 요청
('/user/{id}') // PUT 요청, {id}는 경로 파라미터
('/user/{id}') // DELETE 요청
Retrofit은 Dio와 함께 사용할 때 json_serializable 같은 JSON 파싱 라이브러리와 결합해
JSON 데이터를 자동으로 모델 객체로 변환합니다.
서버에서 받은 JSON 데이터를 Flutter에서 바로 사용할 수 있도록 모델 객체로 변환합니다.
@GET('/user')
Future<UserModel> getUser();
서버에서 JSON 응답을 받으면 Retrofit이 이를 UserModel로 자동 변환해 반환합니다.
UserModel 클래스는 JSON 데이터를 받아 객체로 바꾸기 위한 fromJson 메소드를 정의합니다.
Retrofit은 HTTP 에러 응답(예: 400, 404, 500)을 관리할 수 있도록 기본적으로 예외 처리를 제공합니다.
개발자가 원하는 방식으로 에러를 핸들링할 수 있도록 try-catch 구문이나 onError 콜백을 사용할 수 있습니다.
Future<void> fetchUserData() async {
try {
UserModel user = await userRepository.getUser();
print('User Data: $user');
} catch (e) {
if (e is DioError && e.response?.statusCode == 404) {
print('User not found');
} else {
print('Error: $e');
}
}
}
DioError와 같은 에러 객체를 통해 HTTP 에러 코드를 확인하고, 상황에 맞는 에러 처리를 할 수 있습니다.
final userMeRepositoryProvider = Provider<UserMeRepository>(
(ref) {
final dio = ref.watch(dioProvider);
return UserMeRepository(dio, baseUrl: 'http://$ip/user/me');
},
);
이 부분에서는 userMeRepositoryProvider라는 프로바이더를 생성합니다.
이 프로바이더는 UserMeRepository 객체를 반환하는 역할을 하며,
dioProvider에서 Dio 객체를 가져와 UserMeRepository 생성자에 전달합니다.
baseUrl 파라미터로 경로를 넘겨주어
UserMeRepository의 기본 API 경로를 설정합니다.
이처럼 Provider를 사용하면 의존성 주입(Dependency Injection) 방식으로 Repository를 관리할 수 있습니다.
()
abstract class UserMeRepository {
factory UserMeRepository(Dio dio, {String baseUrl}) = _UserMeRepository;
UserMeRepository는 추상 클래스이자 Retrofit이 사용할 인터페이스입니다.
@RestApi() 어노테이션을 붙여서 Retrofit용 클래스임을 표시하고,
factory 생성자를 통해 Retrofit이 이 클래스를 구현하도록 합니다.
Dio 객체와 baseUrl은 UserMeRepository 생성 시 전달되며, 이 값들은 실제 HTTP 통신에 사용됩니다.
('/')
({
'accessToken': 'true',
})
Future<UserModel> getMe();
@GET('/'): HTTP GET 요청을 보냅니다.
baseUrl 뒤에 이어서 '/' 경로로 요청을 보내는 역할을 합니다.
@Headers(): 요청 헤더에 accessToken 필드를 추가합니다.
여기서는 accessToken: true로 설정되어 있어, API 호출 시 특정 헤더를 요구하는 경우 사용할 수 있습니다.
Future: 이 메소드는 UserModel 타입의 데이터를 반환하는 Future를 반환합니다.
비동기 방식으로 데이터를 받아옵니다.
('/basket')
({
'accessToken': 'true',
})
Future<List<BasketItemModel>> getBasket();
@GET('/basket'): /basket 경로로 GET 요청을 보냅니다.
Future<List> 타입의 데이터를 반환하는 비동기 메소드로,
장바구니의 아이템 리스트를 가져오는 역할을 합니다.
('/basket')
({
'accessToken': 'true',
})
Future<List<BasketItemModel>> patchBasket({
() required PatchBasketBody body,
});
@PATCH('/basket'): /basket 경로로 PATCH 요청을 보냅니다.
@Body(): 요청의 본문(Body) 데이터를 설정합니다.
여기서는 PatchBasketBody 타입의 body가 요청 본문에 포함됩니다.
이 메소드는 서버에 특정 데이터를 수정 요청으로 보내고,
수정된 장바구니 아이템 리스트를 반환하는 역할을 합니다.