Nari

LevelUp Piano — MIDI 피아노 연습 앱 · 블로그

한 앱, 네 개의 사용자 — 역할 설계 이야기

블로그 목록
개발 일지2026-07-05
아키텍처역할 설계FlutterKotlin
역할 선택 화면
역할 선택 — 선생님 · 학생 · 혼자 배우기(독립 학습자). 운영자는 내부 스태프라 여기 없다.
4역할
1공용 플레이어 코어
2코어 공유 역할 (학생·러너)
서버전환 승인

왜 역할을 나눴나

피아노 연습이라는 같은 목적을 두고도, 사람마다 관계가 다릅니다. 선생님에게 배우는 사람이 있고, 혼자 하는 사람이 있고, 가르치는 사람이 있습니다. 이들을 하나의 화면에 욱여넣으면 누구에게도 맞지 않는 앱이 됩니다.

그래서 LevelUp Piano는 처음부터 역할(role) 을 1급 개념으로 두었습니다. 로그인하면 자신이 누구인지 고르고, 이후의 홈 화면·권한·데이터가 그에 맞게 흐릅니다.

네 역할

Learnerindependent · subscriptionStudentteacher-assignedTeachercreate · assignOperatorofficial catalogShared Player Coregameplay · judging · resultsTeacher · Operator = 관리(셸)
역할 구조 — 학생·독립 학습자는 같은 플레이어 코어를 공유(곡 출처만 다름), 선생님·운영자는 관리 셸
  • 독립 학습자(Learner) — 선생님 없이 혼자 배우는 일반 사용자. 구독으로 커리큘럼과 카탈로그를 이용하고, 게이미피케이션이 습관을 만듭니다.
  • 학생(Student) — 선생님에게 배정받은 곡을 연습합니다. 선생님이 진도와 약점을 봅니다.
  • 선생님(Teacher) — MIDI로 곡을 만들어 학생에게 배정하고, 학생 목록·레슨·코칭을 관리합니다.
  • 운영자(Operator) — 모두가 공유하는 공식 곡 카탈로그를 관리하는 내부 스태프 역할입니다.

역할마다 접근 권한이 다릅니다. 이 경계는 화면에 흩뿌린 조건문이 아니라 서버가 라우트마다 역할을 확인해 강제합니다(권한이 없으면 403).

권한LearnerStudentTeacherOperator
곡 조회카탈로그배정 곡자기 곡전체
곡 업로드
학생 진도 조회배정 학생
공식 카탈로그 게시
점수 제출테스트(미제출)

학생 vs 독립 학습자 — 닮았지만 다른

학생 홈 화면
학생 홈 — 오늘의 과제 Stage · 경로(Path) · 반 순위 · 오늘의 미션 · 배정 곡. 마스코트가 응원한다.

가장 흥미로운 지점은 학생독립 학습자의 관계였습니다. 둘 다 "연주하고 점수를 받는" 플레이어지만, 곡이 오는 곳이 다릅니다.

  • 학생의 곡은 선생님이 배정한 곡에서 옵니다.
  • 독립 학습자의 곡은 공식 카탈로그의 커리큘럼에서 옵니다.

연습 화면과 판정 엔진, 결과·셀러브레이션은 완전히 같아야 하고, 곡의 출처만 달라야 합니다. 그래서 공용 플레이어 코어(진행 읽기·게임플레이·결과)를 두고, 그 위에 역할별 데이터 소스와 홈 셸을 얹는 구조로 잡았습니다. 코어는 곡이 어디서 오는지 모릅니다 — 배정 곡이든 카탈로그든 같은 계약을 구현할 뿐입니다(구조를 단순화하면 이렇습니다).

// 플레이어 코어는 곡의 출처를 모른다 — 이 계약만 안다.
abstract interface class SongSource {
  Future<List<PlayableSong>> load(); // Student → 배정 곡, Learner → 카탈로그
}

두 역할이 같은 엔진을 공유하니, 게임플레이를 개선하면 양쪽이 동시에 좋아집니다.

역할별 셸

학생 로그인 → 역할 선택 → 홈 진입 (무음 자동재생·루프)

각 역할은 자기만의 (홈·탭·설정)을 가집니다.

  • 학습자 셸: 커리큘럼 경로, 리그, 친구, 상점
  • 학생 셸: 배정 곡, 선생님의 응원, 진도
  • 선생님 셸: 대시보드, 곡 관리, 학생 관리, 초대 코드, 레슨
  • 운영자 셸: 공식 카탈로그 관리(게시/숨김·테스트 플레이·업로드)

라우팅은 로그인 상태와 역할에 따라 적절한 셸로 보냅니다. 구독이 필요한데 없으면 결제 화면으로, MIDI 연결이 필요한 흐름이면 연결 화면으로 — 역할 중립적인 화면(연습·MIDI 연결)은 여러 역할이 공유합니다.

역할 전환

운영자는 별도로 가입하는 계정이 아니라, 자격이 있는 스태프가 자신의 원래 역할과 운영자 모드를 오갈 수 있게 설계했습니다. 전환은 서버가 승인하며, 언제든 원래 역할로 안전하게 복귀합니다. 전환 중에는 전체 화면 로딩으로 끊기지 않도록 매끄럽게 처리하고, 전환이 실패해도 현재 역할을 그대로 유지합니다.

💡 Insight

학생과 독립 학습자는 같은 게임플레이 엔진을 공유하고 곡의 출처만 다르다. 공용 코어 + 역할별 셸 — 사용자층이 완전히 달라도 핵심 경험은 한 번만 잘 만들면 됐다.

설계에서 배운 것

역할을 데이터 모델·라우팅·서버 인가의 공통 축으로 두니 "누가 무엇을 볼 수 있고 수정할 수 있는가"가 화면 조건문에 흩어지지 않고 한 곳에서 결정됩니다. 그리고 공용 코어 + 역할별 셸이라는 분리 덕분에, 사용자층이 완전히 달라도 핵심 경험(연주와 판정)은 한 번만 잘 만들면 됐습니다. 권한이 서버로 모이니 역할별 라우팅·권한 API·운영자 전환을 각각 테스트로 고정할 수도 있었습니다. 여러 페르소나를 담는 앱에서 가장 크게 남은 교훈입니다.