์Šคํ”„๋ง๋ถ€ํŠธ ์›น MVC ํ”„๋กœ์ ํŠธ์—์„œ ์ƒํƒœ ์ฝ”๋“œ์— ๋”ฐ๋ผ ์—๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋ทฐ์— ๊ฐ๊ฐ์˜ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์›น ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐ŸŒฑ ์›น ํŽ˜์ด์ง€ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๋งŒ์•ฝ Github์—์„œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€๋กœ ์ ‘์†ํ•  ๊ฒฝ์šฐ ์˜ฅํ† ๋น„-์™„์ด ์—ฌ๋Ÿฌ๋ถ„์„ ๋งž์•„์ค๋‹ˆ๋‹ค. ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ๊ฐ„๋‹จํžˆ ํŠน์ • ์œ„์น˜์— htmlํŽ˜์ด์ง€๋งŒ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ์ด๋Ÿฐ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image.png

๐ŸŒฑ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

  • IntelliJ ULTIMATE ๋ฒ„์ „์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด IDE์—์„œ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

1.png

  • Spring Initializr

    Spring Initializr์— ์ ‘์†ํ•˜์—ฌ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  .zip ํŒŒ์ผ๋กœ ๋‹ค์šด๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  web ์˜์กด์„ฑ์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ Spring MVC and Tomcat์„ ์ถ”๊ฐ€ํ•ด์ค€ ๋‹ค์Œ Generate Project ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์šด๋ฐ›์Šต๋‹ˆ๋‹ค.
    spirnginitializr.png

์ €๋Š” ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ธ Spring Initializr๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ErrorHandle ์ด๋ผ๋Š” ์ด๋ฆ„์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

๐ŸŒฑ ์Šคํ”„๋ง์˜ ๊ธฐ๋ณธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

์ƒ์„ฑํ•œ ์Šคํ”„๋ง ํ”„๋กœ์ ํŠธ๋ฅผ ์—ด๊ณ  ๊ธฐ๋ณธ ํ”„๋กœ์ ํŠธ๊ฐ€ ์…‹ํŒ…๋œ ๊ทธ๋Œ€๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์—์„œhttp://localhost:8080 ์ฃผ์†Œ๋กœ ์ ‘์†ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Untitled.png

์ด ์—๋Ÿฌ ํŽ˜์ด์ง€๋Š” Tomcat์—์„œ ๋งŒ๋“ค์–ด์ง€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์Šคํ”„๋ง๋ถ€ํŠธ์— ์˜ํ•ด ๋งŒ๋“ค์–ด์ง„ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค. ์Šคํ”„๋ง์—์„œ ์ด๋Ÿฐ ๊ธฐ๋ณธ์ ์ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๊ณณ์€ BasicErrorController์ž…๋‹ˆ๋‹ค.

BasicErrorController

    // ...

    // text/html๊ฐ€ ํฌํ•จ๋œ ํ—ค๋”๊ฐ€ ์žˆ๋Š” ์š”์ฒญ์—๋Š” ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜
    @RequestMapping(produces = {"text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    // ๊ทธ ์™ธ์—๋Š” json ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
    @RequestMapping
      public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
          Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
          HttpStatus status = this.getStatus(request);
          return new ResponseEntity(body, status);
      }

    // ...

BasicErrorController ์ฝ”๋“œ์˜ ํ•ด๋‹น ๋ถ€๋ถ„์€ HTTP ์š”์ฒญ์˜ Accept ํ—ค๋”์— text/html๊ฐ€ ํฌํ•จ๋œ ๊ฒฝ์šฐ์—” html ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ทธ ์™ธ์—๋Š” json ๊ฐ์ฒด๋ฅผ ๊ฒฐ๊ณผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ Whitelabel Error Page๋Š” ์ „์ž์˜ ๊ฒฝ์šฐ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€๋กœ, ์›น ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹ˆ๋ผ ์ฝ˜์†”์—์„œ curl๋กœ ํ—ค๋” ์—†์ด ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜๋ฉด json ํ˜•ํƒœ์˜ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ๋ฐ›์Šต๋‹ˆ๋‹ค.

curl http://localhost:8080/
{"timestamp":"2019-04-19T00:32:38.982+0000","status":404,"error":"Not Found","message":"No message available","path":"/"}

ํ—ค๋”์— Accept: text/html ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์›น ๋ธŒ๋ผ์šฐ์ €์™€ ๋™์ผํ•˜๊ฒŒ html ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

curl -H "Accept: text/html" http://localhost:8080
<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Fri Apr 19 17:01:20 KST 2019</div><div>There was an unexpected error (type=Not Found, status=404).</div><div>No message available</div></body></html>

๐ŸŒฑ ์ •์ƒ ๋™์ž‘ ํŽ˜์ด์ง€ ์ƒ์„ฑ

http://localhost:8080 ์ฃผ์†Œ๋กœ ์ ‘๊ทผํ•˜๋ฉด ํ˜„์žฌ๋Š” ์Šคํ”„๋ง์ด BasicErrorController๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•œ Whitelabel Error Page๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์—๋Ÿฌ ํŽ˜์ด์ง€๋Š” ์„ค์ • ํŒŒ์ผ์— server.error.path๋‚˜ error.path๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ๋งˆ์ง€๋ง‰์œผ๋กœ ์Šคํ”„๋ง์ด ๋งŒ๋“ค์–ด์ฃผ๋Š” ์—๋Ÿฌ ํŽ˜์ด์ง€์ž…๋‹ˆ๋‹ค.

2.png

Whitelabel Error Page๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด, ๋จผ์ € ๋ฃจํŠธ ์ฃผ์†Œ("/")์— ๋Œ€ํ•ด์„œ๋Š” index.html์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ •์ƒ์ ์œผ๋กœ ์ ‘๊ทผ๋˜๋Š” ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค.

index.html์€ ์ •์  ๋ฆฌ์†Œ์Šค ๋งตํ•‘ ์‹œ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌ์†Œ์Šค ๊ธฐ๋ณธ ๊ฒฝ๋กœ ์ค‘ ์•„๋ฌด๊ณณ์—๋‚˜ ์œ„์น˜์‹œ์ผœ๋„ ๋ฉ๋‹ˆ๋‹ค.

  • classpath:/static
  • classpath:/public
  • classpath:/resources
  • classpath:/META-INF/resources

์ €๋Š” src/main/resources/static ์œ„์น˜์— index.html๊ณผ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Title</title>
  </head>
  <body>
    <img src="toto.jpeg" width="300" height="400" alt="index cat">
    <h1>Hello, Godori!</h1>
  </body>
</html>

์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๋ฃจํŠธ๋กœ ์ ‘์†ํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ index.html์ด ๋ณด์ž…๋‹ˆ๋‹ค.

success.png

๐ŸŒฑ ์ปค์Šคํ…€ ์—๋Ÿฌ ํŽ˜์ด์ง€ ๋งŒ๋“ค๊ธฐ

https://http.cat ์€ ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๊ณ ์–‘์ด ์‚ฌ์ง„์œผ๋กœ ํ‘œํ˜„ํ•œ ์žฌ๋ฐŒ๋Š” ์‚ฌ์ดํŠธ์ž…๋‹ˆ๋‹ค.

Github์˜ 404 ์—๋Ÿฌ ํŽ˜์ด์ง€์ฒ˜๋Ÿผ, ํŠน์ • ์ƒํƒœ ์ฝ”๋“œ์— ๋”ฐ๋ผ ์„œ๋กœ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ์ปค์Šคํ…€ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๋‹ค์Œ HTTP Cats์˜ ์ด๋ฏธ์ง€๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“ค์–ด ๋ณผ๊ฒŒ์š”.

httpcat.png

resources์˜ static ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์— error ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  404.html๊ณผ 5xx.html ํŒŒ์ผ ๋‘ ๊ฐœ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ์ด๋ฆ„์€ ์ƒํƒœ ์ฝ”๋“œ์™€ ๋™์ผํ•˜๊ฒŒ 404 ๋“ฑ์œผ๋กœ ์ ๊ฑฐ๋‚˜, 500๋ฒˆ๋Œ€ ์ƒํƒœ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•œ๋‹ค๋Š” ์˜๋ฏธ๋กœ 5xx.html๋“ฑ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(๋งŒ์•ฝ static ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์˜ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ error ํ•˜๋‚˜ ๋ฟ์ด๋ผ๋ฉด ๋””๋ ‰ํ† ๋ฆฌ ์ด๋ฆ„์ด static.error ๋ผ๋Š” ํ˜•ํƒœ๋กœ ๋ณด์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค)

errorpage.png

404์™€ 500 ์—๋Ÿฌ์— ๋Œ€ํ•ด ๊ฐ๊ฐ์˜ html ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ , SampleController ๋ผ๋Š” ์ด๋ฆ„์˜ Controller๋ฅผ ์ƒ์„ฑํ•˜์—ฌ localhost:8080/error ๋กœ ์ ‘์†ํ•˜๋ฉด ์—๋Ÿฌ๋ฅผ ๋˜์ง€๋Š” ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    package com.godori.ErrorHandle;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;

    @Controller
    public class SampleController {

        @GetMapping("/error")
        public String errorpage(){
            throw new IllegalStateException("Error");
        }
    }

์ด์ œ / ํ˜น์€ /error ์ฃผ์†Œ ์™ธ์˜ ํŽ˜์ด์ง€๋กœ ์ ‘๊ทผํ•˜๋ฉด 404 Not Found ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ, /error๋กœ ์ ‘๊ทผํ•˜๋ฉด 500 Internal Server Error ํŽ˜์ด์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

404error.png

500error.png

๐ŸŒฑ ์ •๋ฆฌ

์—๋Ÿฌ ํŽ˜์ด์ง€๋Š” ๋ณ„๋กœ ๋ณด๊ณ  ์‹ถ์ง€ ์•Š์€ ํŽ˜์ด์ง€์ง€๋งŒ ๋งŒ๋“ค์–ด ๋‘๋ฉด ์‚ฌ์ดํŠธ์˜ ์™„์„ฑ๋„๋ฅผ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋ณธ์ธ๋งŒ์˜ ์—๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด ์ฒ˜๋ฆฌํ•ด ๋ณด์•„์š”.

๐ŸŒฟ ์ฐธ๊ณ 

  • Inflean ์Šคํ”„๋ง๋ถ€ํŠธ ๊ฐœ๋…๊ณผ ํ™œ์šฉ - ๋ฐฑ๊ธฐ์„ ๋‹˜ ๊ฐ•์ขŒ
  • https://http.cat/