๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด๋ถ€ sendRedirect() ์‚ฌ์šฉ ์šฐํšŒ ๋ฐฉ๋ฒ•

by ํ‘์‹œ๋ฐ” 2023. 8. 26.

๐Ÿ“Œ ๋ฐฐ๊ฒฝ

sendRedirect()๋Š” ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

 

๋งŒ์•ฝ ํ•˜๋‚˜์˜ Response์—์„œ sendRedirect()๋ฅผ ๋‘ ๋ฒˆ ์ด์ƒ ํ˜ธ์ถœ์„ ์‹œ๋„ํ•˜๋Š” ๊ฒฝ์šฐ, java.lang.IllegalStateException : Cannot call sendRedirect() after the response has been committed ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ๋งŒ์•ฝ ํŠน์ • ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์–ด๋–ค ๊ธฐ๋Šฅ์ด HttpServletResponse๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„์„œ ๋‚ด๋ถ€์ ์œผ๋กœ sendRedirect๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž๋Š” sendRedirect๋ฅผ ๋ง‰๊ฑฐ๋‚˜ ์šฐํšŒํ•  ์ˆ˜๋Š” ์—†๋Š” ๊ฑธ๊นŒ?

์˜ˆ์‹œ ) cors ๋ฌธ์ œ ๋ฐœ์ƒ, ํŠน์ • ๋„๋ฉ”์ธ ๊ฒฝ๋กœ๋กœ ๊ฐ€๋Š” ๊ฒŒ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋“ฑ

โœ‚๏ธ ์˜ˆ์‹œ

public class ExternalLib {

    private final HttpServletResponse response;

    public ExternalLib(HttpServletResponse response) {
        this.response = response;
    }

    public void call() {
        System.out.println("๊ธฐ๋Šฅ ์ˆ˜ํ–‰ ์‹œ์ž‘!");
        System.out.println("call() ์—๋Š” ๋Œ€์ถฉ ์—„์ฒญ๋‚œ ๊ธฐ๋Šฅ์ด ํฌํ•จ๋˜์–ด ์žˆ์Œ.");
        try {
            this.response.sendRedirect("https://shiba-holic.tistory.com/");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

 

ExternalLib๋Š” call์ด๋ผ๋Š” API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. ํ•ด๋‹น API๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ƒ์„ฑ์ž์—์„œ ์ „๋‹ฌ๋ฐ›์€ response์˜ sendRedirect()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

@Component
public class ShibaFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ExternalLib externalLib = new ExternalLib(response);
        externalLib.call();
        response.sendRedirect("https://www.naver.com");
    }
}

 

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์ž๋Š” ํ•„ํ„ฐ์—์„œ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜์ง€๋งŒ, ์ž๊ธฐ๊ฐ€ ์›ํ•˜๋Š” ๋„๋ฉ”์ธ์— ๊ฐ”์œผ๋ฉด ์ข‹๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด์„œ Redirect๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

 

ํ•˜์ง€๋งŒ sendRedirect()๋Š” ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๐Ÿ”Ž ํ•ด๊ฒฐ

์ด๋Ÿฐ ๊ฒฝ์šฐ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด์•ผ ํ• ๊นŒ?

ํ•„์ž๋Š” ํ•ด๋‹น ์ผ€์ด์Šค์—์„œ ๋Œ€๋ฆฌ ํด๋ž˜์Šค(Delegator)๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋‹ค.

 

public class HttpServletResponseDelegator extends HttpServletResponseWrapper {

    public HttpServletResponseDelegator(HttpServletResponse response) {
        super(response);
    }

    @Override
    public void sendRedirect(String location) throws IOException {
        // ignored
    }
}

 

Spring์—์„œ๋Š” Response ๋ฉ”์„œ๋“œ ๊ธฐ๋Šฅ์„ ์˜ค๋ฒ„๋ผ์ด๋“œ๋ฅผ ํ†ตํ•ด ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” HttpServletResponseWrapper๋ผ๋Š” Wrapper ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ ๋Œ€๋ฆฌ ํด๋ž˜์Šค(Delegator)๋ฅผ ์ƒ์„ฑํ•ด์„œ ๊ธฐ์กด Response๋ฅผ ๋„˜๊ฒจ๋ฐ›๊ณ , sendRedirect๋Š” ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์„œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„๋ฌด๋Ÿฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š๋„๋ก ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.

 

@Component
public class ShibaFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ExternalLib externalLib = new ExternalLib(new HttpServletResponseDelegator(response));
        externalLib.call(); // ์ด๋™ํ•˜์ง€ ์•Š์Œ
        response.sendRedirect("https://www.naver.com"); // ์›ํ•˜๋Š” ๊ฒฝ๋กœ๋กœ ์„ค์ • ๊ฐ€๋Šฅ!
    }
}

 

ํ•ด๋‹น ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋ฉด ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ง€์ •ํ•œ ๊ฒฝ๋กœ๋กœ ๋ฆฌ๋””๋ ‰์…˜ ๋˜์ง€ ์•Š๊ฒŒ ๋˜๊ณ , ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๊ฒฝ๋กœ๋กœ ๋ฆฌ๋””๋ ‰์…˜์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

๋Œ“๊ธ€