이 글은 누구를 위한 것인가
- 설정 페이지가 너무 복잡해서 사용자가 원하는 설정을 못 찾는 팀
- "저장" 버튼 없이 실시간으로 설정을 반영해야 하는지 고민하는 팀
- 설정 UX를 처음 구조화하는 프로덕트 디자이너
들어가며
설정 페이지는 "고급 사용자"가 사용하는 공간이다. 하지만 잘못 설계하면 기본 사용자도 헷갈린다. 계층 구조, 즉시 저장 여부, 설정 충돌 처리가 핵심이다.
이 글은 bluefoxdev.kr의 설정 UX 가이드 를 참고하여 작성했습니다.
1. 설정 계층 구조 설계
[설정 분류 기준]
계정 설정:
프로필 (이름, 사진, 이메일)
비밀번호 & 보안 (2FA, 세션)
연결된 계정 (소셜 로그인)
알림 설정:
채널별 (이메일/SMS/푸시)
유형별 (주문/마케팅/시스템)
시간대 설정
앱 환경설정:
언어 및 지역
테마 (라이트/다크/시스템)
접근성
개인정보 & 데이터:
데이터 다운로드
계정 삭제
쿠키 설정
[설정 저장 방식 선택]
즉시 저장 (토글, 드롭다운):
변경 즉시 반영 → 저장 버튼 불필요
✅ 알림 On/Off
✅ 테마 전환
✅ 언어 변경
저장 버튼 필요 (폼):
여러 필드를 한번에 저장
✅ 프로필 정보 (이름, 주소)
✅ 배송지 추가
[설정 완료 피드백]
즉시 저장: "저장됨" 토스트 (간결하게)
폼 저장: 인라인 성공 메시지 또는 토스트
오류: 인라인 오류 + 스크롤 오류 위치로 이동
2. 설정 페이지 컴포넌트
interface SettingToggleProps {
label: string;
description?: string;
value: boolean;
onChange: (value: boolean) => void;
disabled?: boolean;
}
function SettingToggle({ label, description, value, onChange, disabled }: SettingToggleProps) {
const id = `toggle-${label.replace(/\s/g, "-").toLowerCase()}`;
return (
<div className="flex items-start justify-between py-4 border-b last:border-b-0">
<div className="flex-1 mr-4">
<label htmlFor={id} className="text-sm font-medium text-gray-900 cursor-pointer">
{label}
</label>
{description && (
<p className="text-xs text-gray-500 mt-0.5">{description}</p>
)}
</div>
<button
id={id}
role="switch"
aria-checked={value}
onClick={() => !disabled && onChange(!value)}
disabled={disabled}
className={`relative inline-flex h-6 w-11 flex-shrink-0 rounded-full border-2 border-transparent transition-colors duration-200 ${
value ? "bg-blue-600" : "bg-gray-200"
} ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`}
>
<span
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ${
value ? "translate-x-5" : "translate-x-0"
}`}
/>
</button>
</div>
);
}
// 알림 설정 섹션
function NotificationSettings() {
const [settings, setSettings] = useState({
emailOrders: true,
emailMarketing: false,
pushOrders: true,
pushMarketing: true,
smsOrders: false,
});
const { addToast } = useToast();
const updateSetting = async (key: keyof typeof settings, value: boolean) => {
const prev = settings[key];
setSettings((s) => ({ ...s, [key]: value })); // 낙관적 업데이트
try {
await updateNotificationSetting(key, value);
addToast({ type: "success", message: "알림 설정이 저장되었습니다" });
} catch {
setSettings((s) => ({ ...s, [key]: prev })); // 롤백
addToast({ type: "error", message: "저장에 실패했습니다" });
}
};
return (
<section>
<h2 className="text-lg font-semibold mb-1">알림 설정</h2>
<p className="text-sm text-gray-500 mb-6">수신할 알림 종류를 선택하세요</p>
<div className="bg-white rounded-xl border overflow-hidden">
<div className="px-4 py-3 bg-gray-50 border-b">
<h3 className="text-sm font-medium text-gray-700">이메일 알림</h3>
</div>
<div className="px-4">
<SettingToggle
label="주문 알림"
description="주문 상태 변경 시 이메일로 알림"
value={settings.emailOrders}
onChange={(v) => updateSetting("emailOrders", v)}
/>
<SettingToggle
label="마케팅 이메일"
description="새 상품, 할인, 이벤트 소식"
value={settings.emailMarketing}
onChange={(v) => updateSetting("emailMarketing", v)}
/>
</div>
</div>
</section>
);
}
마무리
설정 UX에서 가장 중요한 것은 "변경 사항이 즉시 반영되는가"다. 토글을 바꿨는데 저장 버튼을 눌러야 적용된다면 사용자는 혼란스럽다. 토글·드롭다운은 즉시 저장, 복잡한 폼은 저장 버튼으로 일관성 있게 구분하라.