fix: API 응답 파싱 오류 수정 및 에러 처리 개선

주요 변경사항:
- 창고 관리 API 응답 구조와 DTO 불일치 수정
  - WarehouseLocationDto에 code, manager_phone 필드 추가
  - RemoteDataSource에서 API 응답을 DTO 구조에 맞게 변환
- 회사 관리 API 응답 파싱 오류 수정
  - CompanyResponse의 필수 필드를 nullable로 변경
  - PaginatedResponse 구조 매핑 로직 개선
- 에러 처리 및 로깅 개선
  - Service Layer에 상세 에러 로깅 추가
  - Controller에서 에러 타입별 처리
- 새로운 유틸리티 추가
  - ResponseInterceptor: API 응답 정규화
  - DebugLogger: 디버깅 도구
  - HealthCheckService: 서버 상태 확인
- 문서화
  - API 통합 테스트 가이드
  - 에러 분석 보고서
  - 리팩토링 계획서
This commit is contained in:
JiWoong Sul
2025-07-31 19:15:39 +09:00
parent ad2c699ff7
commit f08b7fec79
89 changed files with 10521 additions and 892 deletions

View File

@@ -46,18 +46,20 @@ class WarehouseLocationDto with _$WarehouseLocationDto {
const factory WarehouseLocationDto({
required int id,
required String name,
String? code,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
int? capacity,
@JsonKey(name: 'is_active') required bool isActive,
@JsonKey(name: 'created_at') required DateTime createdAt,
// API에 없는 필드들은 nullable로 변경
String? address,
String? city,
String? state,
@JsonKey(name: 'postal_code') String? postalCode,
String? country,
int? capacity,
@JsonKey(name: 'manager_id') int? managerId,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'is_active') required bool isActive,
@JsonKey(name: 'created_at') required DateTime createdAt,
@JsonKey(name: 'updated_at') required DateTime updatedAt,
// 추가 정보
@JsonKey(name: 'updated_at') DateTime? updatedAt,
@JsonKey(name: 'current_stock') int? currentStock,
@JsonKey(name: 'available_capacity') int? availableCapacity,
}) = _WarehouseLocationDto;

View File

@@ -680,23 +680,27 @@ WarehouseLocationDto _$WarehouseLocationDtoFromJson(Map<String, dynamic> json) {
mixin _$WarehouseLocationDto {
int get id => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String? get code => throw _privateConstructorUsedError;
@JsonKey(name: 'manager_name')
String? get managerName => throw _privateConstructorUsedError;
@JsonKey(name: 'manager_phone')
String? get managerPhone => throw _privateConstructorUsedError;
int? get capacity => throw _privateConstructorUsedError;
@JsonKey(name: 'is_active')
bool get isActive => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
DateTime get createdAt =>
throw _privateConstructorUsedError; // API에 없는 필드들은 nullable로 변경
String? get address => throw _privateConstructorUsedError;
String? get city => throw _privateConstructorUsedError;
String? get state => throw _privateConstructorUsedError;
@JsonKey(name: 'postal_code')
String? get postalCode => throw _privateConstructorUsedError;
String? get country => throw _privateConstructorUsedError;
int? get capacity => throw _privateConstructorUsedError;
@JsonKey(name: 'manager_id')
int? get managerId => throw _privateConstructorUsedError;
@JsonKey(name: 'manager_name')
String? get managerName => throw _privateConstructorUsedError;
@JsonKey(name: 'is_active')
bool get isActive => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
DateTime get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at')
DateTime get updatedAt => throw _privateConstructorUsedError; // 추가 정보
DateTime? get updatedAt => throw _privateConstructorUsedError;
@JsonKey(name: 'current_stock')
int? get currentStock => throw _privateConstructorUsedError;
@JsonKey(name: 'available_capacity')
@@ -721,17 +725,19 @@ abstract class $WarehouseLocationDtoCopyWith<$Res> {
$Res call(
{int id,
String name,
String? code,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
int? capacity,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'created_at') DateTime createdAt,
String? address,
String? city,
String? state,
@JsonKey(name: 'postal_code') String? postalCode,
String? country,
int? capacity,
@JsonKey(name: 'manager_id') int? managerId,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'created_at') DateTime createdAt,
@JsonKey(name: 'updated_at') DateTime updatedAt,
@JsonKey(name: 'updated_at') DateTime? updatedAt,
@JsonKey(name: 'current_stock') int? currentStock,
@JsonKey(name: 'available_capacity') int? availableCapacity});
}
@@ -754,17 +760,19 @@ class _$WarehouseLocationDtoCopyWithImpl<$Res,
$Res call({
Object? id = null,
Object? name = null,
Object? code = freezed,
Object? managerName = freezed,
Object? managerPhone = freezed,
Object? capacity = freezed,
Object? isActive = null,
Object? createdAt = null,
Object? address = freezed,
Object? city = freezed,
Object? state = freezed,
Object? postalCode = freezed,
Object? country = freezed,
Object? capacity = freezed,
Object? managerId = freezed,
Object? managerName = freezed,
Object? isActive = null,
Object? createdAt = null,
Object? updatedAt = null,
Object? updatedAt = freezed,
Object? currentStock = freezed,
Object? availableCapacity = freezed,
}) {
@@ -777,6 +785,30 @@ class _$WarehouseLocationDtoCopyWithImpl<$Res,
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
code: freezed == code
? _value.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
managerName: freezed == managerName
? _value.managerName
: managerName // ignore: cast_nullable_to_non_nullable
as String?,
managerPhone: freezed == managerPhone
? _value.managerPhone
: managerPhone // ignore: cast_nullable_to_non_nullable
as String?,
capacity: freezed == capacity
? _value.capacity
: capacity // ignore: cast_nullable_to_non_nullable
as int?,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
address: freezed == address
? _value.address
: address // ignore: cast_nullable_to_non_nullable
@@ -797,30 +829,14 @@ class _$WarehouseLocationDtoCopyWithImpl<$Res,
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String?,
capacity: freezed == capacity
? _value.capacity
: capacity // ignore: cast_nullable_to_non_nullable
as int?,
managerId: freezed == managerId
? _value.managerId
: managerId // ignore: cast_nullable_to_non_nullable
as int?,
managerName: freezed == managerName
? _value.managerName
: managerName // ignore: cast_nullable_to_non_nullable
as String?,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
updatedAt: null == updatedAt
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
as DateTime?,
currentStock: freezed == currentStock
? _value.currentStock
: currentStock // ignore: cast_nullable_to_non_nullable
@@ -844,17 +860,19 @@ abstract class _$$WarehouseLocationDtoImplCopyWith<$Res>
$Res call(
{int id,
String name,
String? code,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'manager_phone') String? managerPhone,
int? capacity,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'created_at') DateTime createdAt,
String? address,
String? city,
String? state,
@JsonKey(name: 'postal_code') String? postalCode,
String? country,
int? capacity,
@JsonKey(name: 'manager_id') int? managerId,
@JsonKey(name: 'manager_name') String? managerName,
@JsonKey(name: 'is_active') bool isActive,
@JsonKey(name: 'created_at') DateTime createdAt,
@JsonKey(name: 'updated_at') DateTime updatedAt,
@JsonKey(name: 'updated_at') DateTime? updatedAt,
@JsonKey(name: 'current_stock') int? currentStock,
@JsonKey(name: 'available_capacity') int? availableCapacity});
}
@@ -874,17 +892,19 @@ class __$$WarehouseLocationDtoImplCopyWithImpl<$Res>
$Res call({
Object? id = null,
Object? name = null,
Object? code = freezed,
Object? managerName = freezed,
Object? managerPhone = freezed,
Object? capacity = freezed,
Object? isActive = null,
Object? createdAt = null,
Object? address = freezed,
Object? city = freezed,
Object? state = freezed,
Object? postalCode = freezed,
Object? country = freezed,
Object? capacity = freezed,
Object? managerId = freezed,
Object? managerName = freezed,
Object? isActive = null,
Object? createdAt = null,
Object? updatedAt = null,
Object? updatedAt = freezed,
Object? currentStock = freezed,
Object? availableCapacity = freezed,
}) {
@@ -897,6 +917,30 @@ class __$$WarehouseLocationDtoImplCopyWithImpl<$Res>
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String,
code: freezed == code
? _value.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
managerName: freezed == managerName
? _value.managerName
: managerName // ignore: cast_nullable_to_non_nullable
as String?,
managerPhone: freezed == managerPhone
? _value.managerPhone
: managerPhone // ignore: cast_nullable_to_non_nullable
as String?,
capacity: freezed == capacity
? _value.capacity
: capacity // ignore: cast_nullable_to_non_nullable
as int?,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
address: freezed == address
? _value.address
: address // ignore: cast_nullable_to_non_nullable
@@ -917,30 +961,14 @@ class __$$WarehouseLocationDtoImplCopyWithImpl<$Res>
? _value.country
: country // ignore: cast_nullable_to_non_nullable
as String?,
capacity: freezed == capacity
? _value.capacity
: capacity // ignore: cast_nullable_to_non_nullable
as int?,
managerId: freezed == managerId
? _value.managerId
: managerId // ignore: cast_nullable_to_non_nullable
as int?,
managerName: freezed == managerName
? _value.managerName
: managerName // ignore: cast_nullable_to_non_nullable
as String?,
isActive: null == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool,
createdAt: null == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,
updatedAt: null == updatedAt
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,
as DateTime?,
currentStock: freezed == currentStock
? _value.currentStock
: currentStock // ignore: cast_nullable_to_non_nullable
@@ -959,17 +987,19 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
const _$WarehouseLocationDtoImpl(
{required this.id,
required this.name,
this.code,
@JsonKey(name: 'manager_name') this.managerName,
@JsonKey(name: 'manager_phone') this.managerPhone,
this.capacity,
@JsonKey(name: 'is_active') required this.isActive,
@JsonKey(name: 'created_at') required this.createdAt,
this.address,
this.city,
this.state,
@JsonKey(name: 'postal_code') this.postalCode,
this.country,
this.capacity,
@JsonKey(name: 'manager_id') this.managerId,
@JsonKey(name: 'manager_name') this.managerName,
@JsonKey(name: 'is_active') required this.isActive,
@JsonKey(name: 'created_at') required this.createdAt,
@JsonKey(name: 'updated_at') required this.updatedAt,
@JsonKey(name: 'updated_at') this.updatedAt,
@JsonKey(name: 'current_stock') this.currentStock,
@JsonKey(name: 'available_capacity') this.availableCapacity});
@@ -980,6 +1010,23 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
final int id;
@override
final String name;
@override
final String? code;
@override
@JsonKey(name: 'manager_name')
final String? managerName;
@override
@JsonKey(name: 'manager_phone')
final String? managerPhone;
@override
final int? capacity;
@override
@JsonKey(name: 'is_active')
final bool isActive;
@override
@JsonKey(name: 'created_at')
final DateTime createdAt;
// API에 없는 필드들은 nullable로 변경
@override
final String? address;
@override
@@ -992,23 +1039,11 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
@override
final String? country;
@override
final int? capacity;
@override
@JsonKey(name: 'manager_id')
final int? managerId;
@override
@JsonKey(name: 'manager_name')
final String? managerName;
@override
@JsonKey(name: 'is_active')
final bool isActive;
@override
@JsonKey(name: 'created_at')
final DateTime createdAt;
@override
@JsonKey(name: 'updated_at')
final DateTime updatedAt;
// 추가 정보
final DateTime? updatedAt;
@override
@JsonKey(name: 'current_stock')
final int? currentStock;
@@ -1018,7 +1053,7 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
@override
String toString() {
return 'WarehouseLocationDto(id: $id, name: $name, address: $address, city: $city, state: $state, postalCode: $postalCode, country: $country, capacity: $capacity, managerId: $managerId, managerName: $managerName, isActive: $isActive, createdAt: $createdAt, updatedAt: $updatedAt, currentStock: $currentStock, availableCapacity: $availableCapacity)';
return 'WarehouseLocationDto(id: $id, name: $name, code: $code, managerName: $managerName, managerPhone: $managerPhone, capacity: $capacity, isActive: $isActive, createdAt: $createdAt, address: $address, city: $city, state: $state, postalCode: $postalCode, country: $country, managerId: $managerId, updatedAt: $updatedAt, currentStock: $currentStock, availableCapacity: $availableCapacity)';
}
@override
@@ -1028,22 +1063,25 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
other is _$WarehouseLocationDtoImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.code, code) || other.code == code) &&
(identical(other.managerName, managerName) ||
other.managerName == managerName) &&
(identical(other.managerPhone, managerPhone) ||
other.managerPhone == managerPhone) &&
(identical(other.capacity, capacity) ||
other.capacity == capacity) &&
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.address, address) || other.address == address) &&
(identical(other.city, city) || other.city == city) &&
(identical(other.state, state) || other.state == state) &&
(identical(other.postalCode, postalCode) ||
other.postalCode == postalCode) &&
(identical(other.country, country) || other.country == country) &&
(identical(other.capacity, capacity) ||
other.capacity == capacity) &&
(identical(other.managerId, managerId) ||
other.managerId == managerId) &&
(identical(other.managerName, managerName) ||
other.managerName == managerName) &&
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt) &&
(identical(other.currentStock, currentStock) ||
@@ -1058,16 +1096,18 @@ class _$WarehouseLocationDtoImpl implements _WarehouseLocationDto {
runtimeType,
id,
name,
code,
managerName,
managerPhone,
capacity,
isActive,
createdAt,
address,
city,
state,
postalCode,
country,
capacity,
managerId,
managerName,
isActive,
createdAt,
updatedAt,
currentStock,
availableCapacity);
@@ -1094,17 +1134,19 @@ abstract class _WarehouseLocationDto implements WarehouseLocationDto {
const factory _WarehouseLocationDto(
{required final int id,
required final String name,
final String? code,
@JsonKey(name: 'manager_name') final String? managerName,
@JsonKey(name: 'manager_phone') final String? managerPhone,
final int? capacity,
@JsonKey(name: 'is_active') required final bool isActive,
@JsonKey(name: 'created_at') required final DateTime createdAt,
final String? address,
final String? city,
final String? state,
@JsonKey(name: 'postal_code') final String? postalCode,
final String? country,
final int? capacity,
@JsonKey(name: 'manager_id') final int? managerId,
@JsonKey(name: 'manager_name') final String? managerName,
@JsonKey(name: 'is_active') required final bool isActive,
@JsonKey(name: 'created_at') required final DateTime createdAt,
@JsonKey(name: 'updated_at') required final DateTime updatedAt,
@JsonKey(name: 'updated_at') final DateTime? updatedAt,
@JsonKey(name: 'current_stock') final int? currentStock,
@JsonKey(name: 'available_capacity') final int? availableCapacity}) =
_$WarehouseLocationDtoImpl;
@@ -1117,6 +1159,22 @@ abstract class _WarehouseLocationDto implements WarehouseLocationDto {
@override
String get name;
@override
String? get code;
@override
@JsonKey(name: 'manager_name')
String? get managerName;
@override
@JsonKey(name: 'manager_phone')
String? get managerPhone;
@override
int? get capacity;
@override
@JsonKey(name: 'is_active')
bool get isActive;
@override
@JsonKey(name: 'created_at')
DateTime get createdAt; // API에 없는 필드들은 nullable로 변경
@override
String? get address;
@override
String? get city;
@@ -1128,22 +1186,11 @@ abstract class _WarehouseLocationDto implements WarehouseLocationDto {
@override
String? get country;
@override
int? get capacity;
@override
@JsonKey(name: 'manager_id')
int? get managerId;
@override
@JsonKey(name: 'manager_name')
String? get managerName;
@override
@JsonKey(name: 'is_active')
bool get isActive;
@override
@JsonKey(name: 'created_at')
DateTime get createdAt;
@override
@JsonKey(name: 'updated_at')
DateTime get updatedAt; // 추가 정보
DateTime? get updatedAt;
@override
@JsonKey(name: 'current_stock')
int? get currentStock;

View File

@@ -65,17 +65,21 @@ _$WarehouseLocationDtoImpl _$$WarehouseLocationDtoImplFromJson(
_$WarehouseLocationDtoImpl(
id: (json['id'] as num).toInt(),
name: json['name'] as String,
code: json['code'] as String?,
managerName: json['manager_name'] as String?,
managerPhone: json['manager_phone'] as String?,
capacity: (json['capacity'] as num?)?.toInt(),
isActive: json['is_active'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
address: json['address'] as String?,
city: json['city'] as String?,
state: json['state'] as String?,
postalCode: json['postal_code'] as String?,
country: json['country'] as String?,
capacity: (json['capacity'] as num?)?.toInt(),
managerId: (json['manager_id'] as num?)?.toInt(),
managerName: json['manager_name'] as String?,
isActive: json['is_active'] as bool,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
currentStock: (json['current_stock'] as num?)?.toInt(),
availableCapacity: (json['available_capacity'] as num?)?.toInt(),
);
@@ -85,17 +89,19 @@ Map<String, dynamic> _$$WarehouseLocationDtoImplToJson(
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'code': instance.code,
'manager_name': instance.managerName,
'manager_phone': instance.managerPhone,
'capacity': instance.capacity,
'is_active': instance.isActive,
'created_at': instance.createdAt.toIso8601String(),
'address': instance.address,
'city': instance.city,
'state': instance.state,
'postal_code': instance.postalCode,
'country': instance.country,
'capacity': instance.capacity,
'manager_id': instance.managerId,
'manager_name': instance.managerName,
'is_active': instance.isActive,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'current_stock': instance.currentStock,
'available_capacity': instance.availableCapacity,
};