서버로부터 받은 값을, 다시 서버로 보내기까지 어떻게 값들을 관리하고 처리하시나요? #67
-
고민서버로부터 받은 값을 input에 바인딩하고, 유저의 인터렉션이후로, 저는 이 "처리"과정에서 3가지의 타입이 존재한다고 생각하는데요.
한가지 간단한 예시로 위 과정을 설명할게요. [과정1] 서버로부터 어떤 데이터를 받았습니다. 저는 이 응답값의 타입을 아래와 같이 작성했습니다. type Response = {sum : number; itemCode : "01" | "02"; createdAt: "2020-01-01" }; [과정2] 그리고 이 값중 sum은 type Form = { sum : string; itemCode : "01" | "02" | null }; [과정3] 마지막으로 유저로부터 받은 값들을 서버로청을 보내기 위해 아래와 같이 paylaod 타입을 작성했습니다. 대부분 이 타입은 백앤드로부터 받은 명세대로 작성을 하고 있죠. 다만 type RequestPayload = {sum : number; itemCode: "01" | "02" }; 제가 실제로 구현해놓고 나중에 정리하자.. 라고 해놓은 것을 설명하겠습니다. [과정1]에서 [과정2]에서 z.object({
sum: z.string(),
itemCode: z.enum(["01", "02"]),
}); [과정3]에서는 유저의 모든 입력과 그리고 zod를 통한 유효성 검증이 끝났습니다. 이제 서버로 값을 보내려고 하는데 몇가지 처리해야할 것이 있습니다.
return (
<button
onClick={handleSubmit((formValues) => {
if (formValues.itemCode) {
mutate({
sum: formValues.sum || "0",
itemCode: formValues.itemCode,
});
}
})}
>
수정
</button>
); 고민이 되는 점
위 내용이 현재 고민입니다. 예시는 필드가 2개이지만 실제 코드에서는 필드가 20개가 족히 넘어가다보니 어떤식으로 이 값들을 정리해야할지, 다른 분들은 저와 같은 고민을 하신 적이 없는지 궁금합니다.! 코드class ResponseRender {
public sum: number;
constructor(private readonly response: Response) {
this.sum = response.sum.toString()
}
} 참고 자료No response |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments
-
1, 2, 3은 의외로 FE 개발자가 할 수 있는게 거의 없어요 제가 봤을때 고민하시고 있는 부분은 1, 2, 3 각각의 타입의 문제를 떠나서 저라면 별도의 레이어를 만들고 거기 안에서 제약 없이 비즈니스 로직을 표현할 것 같습니다 아무튼 이 기획 의도를 잘 발라내고 캡슐화해서 만든 다음 ""을 0으로 바꾸는걸 기획 의도로 본다면 비즈니스 레이어에서 처리할 수 있고 벨리데이션 로직을 비즈니스 레이어에 진입해서 전부 해결한 뒤 서버 호출을 해도 되고 통제할 수 없는 부분과 있는 부분을 잘 분리하면 의외로 쉽게 풀릴것이라 생각합니다 +이 접근은 반대로 로직대신 UI가 복잡해져도 유용할거에요 |
Beta Was this translation helpful? Give feedback.
-
말씀해주신 고민은 서버로부터 값을 받고 -> 유저가 그 값을 변형하고 -> 다시 서버로 보내는 Data Lifecycle 진행 시 변형되는 값의 형태를 어떻게 처리할지에 대한 내용인 것 같습니다. 공감 가는 지점이 많은 고민 같아요! 제 의견을 말씀드려보자면,
현재 Data Lifecycle이 이렇다면 추상 레이어를 넣으면 이렇게 변할거예요. 코드로 정말 러프하게 예를 들면 이렇게 돼요. // 추상 레이어
async function formValueTypeToResponseType(response: FormValueType): ResponseType {
//
} // 실제 UI 사용처
<button
onClick={handleSubmit((formValues) => {
if (formValues.itemCode) {
mutate(formValueTypeToResponseType(formValues));
}
})}
>
수정
</button> 하지만 이 방법은 레이어가 하나 더 들어가기 때문에 복잡성을 증가시키는 단점이 있어요. 무엇이 가장 적절한지는 각자 처한 상황에 맞게 판단하는 게 맞다고 생각해요. 번외) zod의 transform ? |
Beta Was this translation helpful? Give feedback.
-
이런 고민, 정말 많이 공감돼요 😵💫. 특히 "저는 이런 상황에서 세 가지 방법을 고민해본 적이 있는데요, 이런 식으로 접근해보면 어떨까 싶어요:" 1. onSubmit 내부에서 바로 처리하는 방식
const onSubmit = (formValues: Form) => {
const payload: RequestPayload = {
sum: parseInt(formValues.sum || "0", 10),
itemCode: formValues.itemCode as "01" | "02",
};
mutate(payload);
}; 2. 전용 변환 함수를 만드는 방식
const transformToPayload = (formValues: Form): RequestPayload => ({
sum: parseInt(formValues.sum || "0", 10),
itemCode: formValues.itemCode as "01" | "02",
});
const onSubmit = (formValues: Form) => {
mutate(transformToPayload(formValues));
}; 3. Zod의 transform 활용
const schema = z.object({
sum: z.string().transform((val) => parseInt(val || "0", 10)),
itemCode: z.enum(["01", "02"]),
});
const onSubmit = (formValues: z.infer<typeof schema>) => {
mutate(formValues); // 이미 변환된 값 사용
}; 저 같은 경우엔 zod의 tranform은 가벼운 데이터 변환에만 사용하고, 복잡한 변환은 전용 변환 함수로 분리하는데, 즉 데이터 핸들링 레이어를 따로 만드는 방식을 선호하는 편이에요. 물론 상황에 따라 Zod를 활용하거나 onSubmit에서 바로 처리하기도 했어요.
|
Beta Was this translation helpful? Give feedback.
1, 2, 3은 의외로 FE 개발자가 할 수 있는게 거의 없어요
1, 3은 애초에 서버에서 정해진거고 2는 디자인 의도를 반영해야하니까요 (협업을 하기 때문에 발생하는 문제, 이걸 대화로 풀면 엄청난 팀)
제가 봤을때 고민하시고 있는 부분은 1, 2, 3 각각의 타입의 문제를 떠나서
이 서로 다른 타입에 어떻게 기획 의도(벨리데이션, 디폴트값)를 잘 끼워넣을지가 고민인걸로 보입니다
표현해야하는 기획 의도는 이제 온전히 FE 개발자가 제어하는 부분인데 1, 2, 3이 고정되어 있으니까 움직임에 제약이 생기는거죠
저라면 별도의 레이어를 만들고 거기 안에서 제약 없이 비즈니스 로직을 표현할 것 같습니다
대신 이 레이어에 들어오고 나갈때만 타입을 신경써서 맞춰줄거에요
이 레이어의 표현은 커스텀 훅이 됐건 객체지향이 됐건 본인과 팀이 선호하는 방식을 쓸 수 있어요 (보통은 훅이 국룰이긴 하지만..)
(생각해보면 이 표현은 굉장히 다양해요 예를들어 next의 서버액션이나 고전적인 MVC 모델에선 이걸 FormData로도 받아요, 예전에 저는 Redux Saga로 해본 기억이 있네요)
아무튼 이 기획 의도를 잘 발라내고 캡슐화해서 만든 다음
컴포넌트에 연결고리를 만들면 그나마 깔끔하게 관리할 수 있을거에요 1, 2, 3에서 해결이 안된다면 2.5를 만들어서 해결하는거죠
""을 0으로 바꾸는걸 기획 의도로 본다면 비즈니스 레이어에서 처리할 수 있고