docs(modal): V2 공통 모달창 계획을 기록한다
This commit is contained in:
146
docs/20260708_V2_공통_모달창/plan-task.md
Normal file
146
docs/20260708_V2_공통_모달창/plan-task.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# V2 공통 모달창 구현 계획/TASK
|
||||
|
||||
> **For agentic workers:** 각 단계는 체크박스(`- [ ]`)로 추적하고, 완료 즉시 `- [x]`로 갱신한다. 구현 범위 변경이 생기면 이 문서를 먼저 수정한 뒤 코드에 반영한다.
|
||||
|
||||
**Goal:** Figma `567:17662`(버튼 1개), `567:17666`(버튼 2개) 디자인 기반의 V2 공통 모달창 `V2ModalDialog`를 만들어, 기존 `SodaDialog`처럼 어디서든 title/desc/버튼 title/버튼 action을 조정해 사용할 수 있게 한다.
|
||||
|
||||
**Architecture:** 레거시 `SodaDialog`를 수정하지 않고, `kr.co.vividnext.sodalive.v2.components.modal` 하위에 `AlertDialog` + ViewBinding 기반의 신규 클래스와 신규 layout(`dialog_v2_modal.xml`)을 추가한다. `cancelButtonTitle`이 blank이면 버튼 1개, 아니면 취소+확인 버튼 2개(1:1 비율)로 표시한다.
|
||||
|
||||
**Tech Stack:** Kotlin, Android XML Views, ViewBinding, AlertDialog, JUnit4/Robolectric.
|
||||
|
||||
---
|
||||
|
||||
## 전제와 성공 기준
|
||||
|
||||
- PRD: `docs/20260708_V2_공통_모달창/prd.md`
|
||||
- 기존 참조 구현: `app/src/main/java/kr/co/vividnext/sodalive/base/SodaDialog.kt` (수정 금지, 사용 방식 참조만)
|
||||
- 신규 코드 위치: `app/src/main/java/kr/co/vividnext/sodalive/v2/components/modal/` (빈 패키지 존재 확인 완료)
|
||||
- 색상 리소스: `gray_900`(#202020), `soda_400`(#00BDF7), `white` 기존 정의 재사용 (colors.xml 확인 완료)
|
||||
- 구현 완료 판단 시 Gradle 검증뿐 아니라 Figma `567:17662`, `567:17666`의 layout 구조(컨테이너/타이틀/본문/버튼 영역), 색상, 버튼 구성이 실제 구현에 반영되었는지 확인한다.
|
||||
- 자동 검증에서도 Figma에서 확정된 UI 계약을 확인한다.
|
||||
- `cancelButtonTitle` blank: 확인 버튼 1개만 표시
|
||||
- `cancelButtonTitle` 있음: 취소+확인 버튼 2개 표시
|
||||
- 버튼 클릭: dismiss 후 전달받은 action 실행
|
||||
- 색상: 컨테이너 `gray_900`, 확인 버튼 텍스트 `soda_400`, 취소 버튼 텍스트 white
|
||||
- Figma 대조 결과는 문서 최하단 `Verification Log`에 한국어로 누적 기록한다.
|
||||
- 구현 완료 후 최소 다음 명령을 실행한다.
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
- `./gradlew :app:compileDebugKotlin`
|
||||
- `./gradlew :app:ktlintCheck`
|
||||
- `git diff --check`
|
||||
|
||||
---
|
||||
|
||||
## Figma 참조
|
||||
|
||||
- 버튼 1개: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17662&m=dev`
|
||||
- 버튼 2개: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17666&m=dev`
|
||||
- 확정 디자인 값:
|
||||
- 컨테이너: 배경 `gray_900`(#202020), radius 14dp
|
||||
- title: 높이 64dp, 20sp Bold, white, 가운데 정렬
|
||||
- desc: 패딩 20dp, 16sp Regular, white, 가운데 정렬, line spacing 1.45
|
||||
- 버튼 영역: 좌우 패딩 20dp, 상하 패딩 14dp, 버튼 간 간격 8dp
|
||||
- 버튼: 내부 패딩 14dp, radius 100dp capsule, 18sp Medium
|
||||
- 확인 버튼 텍스트: `soda_400`(#00BDF7), 취소 버튼 텍스트: white
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조
|
||||
|
||||
- 생성: `app/src/main/java/kr/co/vividnext/sodalive/v2/components/modal/V2ModalDialog.kt`
|
||||
- `AlertDialog` 기반 공통 모달 클래스. 생성자 파라미터로 title/desc/confirm title/confirm action/cancel title/cancel action을 받는다.
|
||||
- 생성: `app/src/main/res/layout/dialog_v2_modal.xml`
|
||||
- Figma 기준 모달 layout(title/desc/버튼 영역).
|
||||
- 생성: `app/src/main/res/drawable/bg_v2_modal.xml`
|
||||
- `gray_900` 배경 + radius 14dp 컨테이너 배경.
|
||||
- 테스트 생성:
|
||||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/components/modal/V2ModalDialogTest.kt`
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: 모달 layout과 리소스 구현
|
||||
|
||||
- [x] **Task 1.1: 모달 배경 drawable과 layout 추가**
|
||||
- 생성:
|
||||
- `app/src/main/res/drawable/bg_v2_modal.xml`
|
||||
- `app/src/main/res/layout/dialog_v2_modal.xml`
|
||||
- 작업:
|
||||
- `bg_v2_modal.xml`은 `gray_900` solid, corner radius 14dp로 구성한다.
|
||||
- `dialog_v2_modal.xml`은 Figma 확정 값 기준으로 title(높이 64dp, 20sp Bold, white, 가운데 정렬), desc(패딩 20dp, 16sp Regular, white, 가운데 정렬, line spacing 1.45), 버튼 영역(좌우 20dp/상하 14dp 패딩, 버튼 간격 8dp)을 구성한다.
|
||||
- 취소/확인 버튼은 내부 패딩 14dp, 18sp Medium으로 구성하고, 2개일 때 1:1 비율이 되도록 동일 weight를 준다.
|
||||
- 확인 버튼 텍스트 색상은 `soda_400`, 취소 버튼 텍스트 색상은 white로 지정한다.
|
||||
- 검증 명령:
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
- 기대 결과:
|
||||
- 신규 drawable/layout resource가 resource merge를 통과한다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: V2ModalDialog 클래스 구현
|
||||
|
||||
- [x] **Task 2.1: V2ModalDialog RED 테스트 작성**
|
||||
- 생성:
|
||||
- `app/src/test/java/kr/co/vividnext/sodalive/v2/components/modal/V2ModalDialogTest.kt`
|
||||
- 테스트 케이스:
|
||||
- `cancelButtonTitle`이 blank(빈 문자열/공백)이면 취소 버튼이 `GONE`이고 확인 버튼만 `VISIBLE`이다.
|
||||
- `cancelButtonTitle`이 있으면 취소/확인 버튼이 모두 `VISIBLE`이고 각 버튼 title이 반영된다.
|
||||
- title/desc 텍스트가 파라미터 값으로 표시된다.
|
||||
- 확인 버튼 클릭 시 dialog가 dismiss되고 `confirmButtonClick`이 실행된다.
|
||||
- 취소 버튼 클릭 시 dialog가 dismiss되고 `cancelButtonClick`이 실행된다.
|
||||
- `cancelButtonClick`이 `null`이어도 취소 버튼 클릭 시 dismiss되고 예외가 발생하지 않는다.
|
||||
- layout이 `bg_v2_modal` 배경, `soda_400` 확인 텍스트, white 취소 텍스트를 사용한다(source/resource 계약 확인).
|
||||
- 검증 명령:
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialogTest"`
|
||||
- 기대 결과:
|
||||
- 클래스 구현 전 실패한다.
|
||||
|
||||
- [x] **Task 2.2: V2ModalDialog 구현**
|
||||
- 생성:
|
||||
- `app/src/main/java/kr/co/vividnext/sodalive/v2/components/modal/V2ModalDialog.kt`
|
||||
- 작업:
|
||||
- 생성자 파라미터: `activity: Activity`, `layoutInflater: LayoutInflater`, `title: String`, `desc: String`, `confirmButtonTitle: String`, `confirmButtonClick: () -> Unit`, `cancelButtonTitle: String = ""`, `cancelButtonClick: (() -> Unit)? = null`.
|
||||
- `AlertDialog.Builder`로 `dialog_v2_modal.xml`을 표시하고 `setCancelable(false)`, window 배경 투명 처리를 적용한다.
|
||||
- `cancelButtonTitle.isNotBlank()`일 때만 취소 버튼을 `VISIBLE` 처리한다.
|
||||
- 각 버튼 클릭 시 `dismiss()` 후 전달받은 action을 실행한다.
|
||||
- `show(width)`는 기존 `SodaDialog`와 동일하게 `width - 26.7dp`로 dialog 폭을 지정한다.
|
||||
- 레거시 `SodaDialog.kt`, `dialog_soda.xml`은 수정하지 않는다.
|
||||
- 검증 명령:
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialogTest"`
|
||||
- `./gradlew :app:compileDebugKotlin`
|
||||
- 기대 결과:
|
||||
- 신규 테스트가 PASS하고 컴파일 오류가 없다.
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 회귀 검증과 Figma 대조
|
||||
|
||||
- [x] **Task 3.1: 전체 검증 실행**
|
||||
- 실행:
|
||||
- `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`
|
||||
- `./gradlew :app:mergeDebugResources`
|
||||
- `./gradlew :app:compileDebugKotlin`
|
||||
- `./gradlew :app:ktlintCheck`
|
||||
- `git diff --check`
|
||||
- Figma 기반 자동 확인:
|
||||
- `V2ModalDialogTest`에서 버튼 1개/2개 구성, dismiss 후 action 실행, title/desc/버튼 title 반영 계약을 확인한다.
|
||||
- layout/drawable에 `gray_900` 배경, radius 14dp, `soda_400` 확인 텍스트, white 취소 텍스트가 있는지 확인한다.
|
||||
- Figma 수동 확인:
|
||||
- 버튼 1개 모달이 Figma `567:17662`의 layout 구조와 색상을 반영하는지 확인한다.
|
||||
- 버튼 2개 모달이 Figma `567:17666`의 layout 구조, 버튼 1:1 비율, 색상을 반영하는지 확인한다.
|
||||
- 기대 결과:
|
||||
- 모든 명령이 PASS하고, Figma 두 변형 대조 결과가 PASS한다.
|
||||
- 검증 기록:
|
||||
- 자동 확인 결과와 수동 대조 결과를 `Verification Log`에 누적한다.
|
||||
|
||||
---
|
||||
|
||||
## Verification Log
|
||||
|
||||
- 2026-07-08 문서 작성 단계: 구현 전이므로 Gradle 검증을 실행하지 않았다. Figma `567:17662`, `567:17666` 디자인 컨텍스트와 스크린샷으로 layout 구조/색상 값을 확정했고, `v2/components/modal` 빈 패키지와 `gray_900`/`soda_400` 색상 리소스 존재를 확인했다.
|
||||
- 2026-07-08 구현 단계: `./gradlew :app:mergeDebugResources` PASS. `dialog_v2_modal.xml`, `bg_v2_modal.xml` 리소스 merge를 확인했다.
|
||||
- 2026-07-08 RED 확인: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialogTest"` 실행 시 `V2ModalDialog` 구현 전 `Unresolved reference 'V2ModalDialog'`로 실패해 RED 상태를 확인했다.
|
||||
- 2026-07-08 GREEN 확인: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialogTest"` PASS. 버튼 1개/2개 구성, title/desc/버튼 title 반영, dismiss 후 action 실행, `cancelButtonClick = null` 취소 동작, Figma 리소스 계약을 확인했다.
|
||||
- 2026-07-08 최종 검증: `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 파일의 package-name, trailing comma, indentation 등 사전 존재 위반으로 FAIL했으며, ktlint 보고서에 이번 변경 파일(`V2ModalDialog.kt`, `V2ModalDialogTest.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`)은 포함되지 않았다. Figma `567:17662`, `567:17666` 스크린샷 기준으로 컨테이너/타이틀/본문/버튼 영역, 버튼 1:1 구성, `gray_900`/white/`soda_400` 색상을 대조했다.
|
||||
- 2026-07-08 리뷰 반영: 투명 `bg_v2_modal_button.xml`은 실제 시각 효과가 없어 제거했다. 버튼 중복 클릭 시 action이 여러 번 실행될 수 있는 위험을 막기 위해 최초 클릭 1회만 dismiss/action을 수행하도록 보완했고, 긴 title 줄바꿈 시 잘림을 줄이기 위해 title 높이를 `wrap_content` + `minHeight=64dp`로 조정했다.
|
||||
- 2026-07-08 코드 리뷰/검증: `V2ModalDialog.kt`, `dialog_v2_modal.xml`, `bg_v2_modal.xml`, `V2ModalDialogTest.kt`를 PRD와 Figma MCP `567:17662`, `567:17666` 기준으로 재대조했으며 차단 이슈는 발견하지 않았다. `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.components.modal.*"`, `./gradlew :app:mergeDebugResources`, `./gradlew :app:compileDebugKotlin`, `git diff --check` PASS. 신규 untracked 파일은 `git diff --no-index --check /dev/null <file>`로 별도 확인했고 whitespace 오류 출력이 없었다. `./gradlew :app:ktlintCheck`는 기존 레거시/타 영역 위반으로 FAIL했으며 ktlint 보고서에서 이번 변경 파일명 검색 결과는 없었다.
|
||||
115
docs/20260708_V2_공통_모달창/prd.md
Normal file
115
docs/20260708_V2_공통_모달창/prd.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# PRD: V2 공통 모달창
|
||||
|
||||
## 1. Overview
|
||||
V2 화면 어디서든 사용할 수 있는 Figma 기반 신규 공통 모달창(`V2ModalDialog`)을 제공한다. 기존 `SodaDialog`처럼 title/desc/버튼 title/버튼 action을 호출부에서 조정할 수 있고, 버튼 1개(확인)와 버튼 2개(취소+확인) 구성을 모두 지원한다.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem
|
||||
- 기존 `SodaDialog`(`kr.co.vividnext.sodalive.base.SodaDialog`)는 레거시 디자인(`dialog_soda.xml`) 기반이라 V2 Figma 모달 디자인과 시각적으로 다르다.
|
||||
- 레거시 코드는 직접 수정하지 않는 저장소 원칙에 따라 `SodaDialog`를 고쳐 V2 디자인을 적용할 수 없다.
|
||||
- V2 신규 화면들이 공통으로 사용할 Figma 기준 모달 컴포넌트가 아직 없다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Goals
|
||||
- Figma `567:17662`(버튼 1개), `567:17666`(버튼 2개) 기준의 모달 UI를 구현한다.
|
||||
- `SodaDialog`와 동일하게 어떤 Activity에서도 생성해 표시할 수 있는 범용 클래스를 제공한다.
|
||||
- title, desc, confirm 버튼 title/action, cancel 버튼 title/action을 생성자 파라미터로 조정할 수 있다.
|
||||
- cancel 버튼 title이 비어 있으면 버튼 1개(확인만) 구성으로 표시한다.
|
||||
- 버튼 터치 시 모달을 닫고 전달받은 action을 실행한다.
|
||||
|
||||
---
|
||||
|
||||
## 4. Non-Goals
|
||||
- 기존 `SodaDialog`와 레거시 `dialog_soda.xml`은 수정하지 않는다.
|
||||
- 기존 `SodaDialog` 사용처를 신규 모달로 교체하는 마이그레이션은 이번 범위가 아니다.
|
||||
- Figma 컴포넌트의 우측 상단 close icon 변형(`hasClose=true`)은 두 예시 모두 사용하지 않으므로 이번 범위에서 구현하지 않는다.
|
||||
- 입력 필드, 이미지, 리스트 등 커스텀 콘텐츠 영역은 지원하지 않는다.
|
||||
- 버튼 3개 이상 구성은 지원하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 5. Target Users
|
||||
- V2 화면에서 확인/취소 모달이 필요한 SodaLive Android 사용자.
|
||||
- `kr.co.vividnext.sodalive.v2` 하위 기능을 구현/유지보수하는 Android 개발자.
|
||||
|
||||
---
|
||||
|
||||
## 6. User Stories
|
||||
- 개발자는 V2 화면에서 title/desc/버튼 문구/버튼 action만 전달해 Figma 디자인 모달을 띄우고 싶다.
|
||||
- 개발자는 확인 버튼만 있는 안내 모달과 취소+확인 버튼이 있는 확인 모달을 같은 클래스로 사용하고 싶다.
|
||||
- 사용자는 확인 버튼을 눌러 동작을 실행하거나 취소 버튼을 눌러 모달을 닫고 싶다.
|
||||
|
||||
---
|
||||
|
||||
## 7. Core Features
|
||||
|
||||
### V2 Modal Component
|
||||
Figma 기반 V2 공통 모달 클래스를 제공한다.
|
||||
|
||||
#### Requirements
|
||||
- 신규 클래스는 `kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog`로 작성한다.
|
||||
- `SodaDialog`와 동일한 사용 방식(생성자에 파라미터 전달 후 `show(width)`)을 따른다.
|
||||
- 생성자 파라미터: `activity`, `layoutInflater`, `title`, `desc`, `confirmButtonTitle`, `confirmButtonClick`, `cancelButtonTitle`(기본값 `""`), `cancelButtonClick`(기본값 `null`).
|
||||
- `cancelButtonTitle`이 blank이면 취소 버튼을 숨기고 확인 버튼 1개만 표시한다(Figma `567:17662`).
|
||||
- `cancelButtonTitle`이 있으면 취소+확인 버튼 2개를 동일 너비로 표시한다(Figma `567:17666`).
|
||||
- 버튼 터치 시 모달을 dismiss한 뒤 전달받은 click action을 실행한다.
|
||||
- 모달 외부 터치/back으로 닫히지 않도록 `setCancelable(false)`로 설정한다.
|
||||
- dialog window 배경은 투명 처리하고 layout 배경으로 둥근 모서리를 표현한다.
|
||||
|
||||
#### Edge Cases
|
||||
- `cancelButtonTitle`이 공백 문자열이면 blank로 판단해 버튼 1개 구성으로 표시한다.
|
||||
- `cancelButtonClick`이 `null`이어도 취소 버튼 터치 시 모달은 닫힌다.
|
||||
- desc에 줄바꿈(`\n`)이 포함되면 여러 줄로 가운데 정렬 표시한다.
|
||||
- 긴 title/desc도 잘리지 않고 word-break로 줄바꿈된다.
|
||||
|
||||
### V2 Modal UI (Figma)
|
||||
Figma 디자인 그대로 layout을 구성한다.
|
||||
|
||||
#### Requirements
|
||||
- 컨테이너: 배경 `gray_900`(#202020), corner radius 14dp.
|
||||
- title 영역: 높이 64dp, 텍스트 20sp Bold, white, 가운데 정렬.
|
||||
- desc 영역: 패딩 20dp, 텍스트 16sp Regular, white, 가운데 정렬, line spacing 1.45 기준.
|
||||
- 버튼 영역: 좌우 패딩 20dp, 상하 패딩 14dp, 버튼 간 간격 8dp.
|
||||
- 버튼: 내부 패딩 14dp, radius 100dp(capsule), 텍스트 18sp Medium.
|
||||
- 확인 버튼 텍스트 색상은 `soda_400`(#00BDF7), 취소 버튼 텍스트 색상은 white.
|
||||
- 버튼 2개일 때 두 버튼은 1:1 비율로 영역을 나눈다.
|
||||
|
||||
#### Edge Cases
|
||||
- title이 빈 문자열이어도 layout이 깨지지 않는다.
|
||||
- 좁은 화면 폭에서도 버튼 문구가 잘리지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 8. UX / UI Expectations
|
||||
- 모달 폭은 기존 `SodaDialog.show(width)`와 동일하게 화면 폭 기준(`width - 26.7dp`)으로 표시한다.
|
||||
- 버튼 터치에 즉시 반응하고 중복 실행이 발생하지 않는다(dismiss 후 action 실행).
|
||||
- Figma 두 변형(버튼 1개/2개)의 layout 구조와 색상이 실제 화면에 반영된다.
|
||||
|
||||
---
|
||||
|
||||
## 9. Technical Constraints
|
||||
- Android XML View/ViewBinding 기반으로 구현한다.
|
||||
- 신규 코드는 `kr.co.vividnext.sodalive.v2.components.modal` 하위에 작성한다.
|
||||
- 레거시 `SodaDialog`, `dialog_soda.xml`은 수정하지 않는다.
|
||||
- 색상은 기존 리소스 `gray_900`, `soda_400`, `white`를 재사용한다.
|
||||
- `AlertDialog` 기반으로 구현하고, 추가 라이브러리를 도입하지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## 10. Metrics
|
||||
- 기능 완료 기준: title/desc/버튼 title/버튼 action을 조정해 버튼 1개/2개 모달을 표시할 수 있다.
|
||||
- UI 완료 기준: Figma `567:17662`, `567:17666`의 layout 구조, 색상, 버튼 구성이 구현 화면에 반영된다.
|
||||
- 자동 검증 기준: cancel title blank 시 버튼 1개 구성, 버튼 클릭 시 dismiss 후 action 실행, layout의 색상/구조 계약을 source/resource 테스트로 확인한다.
|
||||
|
||||
---
|
||||
|
||||
## 11. Open Questions
|
||||
- 없음.
|
||||
|
||||
---
|
||||
|
||||
## Figma 참조
|
||||
- 버튼 1개: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17662&m=dev`
|
||||
- 버튼 2개: `https://www.figma.com/design/HmN1yNdJ3EIpqknFL0Hkab/-%EA%B3%B5%EC%9C%A0%EC%9A%A9-%EB%B3%B4%EC%9D%B4%EC%8A%A4%EC%98%A8-UI-UX-%EA%B8%B0%ED%9A%8D%EB%AC%B8%EC%84%9C?node-id=567-17666&m=dev`
|
||||
Reference in New Issue
Block a user