<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Frontend on 얼쑤 · Team Edo</title><link>https://team-edo.github.io/categories/frontend/</link><description>Recent content in Frontend on 얼쑤 · Team Edo</description><generator>Hugo</generator><language>ko-kr</language><lastBuildDate>Mon, 09 Nov 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://team-edo.github.io/categories/frontend/index.xml" rel="self" type="application/rss+xml"/><item><title>글씨를 키워도 연습을 이어갈 수 있을까?</title><link>https://team-edo.github.io/posts/2026/11/compose-practice-layout-font-scale/</link><pubDate>Mon, 09 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/compose-practice-layout-font-scale/</guid><description>&lt;p>휴대폰으로 단소를 연습할 때는 현재 음의 운지를 보고, 다음에 짚을 손가락을 미리 확인합니다. 악보에서 음을 찾고 재생 버튼도 눌러야 합니다. 시스템 글씨를 키웠더니 정간 아래의 가사가 잘리고, 운지 화면에서는 지금 짚을 그림보다 다음 두 음의 그림이 더 크게 보였습니다. 두 문제 모두 글자가 들어가는 칸과 실제 화면에 남은 높이를 따로 다루면서 생겼습니다.&lt;/p>
&lt;h2 id="화면에-남은-높이부터-재기">화면에 남은 높이부터 재기&lt;/h2>
&lt;p>Galaxy S25+의 도라지타령 연습 준비 화면에서 악보에 쓸 수 있는 높이는 약 226dp였습니다. 기본 크기로 두 각을 위아래로 쌓으려면 약 400dp가 필요했습니다. 여기서 각은 정간보의 여러 칸을 묶은 단위입니다. 페이지에 넣을 각의 개수만 조정하면, 같은 두 각이라도 글꼴 설정이나 칸 안 음표 수에 따라 잘리는 부분이 달라집니다.&lt;/p></description></item><item><title>악기 이름을 못 불러와도 내 점수는 볼 수 있어야 한다</title><link>https://team-edo.github.io/posts/2026/11/practice-result-optional-data/</link><pubDate>Mon, 09 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/practice-result-optional-data/</guid><description>&lt;p>같은 곡을 두 번 연주한 뒤 첫 번째 기록을 열었다고 해봅시다. 화면에 곡명이 맞게 표시돼도 점수와 틀린 위치가 두 번째 연주의 것이라면, 사용자는 첫 번째 연주에서 무엇을 고쳐야 할지 판단할 수 없습니다. 반대로 악기 이름만 늦게 도착한 경우라면, 점수와 악보부터 확인할 수 있습니다.&lt;/p>
&lt;p>얼쑤의 이전 연주 결과 화면은 이 두 상황을 서로 다른 상태로 처리합니다. 선택한 기록의 결과인지 먼저 확인한 뒤, 점수를 읽는 데 필요한 데이터와 나중에 도착해도 되는 데이터를 나눴습니다.&lt;/p></description></item><item><title>절쑤가 뛰어도 그림자는 바닥에</title><link>https://team-edo.github.io/posts/2026/11/mascot-gif-floor-shadow/</link><pubDate>Mon, 09 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/mascot-gif-floor-shadow/</guid><description>&lt;p>얼쑤의 팀 캐릭터 절쑤는 갓을 쓰고 한복을 입은 백호입니다. 캐릭터가 확정된 뒤 앱 곳곳에 쓸 수 있도록 포즈 시트의 일러스트를 2초짜리 움짤로 만들었습니다. 감정 표현 12종과 정면·옆면·뒷면 같은 기본 포즈 7종입니다. 시트에서 포즈를 하나씩 잘라 낸 그림에 HTML과 GSAP로 점프, 흔들기, 숨쉬기 같은 움직임을 붙였습니다. 그 장면을 HTML을 영상으로 렌더링하는 오픈소스 도구 &lt;a href="https://hyperframes.heygen.com">HyperFrames&lt;/a>로 480×480, 15fps GIF로 뽑았습니다. 구현은 AI 코딩 에이전트에게 맡기고, 저는 결과 프레임을 보며 방향을 정했습니다.&lt;/p></description></item><item><title>한글을 쳤는데 Enter가 동작하지 않았다</title><link>https://team-edo.github.io/posts/2026/11/compose-web-korean-ime-enter/</link><pubDate>Mon, 09 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/compose-web-korean-ime-enter/</guid><description>&lt;p>정간보 편집기에서 칸을 고르고 &lt;code>청태&lt;/code>를 입력한 뒤 Enter를 눌렀습니다. 입력칸에 글자는 보였지만 악보에 음이 적히지 않았습니다. 미리보기까지 사라져 있었으니, 처음 쓰는 사람이라면 입력이 아직 진행 중인지, 이 음을 쓸 수 없는지, Enter가 동작하지 않은 건지 구별하기 어렵습니다.&lt;/p>
&lt;h2 id="글자가-보이는-시점과-조합이-끝나는-시점">글자가 보이는 시점과 조합이 끝나는 시점&lt;/h2>
&lt;p>한글 입력기는 자모를 모아 글자를 만들고, 조합 중인 범위도 입력 상태에 남깁니다. Compose의 &lt;code>TextFieldValue&lt;/code>에는 현재 문자열과 조합 범위가 함께 들어 있습니다. 브라우저에서는 글자 조합 이벤트와 키 이벤트를 따로 다룹니다. Enter를 눌러도 그 키 이벤트가 앱의 완료 콜백으로 이어지는지는 입력기와 UI가 이벤트를 처리하는 방식에 달려 있습니다. &lt;a href="https://www.w3.org/TR/uievents/#keys-during-composition">W3C UI Events의 조합 중 키 이벤트&lt;/a>&lt;/p></description></item><item><title>곡 목록은 200, 콘솔 오류는 어디서 왔나</title><link>https://team-edo.github.io/posts/2026/11/console-error-origin-before-api/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/console-error-origin-before-api/</guid><description>&lt;p>얼쑤의 곡 목록 API를 점검할 때 Chrome 콘솔에 &lt;code>Cannot read properties of undefined (reading 'sentence')&lt;/code> 오류가 &lt;strong>아홉 건&lt;/strong> 찍혔습니다. 곡 목록을 요청하는 동안 보인 오류라 처음에는 응답을 처리하는 코드부터 봐야 할지 판단하기 어려웠습니다. 같은 요청의 응답은 &lt;strong>HTTP 200&lt;/strong>이었고 본문에는 &lt;code>isSuccess: true&lt;/code>가 들어 있었습니다.&lt;/p>
&lt;h2 id="콘솔에서는-스택의-파일-출처를-봤습니다">스택에서 파일 출처 확인&lt;/h2>
&lt;p>오류 아홉 건의 스택에는 브라우저 확장의 &lt;code>contentScript.js&lt;/code>가 있었습니다. 파일명만으로는 앱의 스크립트인지 알기 어려웠고, 출처가 확장 프로그램의 주소라는 점이 단서였습니다. 같은 기록에는 Swagger UI의 번들에서 발생한 응답 표시 오류도 따로 있었습니다. 두 종류의 메시지는 비슷한 시점에 떴지만 서로 다른 스크립트에서 실행된 코드였습니다. &lt;a href="https://developer.chrome.com/docs/devtools/console/reference">Chrome DevTools 콘솔 문서&lt;/a>&lt;/p></description></item><item><title>Android SDK 없이 녹음 업로드 검증</title><link>https://team-edo.github.io/posts/2026/10/verify-kmp-without-android-sdk/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/verify-kmp-without-android-sdk/</guid><description>&lt;p>얼쑤의 녹음 업로드에는 두 종류의 코드가 있습니다. 연결이 끊긴 녹음을 다시 보낼지, 언제 보관 기록을 지울지는 공통 로직입니다. 녹음을 앱의 보관 디렉터리로 실제 이동하는 일은 플랫폼마다 다릅니다. 개발 환경에 Android SDK가 없었을 때도 업로드 판단을 검증할 수 있도록 두 부분을 분리했습니다.&lt;/p>
&lt;h2 id="업로드-판단은-브라우저의-공통-테스트에서-확인했다">Wasm에서 공통 업로드 로직 검사&lt;/h2>
&lt;p>공통 코드는 파일의 보관·존재 확인·삭제를 &lt;code>RecordingFiles&lt;/code> 인터페이스로 호출합니다. 전송도 인터페이스로 받습니다. Wasm에서 실행한 공통 테스트는 두 인터페이스의 가짜를 넣어, 연결 실패 뒤 기록이 남는지와 같은 세션의 재전송 결과를 확인합니다. 플랫폼의 파일 구현을 불러오지 않아도 테스트가 실행되는 이유입니다.&lt;/p></description></item><item><title>녹음 업로드 202 이후 앱이 기다리는 것</title><link>https://team-edo.github.io/posts/2026/10/recording-analysis-polling/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/recording-analysis-polling/</guid><description>&lt;p>얼쑤에서 연주 녹음을 보낸 사용자는 평가 화면을 기다립니다. 앱 입장에서는 파일을 서버에 전달했는지, 평가를 받을 수 있는지 두 가지를 확인해야 합니다. 둘을 한 요청에서 처리하면 파일을 저장한 뒤에도 분석이 끝날 때까지 업로드 연결을 유지해야 합니다. 저는 녹음을 접수한 뒤 &lt;code>202 Accepted&lt;/code>로 답하고, 앱이 같은 연습 세션의 상태를 따로 조회하도록 연결했습니다. &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#section-15.3.3">RFC 9110의 202 Accepted&lt;/a>&lt;/p>
&lt;h2 id="서버가-접수했다고-답하는-시점">서버의 접수 시점&lt;/h2>
&lt;p>백엔드는 앱이 보낸 파일을 검사해 R2에 원본을 저장합니다. DB에 원본 키와 녹음 정보를 기록하고 세션을 &lt;code>PENDING&lt;/code>으로 바꾼 뒤, 저장이 커밋되면 업로드를 &lt;code>202&lt;/code>로 응답합니다. 분석 제출은 같은 커밋을 기준으로 이벤트 리스너가 따로 시도합니다.&lt;/p></description></item><item><title>상태 조회가 대기 녹음을 지울 수 있던 이유</title><link>https://team-edo.github.io/posts/2026/10/keep-pending-recordings-until-upload/</link><pubDate>Thu, 29 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/keep-pending-recordings-until-upload/</guid><description>&lt;p>연주를 끝낸 직후 연결이 끊기면, 아직 서버에 도착하지 않은 녹음을 다시 보낼 수 있어야 합니다. 얼쑤에서는 녹음 파일을 기기의 보관 위치로 옮긴 다음, 어느 세션에 어떤 파일을 보낼지 기록하고 업로드하도록 만들었습니다. 앱을 다시 열어도 그 기록에서 보낼 녹음을 찾을 수 있습니다.&lt;/p>
&lt;p>리뷰에서 파일을 옮기는 일과 대기 기록을 저장하는 일 사이에 틈이 있다는 걸 발견했습니다. 새 파일은 이미 보관 위치에 있지만, 상태 조회에서 읽을 대기 기록은 아직 없습니다. 기존 조회 함수는 기록이 없으면 보관 폴더에 남은 파일을 정리했습니다. 이 순간에 조회가 끼어들면 방금 옮긴 녹음도 버려도 되는 파일로 보고 지울 수 있었습니다.&lt;/p></description></item><item><title>악보는 29분 30초, 연습은 10분</title><link>https://team-edo.github.io/posts/2026/10/practice-time-idle-checkpoints/</link><pubDate>Tue, 27 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/practice-time-idle-checkpoints/</guid><description>&lt;p>악기를 불면서 악보를 볼 때는 한동안 화면을 건드리지 않습니다. 반대로 악보를 열어 둔 채 자리를 비울 수도 있습니다. 얼쑤의 성장 화면에 연습 시간을 표시하려면 이 두 상황을 구분할 규칙이 필요했습니다. 저는 팀과 집계 기준을 정리하고 서버의 구간 저장 API를 구현했습니다. 앱에서 시간을 재고 기기에 보관해 보내는 부분은 프론트엔드 팀원이 구현했습니다.&lt;/p>
&lt;h2 id="화면이-보인-시간과-연습으로-인정한-시간">화면 표시 시간과 인정 시간&lt;/h2>
&lt;p>악보가 앞에 보이기 시작하면 구간을 하나 엽니다. 화면을 벗어나거나 앱이 뒤로 가면 구간을 닫고, 돌아왔을 때는 새 구간을 엽니다. 구간마다 화면이 보인 시간과 그중 연습으로 인정한 시간을 따로 남깁니다. 성장 화면의 연습 시간은 인정 시간의 합입니다.&lt;/p></description></item></channel></rss>