SlideShare a Scribd company logo
1 of 71
복잡한 오브젝트를 우아하게 처리하기
react-hook-form을 중첩된 배열
오브젝트에서 다루며 써본 개발기
오웬 | 굿닥
발표자 소개
안녕하세요.
레버리지로 성장하는 개발자 오웬입니다.
본명은 오원종이고, MBTI는 ESTP,
굿닥에서 4년차 웹 프론트엔드 개발자로 근무하고 있어요.
블로그 : devowen.com
깃허브 : @dev-owen
링크드인 : linkedin.com/in/wonjongoh
이메일 : jgyuity1289@gmail.com
Intro
오브젝트(Object)
Intro
오브젝트(Object)
여러분은 지금 하고 계신 프로젝트에서 어떤 오브젝트를 다루고 계신가요?
Intro
오브젝트(Object)
여러분은 지금 하고 계신 프로젝트에서 어떤 오브젝트를 다루고 계신가요?
그 과정에서 어떠한 어려움을 가지고 계신가요?
Table of Contents
중첩된 배열 오브젝트(Nested Array Object) 형태의
폼 데이터(Form Data)를 다루며 마주한 문제와 해결에 대한 이야기
1. 미션
2. 첫 번째 문제: 복잡성
3. 두 번째 문제: control
4. 세 번째 문제: 유효성 검사&불변성
5. 내가 배운 것들
Mission
Hey Owen~ 우리가 새로운 프로젝트를 하려 함.
영유아 환자들이 병원에서 진료를 받기 전에 미리
작성할 수 있는 사전 문진 기능을 개발해야 해. PO
Mission
아 네… (관심 없음)
구체적인 요구사항은 어떻게 되나요?
Owen
Mission
국민건강보험에 들어가면 개월 수
별로 영유아 건강검진 문진표가
있어. 여길 보면 항목이 뭐가 있고…
어쩌구… PO
Mission
아 네… (여전히 관심 없음)
(메모 중)
Owen
Mission
그리고 이걸 영유아 환자들 뿐만 아니라 일반 환자들도 쓸 수
있게 커스텀하게 만들꺼야.
병원이 직접 세부 항목들을 넣고 빼고 수정하고 다 할 수 있게!
어때 완전 멋있지 않아??
금방 가능하지?
PO
Mission
네 뭐 크게 어려운 건 없어 보이네요.
3일이면 될 것 같아요.
(뭐 큰일이야 나겠어 ㅋㅋ)
Owen
Mission
Owen 안녕하세요.
병원 문진 데이터 스키마를 대략적으로 고민해 보았는데요.
이게 생각보다 좀 빡세네요..?
대분류, 중분류, 소분류가 있어서 3 depths 오브젝트 배열로
가야 할 것 같아요.
백엔드 개발자 W
Mission
네 알겠습니다!
(시니어 개발자시니까
잘 만들어 주시겠지 ㅋㅋ)
Owen
Mission
3 depths의 오브젝트 배열
CRUD 만들기
Mission
여러분들의 이해를
돕기 위한 데모
Mission
폼 데이터란?
사용자로부터 입력 받을 수 있는
데이터를 받아서 처리하는 방법
onClick, onSubmit, onChange 등
이벤트 리스너를 통해 입력 받은 값에
대한 유효성 검사가 가능
각각의 input 태그에 id 값이 있어서
key-value 형태의 이터러블 구조
Mission
구글 폼과 비슷한 데이터 구조
각각의 필드는 폼 데이터 형태로 값을 받아줄
수 있음
예를 들어 1번 섹션의 1번 질문의 1번 답을
찾고 싶다면
Mission
구글 폼과 비슷한 데이터 구조
각각의 필드는 폼 데이터 형태로 값을 받아줄
수 있음
예를 들어 1번 섹션의 1번 질문의 1번 답을
찾고 싶다면
medicalQuestionnaireState[0]
.children[0]
.children[0]
.value = ‘Hello Answer’;
React-hook-form
● Uncontrolled(비제어식) form
방식으로 form 데이터 관리
● Subscription(구독형) 방식
● 로직을 간편하게 짤 수 있음
(여러 form 관련 인터페이스
제공)
Mission
Problem 1
문제 1.
React-hook-form에서 CRUD 로직을 다
만들어 주려다 보니 로직이 너무 복잡해짐
Problem 1
특정 인덱스의
오브젝트
삭제 기능
depth 1
Problem 1
특정 인덱스의
오브젝트
삭제 기능
depth 2
Problem 1
특정 인덱스의
오브젝트
수정 기능
depth 2
Problem 1
W, 짜다보니 프론트엔드 로직이 좀 복잡해 지네요.
혹시 데이터 스키마를 수정해 볼 수 있을까요?
Owen
Problem 1
오웬, 안녕하세요.
그 때 같이 결정한 이후로 이미 작업이 많이 진행이 되어서
스키마를 아예 바꾸는 건 좀 힘들어 보이네요 ㅠ
프론트엔드에서 더 간결하게 할 수 있는 방법을 찾아
보아야 할 것 같아요 ㅠ
백엔드 개발자 W
Problem 1
그렇군요 알겠습니다. ㅠㅠ
Owen
오웬~~ 배열 오브젝트를 다루고 계시는군요!
공식 문서에 useFieldArray 라는 훅이 있는데,
그거 한 번 써보시는 건 어때요? ㅋㅋ
내 옆에 있는
프론트엔드 개발자 H
Problem 1
Solution 1
useFieldArray
Custom hook for working with Field Arrays
Solution 1
useFieldArray 적용
2 depth remove
AS-IS TO-BE
Problem 1 Recap
문제 1.
직접 CRUD 로직을 만들어 주다 보니,
로직이 너무 복잡해짐
해결 1.
배열 오브젝트를 위해 만들어진
useFieldArray 커스텀 훅을 통해 코드 개선
Problem 2
문제 2.
control이 동작하지 않음
Problem 2
만들어야 하는
화면 레이아웃
Problem 2
중첩된 form data를 하나의 useFieldsArray로 관리
Problem 2
control
react-hook-form에서 form data를 관리하는 프로퍼티
Problem 2
중첩된 form data를 하나의 useFieldArray로 관리
이 때 control이 정상적으로 동작하지 않는 이슈
Problem 2
useFieldArray의 소스
코드를 확인
fields의 참조가 바뀌어야
fields가 업데이트
Problem 2
useFieldArray의 소스
코드를 확인
fields의 참조가 바뀌어야
fields가 업데이트
기존의 방식은 fields의 2
depth, 3 depth 값이 바뀌는
경우에 해당 fields의 참조가
바뀌지 않아서 업데이트가
되지 않음
Solution 2
각각의 depth별로 별도의 useFieldArray를 만들어서
해당 fields 데이터를 컴포넌트에서 받아서 처리
Problem 2 Recap
문제 2.
useForm의 control이 중첩된 배열
오브젝트에서 의도한 대로 동작하지 않음
해결 2.
각각의 뎁스 별로 별도의 fields 속성을
만들어서 하나의 뎁스씩 쪼개서 로직을 처리
Problem 3
문제 3.
유효성 검사와 불변성 유지
Problem 3-1
유효성 검사
사용자가 어떠한 데이터를 폼에서 입력할 지 모르기
때문에 데이터의 오염을 방지하기 위한 장치
백엔드와 프론트엔드에서 둘 다 하는 것을 권장
(사용자에게 피드백을 주는 차원에서)
Problem 3-1
Input form에서 값을 하나만 바꾸어 주더라도
Problem 3-1
Input form에서 값을 하나만 바꾸어 주더라도
해당 필드 이외의 다른 필드를 같이 유효성 검사 해 주어야 하는 경우가 발생
Solution 3-1
useForm 의 reValidateMode: ‘onChange’
Validation strategy after submitting behavior
Solution 3-1
react-hook-form에서
지원하는 resolvers
종류가 다양함
Solution 3-1
Zod
TypeScript를 위한 스키마 선언 및
유효성 검사 도구
● Zero dependency
● 8kb의 가벼운 모듈 사이즈
● 불변성, 함수형 접근
● 선언적(declarative) API
● first-party
Solution 3-1
Reddit 커뮤니티에서도 zod를 추천하는
글이 가장 많음
Solution 3-1
zod resolver로 스키마 유효성 관리
• Input data의 타입이 무엇인지 검증
• 갯수 제한, 에러 메시지 추가
• 유니온 타입 형태의 유효성 검증도 가능
Problem 3-2
불변성(Immutability)
데이터가 한 번 생성이 되면 그 뒤에는 변하지 않는 성질
원시 타입(Primitive Type)은 불변성이 항상 유지되지만,
참조 타입(Reference Type)은 불변성이 항상 유지되지 않음
일반적으로 Object.assign()이나 스프레드 연산자 { … }를 통해 해결
Problem 3-2
불변성이 지켜지지 않는 경우
Problem 3-2
불변성이 지켜지는 경우
Problem 3-2
스프레드 연산자를 가지고 불변성이 유지될 수 있는가?
1 depth object에서는 문제가 되지 않는다.
Problem 3-2
스프레드 연산자를 가지고 불변성이 유지될 수 있는가?
1 depth object에서는 문제가 되지 않는다.
하지만 depth가 깊어지면 이야기가 달라진다.
Problem 3-2
MDN에서도 스프레드
연산자는 1차원 배열을
복사할 때만 사용할 것을
권장하고 있습니다.
Solution 3-2
immer
상태에서 불변성을 유지할 수 있게 도와주는 라이브러리
Solution 3-2
왜 immer?
프로젝트에서 기본 JS 타입들을 사용하고, 러닝 커브가 낮아서
● 보일러 플레이트가 적어서 배우기 쉬움
● 기본 JS 타입에 사용 가능
● JS 네이티브 타입이 아닌 타입도 지원
● 읽기 성능이 빠르고, 쓰기 성능이 느림
Solution 3-2
Immer를 update 로직에 적용해 보았다.
AS-IS
TO-BE
Problem 3 Recap
문제 3.
유효성 검사와 불변성 관리가
제대로 되지 않음
해결 3.
유효성 검사 : zod resolver와 reValidateMode 옵션,
불변성 관리 : immer.js 도입을 통해 해결
What I Learned
내가 배운 것들
What I Learned
문제 1.
직접 CRUD 로직을 만들어 주다 보니,
로직이 너무 복잡해짐
What I Learned
문제 1.
직접 CRUD 로직을 만들어 주다 보니,
로직이 너무 복잡해짐
해결 1.
배열 오브젝트를 위해 만들어진
useFieldArray 커스텀 훅을 통해 코드 개선
What I Learned
문제 2.
useForm의 control이 중첩된 배열
오브젝트에서 의도한 대로 동작하지 않음
What I Learned
문제 2.
useForm의 control이 중첩된 배열
오브젝트에서 의도한 대로 동작하지 않음
해결 2.
각각의 뎁스 별로 별도의 fields 속성을
만들어서 하나의 뎁스씩 쪼개서 로직을 처리
What I Learned
문제 3.
유효성 검사와 불변성 관리가
제대로 되지 않음
What I Learned
문제 3.
유효성 검사와 불변성 관리가
제대로 되지 않음
해결 3.
유효성 검사 : zod resolver와 reValidateMode 옵션,
불변성 관리 : immer.js 도입을 통해 해결
What I Learned
PO와 협업하며 배운 점
일정은 가능한… 최대한 길게 잡아놓고,
차라리 개발이 빨리 끝나면 테스트를 꼼꼼히 하자.
백엔드 개발자와 협업하며 배운 점
백엔드 개발자와 API나 데이터 구조에 대해 이야기를 할 때 더 적극적으로
프론트엔드의 입장을 말한다.
프론트엔드 개발자와 협업하며 배운 점
동료들의 도움을 적극적으로 구하자. 내가 쓰는 도구를 나보다 잘 쓰는 동료가
있다면, 찾아가서 배우고 레버리지로 성장하자.
참고자료
● MDN
● React-hook-form 공식 문서
● Zod 공식 문서
● immer.js 공식 문서
● Reddit
● Mutability vs Immutability in JavaScript (FreeCodeCamp)
● Zod vs Yup vs Joi vs io-ts for Creating Runtime TypeScript
Validation Schemas (Egghead.io)
Q&A
Thank you.
네트워킹 질문
- 나에게 할당된 개발 업무를 하다가 혼자서 도저히 해결이 안
되는 순간, 어떻게 주변의 도움을 받아 문제를 해결할 수 있을까요?
- 주니어 개발자가 레버리지를 통해 성장할 수 있는 방법에는 어떤
것들이 있을까요? (e.g. 테오콘 참석하기 등)

More Related Content

Similar to 복잡한 오브젝트를 우아하게 처리하기

테스팅을위한선행조건 명세
테스팅을위한선행조건 명세테스팅을위한선행조건 명세
테스팅을위한선행조건 명세규동 최규동
 
Aspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&CAspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&Csys4u
 
Spring batch와 함께 하는 TDD
Spring batch와 함께 하는 TDDSpring batch와 함께 하는 TDD
Spring batch와 함께 하는 TDDSanghyuk Jung
 
토비의 스프링 - DI
토비의 스프링 - DI토비의 스프링 - DI
토비의 스프링 - DIJU Chae
 
프로젝트 관리 및 지켜야 할 사항들
프로젝트 관리 및 지켜야 할 사항들프로젝트 관리 및 지켜야 할 사항들
프로젝트 관리 및 지켜야 할 사항들Lee Geonhee
 
[1A4]자바스크립트 라이브러리 개발 운영 경험기
[1A4]자바스크립트 라이브러리 개발 운영 경험기[1A4]자바스크립트 라이브러리 개발 운영 경험기
[1A4]자바스크립트 라이브러리 개발 운영 경험기NAVER D2
 
Java script의 이해
Java script의 이해Java script의 이해
Java script의 이해seungkyu park
 
Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료ssuser776e2d
 
Sql 중심 코드 탈피
Sql 중심 코드 탈피Sql 중심 코드 탈피
Sql 중심 코드 탈피ssuser776e2d
 
Java 강의자료 ed11
Java 강의자료 ed11Java 강의자료 ed11
Java 강의자료 ed11hungrok
 
Game programming patterns 2
Game programming patterns 2Game programming patterns 2
Game programming patterns 2QooJuice
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 
객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것jaypi Ko
 
TDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDTDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDSuwon Chae
 
17.tigerteam design document
17.tigerteam design document17.tigerteam design document
17.tigerteam design document호상 장
 
[강의] OOP 개요
[강의] OOP 개요[강의] OOP 개요
[강의] OOP 개요Nohyun Kee
 

Similar to 복잡한 오브젝트를 우아하게 처리하기 (20)

S66 goos-w7
S66 goos-w7S66 goos-w7
S66 goos-w7
 
테스팅을위한선행조건 명세
테스팅을위한선행조건 명세테스팅을위한선행조건 명세
테스팅을위한선행조건 명세
 
Aspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&CAspect Oriented Programming_SYS4U I&C
Aspect Oriented Programming_SYS4U I&C
 
Spring batch와 함께 하는 TDD
Spring batch와 함께 하는 TDDSpring batch와 함께 하는 TDD
Spring batch와 함께 하는 TDD
 
토비의 스프링 - DI
토비의 스프링 - DI토비의 스프링 - DI
토비의 스프링 - DI
 
프로젝트 관리 및 지켜야 할 사항들
프로젝트 관리 및 지켜야 할 사항들프로젝트 관리 및 지켜야 할 사항들
프로젝트 관리 및 지켜야 할 사항들
 
[1A4]자바스크립트 라이브러리 개발 운영 경험기
[1A4]자바스크립트 라이브러리 개발 운영 경험기[1A4]자바스크립트 라이브러리 개발 운영 경험기
[1A4]자바스크립트 라이브러리 개발 운영 경험기
 
deview2014
deview2014deview2014
deview2014
 
Java script의 이해
Java script의 이해Java script의 이해
Java script의 이해
 
The Introduction to Refactoring
The Introduction to Refactoring The Introduction to Refactoring
The Introduction to Refactoring
 
Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료
 
S66 goos-w6
S66 goos-w6S66 goos-w6
S66 goos-w6
 
Sql 중심 코드 탈피
Sql 중심 코드 탈피Sql 중심 코드 탈피
Sql 중심 코드 탈피
 
Java 강의자료 ed11
Java 강의자료 ed11Java 강의자료 ed11
Java 강의자료 ed11
 
Game programming patterns 2
Game programming patterns 2Game programming patterns 2
Game programming patterns 2
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 
객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것객체지향 단어가 의미하는 것
객체지향 단어가 의미하는 것
 
TDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDTDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDD
 
17.tigerteam design document
17.tigerteam design document17.tigerteam design document
17.tigerteam design document
 
[강의] OOP 개요
[강의] OOP 개요[강의] OOP 개요
[강의] OOP 개요
 

Recently uploaded

데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법JMP Korea
 
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP Korea
 
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?Jay Park
 
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP Korea
 
JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!JMP Korea
 
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석JMP Korea
 
JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP Korea
 
공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화JMP Korea
 

Recently uploaded (8)

데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법
 
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
 
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
 
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
 
JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!
 
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
 
JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례
 
공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화
 

복잡한 오브젝트를 우아하게 처리하기

Editor's Notes

  1. 안녕하세요. <복잡한 오브젝트를 우아하게 처리하기>라는 주제로 발표를 맡게 된 오웬입니다. 만나서 반갑습니다.
  2. 세션을 시작하기 전에 간단한 제 소개를 먼저 해볼께요. 제 이름은 오웬이고 저를 한 마디로 어떻게 소개할까 고민하다가 '레버리지로 성장하는 개발자’ 라는 수식어를 붙여 보았습니다. 이제는 어디 가서 주니어라고 하면 안되는 연차가 되어 버렸지만.. 불과 최근까지만 해도 저는 성장에 진심인 주니어 웹 프론트엔드 개발자였습니다. 하지만 주니어가 성장할 수 있는 기회를 찾는 건 생각보다 쉽지 않았습니다. 저도 여러 시행착오를 거쳤고, 깨달은 결론은 ‘혼자 힘으로 성장하려 하지 말고, 주변의 도움을 받자!’ 였습니다. 마치 우리가 투자를 받아 스타트업을 키우고, 대출을 받아 집을 사는 것 처럼 말이죠. 저는 굿닥이라는 헬스케어 스타트업에서 리액트 기반의 웹 프론트엔드와 리액트 네이티브 기반의 모바일 앱 개발을 하고 있으며, 자세한 사항은 제 블로그, 깃허브, 링크드인 등에 자세히 적어 놓았으니 궁금하신 분들은 확인해 주시면 됩니다. 그러면 본격적으로 저의 발표를 시작해 보도록 하겠습니다.
  3. 이번 발표에서 가장 중요한 키워드는 오브젝트입니다. 오브젝트는 자바스크립트의 데이터 타입 중 하나이며, key-value 형태로 복잡한 엔티티들을 다룰 때 사용합니다. 아마 여러분은 각자 하고 계시는 프로젝트에서 오브젝트를 다뤄본 경험이 한 번씩은 있으실 것이라 생각합니다.
  4. 한 분 한 분 다 여쭤볼 수는 없지만, 이번 발표를 들으면서 다음 질문을 스스로에게 해 보시면 좋을 것 같아요. 여러분은 지금 하고 계신 프로젝트에서 어떠한 형태의 오브젝트를 다루고 계신가요?
  5. 그리고 그 과정에서 어떠한 어려움을 가지고 계신가요? 이번 발표는 몇 개월 전 제가 회사에서 업무를 하면서 다소 복잡한 오브젝트를 다루며 경험했던 개발 이야기입니다. 오늘 저의 발표가 여러분들이 이러한 질문에 대하여 가지고 있는 고민들에 대해 조금이나마 도움이 되기를 바라며, 발표를 시작해 보도록 하겠습니다.
  6. 오늘 발표 주제와 목차는 다음과 같습니다. 제가 중첩된 배열 오브젝트 형태의 폼 데이터를 다루며 마주한 문제와 그 해결에 대한 이야기를 해 보려고 해요, 회사에서 제가 부여받은 미션이 무엇이었고, 그 미션을 해결하는 과정에서 제가 마주했던 문제를 세 가지 정도 공유해 보고자 합니다. 그리고 그 세 가지 문제를 고민하고 해결하며 제가 배운 것들에 대해서 마지막으로 공유를 하고 발표를 마치도록 하겠습니다.
  7. 때는 바야흐로 올해 상반기 어느 날. 저희 팀의 리더이신 PO로부터 새로운 프로젝트를 시작할 것이라는 이야기를 듣습니다. 이 프로젝트를 간단히 소개를 하면, 영유아 환자들이 병원을 가기 전에 사전 문진표를 작성해야 하는데, 그걸 지금은 다 종이로 수기로 받고 있고 이걸 모바일 앱에서 받을 수 있게 디지털로 전환하자는 내용이었습니다.
  8. 저는 당시 3년차 개발자로 회사 일에 큰 흥미를 느끼지 못하던 권태기의 직장인1이었기에 별 감흥 없이, 해당 작업을 하기 위한 구체적인 요구사항을 파악하기 시작했습니다.
  9. PO는 열심히 요구사항에 대해서 설명을 해 주셨고, 딱 들어 보았을 때 이미 포맷이 다 있고 크게 어려운 작업은 아닐 것 같다고 생각했어요.
  10. 저는 아무 생각 없이 메모 하면서 받아 적었죠.. 그런데... 누가 그러던가요? 한국 말은 끝까지 들어야 한다고 말이죠.
  11. 마지막에 이 문진 기능을 병원에서 직접 CRUD가 가능하게도 만들고 싶다고 하시더라구요? 처음에는 사실 이 말이 그렇게 크게 느껴지진 않았습니다. 많이 해 본 작업이었고, 특별히 어려운 점은 없어 보였기 때문이죠. 금방 가능하냐고 물어보셔서, 저는 아무 생각 없이.
  12. 이렇게 대답을 해 버렸죠. ㅎㅎ 여기서 여러분께 제가 드리고 싶은 한 가지 말씀은… 일정은 아무리 여유 있어 보이더라도 일단 최대한 늘려서 받을 수 있으면 늘려서 받으라는 것입니다.
  13. 프로젝트가 시작하고, 백엔드 개발자 분과 API 스펙 공유 미팅을 하던 중 생각보다 데이터 스키마가 복잡해졌다는 이야기를 듣습니다. 저는 이런 결정에 대해서 그 당시 다소 수동적으로 작업을 진행했던 감이 지나고 나 보니 없지 않아 있더라구요. 이러한 의사결정이 프론트엔드 쪽에서 여러 가지 고민거리를 많이 만들 것이라고는 그 당시에 크게 생각하지 못하고 저는 작업을 시작했습니다.
  14. 그래서 저에게 주어진 미션은 한 문장으로 정리하면 3 depths의 오브젝트 배열 CRUD를 만들기 였습니다.
  15. 여러분들의 이해를 돕기 위해 실제로 나온 서비스를 먼저 보면 다음과 같습니다. 각 문진표 별로 제목이 있고, 섹션이 여러개 있으며 각 섹션 아래에 질문이 여러개가 있고 그 질문 아래에도 답변이 여러개가 있을 수 있는 구조였어요. 섹션과 질문, 답변은 자유롭게 추가, 수정, 삭제가 가능하고 각각의 섹션의 순서도 바꾸어 줄 수 있게 했습니다.
  16. 먼저 Form Data에 대해서 개념이 생소하신 분이 계실 수 있을 것 같아 간단하게 짚고 넘어가 보겠습니다.
  17. 실제 작성된 데이터 구조를 보면 다음과 같이 나타낼 수 있습니다. 불필요한 복잡한 부분은 걷어내고 추상화 해서 나타내 보았어요. 여러분들이 많이 쓰시는 서비스 중에서는 아마 구글 폼과 가장 유사하지 않을까 생각이 들었습니다. 각각의 뎁스별로 필드가 여러개 있는데 그 필드는 input form 형태로 데이터를 받아주게 됩니다. 인덱스를 기반으로 원하는 값을 찾아갈 수 있는데 3 depth 이므로 최대 3개까지 인덱스가 필요할 수 있는 상황이었습니다. 예시처럼 1번 섹션의 1번 질문의 1번 답을 찾고 싶다면.. 이렇게 말이죠.
  18. 이렇게 찾아 주면 됩니다.
  19. react-hook-form에 대해서 잘 모르시는 분들을 위해 간단히 설명해 드리면, react에서 사용할 수 있는 form 라이브러리입니다. uncontrolled form 방식으로 form 데이터를 관리하여 컴포넌트 리렌더링 측면에서 최적화가 되어 있다는 장점이 있습니다. 그리고 subscription 방식으로 데이터를 관리하고 있어서, form state를 잘 관리해 주고 컴포넌트에서는 잘 바라보게만 하면 업데이트가 잘 됩니다. form 과 관련된 여러 이벤트들 focus, blur, click 등등에 대해서 충분한 인터페이스를 제공하고 있고 이를 통해 로직을 간편하게 짤 수 있습니다.
  20. 작업을 시작하고 처음에는 순조로웠습니다. 하지만 금방 저는 첫 번째 문제를 마주하게 됩니다. 그것은 바로 제가 해당 로직을 직저부 다 만들어 주려다 보니 로직이 너무 복잡해지는 문제였습니다,
  21. 예를 들어 이 배열의 첫 번째 뎁스의 특정 인덱스에 해당하는 오브젝트를 삭제하는 로직을 만든다고 가정해 보겠습니다. 그러면 저는 이 인덱스를 인자로 받아서 배열이기에 slice 메서드를 가지고 삭제해 줄 인덱스 앞뒤로 자른 후 합쳐서 다음과 같이 state를 업데이트 해 주는 방식을 선택했습니다. 아마 많은 분들이 처음 딱 짜신다면 이와 비슷한 방식으로 짜셨을 것이라 생각합니다.
  22. 첫 번째 뎁스는 어렵지 않았습니다. 문제는 두 번째 뎁스였습니다. 인덱스를 두 개를 받아와야 했고, 첫 번째 인덱스로 배열에서 오브젝트를 한 번 찾고, 그 다음 두 번째 인덱스로 한 번 더 찾은 뒤 아까 사용했던 slice 메서드를 사용해 주었습니다.  제 기준에 이 로직은 복잡했고, 동료가 이 코드를 본다면 바로 이해하기 힘들 것 같다고 판단했습니다. 그리고 지금 depth 2인데 만약 depth 3까지 가야 한다면 유지보수 하기가 너무 힘들 것이 벌써 보였습니다.
  23. 업데이트 로직도 상황은 비슷했습니다. 첫 번째 인덱스와 두 번째 인덱스로 각각 오브젝트를 찾아간 다음 바꾸려는 property와 value를 바꾸어 주고 다시 두 번 오브젝트를 배열에 갈아끼우는 식으로 해야 하죠. 저는 이 문제를 어떻게 더 단순하게 해볼 수 있을지 고민해 보았습니다. 제가 state 안에서 로직을 지지고 볶는 것에는 한계가 있었습니다. 그래서 저는 저희 팀에서 이전부터 사용하고 있던 react-hook-form 공식문서를 다시 한 번 읽어보기 시작했습니다.
  24. 제가 혼자서 앓는 소리를 하는 걸 보던 옆자리에 앉은 팀 동료의 한 마디
  25. 이 공식문서에서 저는 useFieldsArray라는 API를 발견하게 됩니다. 이 API는 공식 문서에 나온 설명을 그대로 읽어보면, 필드 배열의 동작을 위한 커스텀 훅이라고 되어 있습니다.
  26. 저는 바로 적용을 해 보았고, 코드가 정말 간결하고 명료해 지는 결과를 얻게 되었습니다. 문제가 해결이 되자 저는 작업에 속도가 다시 나기 시작했고 이렇게 금방 끝낼 수 있을 거라 굳은 확신을 가지게 되었습니다. 그러나...
  27. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  28. 얼마 지나지 않아 두 번째 문제를 발견하게 됩니다. 그것은 바로 control이 제대로 동작하지 않는 이슈였습니다.
  29. 여기서 문제가 발생했는데, 중첩된 form data를 하나의 useFieldsArray로 관리하니 control이 하위 뎁스의 컴포넌트에서 정상적으로 동작하지 않는 이슈가 있었습니다.
  30. 여기서 문제가 발생했는데, 중첩된 form data를 하나의 useFieldsArray로 관리하니 control이 하위 뎁스의 컴포넌트에서 정상적으로 동작하지 않는 이슈가 있었습니다.
  31. control이 무엇인지 간단하게 설명을 해 보도록 하겠습니다. react-hook-form은 앞서 말씀드린 것 처럼 데이터를 중앙에서 관리하면서 그 데이터의 조작을 control이라는 useForm API의 프로퍼티를 가지고 하게 됩니다. 그래서 이 control을 useFieldsArray에서도 쓸 수가 있고, 오른쪽 예제 코드처럼 컴포넌트에서 Controller 컴포넌트에 control 필드로 넣으면 react-hook-form의 데이터가 바뀌는 로직을 맡길 수 있습니다.
  32. 제가 이 당시 구현했던 방식은 fields와 control을 context state에서 첫 번째 뎁스인 섹션 컴포넌트로 내려주고, 그걸 섹션 컴포넌트가 받아서 핸들링을 한 후 그 자녀 컴포넌트인 질문 컴포넌트로 내려주게 했습니다. 비슷한 방식으로 답변 컴포넌트까지 context의 중앙화된 fields와 control을 내려 받도록 구현했습니다. 이렇게 하니까 정상적으로 동작이 되지 않더라구요. 이 문제를 해결하기 위해 참 많은 시간과 노력을 들였던 기억이 납니다..
  33. 제가 이 당시 구현했던 방식은 fields와 control을 context state에서 첫 번째 뎁스인 섹션 컴포넌트로 내려주고, 그걸 섹션 컴포넌트가 받아서 핸들링을 한 후 그 자녀 컴포넌트인 질문 컴포넌트로 내려주게 했습니다. 비슷한 방식으로 답변 컴포넌트까지 context의 중앙화된 fields와 control을 내려 받도록 구현했습니다. 이렇게 하니까 정상적으로 동작이 되지 않더라구요. 이 문제를 해결하기 위해 참 많은 시간과 노력을 들였던 기억이 납니다..
  34. 이 문제를 해결한 방법은 고민의 시간과 무게에 비해서 비교적 간단했습니다. 기존에 context에서 useFieldsArray를 관리하고 여기서 fields를 섹션 컴포넌트로 내려주고, 그 fields의 섹션 값이 있는데 그 값의 children을 섹션 컴포넌트에서 또 다른 useFieldsArray API를 통해 depth2Fields 이런 식으로 새로 만들어 주었습니다. 그걸 질문 컴포넌트로 내려주었고 비슷한 방법으로 답변 컴포넌트로 갈 때도 depth3Fields를 새로 만들어 주었습니다. 이 문제를 해결하면서 useFieldsArray를 만든 의도가 해당 배열 안의 단순한 오브젝트에서 값을 CRUD하게 하기 위한 의도로 만들었다는 것을 알게 되었습니다.
  35. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  36. 이제 진짜 되는가 싶더니 또 여러 가지 문제들이 저를 가로막았습니다. 바로 유효성 검사와 불변성에 관련된 이슈였습니다.
  37. 하나의 문진 데이터는 수십 개 이상의 input이 있고, 이 가운데 값이 계속해서 바뀌게 됩니다. 그리고 값이 바뀔 때 마다 유효성 검사를 진행해 주어야 합니다.
  38. 하나의 문진 데이터는 수십 개 이상의 input이 있고, 이 가운데 값이 계속해서 바뀌게 됩니다. 그리고 값이 바뀔 때 마다 유효성 검사를 진행해 주어야 합니다.
  39. 여기에서 하나의 데이터가 바뀔 때 다른 데이터까지 같이 유효성 검사를 해 주어야 하는 일이 발생했습니다. 예를 들면 같은 값을 가질 수 없는 여러 개의 인풋이 있는 경우 중복 체크가 필요한 상황이 있었습니다.
  40. 유효성 검사를 어떻게 깔끔하게 처리할지에 대한 고민을 하면서 또 react-hook-form 공식문서를 유심히 보게 되었고, 두 가지 정보를 바탕으로 해결할 수 있었습니다. 첫 번째는 useForm의 reValidateMode 였습니다. 이 값은 값이 제출될 때 마다 유효성 검사를 해주는 옵션이었는데요. 이 이벤트 리스너를 onChange로 설정하면 값이 바뀔 때 마다 모든 필드에 대해서 유효성 검사를 해줄 수 있었습니다.
  41. React-hook-form 은 다양한 스키마 도구를 지원
  42. 그리고 react-hook-form과 궁합이 잘 맞는 zod라는 스키마 라이브러리를 도입했습니다. 이런이런 특징
  43. zod 에서 react-hook-form에 제공하는 zod resolver를 통해 여러 중첩된 오브젝트의 필드별로 상이한 유효성 검사 로직을 하나의 스키마에서 깔끔하게 관리를 할 수 있게 되었습니다.
  44. 진짜 끝나나 싶었는데, 또 마지막에 한 가지 체크할 부분이 더 있었습니다. 바로 불변성과 관련된 부분이었습니다. 여러분들이 잘 아시는 것처럼 spread operator로 값을 복사하면 얕은 복사가 이루어지게 됩니다. 우리가 일반적으로 오브젝트를 다룰 때 뎁스가 그리 깊지 않은 경우가 많아서 그런 경우에는 문제가 되지 않습니다.
  45. 하지만 뎁스가 두 개, 세 개 깊어진다면 이야기가 달라집니다. 불변성이 유지가 되지 않고, 사이드 이펙트가 발생할 수 있으며, 이는 프로그램이 예측 가능성을 유지할 수 없기 때문에 해결을 하고 가야 합니다.
  46. 하지만 뎁스가 두 개, 세 개 깊어진다면 이야기가 달라집니다. 불변성이 유지가 되지 않고, 사이드 이펙트가 발생할 수 있으며, 이는 프로그램이 예측 가능성을 유지할 수 없기 때문에 해결을 하고 가야 합니다.
  47. 여러가지 방법이 있겠지만, 저는 이 부분에서는 immer라는 라이브러리의 도움을 받아 해결했습니다. immer는 상태에서 불변성을 유지할 수 있게 도와주는 라이브러리입니다.
  48. 각각의 장단점 소개
  49. immer를 적용해 주니 코드가 불변성을 유지할 수 있게 되었고, 덤으로 더 간결해지고 깔끔해 졌습니다.
  50. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  51. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  52. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  53. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  54. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  55. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  56. 저는 이 작업을 하면서 세 가지 정도의 문제를 마주하였습니다.
  57. 개발적인 부분 이외에도 배운 점이 참 많습니다.
  58. 개발적인 부분 이외에도 배운 점이 참 많습니다.