Shopify 앱에 작업 기록 붙이기

상품 일괄 변경에 되돌리기를 붙이고 상세 화면과 재조회를 다듬은 기록
calendar icon

2026년 10월 2일

조회 4

tag icon

지난 글에서는 Shopify 관리자 안에서 상품 목록과 편집 패널을 함께 쓰는 앱을 만들었다. 상품을 하나씩 열었다가 목록으로 돌아오는 왕복을 줄이는 것이 시작이었다. 목록 옆에서 가격과 재고, 분류를 고칠 수 있게 되자 다음에 필요한 것도 조금씩 달라졌다.

상품 하나의 값을 바꾸는 기능이 있다고 해서 여러 상품을 관리하는 일이 바로 편해지는 것은 아니었다. 같은 비율로 가격을 조정하거나, 특정 공급업체의 상품만 골라보거나, 방금 저장한 변경을 다시 확인하려면 여전히 손이 많이 갔다. 특히 일괄 작업은 입력을 줄여주는 만큼 잘못 바꿨을 때 확인할 범위도 커졌다.

"여러 개를 한 번에 바꾸게 했으면, 무엇을 바꿨는지도 따라갈 수 있어야 하지 않을까?"

그래서 이번에는 일괄 가격 규칙과 목록 필터를 늘리고, 저장한 작업을 기록하는 화면을 붙이기로 했다. 되돌리기 버튼만 만드는 것으로 끝나지는 않았다. 기록의 의미를 정하고, 목록과 상세에 나눠 담고, 다시 열 때 기다리지 않도록 고치는 과정이 함께 필요했다.


반복 작업을 줄이는 방향

같은 값을 넣는 기능에서 계산 규칙으로 넓혔다

처음 일괄 가격 변경은 선택한 상품에 같은 판매가를 넣는 방식이었다. 단순해서 이해하기는 쉬웠지만, 가격대가 다른 상품을 함께 조정할 때는 쓸 수 있는 범위가 좁았다. 현재 가격을 기준으로 일정 비율을 올리거나 내리는 규칙이 필요했다.

판매가와 비교 가격을 함께 다루도록 규칙을 확장했다. 할인 시작은 현재 판매가를 비교 가격으로 옮기고 할인율을 적용하는 방식으로 만들었다. 할인 종료는 비교 가격을 판매가로 복원하고 비교 가격을 비우도록 했다. 이미 할인 중인 옵션이나 적용할 변화가 없는 옵션은 미리보기에서 구분할 수 있게 했다.

작업이번에 정한 동작
같은 금액 지정선택한 옵션의 판매가를 입력값으로 변경
퍼센트 조정현재 판매가를 기준으로 인상·인하
금액 증감현재 판매가에 지정 금액을 더하거나 빼기
할인 시작현재가를 비교 가격으로 옮긴 뒤 할인율 적용
할인 종료비교 가격으로 판매가를 복원하고 비교 가격 제거
비교 가격 편집비교 가격 지정 또는 제거

계산 중간에 소수점 오차가 쌓이지 않도록 금액은 통화의 최소 단위 정수로 다뤘다. 비율 계산 뒤에는 해당 통화의 소수 자릿수에 맞추고, 필요한 경우 1·10·100·1,000 단위로 끝자리를 정리하게 했다. 반올림·올림·내림은 같은 작업으로 보이지만 결과가 달라지므로 선택 항목으로 나눴다.

계산값을 바로 저장하지는 않았다. 적용할 상품과 옵션, 바뀌기 전후 판매가와 비교 가격을 먼저 보여주고 확인한 뒤 실행하도록 했다. 건너뛴 항목도 이유와 개수를 표시했다. 버튼을 눌렀다는 사실과 선택한 모든 항목이 실제로 바뀌었다는 사실을 같은 것으로 취급하지 않으려 했다.

무엇을 바꿀지 고르는 과정도 줄였다

일괄 작업이 편하려면 대상부터 쉽게 고를 수 있어야 했다. 공급업체·상품 유형·태그를 여러 개 고르고, 재고가 지정한 수량 이하인 상품을 함께 거르는 필터를 추가했다. 같은 조건 안의 값은 OR로, 서로 다른 조건은 AND로 묶었다.

예를 들어 공급업체 두 곳을 선택했다면 둘 중 하나에 해당하는 상품을 찾는다. 여기에 태그와 재고 조건을 더하면 그 조건도 함께 만족해야 한다. 적용한 값은 칩으로 남겨 하나씩 해제하게 했고, 필터 버튼에는 선택값 개수 대신 적용된 조건 개수를 표시했다.

드롭다운을 새로 하나씩 만들지는 않기로 했다. 기존 SelectMenu에 다중 선택과 목록 검색을 더했고, 필터와 컬렉션 선택에서 쓰는 칩은 ChipList로 묶었다. 재고 위치나 분류처럼 이미 쓰고 있던 선택창까지 같은 모양으로 맞춰야 한 곳만 고쳐도 다음 화면에 반영될 수 있었다.


되돌리기의 의미부터 정했다

현재 값을 덮기 전에 기록을 확인한다

작업 기록에는 한 번의 저장이나 일괄 실행을 묶을 식별자를 남겼다. 목록에서는 묶음 단위로 보고, 상세에서는 그 안에 들어간 상품과 작업을 나눠 확인하는 구조다. 기록마다 상품 이름과 변경 전후 값이 있어야 나중에 ID만 보고 상품을 다시 찾아가는 일을 줄일 수 있었다.

되돌리기에 필요한 값은 브라우저가 보내게 하지 않았다. 브라우저는 어떤 작업을 되돌릴지만 전달하고, 서버가 저장된 기록에서 전후 값을 바꾼 명령을 만든다. 기존 저장 경로의 현재 값 검사를 거치므로, 그 사이 다른 값으로 수정됐다면 되돌리기를 거부하도록 했다.

가령 판매가를 100에서 80으로 바꾼 기록이 있어도 현재 가격이 이미 90이라면 바로 100을 넣어서는 안 된다. 사용자는 앞선 변경 하나를 취소하려고 했는데, 나중에 한 변경까지 덮어버릴 수 있기 때문이다. 기록에 있는 이전 값이라고 해서 지금 적용해도 되는 값은 아니었다.

되돌릴 수 있는 종류도 제한했다. 상품 정보, 옵션 가격·코드, 컬렉션 변경은 저장한 전후 값을 이용한다. 재고와 이미지 작업은 이번 구현에서 기록만 보여주기로 했다. 모든 작업에 같은 버튼을 붙이는 것보다 지원하는 범위를 분명히 하는 편이 필요했다.

완료 여부와 되돌림 여부를 분리했다

처음에는 되돌려진 작업의 결과를 ‘되돌림’으로 표시했다. 그런데 완료 필터에서는 그 작업이 계속 보였다. 저장 자체는 성공했으므로 동작이 틀린 것은 아니었지만, 화면의 상태 이름과 필터가 가리키는 의미가 서로 달라졌다.

그래서 결과는 완료로 유지하고 되돌림 여부를 별도로 표시했다. 기존 작업이 나중에 취소됐다는 관계와, 그 작업이 실행됐을 당시 성공했는지는 다른 정보였다. 이 둘을 상태 한 칸에 밀어 넣으니 설명도 필터도 어색해졌다.

기록실행 결과상세에서 보여줄 관계
나중에 되돌려진 원래 작업완료 유지되돌림 시각과 ‘되돌린 작업 보기’
되돌리기로 새로 생성된 작업해당 실행 결과원래 작업 시각과 ‘원래 작업 보기’

두 기록 사이에는 양방향 이동 링크를 뒀다. 링크를 누르면 대상 작업을 펼치고 그 위치로 이동한 뒤 잠깐 강조한다. 다른 페이지에 있는 작업도 찾을 수 있도록 해당 기록을 포함하는 페이지를 조회하게 했다. 단순히 되돌렸다는 표시보다, 어떤 기록과 연결돼 있는지를 직접 따라가는 편이 확인하기 쉬웠다.

강조 표시에서도 작은 문제가 있었다. 서로 연결된 작업을 연달아 누르고 마우스를 그대로 두면 강조가 남아 보이는 경우가 있었다. 이동할 때 실행되는 강조와 평소 행의 호버를 분리해서, 마우스를 옮겨야만 표시가 풀리는 상태를 고쳤다.


목록과 상세의 경계를 다시 그렸다

간단한 정보까지 숨기면 목록이 불편해졌다

작업 기록 화면은 처음부터 만족스럽지 않았다. 상품 이름을 누르면 그 상품의 기록만 보게 했는데, 이름이 링크처럼 보이니 상품 상세 페이지로 이동할 것 같은 인상을 줬다. 결국 상품 범위 선택은 헤더의 별도 드롭다운으로 옮기고, 목록의 상품명은 읽는 정보로 돌려놓았다.

상세를 여는 화살표도 표 맨 왼쪽으로 옮겼다. 작업 종류를 읽기 시작하는 위치에서 바로 펼칠 수 있어야 했는데, 처음에는 변경 내용 열 중간에 들어가 있었다. 화면을 참고했다고 해도 요소의 모양만 비슷하면 같은 동작 흐름이 되는 것은 아니었다.

한때는 기본 행을 간단하게 만들겠다고 변경 내용 요약까지 뺐다. 이것도 지나쳤다. 가격이 얼마에서 얼마로 바뀌었는지, 어떤 이미지가 등록됐는지를 알려면 모든 패널을 하나씩 열어야 했다. 그래서 작업 종류와 한 줄 요약은 목록에 남기고, 여러 항목의 전체 전후 값과 되돌림 관계를 상세로 옮겼다.

이름도 구현 관점에서 사용자 관점으로 바꿨다. 파일을 상품에 붙이는 동작을 ‘이미지 연결’이라고 표시하고 있었지만, 화면에서 하는 일은 이미지를 등록하는 것이었다. 작업 이름과 편집 안내를 ‘이미지 등록’으로 맞추고, 변경 내용에는 같은 문구를 반복하는 대신 파일명을 표시했다.

선을 없앤 뒤에도 구분은 필요했다

펼침 패널 안에는 처음에 선이 너무 많았다. 요약 행 아래에도 선이 있고, 상세 영역 위에도 선이 있고, 각 항목 아래에도 선이 있었다. 정보는 많지 않은데 화면 전체가 작은 표 여러 개를 겹쳐 놓은 것처럼 보였다.

"수정 전에는 요약 행과 상세 항목마다 구분선이 겹쳐 있었다."

"수정 전에는 요약 행과 상세 항목마다 구분선이 겹쳐 있었다."

"선을 줄였는데, 이번에는 어디까지가 한 작업인지 안 보인다."

내부 구분선을 없애고 여백으로 정리하니 한결 나아졌다. 하지만 요약과 상세가 같은 면처럼 이어져 내부 패널이라는 느낌은 약했다. 상세를 들여쓰고 흰색 배경에 둥근 모서리와 약한 그림자를 주어, 작업 아래에 들어간 영역으로 보이게 했다.

그다음에는 배경 대비가 문제였다. 펼친 작업의 바깥 회색이 헤더와 비슷해 영역이 분명하지 않았고, 호버하면 요약 행만 진해져 두 부분이 다시 갈라졌다. 바깥 배경은 헤더보다 진하게 하고, 열린 상태에서는 요약과 상세 바깥 배경이 함께 변하도록 맞췄다.

마지막으로 여러 작업을 동시에 열어봤다. 하나만 열었을 때는 괜찮았던 회색 배경이 연속되면서 여러 작업이 하나의 큰 묶음처럼 보였다. 작업 사이에 8px의 흰색 간격을 넣어 각각의 경계를 만들었다. 상세 안에 선을 다시 늘리지 않고도 작업 단위는 나눌 수 있었다.

"수정 후 실제 화면. 상세는 흰색 내부 패널로, 펼친 작업 사이는 간격으로 구분했다."

"수정 후 실제 화면. 상세는 흰색 내부 패널로, 펼친 작업 사이는 간격으로 구분했다."

확인한 상태드러난 문제수정한 부분
상세 한 개 열기안쪽 선이 과도함항목 구분선을 없애고 여백으로 정리
요약과 상세 함께 보기내부 패널의 깊이가 약함들여쓰기와 별도 흰색 면 추가
열린 행 호버요약만 진해져 분리돼 보임요약과 상세 바깥 배경을 함께 변경
여러 상세 열기인접한 작업이 하나로 보임작업 사이 흰색 간격 추가
연결된 작업으로 이동요약만 강조돼 상세와 갈라짐작업 전체 강조와 현재 배경으로 복귀

연결된 기록으로 이동할 때도 같은 경계 문제가 남아 있었다. ‘원래 작업 보기’나 ‘되돌린 작업 보기’를 누르면 요약 행만 파랗게 강조되고, 바로 아래 상세는 회색으로 남았다. 평소에는 한 작업으로 묶어 놓았는데 이동 순간에만 둘로 갈라지는 셈이었다. 강조 범위를 요약 행과 상세 바깥 배경까지 넓히고, 안쪽 흰색 패널은 그대로 유지했다.

강조가 끝나는 방식도 함께 바꿨다. 마지막 색을 투명으로 고정하는 대신 그 순간의 CSS 배경으로 돌아가게 해, 마우스가 올라가 있으면 호버 배경으로 자연스럽게 이어지도록 했다. 반복해서 이동할 때는 이전 이동 강조만 취소해 다른 전환을 끊지 않도록 했고, 동작 줄이기 설정에서는 부드러운 스크롤을 생략했다. 평소에 한 묶음으로 읽히는 정보는 이동 중에도 같은 단위로 보여야 했다.

이 수정들은 빌드 오류를 해결하는 작업은 아니었다. 기본 상태의 코드가 정상이어도 여러 개를 열거나 마우스를 올리면 읽는 방식이 달라졌다. 한 장의 정적인 화면만 보고 디자인이 끝났다고 판단했던 범위를 넓혀야 했다.


다시 여는 데도 기다리고 있었다

화면을 닫는 일과 데이터를 버리는 일을 분리했다

상세를 몇 번 열다 보니 이미 봤던 작업도 다시 기다려야 했다. 처음 조회가 느린 것인지, 다시 요청하는 것인지부터 확인했다. 코드를 보니 닫기 애니메이션이 끝나면 상세 컴포넌트를 제거했고, 그 안에 있던 조회 결과도 함께 사라지고 있었다.

다음에 열면 컴포넌트가 새로 만들어져 같은 상세를 다시 요청했다. 일부 작업은 옵션이나 컬렉션, 카테고리의 이름을 표시하기 위해 Shopify 조회도 거쳤다. 화면에서는 같은 패널을 다시 펼치는 동작이지만, 내부에서는 처음 여는 과정을 반복하고 있었다.

조회 결과는 개별 패널 밖의 목록 쪽에 보관하도록 바꿨다. 같은 목록에서 다시 열면 저장된 데이터를 사용하고, 아직 불러오는 중에 닫았다 열더라도 진행 중인 요청을 공유한다. 컴포넌트가 사라진 뒤 상태를 갱신하지 않도록 구독하는 쪽의 생명주기는 따로 관리했다.

캐시를 계속 유지하지는 않았다. 목록이 다시 조회되면 캐시도 새로 만들고, 실패한 요청은 다시 열 때 재시도할 수 있게 했다. 재열기의 중복 요청을 줄이는 것과 데이터를 영구히 고정하는 것은 구분해야 했다. 이번 수정은 현재 보고 있는 목록 안에서의 재사용으로 범위를 정했다.

기다리는 시간을 나눠서 측정했다

캐시를 붙였다고 첫 조회까지 빨라졌다고 쓸 수는 없었다. 실제 저장된 가격 변경과 이미지 등록 기록을 대상으로 서버의 상세 조회 함수를 각각 세 번 실행했다. 작업 기록 DB 조회와 Shopify 이름 조회에 걸린 시간을 나눠 확인했다.

상세 유형1회2회3회
가격 변경 상세 전체450ms383ms383ms
그중 Shopify 이름 조회445ms381ms381ms
이미지 등록 상세 전체2ms1ms2ms

이 표의 반복 실행은 브라우저 캐시의 효과를 잰 것이 아니다. 서버 함수를 직접 호출해서 그 안에서 어디에 시간이 쓰이는지 본 결과다. 가격 변경 상세에서는 대부분이 Shopify 이름 조회 대기였고, 이미지 등록 상세는 DB 조회만으로 짧게 끝났다.

측정 범위 밖도 분명했다. 브라우저에서 클릭한 순간부터 화면에 표시될 때까지의 시간, 요청 인증, 개발용 터널을 지나는 시간은 포함하지 않았다. 이미지 상세도 오래 기다려진다면 이번에 측정한 DB 처리 외의 구간을 더 봐야 한다. 이 결과를 전체 응답 시간이나 캐시 적용 전후의 개선율로 바꾸어 적지는 않기로 했다.


결과

상품을 바꾸는 화면에 대상을 고르는 조건과 변경을 되짚어보는 흐름이 붙었다. 일괄 작업의 규칙을 정하고 미리보기로 확인한 뒤 실행하고, 기록에서 전후 값을 살펴보는 과정으로 이어졌다. 되돌리기가 가능한 작업은 원래 기록과 되돌린 기록 사이를 이동하며 확인할 수 있게 했다.

화면에서는 요약과 상세의 역할을 나눴다. 목록만 보고도 기본적인 변경은 파악할 수 있고, 필요한 작업만 펼치면 세부 값과 연결 관계가 나온다. 여러 패널이 열린 상태와 호버 상태까지 보면서 경계를 조정했고, 닫았다 다시 여는 동작에서는 조회 결과를 재사용하도록 했다.

검증에서는 전체 테스트 50개, ESLint, 타입 검사가 통과했다. 마지막 간격 수정 뒤에도 빌드를 실행해 통과를 확인했다. 기능 확장과 상세 캐시를 포함한 변경은 07bcd38, 펼친 작업 사이 간격은 e1bed97 커밋으로 남겼다. 이 단계에서는 로컬 커밋까지만 했고 원격 push는 하지 않았다.

이동 강조 수정은 5f73a36 커밋으로 남겼다. 이 변경 뒤 타입 검사와 ESLint가 통과했고, 별도 브라우저 검증에서 요약·상세 배경의 동시 강조, 반복 이동 시 애니메이션 중복 방지, 호버 배경으로의 복귀를 확인했다. 실제 앱의 전체 흐름을 자동 검증한 결과와는 구분했다.

후속으로 로그인된 데스크톱 브라우저에서 가격 변경과 이미지 등록 상세를 함께 펼친 화면을 확인하고 촬영했다. 다만 확인하지 못한 것도 있다. 모바일 화면 확인은 뒤로 미뤘고, 브라우저에서 재열기 요청 횟수와 전체 체감 시간을 직접 재지는 못했다. 실제 스토어에서 할인 시작부터 종료까지 이어지는 흐름을 끝까지 검증했다는 근거도 아직 없다. 서버 측 측정과 테스트 통과를 이 범위까지 확인한 것으로 확대하지 않았다.


느낀점

  • 일괄 변경에는 확인할 경로도 필요했다. 입력 횟수를 줄이는 기능만으로는 작업을 끝내기 어려웠다. 무엇을 바꿨고 어떤 기록과 연결되는지 따라갈 수 있어야 했다.
  • 목록을 간단하게 만드는 데도 기준이 있었다. 한 줄 요약까지 숨기면 상세를 여는 횟수만 늘었다. 기본 판단에 필요한 정보와 자세히 확인할 정보를 나누는 편이 나았다.
  • 화면의 상태 조합이 디자인을 바꿨다. 하나만 열었을 때와 여러 개를 열었을 때의 인상이 달랐다. 호버를 더하면 경계가 다시 달라져 기본 화면만으로는 확인이 끝나지 않았다.
  • 성능 수치에는 측정한 범위를 붙여야 했다. 서버 함수가 빠르다는 것만으로 클릭 후 화면도 빠르다고 할 수는 없었다. 중복 요청을 없앤 수정과 첫 조회의 대기 원인을 따로 다뤘다.

처음에는 상품 관리에 필요한 기능을 몇 개 더 붙이는 작업이라고 생각했다. 실제로는 바뀐 값을 어떻게 설명하고, 어느 정보를 목록에 남기며, 같은 작업을 다시 확인할 때 무엇을 재사용할지 정하는 시간이 더해졌다. 기능 수가 늘어난 만큼 확인하는 과정도 함께 만들어야 했다.

이제는 남겨둔 실제 화면과 저장 흐름을 이어서 확인할 차례다. 여기까지 읽어주신 분들께 감사드리며 이번 글을 마친다.

좋아요 —
hamburger icon

무링의 개발 블로그

FDE를 향하는 개발자


댓글

GitHub 계정으로 로그인하면 댓글을 남길 수 있습니다.

로그인

연관된 글