✨ 들어가기
우선, 뒤죽박죽 섞여있는 회사 코드 안에서 비즈니스 로직을 분리하기 전에
프론트엔드 개발자로써 view와 business logic이 어떻게 다른지 명확하게 알고 있어야 한다.
📍 View
우선, view는 기본적으로 HTML을 보여주는 UI이며 사용자 이벤트를 통해 HTML을 업데이트 하는 것이다.
즉, view는 단순히 주입받은 정보를 보여줄 뿐이다.
여기서 주의할 점은 해당 과정안에 business logic과 관련된 부분이 있다면 view가 아닌 비즈니스 로직으로 작동하게 된다는 것이다.
다른 말로 비즈니스 로직이 개입되지 않아야 컴포넌트를 재사용할 수 있다는 것이다.
📍 View Logic
isValid, errorText와 같은 UI/UX를 개선하는 코드
예를 들면, 비밀번호를 6자리 미만으로 입력한다면 isValid가 false이지만, 6자리 이상으로 입력하게 될 시에 isValid가 true가 되면서 경고문구가 사라지게 되는 코드가 아래와 같이 있다고 하자.
const [ isValid, setIsValid ] = useState(false);
return {
<p>{isValid && '비밀번호는 6자리이상이어야 합니다.'}</p>
}
여기서 isValid는 view logic을 담당하게 되는 것이고, 비밀번호가 6자리 이상인지 검증해야하는 것이 비즈니스 로직인 것이다.
📍Business Logic
한 기능에 대해서 어떻게 보여줄지(view)에 대한 논의를 제외한 모든 것을 말한다.
쉽게말하면 애플리케이션에서 필요한 데이터를 받아 기능에 맞게 데이터를 가공하거나, 저장하는 부분이라고 생각하면 된다.
그렇다면 우리는 어떻게 이것을 구분하여 기능을 설계해야 하는 것일까?
view는 business logic을 요청하고, 이를 응답받아 보여주는 구조이어야 한다.
현재 내가 다니고 있는 중고나라의 상품등록 form 컴포넌트를 개선하면서 이해해보자.
🔍 상품 등록 Form - 분리해보기
현재 상품등록 버튼은 아래와 같이 나타나며, 등록 버튼 클릭시 useForm의 hookFormMethods인 handleSubmit이 수행된다.
그리고 해당 핸들러로 우리가 정의한 checkFormToSubmit 함수를 실행하게 되는 구조이다.
🔥 문제상황
그렇다면, checkFormToSubmit 함수 내에서는 어떤 액션들이 일어나고 있을까...?
혹시 함수 네이밍으로 유추가 가능한가...?
그렇다.
해당 함수에서는 form의 형식을 체크하고 서버로 등록한 상품데이터를 전송하는 기능을 하는 함수이다.
사실 비즈니스로직으로 체크하는 액션과 상품 데이터 전송을 분리하는 것도 필요해보인다.
우선, 비즈니스 로직조차도 ui에서 분리하지 않았기에 이 과정을 먼저 진행한 뒤 추후 재설계를 해보려고 한다.
💡 View와 Business Logic의 분리 작업
1️⃣ 유효성 검사 내에서의 UI와 비즈니스 로직 구분
상품 등록 페이지에 들어가서 아무것도 입력하지 않은 채로 등록버튼을 누르면, 아래와 같이 UI가 변경되면서 경고 문구가 표시된다.
그리고, 해당 경고를 없애주기 위해 상품 이름과 가격, 정보를 지정된 글자수 이상으로 입력한다면 거래 방법에 대한 경고 문구가 나타나는 것을 확인할 수 있다.
그렇다면 여기서 UI 기능과 비즈니스 로직을 나눠보자.
🎨 UI 기능
- 아무것도 입력하지 않았을 때 👉 등록 버튼의 클릭으로 경고 문구 3개가 노출
- 거래방법 / 직거래 위치 / 편의점 위치 / 카테고리 미선택 👉 각각에 대한 경고 문구 및 팝업 노출
⚒️ 비즈니스 로직
- 거래 방법 선택 여부 조사
- 직거래 위치를 선택했다면, 동네 위치 등록 여부 조사
- 편의점 픽업을 선택했다면, 편의점 등록 여부 조사
- 카테고리를 선택했다면, 마지막 카테고리 선택 여부 조사
( 단, 무료나눔과 NFT 품목은 제외 ) - 1-4번까지의 조사를 순서대로 진행하며, 로직에 만족하지 못하는 결과를 마주하면 경고 문구 및 팝업을 노출
- 1-5의 과정을 모두 통과한 상품만 등록 진행
위와 같이 UI 기능과 비즈니스 로직을 분리할 수 있다.
그렇다면 이제 할 일은 checkFormToSubmit 내부에 구현되어 있는 비즈니스 로직을 따로 함수로 분리하는 것이다.
checkFormToSubmit은 단순히 로직에 대한 결과와 값을 가지고 상품을 서버에 등록해주는 일만 하면 된다.
2️⃣ utils로 상품 정보들의 유효성을 검사
checkFormToSubmit에는 상품 등록의 유효성을 검사하는 비즈니스 로직이 직접적으로 섞여있었다.
이제 이를 utils로 분리하여 우리는 유효성 검사의 결과만을 가지고 UI를 나타내면 된다.
분리하기 전 코드는 아래와 같다.
const checkFormToSubmit = (modifyData: ProductModifyInfoRes | undefined) => {
if (아무것도 입력하지 않았을 때 )
return;
// ⚒️ 비즈니스로직
if (거래 방법 선택 여부 조사) {
setError('tradeType', {
type: 'custom',
message: '거래 방법을 선택해 주세요.',
});
return;
}
if (직거래 위치를 선택했다면, 동네 위치 등록 여부 조사) {
setError('locations', {
type: 'custom',
message: '거래 희망 지역을 추가해 주세요.',
});
return;
}
if (편의점 픽업을 선택했다면, 편의점 등록 여부 조사) {
setError('pickupShops', {
type: 'custom',
message: '거래 희망 편의점을 추가해 주세요.',
});
return;
}
if (카테고리를 선택했다면, 마지막 카테고리 선택 여부 조사) {
toast('카테고리를 선택해주세요');
return;
}
// ✅ 최종 상품 등록 API 호출
if (상품수정 데이터) {
return 상품수정_API_호출;
} else {
return 상품등록_API_호출;
}
};
2번째 if문부터 5번째 if문까지는 모두 비즈니스 로직이다.
따라서 매개변수로 각각에 대한 검증 데이터를 전달하고, 경고 문구 및 팝업은 setError나 toast로 띄워주면 된다.
이를 따로 분리한 뒤,
한 개의 if문을 통해 모든 검증 로직을 통과하지 못한다면, 함수를 종료하고
통과한다면 상품등록 API를 호출하면 된다.
내가 리팩터링한 결과는 아래와 같다.
const checkFormToSubmit = (modifyData: ProductModifyInfoRes | undefined) => {
if (아무것도 입력하지 않았을 때 )
return;
// ⚒️ 비즈니스로직
if ( !( 거래 방법 선택 완료 && 직거래 위치 및 동네 위치 등록 완료
&& 편의점 픽업 및 편의점 등록 완료 && 카테고리 선택 및 마지막 카테고리 선택 완료)){
return ;
}
// ✅ 최종 상품 등록 API 호출
if (상품수정 데이터) {
return 상품수정_API_호출;
} else {
return 상품등록_API_호출;
}
};
로직을 분리하기만 했는데도 checkFormToSubmit 함수가 어떻게 흘러가는지 직관적으로 알 수 있게 되었다.
3️⃣ 앞으로 남은 과제
사실, checkFormToSubmit안에서 검증하고 전송하는 기능, 총 2개의 액션을 다루는 것이 맞는지에 대해서는 의문이 든다.
이 둘을 분리할 방법을 찾아보려고 한다.
더불어 비즈니스 로직으로 분리한 utils들이 받는 매개변수의 역할이 동일한 함수들이 있었다.
이는 추상화를 통해서 구현가능할 것으로 느껴졌다.
🌈 느낀점
한 컴포넌트에서 많은 비즈니스 로직을 담아두면 해당 컴포넌트가 어떤 역할을 하는지,
또 같은 비즈니스 로직이 다른데 쓰일것이라면 이를 중복해서 작성해야 한다는 불편함을 직접 코드로 경험할 수 있었다.
컴포넌트는 컴포넌트대로, 비즈니스 로직은 비즈니스 로직대로 독립적으로 분리해놓고,
기능이 추가될 때, 유연적으로 대응하는 프론트엔드 개발 환경을 만들어나가는 것이 중요함을 알게 되었다.
'WEB > React' 카테고리의 다른 글
중앙화된 인증 처리 시스템 구축하기 (1) | 2024.09.25 |
---|---|
개발자가 만들어가는 어드민 시스템의 UI/UX (0) | 2024.09.24 |
액션/계산/데이터를 활용한 useMuation 기능 개선 (3) | 2023.12.07 |
React의 패키지 구조와 용어 정리 (0) | 2023.12.03 |
[ React ] useQuery 와 useMuataion를 활용한 기능개선 (0) | 2023.11.30 |