개발/Spring

MSA 환경(JAR)에서 실행 가능한 Spring Interceptor 기반 중복 방지 + 버튼 비활성화

피터JK 2025. 9. 12. 12:41
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>

✅ 특징

  1. Interceptor 기반
    • 서버 단에서 세션을 이용해 중복 요청 방지
    • JAR(MSA) 환경에서도 동작
  2. 클라이언트 버튼 비활성화
    • 사용자 측에서 연속 클릭 방지
  3. 간단한 적용
    • URL 패턴만 Interceptor에 등록하면 적용 가능
  4. 확장성
    • Redis 등 분산 캐시와 결합 가능 → MSA 환경에서 다중 인스턴스도 대응 가능

 

728x90