본문으로 건너뛰기

← 블로그 · Projects

교육 콘텐츠를 만들며 배운 것: 원본과 풀이 상태를 나누는 이유

#회고 #React #TypeScript #상태관리

유주환 · Founder, Full Stack Developer

토리스(Toris) · 발행

프로젝트를 돌아보는 30일 · 1/30
과거 프로젝트 경험을 날짜별로 엮은 회고입니다. 표시 날짜는 연재 기준일이며, 실제 작업일과 다를 수 있습니다.

교육 콘텐츠 플랫폼을 만들던 일을 돌아보면, 복잡했던 것은 화면의 개수가 아니었다. 같은 문제를 보여주면서도 학생이 어디까지 풀었는지, 무엇을 눌렀는지, 다시 들어왔을 때 무엇을 남겨야 하는지가 각각 달랐다. 화면에 필요한 값을 한 객체에 모으는 방식만으로는 이 차이를 설명하기 어려웠다.

같은 화면에 있어도 수명은 달랐다

문제 본문과 정답은 콘텐츠의 원본이다. 선택한 보기와 풀이 여부는 사용 중 생기는 상태다. 둘을 함께 수정하면 초기화의 의미부터 모호해진다. 다시 풀기를 눌렀을 때 선택만 지워야 하는데 원본까지 다시 구성해야 하고, 다른 장면으로 이동할 때 어느 값이 남아야 하는지 매번 판단해야 한다.

당시에는 도메인 데이터와 풀이 상태를 분리하고, 문제 ID를 키로 상태를 관리했다. 이 선택이 좋았던 이유는 저장할 데이터가 줄어서만은 아니다. 어떤 값의 소유자가 콘텐츠이고 어떤 값의 소유자가 사용자인지 코드에서 읽을 수 있었기 때문이다.

지금 다시 적어 보는 경계

아래는 당시 소스를 그대로 가져온 것이 아니라, 그 경험을 설명하기 위해 단순화한 타입이다.

type Question = { id: string; answer: string };
type Attempt = { selected: string | null; submitted: boolean };
type Attempts = Record<string, Attempt>;

이렇게 나누면 다시 풀기는 Attempts를 초기화하는 작업이 된다. 문제를 수정하는 작업과 학생의 선택을 수정하는 작업도 구별된다. 다만 ID가 안정적이라는 전제가 필요하다. 렌더링할 때마다 ID를 새로 만들면 상태를 분리한 효과가 사라진다.

아쉬웠던 것은 검증의 단위였다

당시에는 기능을 완성하는 데 집중했고, 테스트와 문서가 충분하지 않았다는 아쉬움이 남았다. 지금 다시 한다면 화면이 잘 나오는지만 확인하지 않겠다. 문제를 풀고 다음 장면으로 이동한 뒤 돌아오기, 다시 풀기, 새로고침을 서로 다른 시나리오로 점검하겠다. 같은 초기화 함수로 묶어도 되는 행동인지부터 확인하는 것이다.

이 경험 이후 상태를 설계할 때 먼저 묻는 질문이 생겼다. “어디에 저장할까?”에 앞서 “누가 바꾸고, 언제 없어져야 할까?”를 묻는다. 저장소 선택은 그 답이 나온 다음이어도 늦지 않았다.

관련 기록: 셈웨어 교육 플랫폼과 풀스택 전환

READERS / LIVE

읽고 난 뒤의 대화

조회

댓글은 바로 공개됩니다. 개인정보와 공격적인 표현은 남기지 말아주세요.