refactor: 프론트엔드 리펙토링

This commit is contained in:
donghyeon-ka
2026-09-18 22:05:42 +09:00
parent 5cc41467ae
commit ec7f20e2ee
100 changed files with 6005 additions and 2867 deletions
@@ -8,6 +8,7 @@ import {
type InstalledBoundaryMapper,
type MappingResult,
} from "../../../contracts/boundary-mapper.ts";
import type { ReferenceResourceDto } from "./reference-schemas.ts";
export type ReferenceResourceView = Readonly<{
resourceId: string;
@@ -16,22 +17,19 @@ export type ReferenceResourceView = Readonly<{
optimistic?: boolean;
}>;
/**
* Operation-specific mapper. The HTTP contract has already validated the wire
* shape, so this mapper owns only wire -> domain construction.
*/
export function mapReferenceResourcePayload(
value: unknown,
dto: ReferenceResourceDto,
): MappingResult<ReferenceResource> {
if (!value || typeof value !== "object") {
return mappingFailure("MAPPING_INVARIANT_REJECTED");
}
const dto = value as Record<string, unknown>;
if (typeof dto.id !== "string" || typeof dto.name !== "string") {
return mappingFailure("MAPPING_INVARIANT_REJECTED");
}
try {
return mappingSuccess(
createReferenceResource({
id: dto.id,
displayName: dto.name,
createdAt: typeof dto.createdAt === "string" ? dto.createdAt : null,
createdAt: dto.createdAt ?? null,
}),
);
} catch {
@@ -40,14 +38,8 @@ export function mapReferenceResourcePayload(
}
export function mapReferenceResourceListPayload(
payload: unknown,
payload: readonly ReferenceResourceDto[],
): MappingResult<readonly ReferenceResource[]> {
if (!Array.isArray(payload)) {
return mappingFailure("MAPPING_INVARIANT_REJECTED");
}
if (payload.length > 100) {
return mappingFailure("OUTPUT_LIMIT_EXCEEDED");
}
const output: ReferenceResource[] = [];
for (const item of payload) {
const mapped = mapReferenceResourcePayload(item);
@@ -57,22 +49,58 @@ export function mapReferenceResourceListPayload(
return mappingSuccess(Object.freeze(output));
}
function isReferenceResourceDto(value: unknown): value is ReferenceResourceDto {
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
const dto = value as Record<string, unknown>;
return (
typeof dto.id === "string" &&
typeof dto.name === "string" &&
(dto.createdAt === undefined || typeof dto.createdAt === "string")
);
}
function mapUnknownReferenceResourcePayload(
value: unknown,
): MappingResult<ReferenceResource> {
return isReferenceResourceDto(value)
? mapReferenceResourcePayload(value)
: mappingFailure("MAPPING_INVARIANT_REJECTED");
}
function mapUnknownReferenceResourceListPayload(
value: unknown,
): MappingResult<readonly ReferenceResource[]> {
if (
!Array.isArray(value) ||
value.length > 100 ||
!value.every(isReferenceResourceDto)
) {
return mappingFailure(
Array.isArray(value) && value.length > 100
? "OUTPUT_LIMIT_EXCEEDED"
: "MAPPING_INVARIANT_REJECTED",
);
}
return mapReferenceResourceListPayload(value);
}
/**
* Registry-facing compatibility projection. Feature adapters should prefer the
* operation-specific mapper functions above so their result type is exact.
* Registry-facing compatibility projection. The registry is intentionally
* untyped, so its adapter performs the defensive shape check. Feature HTTP
* bindings use the typed operation-specific mappers above.
*/
export function mapReferenceOperation(
operationId: string,
payload: unknown,
): MappingResult<ReferenceResource | readonly ReferenceResource[]> {
if (operationId === "LIST_REFERENCE_RESOURCES") {
return mapReferenceResourceListPayload(payload);
return mapUnknownReferenceResourceListPayload(payload);
}
if (
operationId === "CREATE_REFERENCE_RESOURCE" ||
operationId === "GET_REFERENCE_RESOURCE"
) {
return mapReferenceResourcePayload(payload);
return mapUnknownReferenceResourcePayload(payload);
}
return mappingFailure("MAPPING_INVARIANT_REJECTED");
}
@@ -85,7 +113,7 @@ export const REFERENCE_BOUNDARY_MAPPERS = Object.freeze({
outputContractId: "ReferenceResourceList",
owner: "feature-frontend-reference-feature-vertical-slice",
maxOutputItems: 100,
map: mapReferenceResourceListPayload,
map: mapUnknownReferenceResourceListPayload,
}),
ReferenceResourceMapper: Object.freeze({
mapperId: "ReferenceResourceMapper",
@@ -94,7 +122,7 @@ export const REFERENCE_BOUNDARY_MAPPERS = Object.freeze({
outputContractId: "ReferenceResource",
owner: "feature-frontend-reference-feature-vertical-slice",
maxOutputItems: 1,
map: mapReferenceResourcePayload,
map: mapUnknownReferenceResourcePayload,
}),
} satisfies Readonly<Record<string, InstalledBoundaryMapper>>);