/images/profile.png

์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋ฏธ๋ž˜ ์˜ˆ์ธก ํ•˜๊ธฐ(Anomaly Detection)

๊ธ‰๋ณ€ํ•˜๋Š” ๋‚ ์”จ๋ฅผ ์˜ˆ์ธกํ•˜๋ ค๋ฉด ์–ด๋– ํ•œ ์ •๋ณด๊ฐ€ ์žˆ์–ด์•ผ ํ• ๊นŒ? ๋˜๋Š” ๋งˆํŠธ๋ฅผ ์šด์˜ํ•˜๋Š” ๋‹ด๋‹น์ž์ธ ๊ฒฝ์šฐ ๋งค์žฅ ์šด์˜์‹œ๊ฐ„์„ ์ •ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์–ด๋– ํ•œ ๊ธฐ์ค€์œผ๋กœ? ๋œจ๊ฑฐ์šด ๊ฐ์ž์ธ ๋น„ํŠธ์ฝ”์ธ ์‹œ์žฅ์—์„œ ์ˆ˜์ต์„ ์–ป์œผ๋ ค๋ฉด ์–ด๋–ค ์ •๋ณด๋“ค์ด ์žˆ์–ด์•ผ ๋ฌผ๋ฆฌ์ง€(?) ์•Š์„์ˆ˜ ์žˆ์„๊นŒ?

์œ„ ์งˆ๋ฌธ์— ๊ณตํ†ต๋œ ์ •๋‹ต์€ ์˜ˆ์ „ ๊ธฐ๋ก๋“ค์ธ๊ฒƒ ๊ฐ™๋‹ค. ๋‚ ์”จ์˜ˆ์ธก์€ ๊ธฐ์ƒ์ฒญ์—์„œ ๊ณผ๊ฑฐ ๊ธฐ๋ก๋“ค์„ ๋ณด๊ณ  ๋น„๊ฐ€ ์˜ฌ์ง€ ๋ง์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ  ( ๊ณผ๊ฑฐ ๋‚ ์”จ ์„œ๋น„์Šค๋ฅผ ๋‹ด๋‹นํ•ด๋ดค์ง€๋งŒ ๋‹จ์ˆœํžˆ ๊ณผ๊ฑฐ ๊ธฐ๋ก๋“ค๋กœ ์˜ˆ์ธกํ•œ๋‹ค๋Š”๊ฑด ๋ถˆ๊ฐ€๋Šฅ์— ๊ฐ€๊น๊ธด ํ•˜๋‹ค. ) ๋งค์žฅ ์šด์˜์‹œ๊ฐ„์€ ์˜ˆ์ „์— ์†๋‹˜๋“ค์ด ์–ธ์ œ์™”๋Š”์ง€์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ณ . ๋น„ํŠธ์ฝ”์ธ์ด๋‚˜ ์ฃผ์‹์€ ์ฐจํŠธ๋ฅผ ๋ณด๊ณ  ์–ด๋А์ •๋„๋Š” ์ƒ์Šน์žฅ์ผ์ง€ ํ•˜๋ฝ์žฅ์ผ์ง€ ์ถ”์ธก์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•œ๋‹ค. ( ๋ฌผ๋ก  ํ˜ธ์žฌ/์•…์žฌ์— ๋”ฐ๋ผ ํ”๋“ค๋ฆฌ์ง€๋งŒ..ใ… ใ… ..?? ) ์ด์ฒ˜๋Ÿผ ์‹œ๊ฐ„์˜ ํ๋ฆ„์— ๋”ฐ๋ผ ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๋Š”๊ฒƒ์„ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋ผ ๋ถ€๋ฅด๊ณ  ์žˆ๋‹ค. ํ•„์ž๊ฐ€ ์šด์˜ํ•˜๋Š” ์„œ๋น„์Šค์—์„œ ์‹œ๊ณ„์—ด ๋ฐ์ดํ„ฐ ๋ถ„์„์„ ํ†ตํ•ด ์žฅ์• ๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•˜๋Š” ์‚ฌ๋ก€๋ฅผ ๊ณต์œ  ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

์ƒํ™ฉํŒŒ์•…๋ถ€ํ„ฐ

์†์ž๋ณ‘๋ฒ•์—๋Š” ์ง€ํ”ผ์ง€๊ธฐ ๋ฐฑ์ „๋ถˆํƒœ ๋ผ๋Š” ๋ง์ด ์žˆ๋‹ค. ๊ทธ๋งŒํผ ํ˜„ ์ƒํ™ฉ์„ ์ž˜ ์•Œ์•„์•ผ ๋Œ€์‘์„ ์ž˜ํ• ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒƒ. ํ•„์ž๊ฐ€ ์šด์˜ํ•˜๋Š” ์„œ๋น„์Šค๋Š” PG(Payment Gateway) ์„œ๋น„์Šค๋กœ ์‡ผํ•‘๋ชฐ๊ฐ™์€ ์˜จ/์˜คํ”„๋ผ์ธ ์‚ฌ์—…์ž์™€ ์‹ค์ œ ์นด๋“œ์‚ฌ์™€์˜ ์ค‘๊ฐ„ ์—ญํ™œ์„ ํ•ด์ฃผ๊ณ  ์žˆ๋‹ค. ์ด๋ฅผํ…Œ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ƒ์ˆ˜๋ฅผ 10,000์›์— XX์นด๋“œ๋กœ ๊ตฌ๋งคํ•ด์ค˜ ๋ผ๊ณ  ์š”์ฒญ์ด ์˜ค๋ฉด ๊ทธ ์ •๋ณด๋ฅผ ๋‹ค์‹œ ํ˜•์‹์— ๋งž์ถฐ ์นด๋“œ์‚ฌ๋กœ ์ „๋‹ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฌผ๊ฑด์„ ๊ตฌ๋งคํ• ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค€๋‹ค.

/images/anomaly-detection/pg.png
PG์„œ๋น„์Šค : ์‡ผํ•‘๋ชฐ๊ณผ ์นด๋“œ์‚ฌ์˜ ์ค‘๊ฐ„์—์„œ ๋ฆด๋ ˆ์ด ํ•ด์ฃผ๋Š” ์—ญํ™œ์ด๋ผ ๋ณด๋ฉด๋œ๋‹ค.

์š”๊ตฌ์‚ฌํ•ญ ๋ฐ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ ๋ถ„์„

์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•ด๋ณด๋‹ˆ ๊ฐ์ง€ํ•˜๊ธฐ ์–ด๋ ค์šด ์ƒํ™ฉ๋“ค์ด ์žˆ์—ˆ๋‹ค.

  • ์—ฐ๋™ํ•˜๋Š” ์‡ผํ•‘๋ชฐ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ• ๊ฒฝ์šฐ ์ฆ‰, ํŠธ๋ž˜ํ”ฝ์ด ํ‰์†Œ๋ณด๋‹ค ์ ๊ฒŒ ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ
  • ์ •์ƒ์ ์ธ ์—๋Ÿฌ(e.g. ์ž”์•ก๋ถ€์กฑ) ๊ฐ€ ๊ฐ‘์ž๊ธฐ ๋งŽ์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ

์ด๋ฅผ ๋ถ„์„ํ•˜๊ธฐ์œ„ํ•ด ๊ธฐ์กด์˜ ํŠธ๋ž˜ํ”ฝ/๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•ด๋ด์•ผ ํ–ˆ๋‹ค.

/images/anomaly-detection/trade_count.png
๊ฒฐ์ œ๊ฑด์ˆ˜ Kibana Visualize, ๊ธฐ์˜์ด ํŒจํ„ด

์œ„ ๊ทธ๋ž˜ํ”„๋Š” ๊ฒฐ์ œ๋ฐ์ดํ„ฐ ์นด์šดํŠธ ์ธ๋ฐ ์–ด๋А์ •๋„ ํŒจํ„ด์„ ์ฐพ์„์ˆ˜ ์žˆ๋‹ค.

/images/anomaly-detection/error_count.png
์—๋Ÿฌ๊ฑด์ˆ˜ Kibana Visualize, ์•…์–ด ํŒจํ„ด..(๋ฌด๋ฆฌ์ˆ˜..)

์œ„ ๊ทธ๋ž˜ํ”„๋Š” ์—๋Ÿฌ์นด์šดํŠธ ์ธ๋ฐ ์ผ์ •ํ•œ ํŒจํ„ด ์†์—์„œ ์–ด๋А ์ง€์ ์—์„œ๋Š” ํŠ€๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค. (๋นจ๊ฐ„์ƒ‰ ์˜์—ญ) ๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ค ๋ฐฉ๋ฒ•์œผ๋กœ ์žฅ์• ์ƒํ™ฉ๋ณด๋‹ค ์•ž์„œ์„œ ๊ฐ์ง€๋ฅผ ํ• ์ˆ˜ ์žˆ์„๊นŒ? ( ์žฅ์•  : ์–ด๋– ํ•œ ๋‚ด/์™ธ๋ถ€ ์š”์ธ์œผ๋กœ ์ธํ•ด ์ •์ƒ์ ์ธ ์„œ๋น„์Šค๊ฐ€ ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ )

์žฅ์• ๋ฐœ์ƒ ์ „์— ๋จผ์ € ์ฐพ์•„๋ณด์ž!

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

์ง€๋‚œ ๋ฐ์ดํ„ฐ์™€ ๋น„๊ตํ•˜๊ธฐ

์ผ์ฃผ์ผ ๊ธฐ์ค€์œผ๋กœ ์ง€๋‚œ ์ผ์ฃผ์ผ๊ณผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„๊ตํ•ด๋ณด๋Š” ๋ฐฉ๋ฒ•๋˜ํ•œ ์žˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด ์ด๋ฒˆ์ฃผ ์›”์š”์ผ 10์‹œ์˜ ๋ฐ์ดํ„ฐ์™€ ์ง€๋‚œ์ฃผ ์›”์š”์ผ 10์‹œ์˜ ๋ฐ์ดํ„ฐ์˜ ์ฐจ์ด๋ฅผ ๋น„๊ตํ•ด๋ณด๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ํ‚ค๋ฐ”๋‚˜์—์„œ ํด๋ฆญ ๋ช‡๋ฒˆ๋งŒ์œผ๋กœ ์‹œ๊ฐํ™”๋ฅผ ๋„์™€์ฃผ๋Š” Visualize ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์ง€๋‚œ ์ผ์ฃผ์ผ๊ณผ ์ด๋ฒˆ์ฃผ๋ฅผ ๋น„๊ตํ•ด๋ณด๋ฉด ์•„๋ž˜ ๊ทธ๋ž˜ํ”„์ฒ˜๋Ÿผ ํ‘œํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

/images/anomaly-detection/visualize.png
์ผ์ฃผ์ผ ์ „ ๋ฐ์ดํ„ฐ์™€ ๋‹จ์ˆœ ๋น„๊ต

์ด ๊ฒฝ์šฐ๋„ ์ง€๋‚œ์ฃผ ์ƒํ™ฉ๊ณผ ์ด๋ฒˆ์ฃผ ์ƒํ™ฉ์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋Š” ์›ํ•˜๋Š” ๋น„๊ต ํ•ญ๋ชฉ ์™ธ์— ๋‹ค๋ฅธ ์š”์ธ์ด ์ถ”๊ฐ€๋˜๊ธฐ ๋•Œ๋ฌธ์— ์›ํ•˜๋Š” ๋น„๊ต๋ฅผ ํ• ์ˆ˜๊ฐ€ ์—†๊ณ  ์œ„์—์„œ ์ˆ˜๋™์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ ๋ณ„๋ฐ˜ ๋‹ค๋ฅผ๋ฐ” ์—†์„๊ฒƒ์œผ๋กœ ์ƒ๊ฐ๋œ๋‹ค.

์กฐ๊ธˆ๋” ์šฐ์•„ํ•˜๊ฒŒ! (์–ธ์ œ๋ถ€ํ„ด๊ฐ€ ์šฐ์•„ํ•˜๋‹จ ๋ง์„ ์ข‹์•„ํ•˜๋Š”๊ฒƒ ๊ฐ™๋‹ค..)

๊ฐœ๋ฐœ์ž๋Š” ๋ฌธ์ œ์— ๋Œ€ํ•ด์„œ ์–ธ์ œ๋‚˜ ๋ถ„์„์„ ํ† ๋Œ€๋กœ ์ ‘๊ทผ์„ ํ•˜๋Š”๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ด์•ผํ•œ๋‹ค. ์–ธ์ œ๋ถ€ํ„ด๊ฐ€ Hotํ•œ ๋จธ์‹ ๋Ÿฌ๋‹์„ ๋„์ž…ํ•ด ๋ณด๊ณ  ์‹ถ์—ˆ์œผ๋‚˜ ์•„์ง ๊ทธ๋Ÿฐ ์‹ค๋ ฅ์ด ๋˜์งˆ ๋ชปํ•˜๊ณ … ํญํ’ ๊ตฌ๊ธ€๋ง์„ ํ†ตํ•ด ์•Œ๊ฒŒ๋œ Facebook์—์„œ ๋งŒ๋“  Prophet์ด๋ผ๋Š” ๋ชจ๋“ˆ์„ ํ™œ์šฉํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค. https://opensource.fb.com/#artificial ์ด๊ณณ์— ๊ฐ€๋ณด๋ฉด ์—ฌ๋Ÿฌ Artificial Intelligence ๊ด€๋ จ๋œ ์˜คํ”ˆ์†Œ์Šค๋“ค์ค‘์— Prophet ๋ชจ๋“ˆ์„ ์ฐพ์„์ˆ˜ ์žˆ๋‹ค. ๋‹คํ–‰ํžˆ๋„ BSD License๋ผ์„œ ์‹ค๋ฌด์—์„œ๋„ ๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ• ์ˆ˜ ์žˆ์„๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค. ์นœ์ ˆํ•˜๊ฒŒ๋„ Quick Start์„ ํ†ตํ•ด ์–ด๋–ค์‹์œผ๋กœ ์˜ˆ์ธก์„ ํ•˜๋Š”์ง€ ๋ณด์—ฌ์ค€๋‹ค. ์ฐธ๊ณ ๋กœ Python ๊ณผ R ์„ ์ง€์›ํ•œ๋‹ค. (python ์˜ ๋Œ€๋‹จํ•จ์„ ๋‹ค์‹œ๊ธˆ ๋А๋ผ๋ฉฐ…) ๊ตฌ์„ฑ์€ CentOS 7 + python3.6 + jenkins ๋ฅผ ํ™œ์šฉํ•œ๋‹ค. (python ๊ฒฝํ—˜์ด ๋ถ€์กฑํ•˜๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ํ—ˆ์ ‘ํ• ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์–‘ํ•ด๋ฐ”๋ž€๋‹ค.) ๋ฐ์ดํ„ฐ ๋ถ„์„์‹œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค๋Š” Pandas์™€ ๋Œ€๊ทœ๋ชจ ๋‹ค์ฐจ์› ๋ฐฐ์—ด์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” numpy, ๊ทธ๋ฆฌ๊ณ  bprophet๋ฅผ ๋น„๋กฏํ•œ ํ•„์š”ํ•œ ๋ชจ๋“ˆ๋“ค์„ pip๋กœ ์„ค์น˜ํ•ด์ค€๋‹ค.

์•„ํŒŒ์น˜ ์—‘์„ธ์Šค ๋กœ๊ทธ์— 408์ฝ”๋“œ๊ฐ€?

์˜ˆ์ „์— ์•„ํŒŒ์น˜ ๋กœ๊ทธ๋ฅผ ์—˜๋ผ์Šคํ‹ฑ ์Šคํƒ์„ ํ™œ์šฉํ•˜์—ฌ ๋‚ด ์„œ๋ฒ„์— ๋ˆ„๊ฐ€ ๋“ค์–ด์˜ค๋Š”์ง€๋ฅผ ํ™•์ธํ• ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ์„ ํ•ด๋‘๊ณ  ๋ช‡์ผ๊ฐ„ ์ง€์ผœ๋ณด๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—‘์„ธ์Šค ๋กœ๊ทธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

1.2.3.4 - - [26/Apr/2018:01:27:33 +0900] "GET /aaa/ HTTP/1.1" 200 6001 30788 "http://www.naver.com" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:28:08 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:28:08 +0900] "-" 408 - 28 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:28:08 +0900] "-" 408 - 12 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:28:08 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:28:50 +0900] "GET /aaa/ HTTP/1.1" 200 5999 13521 "http://www.naver.com/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:29:14 +0900] "GET /aaa/ HTTP/1.1" 200 5996 19437 "http://www.naver.com" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:29:15 +0900] "GET /aaa/ HTTP/1.1" 200 5997 17553 "http://www.naver.com" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:29:15 +0900] "GET /aaa/ HTTP/1.1" 200 5998 17429 "http://www.naver.com/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:29:53 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:29:53 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:29:53 +0900] "-" 408 - 32 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:29:53 +0900] "-" 408 - 38 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:29:53 +0900] "-" 408 - 29 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:30:54 +0900] "GET /aaa/ HTTP/1.1" 200 6000 17881 "http://www.naver.com" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
1.2.3.4 - - [26/Apr/2018:01:31:34 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:31:34 +0900] "-" 408 - 30 "-" "-"
1.2.3.4 - - [26/Apr/2018:01:31:34 +0900] "-" 408 - 25 "-" "-"

ํ•œ์‹œ๊ฐ„์— ๋งŒ๊ฑด์ด์ƒ ์‘๋‹ต์ฝ”๋“œ๋Š” 408, referrer๋„ ์—†๊ณ , useragent๋„ ์—†๋Š”, ip๋“ค๋„ ๋งค์šฐ ๋‹ค์–‘ํ•œ ์ด์ƒํ•œ ๋…€์„๋“ค์ด ์š”์ฒญ๋˜๊ณ  ์žˆ์—ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์—‘์„ธ์Šค ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ• ์ˆ˜ ์žˆ๋Š” ๊ตฌ์„ฑ์„ ํ•ด๋‘๊ณ  ๋‚˜๋‹ˆ ๋ณด์˜€์ง€ ์•ˆ๊ทธ๋žฌ์Œ ๊ทธ๋ƒฅ ์ง€๋‚˜๊ฐ”์„ ํ„ฐ..

์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‚ค๋ฐ”๋‚˜์—์„œ ๋ณด๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๋ณผ์ˆ˜์žˆ๋Š”๋ฐ ํ•œ๋ˆˆ์— ๋ด๋„ ๊ณผ์—ฐ ์˜๋ฏธ์žˆ๋Š” ์š”์ฒญ๋“ค์ผ๊นŒ? ํ•˜๋Š” ์˜๊ตฌ์‹ฌ์ด ๋“ค์ •๋„์ด๋‹ค. (1์‹œ๊ฐ„ ์•„ํŒŒ์น˜ ์—‘์„ธ์Šค ๋กœ๊ทธ)

/images/apache-408-response-code/200vs400.png
์ฃผํ™ฉ์ƒ‰์ด 408์‘๋‹ต

๊ทธ๋Ÿผ ์ด๋Ÿฐ ํ˜ธ์ถœ๋“ค์€ ๋„๋Œ€์ฒด ๋ญ˜๊นŒ? ์ฒœ์ฒœํžˆ ์ƒ๊ฐ์ข€ ํ•ด๋ณด์ž.

  1. ์ •์ƒ์ ์ด์ง€ ์•Š๋Š” ํ˜ธ์ถœ๋กœ ์šฐ๋ฆฌ ์„œ๋ฒ„์˜ ์ทจ์•ฝ์ ์„ ํŒŒ์•…ํ•˜๋ ค ํ•˜๋Š”๊ฒƒ๋“ค์ผ๊นŒ?
  2. ์‘๋‹ต์ฝ”๋“œ 408์€ ์š”์ฒญ์‹œ๊ฐ„์ดˆ๊ณผ ์‘๋‹ต์ฝ”๋“œ์ธ๋ฐ… ์˜คํžˆ๋ ค ํด๋ผ์ด์–ธํŠธ ์ž…์žฅ์—์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”๊ฑด ์•„๋‹๊นŒ?
  3. ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์ด ์ž˜๋ชป๋˜์–ด ๋ฌดํ•œ๋ฃจํ”„์— ๋น ์กŒ๋‚˜;

์œ„ํ‚ค๋ฐฑ๊ณผ์—์„œ๋Š” ์•„ํŒŒ์น˜ ์‘๋‹ต์ฝ”๋“œ ์ค‘ 408์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์•Œ๋ ค์ฃผ๊ณ  ์žˆ๋‹ค.

The server timed out waiting for the request. According to HTTP specifications: “The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time

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

# ์žฌํ˜„์ƒํ™ฉ

์šฐ์„  ์•„ํŒŒ์น˜๋ฒ„์ „์€ 2.2์ด๊ณ  KeepAlive Off๊ฐ€ ๋˜์–ด์žˆ๋Š” ์ƒํ™ฉ. ์•„๋ž˜๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์ง‘PC - ๊ณต์œ ๊ธฐ - VPN - Apache - tomcat jenkins ์ƒํ™ฉ์ด์˜€๋Š”๋ฐ ์  ํ‚จ์Šค์— ํ•œ๋ฒˆ ์ ‘์†ํ›„์—๋Š” ํ•ญ์ƒ 408 ์‘๋‹ต์ด ์ฃผ๋ฃจ๋ฃฉ(?) ๋ฐœ์ƒํ•˜๋Š”๊ฒƒ์„ ์•Œ์ˆ˜ ์žˆ์—ˆ๋‹ค. (์‚ฌ์‹ค ๋งจ์œ„์— ์—‘์„ธ์Šค ๋กœ๊ทธ๊ฐ€ ์žฌํ˜„ํ•œ ์—‘์„ธ์Šค ๋กœ๊ทธ์ด๋‹ค.)

๋‚ด ์„œ๋ฒ„์—๋Š” ๋ˆ„๊ฐ€ ๋“ค์–ด์˜ค๋Š”๊ฑธ๊นŒ? (์‹ค์‹œ๊ฐ„ user-agent ๋ถ„์„๊ธฐ)

Desktop ๋ฐ ์Šค๋งˆํŠธํฐ์˜ ๋Œ€์ค‘ํ™”๋กœ ๋‹ค์–‘ํ•œ OS์™€ ๋ธŒ๋ผ์šฐ์ €๋“ค์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๋•Œ, ๋‚ด๊ฐ€ ์šด์˜ํ•˜๋Š” ์›น์„œ๋ฒ„์— ๋“ค์–ด์˜ค๋Š” ์‚ฌ๋žŒ๋“ค์€ ๋ฌด์Šจ ๊ธฐ๊ธฐ๋กœ ์ ‘์†์„ ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ? ํ˜น์—ฌ ํŠน์ • OS์˜ ํŠน์ • ๋ธŒ๋ผ์šฐ์ €์—์„œ๋งŒ ์•ˆ๋˜๋Š” ๋ฒ„๊ทธ๋ฅผ ์žก๊ธฐ ์œ„ํ•ด ๋ช‡์ผ๋ฐค์„ ๊ณ ์ƒํ•˜๋ฉฐ ๊ฒจ์šฐ ์ˆ˜์ •ํ–ˆ๋Š”๋ฐ… ๊ณผ์—ฐ ๊ทธ OS์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ์ ‘์†์€ ํ•˜๊ธฐ๋‚˜ ํ•˜๋Š”๊ฑธ๊นŒ? (ใ… ใ… ) ๋งŒ์•ฝ, ์ ‘์† ์‚ฌ์šฉ์ž์˜ Device ์ •๋ณด๋ฅผ ์•Œ๊ณ ์žˆ๋‹ค๋ฉด ๊ณ ์ƒํ•˜๋ฉฐ ๋ฒ„๊ทธ๋ฅผ ์žก๊ธฐ ์ „์— ๋จผ์ € ํ•ด๋‹น Device ์‚ฌ์šฉ์œจ์„ ์ฒดํฌํ•ด ๋ณผ์ˆ˜๋„ ์žˆ๊ณ (์ˆ˜์ •์ด ์•„๋‹Œ ๊ฐ„๋‹จํ•œ ์–ผ๋Ÿฟ์œผ๋กœ ํ•ด๊ฒฐํ•œ๋‹ค๊ฑฐ๋‚˜?) ๋น„์ง€๋‹ˆ์Šค ๋ชจ๋ธ๊นŒ์ง€ ์ƒ๊ฐํ•ด์•ผํ•˜๋Š” ์„œ๋น„์Šค๋ผ๋ฉด ํƒ€๊ฒŸํŒ…์„ ์ •ํ•˜๋Š” ๋“ฑ ๋‹ค์–‘ํ•œ ํ™œ์šฉ๋„๊ฐ€ ๋†’์€ ๊ฒƒ์ด ๋ฐ”๋กœ User-Agent๋ผ๊ณ  ํ•œ๋‹ค(์ดํ•˜ UA). ์ผ๋ฐ˜ Apache ๋ฅผ ์›น์„œ๋ฒ„๋กœ ์šด์˜ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •์„ ํ•˜๊ณ  ์–ด๋–ป๊ฒŒ ๋ถ„์„์„ ํ• ์ˆ˜ ์žˆ์—ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๋ถ„์„์„ ํ•˜๋ฉฐ ์ข€๋” ์šฐ์•„ํ•œ(?) ๋ฐฉ๋ฒ•์€ ์—†๋Š”์ง€ ์•Œ์•„ ๋ณด๊ณ ์ž ํ•œ๋‹ค.

User-Agent๊ฐ€ ๋ญ์•ผ?

๋ฐฑ๋ฌธ์ด ๋ถˆ์—ฌ์ผํƒ€(?)๋ผ ํ–ˆ๋˜๊ฐ€, ์šฐ์„  http://www.useragentstring.com ๋ฅผ ๋“ค์–ด๊ฐ€๋ณด์ž. ๊ทธ๋Ÿฌ๋ฉด ์ž์‹ ์˜ OS ๋ฐ ๋ธŒ๋ผ์šฐ์ € ๋“ฑ ์ •๋ณด๋ฅผ ํŒŒ์‹ฑํ•ด์„œ ๋ณด์—ฌ์ฃผ๋Š”๋ฐ ์œ„ํ‚ค๋ฐฑ๊ณผ์— ๋”ฐ๋ฅด๋ฉด ‘์‚ฌ์šฉ์ž๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์ผ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์—์ด์ „ํŠธ’๋ผ๊ณ  ํ•œ๋‹ค. ์ฆ‰, UA๋งŒ ์•Œ์•„๋„ ์–ด๋–ค ๊ธฐ๊ธฐ/๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋Š”๊ฒƒ. mozilla์— ๊ฐ€๋ณด๋ฉด ์ŠคํŒฉ ๋“ฑ ๋‹ค์–‘ํ•œ UA๋ฅผ ๋ณผ์ˆ˜๊ฐ€ ์žˆ๋Š”๋ฐ ํŠนํžˆ ๋งจ ์•„๋ž˜๋ณด๋ฉด ๊ธฐ๊ธฐ/๋ธŒ๋ผ์šฐ์ €๋ณ„๋กœ ์ง€์›์ •๋ณด๊ฐ€ ๋‚˜์™€์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋„ ๋ณด๋ฉด ๋ชจ๋“  ๋ชจ๋ฐ”์ผ ์‚ผ์„ฑ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์ „๋ถ€ ์ง€์›์ด ๋˜๋Š”๊ฑธ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

/images/apache-access-log-user-agent/browser_compatibility.png
์ถœ์ฒ˜ : developer.mozilla.org

๊ธฐ์กด์˜ ๋ฐฉ๋ฒ•

๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ๋‚ด ์„œ๋ฒ„์— ๋“ค์–ด์˜จ ์‚ฌ์šฉ์ž๋“ค์˜ UA๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„๊นŒ? (์•ž์„œ Apache๋ฅผ ์›น์„œ๋ฒ„๋กœ ์šด์˜ํ•œ๋‹ค๊ณ  ํ–ˆ์œผ๋‹ˆ) Apache access log ์—๋Š” Apache์—์„œ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋ชจ๋“ˆ์„ ์ด์šฉํ•ด ์ ‘์†ํ•œ ํด๋ผ์ด์–ธํŠธ์˜ ์ •๋ณด๊ฐ€ ๋‚จ๊ฒจ์ง€๊ณค ํ•œ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด access log๋ฅผ ๋ฆฌ๋ˆ…์Šค ๋ช…๋ น์–ด๋“  ์—‘์…€๋กœ ๋ฝ‘์•„์„œ๋“ ์ง€ ํ™œ์šฉํ•ด์„œ ์ •๊ทœ์‹์œผ๋กœ ํฌ๋งทํŒ… ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฃนํ™” ์‹œํ‚ค๋ฉด ์–ผ์ถ” ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•ด ๋‚ผ์ˆ˜ ์žˆ๋‹ค. ( ๋ฒ„๊ฑฐํ˜•๋“ค์ด ๋งŒ๋“ค์–ด๋‘” ์ •๊ทœ์‹์„ ๊ฐ€์ ธ๋‹ค ์‚ฌ์šฉํ• ์ˆ˜๋„ ์žˆ๊ฒ ๋‹ค. https://regexr.com/?37l4e ) ํ•˜์ง€๋งŒ, ์šฐ์„  ์ž๋™ํ™”๊ฐ€ ์•ˆ๋˜์–ด์žˆ์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌํ•˜๊ณ  ์‹ถ์„๋•Œ๋งˆ๋‹ค ๊ท€์ฐจ๋‹ˆ์ฆ˜์— ๊ฑธ๋ฆด์ˆ˜ ์žˆ๊ณ  ์Šˆํผ ๊ฐœ๋ฐœ์ž ํŒŒ์›Œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ(?) ๋ฐ์ดํ„ฐ ์ถ”์ถœ์„ ์ž๋™ํ™” ํ•œ๋“ค ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด๊ณ ์‹ถ์„๋• ์ œํ•œ์‚ฌํ•ญ์ด ๋งŽ๋‹ค.

์ข€๋” ๋‚˜์€ ๋ฐฉ๋ฒ•(?)

์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•˜๋Š”๋ฐ์—๋Š” ๋‹ค์–‘ํ•œ ์˜คํ”ˆ์†Œ์Šค์™€ ๋‹ค์–‘ํ•œ ํˆด์ด ์žˆ๊ฒ ์ง€๋งŒ ๊ฒฝํ—˜์ด ๋ถ€์กฑํ•œ๊ฑด์ง€ ์•„์ง๊นŒ์ง„ ElasticStack ๋งŒํ•œ๊ฑธ ๋ชป๋ณธ๊ฒƒ ๊ฐ™๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…์„ ํ•˜๋ฉด access log ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  front๋‹จ์—์„œ javascript ๋กœ UA๋ฅผ ๊ตฌํ•œ๋‹ค์Œ ์ด๋Ÿฌํ•œ ์ •๋ณด๋ฅผ ๋ฐ›์„์ˆ˜ ์žˆ๋Š” API๋ฅผ ๋งŒ๋“ค์–ด ๊ทธ์ชฝ์œผ๋กœ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„์—์„œ ํ•ด๋‹น UA๋ฅผ ๋ถ„์„ํ•ด์„œ ์นดํ”„์นด๋กœ ๋ณด๋‚ด๊ณ  ..!@#$%^blabla… ^^; ๊ทธ๋ฆผ์œผ๋กœ ๋ณด์ž.

/images/apache-access-log-user-agent/user_agent_method_1.png
์ข€๋” ๋‚˜์€ ๋ฐฉ๋ฒ•

front๋‹จ์—์„œ๋Š” navigator.userAgent๋ฅผ ํ™œ์šฉํ•˜์—ฌ UA๋ฅผ ๊ตฌํ• ์ˆ˜ ์žˆ์—ˆ๊ณ , API์—์„œ๋Š” UA๋ฅผ ๋ฐ›๊ณ  ํŒŒ์‹ฑ์„ ํ•˜๋Š”๋ฐ ๊ด€๋ จ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์˜€๋‹ค.

private static final String VERSION_SEPARATOR = ".";

private void userAgentParsingToMap(String userAgent, Map<String, Object> dataMap) {
    HashMap browser = Browser.lookup(userAgent);
    HashMap os = OS.lookup(userAgent);
    HashMap device = Device.lookup(userAgent);

    dataMap.put("browserName", browser.get("family"));
    dataMap.put("browserVersion", getVersion(browser));
    dataMap.put("osName", os.get("family"));
    dataMap.put("osVersion", getVersion(os));
    dataMap.put("deviceModel", device.get("model"));
    dataMap.put("deviceBrand", device.get("brand"));

}

private String getVersion(HashMap dataMap) {
    String majorVersion = (String)dataMap.get("major");
    if (StringUtils.isEmpty(majorVersion)) {
        return StringUtils.EMPTY;
    }
    String minorVersion = (String)dataMap.get("minor");
    String pathVersion = (String)dataMap.get("path");

    StringBuffer sb = new StringBuffer();
    sb.append(majorVersion);
    if (!StringUtils.isEmpty(minorVersion)) {
        sb.append(VERSION_SEPARATOR);
        sb.append(minorVersion);
    }
    if (!StringUtils.isEmpty(pathVersion)) {
        sb.append(VERSION_SEPARATOR);
        sb.append(pathVersion);
    }

    return sb.toString();
}

์ฐธ๊ณ ๋กœ Java๋‹จ์—์„œ UA๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” parser๊ฐ€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋Š”๋ฐ ๊ทธ์ค‘ uap_clj๋ผ๋Š” ๋ชจ๋“ˆ์ด ๊ทธ๋‚˜๋งˆ ์ž˜ ํŒŒ์‹ฑ์ด ๋˜์–ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

  • ๋ชจ๋“ˆ๋ณ„ ๋น„๊ต
๋ชจ๋“ˆBrowserOSDevice
eu.bitwalker.useragentutils.UserAgentO๋ถˆ๋ช…ํ™•ํ•จ (Android 5.x)X
net.sf.uadetector.UserAgentStringParserOO๋ถˆ๋ช…ํ™•ํ•จ (Smartphone)
uap_clj.java.api.*OOO
  • Parsing ๋น„๊ต
    • UA
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36"
  • ๊ฒฐ๊ณผ
  - Browser : {patch=3239, family=Chrome Mobile, major=63, minor=0}
  - OS : {patch=1, patch_minor=, family=Android, major=5, minor=1}
  - Device : {model=Nexus 6, family=Nexus 6, brand=Generic_Android}

์œ„์™€ ๊ฐ™์ด ๊ตฌ์„ฑ์„ ํ•˜๋ฉด Elasticsearch์— ์ธ๋ฑ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ Kibana์—์„œ ์ž…๋ง›์— ๋งž๊ฒŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณผ์ˆ˜์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค! ํ•˜์ง€๋งŒ API๋ฅผ ๋งŒ๋“œ๋Š” ๋ถ€๋ถ„ + front์—์„œ ๋ณ„๋„์˜ javascript๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๋Š” ๋ถ€๋ถ„ + ์šด์˜ํ•˜๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€์— ํ•ด๋‹น javascript๋ฅผ ๋„ฃ์–ด์•ผ์ง€๋งŒ ๋ณผ์ˆ˜์žˆ๋Š” ๋ถ€๋ถ„ ๋“ฑ ์•ฝ๊ฐ„ ์•„์‰ฌ์šด ๊ฐ์ด ์žˆ๋‹ค. ๋ญ”๊ฐ€ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ? ๊น”๋”ํ•˜๋ฉด์„œ๋„ ์šฐ์•„ํ•œ ๋ฐฉ๋ฒ•. ์งฑ๊ตฌ๋ฅผ ์—ด์‹ฌํžˆ ๋” ๊ตด๋ ค๋ณด์ž.

gzip ์„ค์ •์œผ๋กœ ์†๋„๋ฅผ ๋” ๋น ๋ฅด๊ฒŒ!

๋‚ด๊ฐ€ ์šด์˜์ค‘์ธ ์›น์„œ๋น„์Šค์˜ ์‘๋‹ต์†๋„๋ฅผ ๋ณด๋‹ค ๋” ๋น ๋ฅด๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ์žˆ์„๊นŒ? ์›น ์„œ๋น„์Šค๋ฅผ ์œ„ํ•ด ์„œ๋ฒ„๋ฅผ ๊ตฌ์„ฑํ•  ๊ฒฝ์šฐ ์ผ๋ฐ˜์ ์œผ๋กœ ์•ž๋‹จ์— ์›น์„œ๋ฒ„๋ฅผ ๋‘๊ณ  ๊ทธ๋’ค์— WAS๋ฅผ ๋‘๋Š” ์„ค๊ณ„๋ฅผ ํ•˜๊ณค ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ์›น์„œ๋ฒ„๋Š” ๋Œ€ํ‘œ์ ์œผ๋กœ Apache๋‚˜ Nginx๊ฐ€ ์žˆ๊ณ  WAS๋Š” tomcat์ด๋‚˜ ๊ธฐํƒ€ ๋‹ค๋ฅธ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ๋‘๋‹จ๊ณ„๋กœ ๋‚˜๋ˆ„๋Š” ์ด์œ ๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๊ฒ ์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” ์•ž๋‹จ์˜ ์›น์„œ๋ฒ„(Apache)์˜ ์„ค์ •์œผ๋กœ ์‘๋‹ต์†๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„ ๋ณด๊ณ ์ž ํ•œ๋‹ค.

์›นํŽ˜์ด์ง€์˜ ์‘๋‹ต์†๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋Š” ‘์ผ๋ฐ˜์ ์ธ’๋ฐฉ๋ฒ•๋“ค

๊ผญ ์„œ๋ฒ„์˜ ์„ค์ •๋“ค์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ ๋„ ์›นํŽ˜์ด์ง€์˜ ์‘๋‹ต์†๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์–‘ํ•˜๋‹ค. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ์„ค์ •ํ• ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ์œ„์— ์„ ์–ธํ•˜๊ฑฐ๋‚˜ java script๋Š” ์ฝ”๋“œ ์•„๋ž˜๋ถ€๋ถ„์— ๋„ฃ๋Š”๊ฒƒ๋งŒ์œผ๋กœ๋„ ์–ด๋А์ •๋„ ์‘๋‹ต์†๋„๋ฅผ ์ค„์ผ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค.

(์‚ฌ์กฑ) ์‹ ์ž…์‹œ์ ˆ ํšŒ์‚ฌ ๋Œ€ํ‘œ๋‹˜์ด ํ•„์ˆ˜๋กœ ์ฝ์–ด๋ณด๋ผ๊ณ  ์ „ ์ง์›๋“ค์—๊ฒŒ ์„ ๋ฌผํ•ด์ฃผ์…จ๋˜ ์›น์‚ฌ์ดํŠธ ์ตœ์ ํ™”๊ธฐ๋ฒ• (์Šคํ‹ฐ๋ธŒ ์‚ฌ์šฐ๋”์Šค ์ €)์ด ์ƒ๊ฐ์ด ๋‚œ๋‹ค. ๋ชจ๋‘ ์‚ฌ์ฃผ๋ ค๋ฉด ๋ˆ์ด ์–ผ๋งˆ์•ผ… ๊ทธ๋งŒํผ ์›น๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ค‘์š”ํ•˜๋ฉด์„œ๋„ ํ•œํŽธ์œผ๋กœ๋Š” ๊ธฐ๋ณธ์ด ๋˜๋Š” ๋ถ€๋ถ„๋“ค์ด๋‹ˆ ํ•œ๋ฒˆ์ฏค ๋ชฉ์ฐจ๋ผ๋„ ์ฝ์–ด๋ณด๋Š”๊ฒŒ ์ข‹์„๋“ฏ ํ•˜๋‹ค.

์‚ฌ์‹ค ์ด ํฌ์ŠคํŒ…์„ ์ž‘์„ฑํ•˜๊ฒŒ๋œ ๊ฐ€์žฅ ํฐ ๊ณ„๊ธฐ๋Š” ์–ผ๋งˆ์ „ ์‚ฌ๋‚ด ํ•ด์ปคํ†ค์„ ํ•˜๋ฉด์„œ ๊ฒฝํ—˜ํ•œ ๋ถ€๋ถ„ ๋•Œ๋ฌธ์ด๋‹ค. ( + ๋“ค์–ด๋งŒ ๋ดค์ง€ ์‹ค์ œ๋กœ ํ•ด๋ณด์ง€๋Š” ์•Š์•„์„œ… ) ์„œ๋ฒ„์—์„œ node(React)๋ฅผ ๋„์šฐ๊ณ  ๊ทธ ์•ž๋‹จ์— Apache๋กœ ๋‹จ์ˆœ Port Redirect ( 80 โ†’ 3000 ) ์‹œ์ผœ์ฃผ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ react ์—์„œ ์‚ฌ์šฉํ•˜๋Š” bundle.js์˜ ์šฉ๋Ÿ‰์ด ํฌ๋‹ค๋ณด๋‹ˆ ์ตœ์ดˆ ํŽ˜์ด์ง€ ์ ‘๊ทผ์‹œ ๋กœ๋”ฉ์‹œ๊ฐ„์ด 5์ดˆ ์ด์ƒ๋˜์–ด๋ฒ„๋ฆฐ ๊ฒƒ์ด๋‹ค. bundle.js๋ฅผ ์ค„์—ฌ๋ณด๋Š”๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ๋‹ค๊ฐ€ ๊ฒฐ๊ตญ Apache ์„ค์ •์„ ํ†ตํ•ด 1์ดˆ ์ด๋‚ด๋กœ ์ค„์ผ์ˆ˜ ์žˆ์—ˆ๋‹ค.

gzip

์šฐ์„  gzip์ด๋ž€ ํŒŒ์ผ ์••์ถ•์— ์“ฐ์ด๋Š” ์‘์šฉ ์†Œํ”„ํŠธ์›จ์–ด๋กœ GNU zip์˜ ์ค€๋ง์ด๋ผ๊ณ  ํ•œ๋‹ค. (์ฐธ๊ณ  : ์œ„ํ‚ค๋ฐฑ๊ณผ Gzip) ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ง€์›์„ ํ•ด์•ผํ•˜๋Š”๋ฐ https://caniuse.com/#search=gzip ์„ ๋ณด๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง€์›ํ•˜๋Š”๊ฒƒ์„ ๋ณผ์ˆ˜ ์žˆ๋‹ค.

๋ฐ์ดํ„ฐ ํ๋ฆ„

๊ทธ๋Ÿผ gzip ์„ ์‚ฌ์šฉํ–ˆ์„๋•Œ์™€ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„๋•Œ์˜ ์ฐจ์ด๋Š” ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅผ๊นŒ? ์šฐ์„  Request/Response Flow ๋ฅผ ์ž ๊น ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • gzip ์‚ฌ์šฉ ์ „
/images/apache-gzip/before_gzip.png
์ถœ์ฒ˜ : betterexplained.com
  1. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„์ธก์— /index.html์„ ์š”์ฒญํ•œ๋‹ค.
  2. ์„œ๋ฒ„๋Š” Request๋ฅผ ํ•ด์„ํ•œ๋‹ค.
  3. Response์— ์š”์ฒญํ•œ ๋‚ด์šฉ์„ ๋‹ด์•„ ๋ณด๋‚ธ๋‹ค.
  4. Response๋ฅผ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ๋ณด์—ฌ์ค€๋‹ค. (100kb)
  • gzip ์‚ฌ์šฉ ํ›„
/images/apache-gzip/after_gzip.png
์ถœ์ฒ˜ : betterexplained.com
  1. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„์ธก์— /index.html์„ ์š”์ฒญํ•œ๋‹ค.
  2. ์„œ๋ฒ„๋Š” Request๋ฅผ ํ•ด์„ํ•œ๋‹ค.
  3. Response์— ์š”์ฒญํ•œ ๋‚ด์šฉ์„ ๋‹ด์•„ ๋ณด๋‚ธ๋‹ค. ์—ฌ๊ธฐ์„œ ํ•ด๋‹น ๋‚ด์šฉ์„ ์••์ถ•ํ•˜๋Š” ๊ณผ์ •์ด ์ถ”๊ฐ€๊ฐ€ ๋œ๋‹ค.
  4. Response header์— ์••์ถ•์ด ๋˜์–ด์žˆ๋‹ค๋Š” ์ •๋ณด๋ฅผ ํ™•์ธํ›„ ๋ธŒ๋ผ์šฐ์ €๋Š” ํ•ด๋‹น ๋‚ด์šฉ์„ ๋ฐ›๊ณ (10kb), ์••์ถ•์„ ํ•ด์ œํ•œ ํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค€๋‹ค.

์ •๋ฆฌํ•˜๋ฉด, gzip์„ ์‚ฌ์šฉํ•˜๋ฉด ์„œ๋ฒ„๋Š” Client์—๊ฒŒ ๋ณด๋‚ผ Response๋ฅผ ์••์ถ•ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋„คํŠธ์›Œํฌ ๋น„์šฉ์„ ์ค„์ผ์ˆ˜ ์žˆ์–ด ์‘๋‹ต์†๋„๊ฐ€ ๋น ๋ฅธ ์žฅ์ ์ด ์žˆ๋‹ค.

๋ฌด์กฐ๊ฑด ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฐ€?

๋ฌผ๋ก  ๋ฌด์กฐ๊ฑด ์ข‹์€ (๋งˆ์น˜ show me the money ๊ฐ™์€)์ •๋‹ต์€ ์—†๋‹ค. ์„œ๋ฒ„์—์„œ ์••์ถ•์„ ํ•˜์—ฌ Client์—๊ฒŒ ๋ณด๋‚ด๋ฉด ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š”๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์••์ถ•์„ ํ•ด์ œํ•˜๋Š” ๊ณผ์ •์ด ์ถ”๊ฐ€์ ์œผ๋กœ ํ•„์š”ํ•˜๋‹ค. ์ด๋Ÿฌํ•œ ๊ณผ์ •์—์„œ ๋ธŒ๋ผ์šฐ์ €๋Š” cpu๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์–ด ์˜คํžˆ๋ ค ๋žœ๋”๋ง ํ•˜๋Š” ๊ณผ์ •์ด ๋А๋ ค์งˆ์ˆ˜ ์žˆ์–ด ์ž์นซ ์‘๋‹ต์†๋„๋Š” ๋นจ๋ผ์กŒ๋‹ค ํ•˜๋”๋ผ๋„ ์‚ฌ์šฉ์ž ์ฒด๊ฐ์ƒ ๋” ๋А๋ ค์ง„๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์—ฌ์งˆ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ƒํ™ฉ์— ๋งž์ถฐ gzip์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊ฒƒ์ธ์ง€ ๋ง๊ฒƒ์ธ์ง€์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋งˆ์น˜๋ฉฐ

ํ•™๋ถ€์‹œ์ ˆ ๋˜๋Š” ์‹ ์ž…์‹œ์ ˆ, ์•„ํŒŒ์น˜๋Š” ์ •์ ์ธ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‹ด๋‹นํ•˜๊ณ  ํ†ฐ์ผ“์€ ์„œ๋ธ”๋ฆฟ ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต์ด ํ•„์š”ํ•œ ํŽ˜์ด์ง€๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค๊ณ  ์ฃผ๋ฌธ์„ ์™ธ์šฐ๋“ฏ ํ•˜์˜€์ง€๋งŒ, ์›น์„œ๋ฒ„์—์„œ ์••์ถ•์„ ํ•˜๋ฉด ์–ด๋–ค ํšจ๊ณผ๊ฐ€ ์žˆ๋Š”์ง€ ์‹ค์ œ๋กœ ๊ฒฝํ—˜ํ•ด๋ณด๋Š”๊ฒŒ ๊ฐ€์žฅ ์ค‘์š”ํ•œ๊ฒƒ ๊ฐ™๋‹ค. ์ ์šฉ ๋ฐฉ๋ฒ•์€ ๋ณต๋ถ™ํ•˜๋Š” ๋А๋‚Œ์ด๋ผ ์•„ํŒŒ์น˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋งํฌ ํ•˜๋Š”๊ฒƒ์œผ๋กœ ํ•ด๋‹น ํฌ์ŠคํŒ…์„ ๋งˆ๋ฌด๋ฆฌ ํ•˜๊ฒ ๋‹ค.

RestClientException ์ฒ˜๋ฆฌ

Spring ํ™˜๊ฒฝ์—์„œ (Spring5๋Š” ๋‹ฌ๋ผ์กŒ์ง€๋งŒ…) ์™ธ๋ถ€ API๋กœ์˜ ํ˜ธ์ถœ์„ ํ• ๋•Œ ์ž์ฃผ ์“ฐ์ด๋Š” RestTemplate. ์ด๋•Œ request์— ๋Œ€ํ•ด ์ •์ƒ์ ์ธ ์‘๋‹ต์ด ์•„๋‹Œ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ํ• ๊นŒ? ๋ง‰์—ฐํ•˜๊ฒŒ ์ƒ๊ฐ์„ ํ•ด๋ณด๋ฉด Http Status Code๋ฅผ ๋ฐ›์•„์„œ ํŒ๋ณ„์„ ํ•˜๋ฉด ๋˜์ง€๋งŒ Http Status Code๋งŒ ๋ด๋„ ์—„์ฒญ๋งŽ๋‹ค. ( ์œ„ํ‚คํ”ผ๋””์•„ ์ฐธ๊ณ  : https://en.wikipedia.org/wiki/List_of_HTTP_status_codes ) ifelse ๋กœ ๋‹ค ๋‚˜๋ˆŒ์ˆ˜๋„ ์—†๊ณ … ์šฐ์„  ์„ฑ๊ณต/์‹คํŒจ์— ๋Œ€ํ•œ ํŒ๋ณ„์€ ์–ด๋–ป๊ฒŒ ํ• ๊นŒ ํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ํŒŒ๊ณ ํŒŒ๊ณ  ๋“ค์–ด๊ฐ€๋‹ค๊ฐ€ ์•Œ๊ฒŒ๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด ์ •๋ฆฌ๋ฅผ ํ•ด๋ณด๊ฒ ๋‹ค.

Http Status Code ๊ทธ๋ฃน์ •์˜

Spring-project github์— ๊ฐ€๋ณด๋ฉด HttpStatus ํ•˜์œ„์— ๋‚ด๋ถ€ Enum์œผ๋กœ ์•„๋ž˜์ฒ˜๋Ÿผ ์ •์˜๋˜์–ด ์žˆ๋‹ค. ( ๋งํฌ )

public enum HttpStatus {
	
	...

	public Series series() {
		return Series.valueOf(this);
	}

	...

	public enum Series {

		INFORMATIONAL(1),
		SUCCESSFUL(2),
		REDIRECTION(3),
		CLIENT_ERROR(4),
		SERVER_ERROR(5);

		...

		public static Series valueOf(int status) {
			int seriesCode = status / 100;
			for (Series series : values()) {
				if (series.value == seriesCode) {
					return series;
				}
			}
			throw new IllegalArgumentException("No matching constant for [" + status + "]");
		}
	}
}	

์•ฝ 80์—ฌ๊ฐœ ์‘๋‹ต์ฝ”๋“œ๋“ค์„ ํฌ๊ฒŒ 5๊ฐœ ๋ฌถ์Œ์œผ๋กœ ์ •์˜ํ•ด ๋†“์€๊ฒƒ์„ ๋ณผ์ˆ˜์žˆ๋‹ค. ์ฆ‰, 201 / 201 / 202 ๊ฐ™์€ ๋…€์„๋“ค์€ ์ „๋ถ€ SUCCESSFUL ์„ฑ๊ณต ์œผ๋กœ ๊ทธ๋ฃนํ•‘์ด ๋˜๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ RestTemplate ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‚˜?

์ฝ”๋“œ๋ฅผ ์ญ‰์ญ‰ ๋”ฐ๋ผ๊ฐ€๋‹ค ๋ณด๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ 4xx, 5xx ๋Š” ์—๋Ÿฌ๋ผ๊ณ  ํŒ๋‹จํ•˜๊ณ  ๊ทธ์— ๋”ฐ๋ผ RestClientException ์„ ๋ฐ˜ํ™˜ํ•˜๋Š”๊ฒƒ์„ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค.

try {
	ClientHttpRequest request = createRequest(url, method);
	if (requestCallback != null) {
		requestCallback.doWithRequest(request);
	}
	response = request.execute();
	handleResponse(url, method, response);
	if (responseExtractor != null) {
		return responseExtractor.extractData(response);
	}
	else {
		return null;
	}
}
catch (IOException ex) {
protected boolean hasError(HttpStatus statusCode) {
	return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR || statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}

์„ฑ๊ณต/์‹คํŒจ ์ฒ˜๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ํ• ๊ฒƒ์ธ๊ฐ€

๊ฐ์ž ์ •์˜ํ•˜๊ธฐ ๋‚˜๋ฆ„์ผ๊ฒƒ ๊ฐ™๋‹ค. ์šฐ์„  ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ RestTemplate ๋ฅผ ์‚ฌ์šฉํ• ๋•Œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์—ฌ ์ •์˜๋œ ๋Œ€๋กœ 4xx, 5xx๊ฐ€ ์—๋Ÿฌ๋ผ๊ณ  ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์„๊ฒƒ ๊ฐ™๊ณ 

try {
    responseBody = restTemplate.postForObject(url, httpEntity, byte[].class);
} catch (RestClientException e) { // ์—๋Ÿฌ์ธ ๊ฒฝ์šฐ RestClientException ์„ ๋‚ด๋ฑ‰๋Š”๋‹ค.
    log.error("##### restTemplate error, url = {}", url, e);
}

์ •์˜๋œ ์—๋Ÿฌ(?)์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด DefaultResponseErrorHandler์„ ์ƒ์†๋ฐ›๊ณ  hasError๋ฉ”์†Œ๋“œ๋ฅผ ๋ฌด์กฐ๊ฑด ํŒจ์Šคํ•˜๋„๋ก Override ํ•˜๊ณ ๋‚œ ๋‹ค์Œ ์‘๋‹ต ์ฝ”๋“œ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์„์ˆ˜ ์žˆ๊ฒ ๋‹ค. (๋ฌผ๋ก  ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ• ๋ง๊ณ  ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์ด ์žˆ์„๊ฒƒ ๊ฐ™๋‹ค.)

์†Œ๋‚˜ํ๋ธŒ ์ด์šฉ ์ฝ”๋“œ ์ •์ ๋ถ„์„ ์ž๋™ํ™”

์ฝ”๋“œ ์ •์ ๋ถ„์„์ด๋ผ ํ•จ์€ ์‹ค์ œ ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ฝ”๋“œ๋งŒ์˜ ํ˜•ํƒœ์— ๋Œ€ํ•œ ๋ถ„์„์„ ๋งํ•œ๋‹ค. ์ด๋ฅผํ…Œ๋ฉด ๋ƒ„์ƒˆ๋‚˜๋Š” ์ฝ”๋“œ(?)๋ผ๋˜์ง€, ์œ„ํ—˜์„ฑ์ด ์žˆ๋Š” ์ฝ”๋“œ, ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ทœ์น™์ด๋‚˜ ์ฝ”๋”ฉ ํ‘œ์ค€์„ ์ค€์ˆ˜ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๋ถ„์„์„ ๋งํ•˜๋Š”๋ฐ java ๊ธฐ์ค€์œผ๋กœ๋Š” ์•„๋ž˜ ๋‹ค์–‘ํ•œ (์ž˜ ์•Œ๋ ค์ง„) ์ •์ ๋ถ„์„ ๋„๊ตฌ๋“ค์ด ์žˆ๋‹ค.

  • PMD
    • ๋ฏธ์‚ฌ์šฉ ๋ณ€์ˆ˜, ๋น„์–ด์žˆ๋Š” ์ฝ”๋“œ ๋ธ”๋ฝ, ๋ถˆํ•„์š”ํ•œ ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ๊ณผ ๊ฐ™์€ Defect์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๊ฒ€์‚ฌ
    • https://pmd.github.io
  • FindBugs
    • ์ •ํ•ด์ง„ ๊ทœ์น™์— ์˜ํ•ด ์ž ์žฌ์ ์ธ ์—๋Ÿฌ ํƒ€์ž…์„ ์ฐพ์•„์คŒ
    • http://findbugs.sourceforge.net
  • CheckStyle
    • ์ •ํ•ด์ง„ ์ฝ”๋”ฉ ๋ฃฐ์„ ์ž˜ ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š”์ง€์— ๋Œ€ํ•œ ๋ถ„์„
    • http://checkstyle.sourceforge.net

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

์ง€๊ธˆ๋ถ€ํ„ฐ Jenkins + github web-hook + SonarQube ๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  PullRequest๋ฅผ ์˜ฌ๋ฆฌ๊ฒŒ ๋˜๋ฉด ์ˆ˜์ •ํ•œ ํŒŒ์ผ์— ๋Œ€ํ•ด ์ž๋™์œผ๋กœ ์ •์ ๋ถ„์„์ด ์ด๋ค„์ง€๊ณ , ๊ทธ์—๋Œ€ํ•œ ๋ฆฌํฌํŒ…์ด ํ•ด๋‹น PullRequest์— ๋Œ“๊ธ€๋กœ ๋‹ฌ๋ฆฌ๋„๋ก ์„ค์ •์„ ํ•ด๋ณด๊ฒ ๋‹ค. (์ฝ”๋“œ๋ฆฌ๋ทฐ๋ฅผ ๋ด‡(?)์ด ์ž๋™์œผ๋กœ ํ•ด์ฃผ๋Š”๊ฒŒ ์–ผ๋งˆ๋‚˜ ํŽธํ•œ ์ผ์ธ๊ฐ€…)

๊ธฐ๋ณธ ์ปจ์…‰

์ „์ฒด์ ์ธ ์ปจ์…‰์€ ๋‹ค์Œ ๊ทธ๋ฆผ๊ณผ ๊ฐ™๋‹ค.

/images/jenkins-sonar-github-integration/concept.png
์ „์ฒด ์ปจ์…‰
  1. IDE์—์„œ ์ฝ”๋“œ์ˆ˜์ •์„ ํ•˜๊ณ  remote ์ €์žฅ์†Œ์— commit & push๋ฅผ ํ•œ๋‹ค. ๊ทธ ๋‹ค์Œ github์—์„œ master(ํ˜น์€ stableํ•œ branch)์— ๋Œ€ํ•ด ์ž‘์—… branch๋ฅผ PullRequest ์˜ฌ๋ฆฐ๋‹ค.
  2. ๋ฏธ๋ฆฌ ๋“ฑ๋กํ•œ github์˜ web-hook์— ์˜ํ•ด PullRequest ์ •๋ณด๋“ค์„ jenkins์— ์ „์†กํ•œ๋‹ค.
  3. ์ „๋‹ฌ๋ฐ›์€ ์ •๋ณด๋ฅผ ์žฌ ๊ฐ€๊ณตํ•˜์—ฌ SonarQube๋กœ ์ •์ ๋ถ„์„์„ ์š”์ฒญํ•œ๋‹ค.
  4. SonarQube์—์„œ ๋ถ„์„ํ•œ ์ •๋ณด๋ฅผ ๋‹ค์‹œ jenkins๋กœ return ํ•ด์ค€๋‹ค.
  5. SonarQube์œผ๋กœ๋ถ€ํ„ฐ return ๋ฐ›์€ ์ •๋ณด๋ฅผ ํ•ด๋‹น PullRequest์˜ ๋Œ“๊ธ€์— ๋ฆฌํฌํŒ…์„ ํ•ด์ค€๋‹ค.

๊ฐ„๋‹จํžˆ ๋ณด๋ฉด (๋ญ ๊ฐ„๋‹จํ•˜๋‹ˆ ์‰ฝ๋„ค~) ๋ผ๊ณ  ๋ณผ์ˆ˜๋„ ์žˆ๊ฒ ์ง€๋งŒ ๋‚˜๋Š” ์ด๋Ÿฐ ์ „์ฒด ํ๋ฆ„์„ ์„ค์ •ํ•˜๋Š”๋ฐ ์žˆ์–ด ์–ด๋ ค์› ๋‹ค.

์‚ฌ์‹ค ์…‹ํŒ…ํ•˜๋Š” ๊ณผ์ •์—์„œ ์ ์ง€์•Š์€ ์‚ฝ์งˆ์„ ํ–ˆ์—ˆ๊ธฐ์—, ์ด ํฌ์ŠคํŒ…์„ ์ ๋Š” ์ด์œ ์ผ์ˆ˜๋„ ์žˆ๊ฒ ๋‹ค. ๋”๋ถˆ์–ด ๊ฒ€์ƒ‰์„ ํ•ด๋ด๋„ ์ด๋ ‡๊ฒŒ ์ „์ฒดํ๋ฆ„์ด ์ •๋ฆฌ๋œ ๊ธ€์ด ์ž˜ ์•ˆ๋ณด์—ฌ์„œ + ๋‚ด๊ฐ€ ํ•œ ์‚ฝ์งˆ์„ ๋‹ค๋ฅธ ๋ˆ„๊ตฐ๊ฐ€๋„ ํ• ๊ฒƒ๊ฐ™์•„์„œ(?)

Maven ์„ค์น˜

๊ธฐ๋ณธ์ ์œผ๋กœ Maven์˜ H2DB๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ SonarQube๋ฅผ ์„ค์น˜ํ•˜๊ธฐ์ „์— Maven๋ถ€ํ„ฐ ์„ค์น˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

$ wget http://apache.mirror.cdnetworks.com/maven/maven-3/3.5.2/binaries/apache-maven-3.5.2-bin.tar.gz
$ tar -zxvf apache-maven-3.5.2-bin.tar.gz
(ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์…‹ํŒ…ํ›„ )
$  mvn -version
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T16:58:13+09:00)
...

SonarQube ์„ค์น˜

์ •์ ๋ถ„์„์„ ๋„์™€์ฃผ๋Š” SonarQube๋ฅผ ์„ค์น˜ํ•ด๋ณด์ž.

$ wget https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-6.7.1.zip
$ unzip sonarqube-6.7.1.zip
$ cd sonarqube-6.7.1/bin/linux-x86-64
$ ./sonar.sh start
Starting SonarQube...
Started SonarQube.

๊ธฐ๋ณธ์ ์œผ๋กœ 9000ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ๋‹ค๋ฅธํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด /sonarqube-6.7.1/conf/sonar.properties ๋‚ด sonar.web.port=9000 ์„ ์ˆ˜์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. (SonarQube๋„ Elasticsearch๋ฅผ ์‚ฌ์šฉํ•˜๊ตฌ๋‚˜…) ์„ค์น˜ํ›„ ์‹คํ–‰์„ ํ•œ๋’ค ์„œ๋ฒ„IP:9000์„ ์ ‘์†ํ•ด๋ณด๋ฉด ์•„๋ž˜ ํ™”๋ฉด์ฒ˜๋Ÿผ ๋‚˜์˜จ๋‹ค. (ํ˜น์‹œ ์ ‘์†์ด ์•ˆ๋œ๋‹ค๊ฑฐ๋‚˜ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰์ด ์•ˆ๋œ๋‹ค๋ฉด ./sonar.sh console๋กœ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ๋ฌธ์ œํ•ด๊ฒฐ์— ๋„์›€์ด ๋ ์ˆ˜๋„ ์žˆ๋‹ค. )

/images/jenkins-sonar-github-integration/sonar_main.png
SonarQube ๋ฉ”์ธํ™”๋ฉด

SonarQube Scanner ์„ค์น˜

์†Œ์Šค๋ฅผ ์—ฐ๋™์‹œ์ผœ ์ •์ ๋ถ„์„์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” SonarQube Scanner ๋ผ๋Š”๊ฒŒ ํ•„์š”ํ•˜๋‹ค๊ณ  ํ•œ๋‹ค. ์•„๋ž˜ url์—์„œ ๋‹ค์šด๋ฐ›์•„ ์ ์ ˆํ•œ ๊ณณ์— ์••์ถ•์„ ํ’€์–ด๋‘์ž. https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner

$ wget https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.0.3.778-linux.zip
$ unzip sonar-scanner-cli-3.0.3.778-linux.zip

# jenkins ์„ค์น˜ ๋ฐ SonarQube ์—ฐ๋™

jenkins ์„ค์น˜๋Š” ๊ฐ„๋‹จํ•˜๋‹ˆ ๋ณ„๋„ ์–ธ๊ธ‰์€ ์•ˆํ•˜๊ณ  ๋„˜์–ด๊ฐ€…๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, ํ•˜๋‚˜๋ถ€ํ„ฐ ์—ด๊นŒ์ง€ ์ •๋ฆฌํ•œ๋‹ค๋Š” ๋งˆ์Œ์œผ๋กœ~ https://jenkins.io/download/ ์—์„œ ์ตœ์‹ ๋ฒ„์ „์„ tomcat/webapps/ ์•„๋ž˜์— ๋‹ค์šด๋ฐ›๊ณ  server.xml ์„ ์ ์ ˆํ•˜๊ฒŒ ์ˆ˜์ •ํ•ด์ค€๋‹ค.

$ wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war
$ vi tomcat/conf/server.xml
<Connector port="19001" protocol="HTTP/1.1" # ํฌํŠธ ๋ณ€๊ฒฝ
<Context path="/jenkins" debug="0" privileged="true" docBase="jenkins.war" /> #์ถ”๊ฐ€
# tomcat/bin/startup.sh

jenkins ์„ค์น˜๋ฅผ ์™„๋ฃŒ ํ•œ ํ›„ ํ•„์š”ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ถ”๊ฐ€๋กœ ์„ค์น˜ํ•ด์ค€๋‹ค.

  • Python Plugin
  • GitHub Pull Request Builder
  • GitHub plugin

์ ‘์† : ์„œ๋ฒ„IP:19001 (์ฐธ๊ณ ๋กœ ํ•œ ์„œ๋ฒ„์—์„œ ๋‹ค ์„ค์น˜ํ•˜๋‹ค๋ณด๋‹ˆ port ์ถฉ๋Œ์„ ์‹ ๊ฒฝ์“ฐ๊ฒŒ๋˜์—ˆ๋‹ค. ) ์ฒ˜์Œ jenkins๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ด๋Ÿฐ์ €๋Ÿฐ ์„ค์ •์„ ํ•˜๋Š”๋ฐ ํŠน๋ณ„ํ•œ ์„ค์ • ๋ณ€๊ฒฝ์—†์ด next๋ฒ„ํŠผ์„ ์—ฐ์‹  ๋ˆŒ๋Ÿฌ๋ฉด ์„ค์น˜๊ฐ€ ์™„๋ฃŒ ๋˜๊ณ , SonarQube๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด SonarQube Scanner for Jenkins๋ผ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•ด์ฃผ์ž. (์ด๊ฑด ๊ฐ ๋ฒ„์ „๋งˆ๋‹ค ๊ถํ•ฉ(?)์ด ์•ˆ๋งž์„์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ํ™•์ธ์ด ํ•„์š”ํ• ์ˆ˜๋„ ์žˆ๋‹ค. ๋‚ด๊ฐ€ ์„ค์น˜ํ•œ ๋ฒ„์ „์€ jenkins 2.89, SonarQube Plugin 2.6.1์ด๋‹ค.) ์„ค์น˜๋ฅผ ํ•˜๋ฉด jenkins > configure ์—์„œ SonarQube servers์ •๋ณด๋ฅผ ๋“ฑ๋กํ•ด์ค€๋‹ค.