JiwonDev

#1 HTTP ์š”์ฒญ ๋งคํ•‘๊ณผ ๊ธฐ๋ณธ ๋กœ๊น…

by JiwonDev

# ๊ฐ„๋‹จํ•œ Logging (@Slf4j)

์›น ์„œ๋ฒ„์—์„œ๋Š” System.out.println์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ฐ๊ธฐ์—๋Š” ๋ฌด๋ฆฌ๊ฐ€ ์žˆ๋‹ค. ์Šคํ”„๋ง๋ถ€ํŠธ์— ํฌํ•จ๋œ ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (slf4j)๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ๋งŒ ๋ฐฐ์›Œ๋ณด์ž. ์ฐธ๊ณ ๋กœ slf4j๋Š” Logback, Log4J, Log4J2 ๋“ฑ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“  ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” Logback์„ ๊ธฐ๋ณธ์œผ๋กœ ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค.

(์ฐธ๊ณ ) @Controller์™€ @RestController์˜ ์ฐจ์ด

๋”๋ณด๊ธฐ

@Controller ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด String ์ด๋ฉด ๋ทฐ ์ด๋ฆ„์œผ๋กœ ์ธ์‹๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๋ทฐ๋ฅผ ์ฐพ๊ณ  ๋ทฐ๊ฐ€ ๋žœ๋”๋ง ๋œ๋‹ค.

 

@RestController ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๋ทฐ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ”๋กœ ์ž…๋ ฅํ•œ๋‹ค.

์ด๋Š” REST API์— ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ @ResponseBody ์™€ ๊ด€๋ จ์ด ์žˆ๋Š”๋ฐ, ๋‚˜์ค‘์— ์ถ”๊ฐ€๋กœ ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

// @Controller ๋ฐ˜ํ™˜ ๊ฐ’์„ View ์ด๋ฆ„์œผ๋กœ ์ทจ๊ธ‰ํ•˜๊ณ , ํ•ด๋‹น View ๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜๋œ๋‹ค.
@RestController // ๋ฐ˜ํ™˜ ๊ฐ’์„ ๋ฌธ์ž์—ด ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•œ๋‹ค. ๋ณดํ†ต REST API์— ์‚ฌ์šฉ๋œ๋‹ค.
public class LogTestController {
    private final Logger log = LoggerFactory.getLogger(getClass());
    // ๋‹ค๋ฅธ ํด๋ž˜์Šค๋ฅผ Logger ์— ๋“ฑ๋กํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•˜๋ฉด ๋œ๋‹ค.
    // private static final Logger log = LoggerFactory.getLogger(Xxx.class)

    @RequestMapping("/log-test")
    public String logTest() {
        String name = "Spring";
		
        // ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
        // TRACE > DEBUG > INFO(๊ธฐ๋ณธ ๊ฐ’) > WARN > ERROR
        log.trace("lv 1์ถ”์  log={}", name);
        log.debug("lv 2๋””๋ฒ„๊ทธ log={}", name);
        log.info(" lv 3์ •๋ณด log={}", name);
        log.warn(" lv 4๊ฒฝ๊ณ  log={}", name);
        log.error("lv 5์—๋Ÿฌ log={}", name);

        // ์ฐธ๊ณ ๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด String ๋ง์…ˆ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
        // ์ด๋Š” JAVA์˜ String ์—ฐ์‚ฐ๊ณผ ๊ด€๋ จ์ด ์žˆ๋‹ค. ์ŠคํŠธ๋ง ๋ง์…ˆ์€ ๊ฐ์ฒด๊ฐ€ ๋กœ๋”ฉ ๋  ๋•Œ ๋ฏธ๋ฆฌ ์‹คํ–‰๋˜์–ด ์ตœ์ ํ™”๋œ๋‹ค.
        // ์ฆ‰ ๋กœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ a+b ๊ณ„์‚ฐ ๋กœ์ง์ด ์‹คํ–‰๋˜๋ฉฐ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‚ญ๋น„ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
//        log.debug("String concat log=" + name);
        return "ok";
    }
}

์ฐธ๊ณ ๋กœ lombok์— ์žˆ๋Š” @Slf4j ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Logger๋ฅผ ๋“ฑ๋กํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

@Slf4j
@RestController
public class LogTestController {
    // Logger ์ƒ์„ฑ์ฝ”๋“œ ํ•„์š”์—†์Œ. lombok์ด ์ƒ์„ฑ
    
    @RequestMapping("/log-test")
    public String logTest() {...}
}

์Šคํ”„๋ง์—์„œ ๋„์›Œ์ง€๋Š” ๋กœ๊ทธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Slf4j - Logback์„ ์‚ฌ์šฉํ•ด์„œ ์ฝ˜์†”์ฐฝ์— ๋„์šด๋‹ค.

2021-08-11 07:14:16.825  WARN   23784     --- [nio-8080-exec-1] hello.myClass       :  ๊ฒฝ๊ณ  log=Spring
2021-08-11 07:14:16.825  ์ข…๋ฅ˜   ํ”„๋กœ์„ธ์ŠคID --- [์Šค๋ ˆ๋“œ ์ด๋ฆ„]      Controller ํด๋ž˜์Šค   :  ๋ฉ”์‹œ์ง€

 

์ฐธ๊ณ ๋กœ ์„ค์ •ํ•œ ๋กœ๊ทธ๋ ˆ๋ฒจ (debug, info)๋Š” ์„ค์ •์„ ํ†ตํ•ด ํŠน์ • ๋กœ๊ทธ ๋ ˆ๋ฒจ๋งŒ ์ˆจ๊ธฐ๊ฑฐ๋‚˜ ๋ณด์ด๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹น์—ฐํ•œ ๊ฑฐ์ง€๋งŒ ๋””๋ฒ„๊น…ํ•  ๋•Œ ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์šด์˜ ์„œ๋ฒ„์— ๋„์šฐ๋ฉด ์š”์ฒญ์— ๋”ฐ๋ผ ์—„์ฒญ๋‚œ ๋Ÿ‰์˜ ์“ธ๋ชจ์—†๋Š” ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋จ์„ ์œ ์˜ํ•˜์ž.

# resources/application.yaml

# ์ „์ฒด ๋กœ๊ทธ๋ ˆ๋ฒจ
logging.level.root = info

# ํŠน์ • ํŒจํ‚ค์ง€๋งŒ ์ ์šฉ
logging.level.hello.package = debug

 

@ ์ฝ˜์†”์— ์ฐ๋Š” ๊ฑฐ๋ž‘ ๋ญ๊ฐ€ ๋‹ค๋ฅด์ฃ ?

  • ์“ฐ๋ ˆ๋“œ ์ •๋ณด, ํด๋ž˜์Šค ์ด๋ฆ„ ๊ฐ™์€ ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ์ถœ๋ ฅ ๋ชจ์–‘์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์ง€์ •ํ•ด ์šด์˜์„œ๋ฒ„์—์„œ๋Š” ์ถœ๋ ฅํ•˜์ง€ ์•Š๋Š” ๋“ฑ ๋กœ๊ทธ๋ฅผ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹œ์Šคํ…œ ์•„์›ƒ ์ฝ˜์†”์—๋งŒ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํŒŒ์ผ์ด๋‚˜ ๋„คํŠธ์›Œํฌ ๋“ฑ, ๋กœ๊ทธ๋ฅผ ๋ณ„๋„์˜ ์œ„์น˜์— ๋‚จ๊ธธ ์ˆ˜ ์žˆ๋‹ค. 
  • ํŠนํžˆ ํŒŒ์ผ๋กœ ๋‚จ๊ธธ ๋•Œ๋Š” ์ผ๋ณ„, ํŠน์ • ์šฉ๋Ÿ‰์— ๋”ฐ๋ผ ๋กœ๊ทธ๋ฅผ ๋ถ„ํ• ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์„ฑ๋Šฅ๋„ ์ผ๋ฐ˜ System.out๋ณด๋‹ค ์ˆ˜์‹ญ ๋ฐฐ๋Š” ์ข‹๋‹ค. (๋‚ด๋ถ€ ๋ฒ„ํผ๋ง, ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ๋“ฑ๋“ฑ)

๋กœ๊น… ๊ธฐ๋Šฅ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ–ˆ์ง€๋งŒ, ๊นŠ๊ฒŒ ๋“ค์–ด๊ฐ€๋ฉด ์ด๊ฒƒ๋„ ๊ณต๋ถ€ํ•  ๋‚ด์šฉ์ด ๋งŽ๋‹ค. ์ผ๋‹จ์€ ์ด ์ •๋„๋งŒ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋„๋ก ํ•˜์ž.


# ์š”์ฒญ ๋งคํ•‘ํ•˜๊ธฐ (@Controller)

์š”์ฒญ์ด ์™”์„ ๋•Œ ์–ด๋–ค ์Šคํ”„๋ง ๋นˆ(์„œ๋ธ”๋ฆฟ ์—ญํ• )์ด ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋Š”๊ฐ€?

  • @Controller ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด String ์ด๋ฉด ๋ทฐ ์ด๋ฆ„์œผ๋กœ ์ธ์‹๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๋ทฐ๋ฅผ ์ฐพ๊ณ  ๋ทฐ๊ฐ€ ๋žœ๋”๋ง ๋œ๋‹ค.
  • @RestController ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๋ทฐ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, HTTP ๋ฉ”์‹œ์ง€ ๋ฐ”๋””์— ๋ฐ”๋กœ ์ž…๋ ฅํ•œ๋‹ค.

์šฐ๋ฆฌ๋Š” ๋งŒ๋“ค์–ด๋‘” View(HTML ๋˜๋Š” ํ…œํ”Œ๋ฆฟ)๊ฐ€ ์—†์œผ๋ฏ€๋กœ, @RestContoller๋ฅผ ํ†ตํ•ด ๋งคํ•‘ ๋ฐฉ๋ฒ•์„ ๋ฐฐ์›Œ๋ณด์ž. 

์ฐธ๊ณ ๋กœ jiwon.com/hello ์™€ jiwon.com/hello/ ๋Š” HTTP์ƒ์œผ๋กœ ๋‹ค๋ฅธ ์š”์ฒญ์ด์ง€๋งŒ, ์Šคํ”„๋ง์€ ๊ฐ™์€ ์š”์ฒญ์œผ๋กœ ๋งคํ•‘ํ•œ๋‹ค.

@Slf4j
@RestController
public class MappingController {
	
    // ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋งคํ•‘ ํ•˜๊ณ ์‹ถ๋‹ค๋ฉด ({"/hello1" , "/hello2"})
    @RequestMapping("/hello-basic")
    public String helloBasic() {
        log.info("helloBasic");
        return "ok";
    }
}

 

์œ„์™€ ๊ฐ™์ด HTTP Method๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“  ์š”์ฒญ(GET, HEAD, POST..)๋ฅผ ๋‹ค ๋ฐ›์•„๋“ค์ธ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ์ง€์ •ํ•ด์ฃผ์ž.

@RequestMapping("/mapping-get-v1", method = RequestMethod.GET)
    public String mappingGetV1() {
        log.info("mappingGetV1");
        return "ok";
    }

 

์ข€ ๋” ์ง๊ด€์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์Šคํ”„๋ง์—์„œ๋Š” ์ถ•์•ฝํ˜•(@GetMapping, @PostMapping)๋„ ์ œ๊ณตํ•œ๋‹ค. ๋™์ž‘์€ ์œ„์™€ ๊ฐ™๋‹ค.

@GetMapping("/mapping-get-v2")
    public String mappingGetV1() {
        log.info("mappingGetV1");
        return "ok";
    }

 

์ฐธ๊ณ ๋กœ ์ž˜๋ชป๋œ ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ ์Šคํ”„๋ง์—์„œ JSON ํ˜•์‹์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ์ด๋Š” RestContoller๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ทธ๋ ‡๋‹ค. ์ด๋Š” ๋‚˜์ค‘์— ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ๋ฐฐ์šฐ๋ฉฐ ์•Œ๊ฒŒ ๋˜๋‹ˆ ์ผ๋‹จ ๋„˜์–ด๊ฐ€๋„๋ก ํ•˜์ž.


@ PathVariable, ๊ฒฝ๋กœ ๋ณ€์ˆ˜

์š”์ฒญ ๋งคํ•‘์„ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ง€์ •ํ•˜๊ณ  ์‹ถ์€ ๋ณ€์ˆ˜ ์•ž์— @PathVariable("~")์„ ์ ์œผ๋ฉด ๋œ๋‹ค. ์˜›๋‚ ์—๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ( a.com/hello?userId=3)๋ฅผ ๋งŽ์ด์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ์ตœ๊ทผ์˜ API๋Š” ์ด๋ ‡๊ฒŒ ์‹๋ณ„์ž๋ฅผ ๋„ฃ๋Š” ์Šคํƒ€์ผ์„ ๋” ์„ ํ˜ธํ•œ๋‹ค. 

# ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฉ”ํƒ€
http://a.com/mapping?users=userA&orders=100

# http://a.com/mapping/users/{userId}/orders/{orderId}
http://a.com/mapping/users/userA/orders/100
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
 log.info("mappingPath userId={}", data); // {userId}์— data๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.
 return "ok";
}

// ์ฐธ๊ณ ๋กœ {~}์™€ ๊ฐ™์€ ์ด๋ฆ„์˜ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ƒ๋žตํ•ด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable userId) {
 log.info("mappingPath userId={}", data);
 return "ok";
}

@ ํŒŒ๋ผ๋ฉ”ํƒ€ ์กฐ๊ฑด ๋งคํ•‘

์œ„์˜ @PathVariable ๋•Œ๋ฌธ์— ์ž˜ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์ง€๋งŒ, ํŠน์ • ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์‹คํ–‰๋˜๋„๋ก ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

/**
 * ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ถ”๊ฐ€ ๋งคํ•‘
 * params="mode",
 * params="!mode" (์ฃผ์–ด์ง„ ๊ฐ’์ด mode๊ฐ€ ์•„๋‹๋•Œ๋งŒ)
 * params="mode=debug"
 * params="mode!=debug" (mode=debug๊ฐ€ ์•„๋‹๋•Œ๋งŒ)
 * params = {"mode=debug","data=good"}
 */
 
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
    log.info("mappingParam");
    return "ok";
}

400 ์˜ค๋ฅ˜์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ž˜๋ชป๋œ ์š”์ฒญ์ด๋ผ๊ณ  ์‘๋‹ตํ•œ๋‹ค.

 

@ ๋ฏธ๋””์–ด ํƒ€์ž… ์กฐ๊ฑด ๋งคํ•‘

consumes ="~" ์ด์šฉํ•ด์„œ HTTP ํ—ค๋”๋ฅผ ํ™•์ธํ•ด ํŠน์ • ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…๋งŒ ๋ฐ›๋„๋ก ๋งŒ๋“ค์ˆ˜๋„ ์žˆ๋‹ค.

content-type์ด๋ผ๊ณ  ์ ์ง€ ์•Š๊ณ  ์ปจ์Š˜์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉํ•˜๋Š”(consumes) ๋ฐ์ดํ„ฐ์™€ ์ƒ์‚ฐ(produces)ํ•˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋”ฐ๋กœ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.

/**
 * Content-Type ํ—ค๋” ๊ธฐ๋ฐ˜ ์ถ”๊ฐ€ ๋งคํ•‘ Media Type
 * consumes = "text/plain"
 * consumes ="!application/json" (Json์ด ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋งŒ ํ—ˆ์šฉ)
 * consumes = {"text/plain", "application/*"}
 * consumes = MediaType.TEXT_PLAIN_VALUE (์ƒ์ˆ˜ ์‚ฌ์šฉ)
 */
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
    log.info("mappingConsumes");
    return "ok";
}
// ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ Accept ํ—ค๋”์™€ ๋น„๊ตํ•ด์„œ ๋™์ž‘ํ•œ๋‹ค. ๋งŒ์•ฝ ๋‹ค๋ฅด๋‹ค๋ฉด 406 ์˜ค๋ฅ˜์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
    log.info("mappingProduces");
    return "ok";
}

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ