-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FE] roomInfoStore의 구조변경을 수행한다. #809
Conversation
정말 고생하셨습니다 제이드..!! 이 PR 은 정말 제이드의 많은 능력과 노력이 담긴 대단한 PR 인 것 같아요..ㅎㅎ |
roomInfoNonValidatedStore 가 없어질 줄은 몰랐네요..! 이미 모든 AddressModal 의 로직을 해당 스토어를 사용하도록 리팩토링 했는데, 제이드가 아마 만들면서 더 유용하다고 생각해서 하신 것 같아요. 혹시 그 이유를 들어볼 수 있을까요? 예전에 훅을 만들면서, 스토어가 너무 비대해져서 나눈다고 들었던 것 같아서요! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전반적으로 매우 깔끔하게 리팩토링되었네요~!!! 매우 좋습니다.
특히 action
에서 각각의 value name
으로 변경되니 가독성이 더 좋아진거 같아요!
대신 기존에 action 을 통합해서 묶었던 방식에서 변경된거 같은데 정확히 이해한게 맞을까요?
frontend/src/apis/checklist.ts
Outdated
checklist.room = roomInfoApiMapper(checklist.room); | ||
const response = await fetcher.post({ | ||
url: BASE_URL + ENDPOINT.CHECKLISTS_V1, | ||
body: { ...checklist, room: checklist.room }, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
const deposit = useRoomInfoValidated('deposit'); | ||
const rent = useRoomInfoValidated('rent'); | ||
|
||
const errorMessage = errorMessageDeposit || errorMessageRent; | ||
const errorMessage = deposit.errorMessage || rent.errorMessage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 매우 깔끔해졌네요~!! 최곱니다
frontend/src/types/room.ts
Outdated
|
||
// export type RoomInfo0 = { | ||
// roomName: string; | ||
// deposit: number; | ||
// rent: number; | ||
// maintenanceFee: number; | ||
// contractTerm: number; | ||
// floorLevel: string; | ||
// floor: number; | ||
// station: string; | ||
// walkingTime: number; | ||
// realEstate: string; | ||
// size: number; | ||
// structure: string; | ||
// occupancyMonth: number; | ||
// occupancyPeriod: OccupancyPeriod; | ||
// summary: string; | ||
// memo: string; | ||
// type: string; | ||
// // createdAt?: string; | ||
// address: string; | ||
// buildingName: string; | ||
// includedMaintenances: number[]; // 관리비 포함항목 | ||
// }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
불필요한 주석이 함께 포함되었네요~!
@@ -25,4 +25,28 @@ export type RoomInfo = Partial<{ | |||
buildingName: string; | |||
includedMaintenances: number[]; // 관리비 포함항목 | |||
}>; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
불필요해진 타입일까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트용으로 만들고 주석쳐놓은거라 지울게요
); | ||
|
||
export const roomInfoApiMapper = (values: RoomInfo | Nullable<RoomInfo>) => { | ||
const result = { ...values, structure: values.structure === '' ? undefined : '' }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
방 구조의 경우만 '' 일때 undefined으로 고친 이유는 undefined 로 post 가 가야해서 그런걸까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
방구조 API타입이 null | '원룸' | '투룸' 이런느낌이라서
비어있을때 null로 보내야하기때문에 undefined해줬습니다.
(프젝내부의 undefined는 나중에 null로 바뀌기때문)
setRawValues: (rawValues: Partial<RoomInfo>) => { | ||
set({ ...objectMap(rawValues, ([key, value]) => [key, value.rawValue]) }); | ||
}, | ||
getRawValues: () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 메서드들은 쉽게 이해가 되는 데 getRawValues, getParsedValues 의 경우 저번에 정한 컨벤션 처럼 docs 를 간략하게 달아주면 많은 도움이 될 것 같습니다:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
알겠습니다
getParsedValues: () => RoomInfo; | ||
} | ||
|
||
export const roomInfoStore = createStore<RoomInfoState & { actions: RoomInfoActions }>()( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
진짜 너무 많이 깔끔해졌네요...가독성도,사용성도👍👍
const actions = useStore(roomInfoStore, state => state.actions); | ||
const value = parseRoomInfo(name, rawValue); | ||
|
||
const set = useCallback( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useRoomInfoUnvalidate 에 들어갈 roomStructure 같은 경우는 값 변환 함수를 사용해서 만들면 될까요??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mapper는 api에서 쓰는용도라 스토어내부의 액션을 사용해주시면됩니다.
단일값을 set해주고싶으면 set({rawValue:'넣을내용',errorMessage:''})
여러 값을 한꺼번에 set해주고싶으면 setRawValues
를 이용하면됩니다.
get도 마찬가지로
단일값을 get해주고싶으면 get
해서 rawValue를 꺼내서쓰고
여러값을 한꺼번에 get해주고싶으면 getRawValues
를 해주면됩니다.
❗ Issue
✨ 구현한 기능
사용예시
구현내용은 종전 얘기한 방식과 크게 다르진 않지만 작은 차이가 있습니다.
roomInfoStore
: 검증,비검증 안따지고 모두를 담는 저장소.하나하나에 접근하고싶을때,
useRoomInfoValidated
훅에서 접근. (내부적으로roomInfoStore
를 사용)검증이필요없는 자료형은,
useRoomInfoNonValidated
훅에서 접근. (내부적으로roomInfoStore
를 사용)저장된 데이터를 가져다 POST API날리고싶을때 : 저장소(
roomInfoStore
)에 접근해 API요청함.roomInfoStore가 모두를 담는 저장소가 됨에 따라
현재 존재하는 roomInfoNonValidatedStore는, 이 PR이 머지 되면 제거하는 방향으로 진행하고자 합니다.
고민한 것 공유 (그냥 참고)
이번 구현에 그치지 않고 향후 조금 복잡한(?) zustand 스토어를 구현할 때에도
동일한 방식으로 구현할 만한 보편적 패턴을 찾고 싶어 고민했습니다.
종전에는 검증 로직을 가진 아주 작은 스토어를 여러개 한꺼번에 갖고 있게 구현을 시도했었는데,
만들고보니 설계자체도 괜찮고 사용도 괜찮았지만, 짜는 과정에 타입적인 어려움이 좀 있었습니다.
zustand store의 제네릭도 일일이 신경써줘야했고, 배열메소드, 객체순회 돌릴때 타입추론이 썩 만족스럽지 않았습니다.
가까스로 해결을 하고 완성은 했었지만,
패턴은 손쉽게 짤 정도로 쉽고단순하지않으면 의미가 없다고 생각해 좀 더 쉬운 패턴으로 다시 짰습니다.
( store를 동적으로 만들 때 어렵다고 느낀 이유 :
zustand store의 긴 제네릭
도 일일이 신경써줘야하고,객체순회
시 타입스크립트의 추론이 만족스럽지 않아 어려움이 있었습니다. )라는 Tkdodo 발언의 의미를 이 때 조금 느꼈습니다.
combine stores into slices - zustand 공식문서 tkdodo 발언
그래서 좀 더 직관적이고 쉬운 코드를 짜봤는데,
RoomInfo스토어 roomInfoStore는 로직이 거의 없이 저장 역할만 하고 (number이든, number[]이든, string이든 담음)
로직들은 훅(
useRoomInfoValidated
)에서 구현(입출력로직, 검증 등)하게 해봤습니다.다 짜고 난 지금 이상적 형태가 무엇일까 한번더 생각해보면
단순한 스토어를 적당히 3~4개 짜고, 훅 1개가 이들을 사용하는 형태라고 생각합니다. (현재는 스토어1개, 훅2개)
📢 논의하고 싶은 내용
🎸 기타