728x90
1️⃣ Interceptor 구현
package com.example.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 동일 세션 내 중복 요청 방지 Interceptor
*/
@Component
public class DuplicateRequestInterceptor implements HandlerInterceptor {
private static final String LAST_REQUEST_TIME = "LAST_REQUEST_TIME";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Long lastTime = (Long) request.getSession().getAttribute(LAST_REQUEST_TIME);
long now = System.currentTimeMillis();
// 3초 이내 중복 요청 차단
if (lastTime != null && (now - lastTime) < 3000) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "중복 요청이 감지되었습니다.");
return false;
}
request.getSession().setAttribute(LAST_REQUEST_TIME, now);
return true;
}
}
2️⃣ Interceptor 등록 (Spring Boot 설정)
package com.example.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.example.interceptor.DuplicateRequestInterceptor;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DuplicateRequestInterceptor duplicateRequestInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 모든 /privacy/save/** POST 요청에 대해 중복 방지
registry.addInterceptor(duplicateRequestInterceptor)
.addPathPatterns("/privacy/**")
.excludePathPatterns("/privacy/form/**"); // 제외 경로 가능
}
}
3️⃣ Controller 예제
package com.example.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class PrivacyController {
// 입력 폼 화면
@GetMapping("/privacy/form")
public String form() {
return "privacy/form";
}
// 실제 저장 처리
@PostMapping("/privacy/save")
public String save(HttpServletRequest request, Model model) {
// 비즈니스 로직 처리
model.addAttribute("msg", "정상적으로 저장되었습니다.");
return "privacy/success";
}
}
4️⃣ JSP 폼 예제 (버튼 비활성화 적용)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Privacy Form</title>
</head>
<body>
<h2>개인정보 입력</h2>
<form id="privacyForm" action="/privacy/save" method="post">
<input type="text" name="username" placeholder="이름 입력" required />
<button type="submit" id="submitBtn">저장</button>
</form>
<script>
document.getElementById("privacyForm").addEventListener("submit", function() {
var btn = document.getElementById("submitBtn");
btn.disabled = true; // 버튼 비활성화
btn.innerText = "처리중..."; // 상태 변경
});
</script>
</body>
</html>
✅ 특징
- Interceptor 기반
- 서버 단에서 세션을 이용해 중복 요청 방지
- JAR(MSA) 환경에서도 동작
- 클라이언트 버튼 비활성화
- 사용자 측에서 연속 클릭 방지
- 간단한 적용
- URL 패턴만 Interceptor에 등록하면 적용 가능
- 확장성
- Redis 등 분산 캐시와 결합 가능 → MSA 환경에서 다중 인스턴스도 대응 가능
728x90
'개발 > Spring' 카테고리의 다른 글
| Spring에서 인터셉터(HandlerInterceptor) 와 AOP(Aspect Oriented Programming) 차이 (0) | 2025.10.03 |
|---|---|
| 스프링(Spring) 인터셉터(Interceptor) (0) | 2025.10.03 |
| Spring Interceptor 기반 중복방지 (0) | 2025.09.12 |
| 스프링에서 생성자 주입(Constructor Injection)을 권장 (0) | 2025.09.12 |
| Spring Boot와 Jasypt로 비밀번호 암호화 및 복호화 설정 (0) | 2025.03.17 |