산을 배경으로 한 한국 포스터
📝 한줄 요약
제출 완료 메시지를 “하단에 조용히 있던” Notice에서 “우상단에 튀어나왔다가 자동으로 사라지는” 토스트 팝업으로 바꾸니, 사용자가 제출이 성공했다는 걸 빠르게 인지했습니다.
Before: 페이지 하단 Notice (스크롤 해야 볼 수 있음, 계속 남아있음)
After: 우상단 토스트 (눈에 즉시 띔, 3초 후 자동 사라짐)
👥 이런 분께 추천
사용자가 행동 결과를 명확히 인지해야 하는 앱을 만드는 분
“완료됐는데 사용자가 못 본다” 같은 피드백을 받은 분
Toast Notification 패턴을 배우고 싶은 분
React에서 고정 위치 UI와 자동 닫힘 로직을 구현하고 싶은 분
🙋 내 스펙 & 환경
항목
내용
직업
초과근무관리 앱 제작
코딩 경험
React / TypeScript / Tailwind CSS
개발 스택
Next.js 14
버전 관리
Git / Vercel 배포
진행 방법
1. 문제: 메시지가 눈에 띄지 않는다
사용자가 Submit 버튼을 눌러서 제출을 완료하면, 페이지 하단에 “Your meeting has been submitted.” 메시지가 나타났습니다.
하지만 문제가 있었어요:
위치: 하단에 있어서, 사용자가 스크롤해야 봤음
시간: 메시지가 계속 남아있어서, 나중에 남은 메시지를 보고 “뭐지?” 하게 됨
인지: 중요한 피드백인데 조용해서 놓치기 쉬움
사용자는 “완료됐나?” 하고 페이지를 새로고침하거나, 제출 버튼을 다시 눌렀어요.
2. 해결책 조사: Toast Notification 패턴
다른 앱들을 보니 성공/오류 메시지를 “토스트”로 띄웠어요:
Gmail: 메일 전송 완료 → 화면 하단에서 “메일이 발송되었습니다” 팝업 (2초 후 사라짐)
Slack: 메시지 전송 → 우상단에 ”✓ Sent” (1초 후 사라짐)
Figma: 파일 저장 → 중앙 상단에서 약간 내려온 위치에 “Saved” (자동 닫힘)
공통점:
중요한 위치: 화면 상단 모서리 (우상단이 가장 흔함)
짧은 시간: 2~3초 후 자동으로 사라짐
애니메이션: 튀어나왔다가 부드럽게 사라짐
명확한 상태: 성공(녹색), 오류(빨강), 정보(파랑) 등 색으로 구분
3. 구현: 토스트 컴포넌트 만들기
상태 추가:
const [showSuccessToast, setShowSuccessToast] = useState(false);제출 성공 시 토스트 활성화:
const handleSubmitClick = async () => {
// ... 제출 로직 ...
setShowSuccessToast(true);
// 3초 후 자동 닫기
setTimeout(() => setShowSuccessToast(false), 3000);
};토스트 UI (JSX):
{showSuccessToast ? (
<div className="fixed bottom-4 right-4 z-50 rounded-md border border-green-200 bg-green-50 px-4 py-3 text-sm font-medium text-green-700 shadow-lg animate-in fade-in slide-in-from-bottom-4 duration-300">
✓ Your meeting has been submitted.
</div>
) : null}핵심 CSS 클래스들:
fixed: 스크롤 무시하고 화면 고정bottom-4 right-4: 우하단 배치 (또는 우상단은top-4)z-50: 다른 요소 위에 떠있게 (z-index)shadow-lg: 그림자로 떠있는 느낌animate-in fade-in slide-in-from-bottom-4: Tailwind 애니메이션 (옅어지면서 튀어나옴)duration-300: 애니메이션 300ms
4. 개선 효과
사용자 테스트 결과:
제출 후 메시지를 놓치는 일 없음 (이전엔 하단을 못 봐서 놓치던 경우 있음)
자동으로 사라지니까 “이 메시지가 뭐지?” 혼동 없음
다른 화면으로 이동해도 완료 피드백이 명확함
5. 확장: 다른 상황에도 적용
토스트는 제출 완료뿐 아니라 많은 상황에서 쓸 수 있어요:
// 오류 토스트
<Toast type="error">
Failed to save. Please try again.
</Toast>
// 정보 토스트
<Toast type="info">
Your settings have been updated.
</Toast>
// 경고 토스트
<Toast type="warning">
This action cannot be undone.
</Toast>🎓 개념: Toast Notification과 Modal
Toast란?
정의: 사용자의 행동에 대한 피드백을 주는 일시적인 메시지 팝업입니다.
“Toast”라는 이름의 유래:
토스터에서 빵이 튀어나오는 이미지 → “팝업” 느낌
잠깐 나타났다가 사라짐 → 한 번의 알림
거슬리지 않게 한 모서리에 떠있음
Toast vs Modal의 차이
구분
Toast
Modal
목적
행동 결과 피드백 (간단함)
사용자 입력/확인 필요 (복잡함)
위치
화면 모서리 (우상단, 우하단)
화면 중앙 (오버레이 배경)
자동 닫힘
예 (2~3초)
아니오 (사용자가 닫기)
상호작용
최소 (보기만 함)
많음 (입력, 선택 등)
예시
”저장되었습니다"
"정말 삭제하시겠어요?”
사용 예시
제출 완료, 복사 완료
데이터 수정, 삭제 확인, 양식 입력
구체적인 예:
// Toast (피드백만)
"✓ Your meeting has been submitted."
→ 2초 후 자동으로 사라짐
// Modal (사용자 입력 필요)
"Edit Meeting"
[Type 선택] [Confirmed Date 입력]
[Cancel] [Save]
→ 사용자가 Save/Cancel을 누를 때까지 남아있음특징
특징
설명
위치
화면 모서리 (우상단, 우하단, 중앙 하단 등)
기간
2~3초 후 자동 사라짐 (사용자 클릭으로도 닫을 수 있음)
상호작용
거의 없음 (닫 기 버튼 있을 수도)
목적
행동의 결과 알리기 (성공/오류/정보)
스타일
색으로 상태 구분 (성공: 녹색, 오류: 빨강 등)
언제 쓰나?
적절한 경우:
제출 완료: “저장되었습니다”
파일 삭제: “파일이 삭제되었습니다”
설정 변경: “설정이 업데이트되었습니다”
오류: “네트워크 연결 오류”
부적절한 경우:
중요한 경고: “계정이 삭제됩니다” → 모달이 맞음
사용자 입력 필요: “어느 것을 선택하시겠어요?” → 대화상자가 맞음
상세 정보: 길고 설명이 많은 메시지 → 알림 센터가 맞음
구현 팁
1. 자동 닫기 타이밍
빠른 작업 (저장, 삭제): 2초
느린 작업 (업로드): 3초
중요한 오류: 5초 (사용자가 읽을 시간)
2. 다중 토스트
여러 개 동시에 뜰 때 → 아래부터 쌓임
같은 메시지 반복 → 하나만 유지 (중복 방지)
3. 접근성
스크린 리더가 읽을 수 있게
role="alert"색만으로 구분 X → 텍스트와 아이콘 함께 사용 (✓, ✕, ⓘ)
🛠 사용한 도구
도구
역할
React State (useState)
토스트 열기/닫기 상태 관리
setTimeout
3초 후 자동 닫기
Tailwind CSS
fixed, z-50, 애니메이션 클래스
tailwindcss/forms
animate-in, fade-in, slide-in 클래스
배운 점
💡 핵심 교훈
1. 메시지의 위치가 인지도를 결정한다
하단 → 우상단으로 옮겼더니 같은 메시지도 훨씬 눈에 띈다. UI의 위치는 정보 구조만큼 중요합니다.
2. 자동 닫힘의 심리학
메시지가 계속 남으면 사용자는 의심합니다. “이게 완료 메시지인가? 오류인가? 뭔가 잘못된 건 아닌가?” 자동으로 사라지면 “완료됐고, 이제 다음으로 넘어가자” 심리가 생깁니다.
3. “피드백”은 필수, “위치”는 선택
피드백 자체는 필수예요. 하지만 어디에 띄울지는 작업에 따라 다릅니다. 성공은 눈에 띄게, 오류는 더 눈에 띄게, 정보는 편하게.
4. 작은 디테일이 사용성을 크게 바꾼다
메시지 위치 하나 바꿨는데, 사용자가 “이제 완료가 명확해졌다”고 느낍니다.
적용할 점
🎯 패턴으로 가져가기
// 1. Toast 타입 정의
type ToastType = 'success' | 'error' | 'info' | 'warning';
interface Toast {
id: string;
type: ToastType;
message: string;
duration?: number; // ms, 기본값 3000
}
// 2. Toast 상태 관리
const [toasts, setToasts] = useState<Toast[]>([]);
// 3. 토스트 추가 함수
const addToast = (message: string, type: ToastType = 'success') => {
const id = Date.now().toString();
setToasts(prev => [...prev, { id, type, message }]);
setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== id));
}, 3000);
};
// 4. 사용
addToast('Saved successfully', 'success');
addToast('Network error', 'error');🔜 다음 계획
Toast 컴포넌트 라이브러리화 모든 화면에서 쓸 수 있게 Context로 관리
액션 취소 기능 “저장됨” 토스트에 “Undo” 버튼 추가
사운드 알림 오류 토스트에 소리 추가 (옵션)
솔직한 소감
처음엔 “그냥 메시지 띄우는 거 아닌가?” 했는데, 위치와 타이밍을 바꾸니까 사용 경험이 확실히 달라지네요.
특히 자동 닫힘이 중요했어요. 메시지가 계속 남으면 사용자가 불안해하는데, 자동으로 사라지니까 오히려 신뢰도가 올라가는 게 신기합니다.
작은 UI 디테일이지만, 사용자가 느끼는 완성도에서는 큰 차이를 만드는 걸 배웠어요.