DOKBARO 프로젝트에 실제로 어떻게 상태관리를 적용했는지에 대해 기록해보려고 한다.
이 포스팅은 DOKBARO를 개발하면서 경험한 것을 기반으로 제작하였습니다.
자기계발과 성장을 위해 독서와 스터디를 활용하는 개발자들을 위한 퀴즈 학습 플랫폼, DOKBARO입니다.
개발 서적을 즐겨 읽지만, 매번 내용을 제대로 이해했는지 확인하기 어렵지 않으셨나요? 혹은 이해 부족으로 인해 독서 스터디가 소수만 적극적으로 참여하는 형태로 변질되는 경험을 하셨을지도 모릅니다.
그래서, DOKBARO는
📚 퀴즈 출제 및 풀이 기능으로 도서 내용을 재미있고 효과적으로 이해하도록 도와드려요.
💡 스터디 리포트 기능으로 스터디원들이 책에 대해 자유롭게 의견을 나누고, 서로의 학습 현황을 확인할 수 있어요.
DOKBARO와 함께라면 도서 이해도를 높이고, 스터디 활동을 보다 풍성하고 활발하게 만들어 이상적인 독서 환경을 경험하실 수 있습니다. ✌️
현재 베타 오픈중이니 아래 링크를 통해 이용해보실 수 있어요!
https://dokbaro.com
소개 끝~..
특히 정리하고싶은 부분은 퀴즈 작성 단계에서 어떻게 상태관리를 적용했는지이다. 좌측 progress bar를 통해 여러 단계의 fragment로 진입 할 수 있고, 그 과정에서 각 fragment의 존재하는 입력 창의 유효성 검사를 통과해 "다음"버튼이 활성화 되는지 여부에 대한 상태관리가 필요했다.

퀴즈 작성 단계에서 "다음 버튼" 활성화 여부를 여러 페이지(fragment이나 편의상 페이지라고 부르겠다)에서 분산적으로 관리하고 있었다. 예를 들어, 모든 페이지 코드에서 아래와 같은 로직으로 코드를 작성해줬다.
const [isQuizNextButtonEnabled, setIsQuizNextButtonEnabled] = useAtom<boolean>(isQuizNextButtonEnabledAtom);
useEffect(()=>{
setIsQuizNextButtonEnabled(false);
},[]);
// 유효성 검사 코드.. (생략)
if(유효성검사 통과){
setIsQuizNextButtonEnabled(true);
}
문제점

퀴즈 작성 단계가 현재 5개가 있고, 모든 페이지에 위와같은 상태 변경 코드를 작성해줘야 했다. 유효성 검증코드가 생략되어 비교적 간결해 보이나 원래 코드는 정말 .. 쉽지않은 코드였다. (유효성 검사도 개선 전엔 custom hook으로 작성되어있지 않아 코드가 매우 .. 길고 어지러웠다.)
결정적으로는 오류가 계속 발견되었다.
문제는 상태를 관리하는 로직이 여러 곳에 분산되어 있어, 어떤 페이지에서 상태가 변경되고 있는지, 어떤 순서로 로직이 실행되고 있는지를 파악하기 어려웠다. 특히, 스터디 선택 단계는 항상 true 상태를 유지해야 했고, 퀴즈 작성 폼 단계에서는 폼을 추가하거나 삭제할 때 상태가 변경되어야 했지만, 이러한 상태 변경이 다른 단계에도 영향을 미치게 되었다.
상태를 한 곳에서 관리하도록 개선하기 위해, get 메서드를 사용하여 모든 상태를 한 번에 계산하도록 했다. 이제 상태 변경은 atom에서만 이루어지며, 각 페이지에서 불필요한 set 호출을 피할 수 있게 되었다.
개선 전 코드를 다시 보자.
const [isQuizNextButtonEnabled, setIsQuizNextButtonEnabled] = useAtom<boolean>(isQuizNextButtonEnabledAtom);
useEffect(()=>{
setIsQuizNextButtonEnabled(false);
},[]);
// 유효성 검사 코드.. (생략)
if(유효성검사 통과){
setIsQuizNextButtonEnabled(true);
}
각 페이지에서 버튼의 활성화 상태를 변경하다 보니, 로직이 복잡했다.
개선 후 코드
상태 변경 로직을 atom 안으로 통합하여, 퀴즈생성단계Atom과 퀴즈폼입력값Atom을 기반으로 상태를 계산하게 했다.
export const 퀴즈다음버튼활성화여부Atom = atom<boolean>((get) => {
const step = get(퀴즈생성단계Atom);
const quizInfo = get(퀴즈폼입력값Atom);
switch (step) {
case 스터디선택단계:
return true;
case 책선택단계:
return quizInfo.book !== null;
case 퀴즈기본정보작성단계:
case 퀴즈기본정보작성하위단계:
return quizInfo.title !== null && quizInfo.description !== null;
case 퀴즈폼입력단계:
return 배열유효성검증 && 길이가0이아님;
case 기타설정단계:
return quizInfo.viewScope !== null;
default:
return true;
}
});
이제 상태 값은 각 단계에 맞게 계산되고, 페이지에서 별도로 상태를 설정할 필요가 없어졌다. 버그리포트는 더이상 올라오지 않게되었다.
Happy Ending~