์˜คํ”ˆ์†Œ์Šค CATS๋กœ ๊ฐ„๋‹จํžˆ ํผ์ฆˆ(Fuzz) ํ…Œ์ŠคํŠธ

ํผ์ง•์ด๋ž€

ํผ์ฆˆ(Fuzz) ๋˜๋Š” ํผ์ง•(Fuzzing)์ด๋ž€ ๋ธ”๋ž™๋ฐ•์Šค ํ…Œ์ŠคํŠธ ์˜ ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ •์ƒ/๋น„์ •์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ํ•จ์ˆ˜๋‚˜ API์— ์ „๋‹ฌํ•œ๋‹ค.ย ์–ด๋–ป๊ฒŒ ๋ณด๋ฉด ์•”ํ˜ธ ํ•ด๋…(crypto anaylysis) ๊ณผ์ •๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋ณด์ด์ง€๋งŒ ํผ์ง•์€ ํ”„๋กœํ† ์ฝœ์ด๋‚˜ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ํƒ€์ž…์— ์˜์กด์ ์ด๋‹ค. ๊ทธ๋ž˜์„œ ์‹คํŒจํ•œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋“ค์€ ์•”ํ˜ธย ํ•ด๋…๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ ์‹ค์ œ๋กœ ์œ ์˜๋ฏธํ•˜๋‹ค.

์—ฌ๋‹ด์œผ๋กœ ๋ช‡๋…„์ „ ๋Œ€๊ธฐ์—…์—์„œ๋Š” ์œ ์‚ฌํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ „๋ฌธ ์ธ๋ ฅ๊นŒ์ง€ ๋‘์—ˆ์—ˆ๋‹ค. ๊ทธ๋•Œ๋„ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ธด ํ–ˆ์ง€๋งŒ ํ…Œ์ŠคํŠธ ์„ค์ •๋ถ€ํ„ฐ ์ˆ˜ํ–‰ ํ›„ ๋ณด๊ณ ์„œ ์ž‘์„ฑ๊นŒ์ง€ ์ƒ๋‹นํžˆ ๋งŽ์€ ๋ถ€๋ถ„์—์„œ ๊ณ ๊ธ‰ ์ธ๋ ฅ๋“ค์„ ํˆฌ์ž…ํ–ˆ์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ์— ๋ฐœ๊ฒฌํ•œ ๋„๊ตฌ๋Š” ๋งŒ์•ฝ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ด Rest API์ด๊ณ  Open API ์ŠคํŽ™ (i.e. swagger)์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด CI/CD์— ๋ฐ”๋กœ ์—ฐ๋™ํ•˜๊ณ  ๋ฐ”๋กœ ๊น”๋”ํ•œ ๋ฆฌํฌํŠธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ์ •๋„๋กœ ๊ณ ๋„ํ™” ๋˜์—ˆ๋‹ค. ์†Œํ”„ํŠธ์›จ์–ด ํ…Œ์ŠคํŠธ์ชฝ์€ ์ •๋ง ๋น ๋ฅด๊ฒŒ ์ปดํ“จํ„ฐ๋กœ ๋Œ€์ฒด๋˜์–ด๊ฐ€๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™๋‹ค.

CATS

https://github.com/Endava/cats

CATS๋Š” ์ž๋ฐ” ๊ธฐ๋ฐ˜์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ฑฐ์˜ ์ฝ”๋”ฉ์„ ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ช‡๋ฐฑ๊ฐ€์ง€์˜ APIํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค. 6.0 ๋ฒ„์ „ ๊ธฐ์ค€์œผ๋กœ 76๊ฐœ์˜ ํผ์ €(Fuzzer)๊ฐ€ ์กด์žฌํ•œ๋‹ค.

ํผ์ €๋Š” ํฌ๊ฒŒ 5๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค

  • ํ•„๋“œ ํผ์ € – Post ์š”์ฒญ์˜ ๋ชธํ†ต์ด๋‚˜ URL์˜ ๊ฒฝ๋กœ ๋ณ€์ˆ˜(path variable)์„ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ํผ์ €
  • ํ—ค๋” ํผ์ € - HTTP ํ—ค๋”๋“ค์„ ๋Œ€์ƒ์œผ๋กœํ•˜๋Š” ํผ์ €
  • HTTP ํผ์ € - ํ•„๋“œ๋‚˜ ํ—ค๋”์™€ ๊ด€๊ณ„์—†๋Š” HTTP ์š”์ฒญ์„ ๋Œ€์ƒ์œผ๋กœํ•˜๋Š” ํผ์ €
  • API ๊ณ„์•ฝ ๊ฒ€์ฆ ํผ์ € – Open API ์ •์˜๊ฐ€ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ•˜๋Š” ํผ์ €.
  • ํŠน์ˆ˜ ํผ์ € -ย ๋ณด์•ˆ์ด๋‚˜ ํŠน๋ณ„ํ•œ ์ ˆ์ฐจ๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” ์กฐ๊ธˆ ๋” ๋ณต์žกํ•œ ํ–‰๋™๋“ค์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•œ ํผ์ €

ํŒ€์—์„œ ๊ด€๋ฆฌ์ค‘์ธ ๋‚ด๋ถ€ API๋Š” ์ด๋ฏธ swagger 2.0 ์œผ๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— CATS jar๋ฅผ ๋‹ค์šด ๋ฐ›์€ ๋’ค ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

cats.jar --contract=swagger.yaml --server=$FUZZ_SERVER_ADDR --headers=header.yml --refData=refData.yml

์•„ํ‹€๋ผ์‹œ์•ˆ์€ ๋‚ด๋ถ€ API ์ธ์ฆ์— JWTํ† ํฐ์˜ ์ผ์ข…์ธ ASAP์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  API ํ˜ธ์ถœ์— Authorization ํ—ค๋”๋ฅผ ์ œ๊ณตํ•ด์ค˜์•ผ ํ•œ๋‹ค. herader.yml ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•œ๋‹ค. CI/CD์—์„œ๋Š” ๋งค ๋นŒ๋“œ๋งˆ๋‹ค ๊ฐ’์„ ๊ฐฑ์‹  ์‹œ์ผœ์ค€๋‹ค.

all:
  Authorization: Bearer TOKEN_PLACE_HOLDER

๊ทธ ๋‹ค์Œ์ด ๋ฐ์ดํ„ฐ ํŒŒ์ผ๋กœ API์— ๋น„์ฆˆ๋‹ˆ์Šค ํŠน์œ ์˜ ๊ฐ’์„ ์ œ๊ณตํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ฐ„๋‹จํžˆ ๊ณ ์ •๋œ ๊ฐ’์„ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ Apache Common์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ฑ๋„ ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•ด ๋ณด์ธ๋‹ค.

all:
    boardId: 29c3fd3c-0239-3cbb-ac02-3ef08e267d4f
    columnId: 52

์ด๊ฒƒ์œผ๋กœ ๊ธฐ๋ณธ์ ์ธ ์ค€๋น„๋Š” ๋ชจ๋‘ ๋์ด๋‹ค. ๋กœ์ปฌ์—์„œ ๋Œ์•„๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  ๋ฐ”๋กœ Bitbucket Cloud ์˜ custom Pipe๋ฅผ ์ƒ์„ฑํ•ด CATS jar๋ฅผ ํฌํ•จํ•˜๋Š” ๋„์ปค์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•œ ๋’ค API ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์ผ์ฃผ์ผ์— ํ•œ๋ฒˆ์”ฉ ํ…Œ์ŠคํŠธ๊ฐ€ ์ˆ˜ํ–‰๋˜๋„๋ก ์„ค์ •ํ•ด ๋†“์•˜๋‹ค. Github Action์„ ์‚ฌ์šฉํ•ด์„œ๋„ ์œ ์‚ฌํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์œผ๋ฆฌ๋ผ ์ƒ๊ฐํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

์‹ค์ œ ๋‚ด๊ฐ€ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ…Œ์ŠคํŠธ๋“ค์ด ์‹คํŒจํ–ˆ๋‹ค. ์‹คํŒจํ•œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๋“ค์„ ์ˆ˜์ •ํ•˜์ง€๋Š” ์•Š์„ ๊ฒƒ ์ด๋‹ค. ์•„๋งˆ REST API๊ฐ€ ์™ธ๋ถ€์— ๊ณต๊ฐœ๋œ ๊ฒƒ์ด๋ผ๋ฉด ๋Œ€๋‹จํžˆ ์œ ์šฉํ•  ๊ฒƒ ๊ฐ™๋‹ค.

๊ถŒ๋‹น๋˜๋Š” ํ—ค๋”๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ์—๋Ÿฌ,ย ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋งํฌ์— ์žˆ๊ณ  ย ์‚ฌ์„ค API์ด๊ธฐ ๋•Œ๋ฌธ์—ย ์ด๋Š” ๋ฌด์‹œํ•ด๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.ย  ย [{name=X-XSS-Protection, value=1; mode=block}]

X-XSS-Protection – Preventing Cross-Site Scripting Attacks – KeyCDN

์ค‘๋ณต๋œ ํ—ค๋”์ž…๋ ฅ

์ค‘๋ณต๋œ ํ—ค๋”์ž…๋ ฅ์„ ํ—ˆ์šฉํ•˜๊ณ  ๋‚ด๋ถ€์ ์œผ๋กœ๋Š” ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜๋˜๊ณ  ์žˆ์Œ. HTTP ์ŠคํŽ™์—์„œ๋Š” ํ—ˆ์šฉ๋˜์ง€๋งŒ ๋ณด์•ˆ์ƒ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Œ.

HTTP Desync Attacks in the Wild and How to Defend Against Them | Imperva

์œ ๋‹ˆ์ฝ”๋“œ ์ œ์–ด ๋ฌธ์ž๋ฅผ ์ธ์‹ํ•˜์ง€ ๋ชปํ•จ

์œ ๋‹ˆ์ฝ”๋“œ ์ œ์–ด๋ฌธ์ž๋Š” ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž…๋ ฅ์— ํฌํ•จ๋˜์–ด ์žˆ์„ ๊ฒฝ์šฐ์— ์ผ๋ฐ˜ ๋ฌธ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ์ œ์–ด๋ฌธ์ž๋กœ ์ธ์‹๋˜์–ด์•ผ ํ•œ๋‹ค.

Unicode control characters

๊ถŒ์žฅ๋˜๋Š” REST API ๋„ค์ด๋ฐ ๊ทœ์น™์„ ๋”ฐ๋ฅด์ง€ ์•Š์Œ

API๊ฒฝ๋กœ์— ๋ณต์ˆ˜ํ˜•, ๋ช…์‚ฌ, ์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฉฐ ์—”๋“œํฌ์ธํŠธ์—๋Š” ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋‚˜ ์ผ€๋ฐฅ ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.ย JSONย ํ”„๋กœํผํ‹ฐ์—๋Š” ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค์™€ ์นด๋ฉœ ์ผ€์ด์Šค๋ฅผ ํ—ˆ์šฉํ•จ.

์ƒˆ๋กœ์šด JSON ํ•„๋“œ ์ž…๋ ฅ์„ ํ—ˆ์šฉํ•จ

์š”์ฒญ์˜ ๋ชธํ†ต์— ์ƒˆ๋กœ์šด JSON ํ•„๋“œ ์ž…๋ ฅ์„ ํ—ˆ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ OWASP ์—์„œ๋Š” ์ด๊ฒฝ์šฐ์— ์š”์ฒญ์„ ๊ฑฐ์ ˆํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.

REST Security – OWASP Cheat Sheet Series

์กด์žฌํ•˜์ง€ ์•Š๋Š” HTTP ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด์„œ 405(Method Not Allowed)๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ

์ œ๋ชฉ ๊ทธ๋Œ€๋กœ CONTENT์™€ ๊ฐ™์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” HTTP ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด 403 ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๋‹ค.

์ง€์›ํ•˜์ง€ ์•Š๋Š” Content Types ํ—ค๋”๋ฅผ ํ—ˆ์šฉํ•จ

Content-Type ํ—ค๋”์— OpenAPI ๊ณ„์•ฝ์•„ ์ •์˜๋˜์ง€ ์•Š์€ ๊ฐ’์„ ๋ณด๋ƒˆ์„ ๋•Œ ์š”์ฒญ์„ ํ—ˆ์šฉํ•จ. OWASP ๋Š” ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๊ถŒ์žฅํ•œ๋‹ค.

REST Security – OWASP Cheat Sheet Series

API ์‚ฌ์–‘์˜ GET ๊ฒฝ๋กœ์— ๊ถŒ์žฅ๋˜๋Š” ํ—ค๋”์ธ TracedId/CorrelationId๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ.

๋งˆ์น˜๋ฉฐ

์‹ค์ œ jar ํŒŒ์ผ์„ ๋‹ค์šด๋ฐ›์•„์„œ ๋กœ์ปฌ์—์„œ ์‹คํ—˜ํ•ด ๋ณด๋Š”๋ฐ๋Š” ํ•œ์‹œ๊ฐ„๋„ ๊ฑธ๋ฆฌ์ง€ ์•Š์•˜๋‹ค. Open API ์ŠคํŽ™์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ๋Œ๋ ค๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค. CATS์—์„œ ์ข€ ์•„์‰ฌ์šด ์ ์€ ํ…Œ์ŠคํŠธ ๊ฐฏ์ˆ˜๊ฐ€ ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€๋ฐ ๋น„ํ•ด ํด๋ผ์ด์–ธํŠธ ์ธก Rate limiting ์ด๋‚˜ Throttling ๊ธฐ๋Šฅ์ด ์žˆ์–ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค. ๋งŽ์€ API ์‘๋‹ต์ด 503 ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์„œ ์˜๋„์น˜ ์•Š๊ฒŒ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๊ฐ€ ๋˜์–ด๋ฒ„๋ฆด ์ˆ˜๋„ ์žˆ๋‹ค.

์˜คํ”ˆ์†Œ์Šค CATS๋กœ ๊ฐ„๋‹จํžˆ ํผ์ฆˆ(Fuzz) ํ…Œ์ŠคํŠธ

Clean (Reactive) Code

Clean Code is an excellent book. One of the key takeaways from the book is that we code not only for the computer but also for the other developers we work alongside.
I believe that Clean Code is a subjective topic even though itโ€™s shadowed by the popularity of the book. Many people would have their way of writing code in a more readable and maintainable way. They simply would have not published a book about the topic or they did but not gained as much attention as Uncle Bobโ€™s one.

Ever since I started using WebFlux in 2018 from stock trading service, Iโ€™ve been quite content with it. As I started to review more and more code written in reactive functional style including both Reactor and RxJava, I realized that many people tend to write (reactive) code in their way which makes me spend more time to understand the logic behind the various types of looking.

Putting aside all pros/cons of reactive programming, discussion of which would take more time than the subject Iโ€™m handling here, reactive programming indeed lacks a common style guide to help speed up the cycle of development and review.

So, here I am listing some of the practices I follow while writing reactive code and find it easier to read.
The code below is written in Kotlin, but I believe itโ€™s not much different from Java otherwise I mention it separately.

1. Reactive Operator is the key to Reactive programming

In reactive programming, an operator is a basic unit. Consider arranging operators from left-to-right then top-to-bottom so it is much easier to understand the whole flow of data when code is undergoing a review.

//Bad
userService.getFavorites(userId).map(Favorite:toRequestModel)
           .flatMap(favoriteService::getDetails) 
 
// Good
userService.getFavorites(userId) 
           .map(Favorite:toRequestModel)
           .flatMap(favoriteService::getDetails) 

2. Reduce the distance between Operators

Like rule number 1, in an effort to emphasize operators, Keep the lambda function short and simple inside the operator such as map or flatMap. If it is going to be longer, extract the it as a separate function.

//Bad
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               val userRequest = user.toUserRequest()
               GetFavoriteDetailRequest(
                   favorites = favorites,
                   user = userRequest
               )
            }
           .flatMap(favoriteService::getDetails) 
 
//Good
userService.getFavorites(userId)
           .map(this:toRequestModel)
           .flatMap(favoriteService::getDetails) 
 
fun toRequestModel(input: Tuple2<Favorites, Users>) {
     val (favorites, user) = input
     userRequest = user.toUserRequest()
     GetFavoriteDetailRequest(
        favorites = favorites,
        user = userRequest
     )
} 

3. Use Operators matching the name

The name of operators is as much important as how it operates. For instance, map should be used for mapping a value to another value, not for side effects. Similarly, flatMap should be used for mapping a value to Publisher type that is an abstraction of computation, inherently asynchronous.
The consistency between the name and the usage will reduce the cognitive load when reading code.

//Bad
userService.getFavorites(userId)
           .map { 
               log.info("Received favoirtes, $it")
               it.toRequestModel()
            }
           .flatMap(favoriteService::getDetails) 
 
//Good
userService.getFavorites(userId)
           .doOnNext { log.info("Received favoirtes, $it") }
           .map(this:toRequestModel)
           .flatMap(favoriteService::getDetails) 

4. Use Reactive type discreetly

Like I mentioned in rule number 3, Publisher, whose type is a supertype of Mono and Observable, is an abstraction of future computation. Check whether itโ€™s accessible in runtime before declaring the property as a subtype of Publisher otherwise it brings more complexity into code without much gain.

//Bad
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               GetRequestModel(
                   Mono.just(favorites),
                   Mono.just(user)
               )
            }
           .flatMap(favoriteService::getDetails) 
 
data class GetRequestModel(
     val favorites: Mono<Favorites>,
     val user: Mono<User>
)
 
//Good
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               GetRequestModel(favorites, user)
           }
           .flatMap(favoriteService::getDetails) 
 
data class GetRequestModel(
     val favorites: Favorites,
     val user: User
)

5. Null equivalent in reactive is Empty

If you return null when the function is expected to return Publisher type, it will throw an exception because null equivalent in reactive is an empty Publisher. For example, it is allowed to return null in map operator but in flatMap.
In Kotlin, we would rarely come across such situation since itโ€™s supporting non-nullable type, whereas, in Java, one need to take care more about null handling in reactive programming.

// Bad
Mono
   .just("test")
   .flatMap { testFunc(it) }
 
// Good
Mono
  .just("test")
  .flatMap { 
      testFunc(it) ?: Mono.empty()
   }
 
private fun testFunc(seed: String): Mono<String>? =
    if (seed  == "test") {
        null
    } else {
        Mono.just("Mono - test")
    }

6. Use a method reference

It is hard to avoid using nested functions also itโ€™s not helping readability since variable scopes start to be affecting each other. Soon code base is likely to be covered with it, an implicit name of a lambada parameter in Kotlin, or with the series of a similar variable name in Java. Always prefer method reference that allows us to skip the boilerplate. If you could use SAM(Single Abstract Method) instead, Itโ€™s even better! We donโ€™t even have to look at the method name. The type name will tell us spontaneously.

// Bad
userService.getUser(userId)
           .map { it.toFavoriteReq() }
           .flatMap { 
               favoriteService
                       .getFavorites(it)
                       .flatMap { 
                          favoriteService.getDetails(it.toDetailRequest) 
                       }
 
           }
 
// Good 
userService.getUser(userId)
           .map(User::toFavoriteReq)
           .flatMap { favoriteReq ->
               favoriteService
                       .getFavorites(favoriteReq)
                       .map(Favorite:toDetailRequest)
                       .flatMap(favoriteService::getDetail) 
                           
           }

7. Don’t let Collection API shadow Operators

The name of most reactive operators make a pair with that of Collection API. map, flatMap, reduce, filter, the list goes on. If these names appear in the same place, it will confuse the reviewer. Which is which? Separate the usage of API or use an unambiguous name from the other.

// val books: List<Book>
// Bad
Flux.merge(
  books.map { book ->
     if  (book.id == null) {
       Mono.just(card.copy(id = UUID.randomUUID()))
     } else {
       Mono.just(book)
     }
  }
)
 
// Good
Flux.merge(
  books.jdkMap { book ->
     if  (book.id == null) {
       Mono.just(card.copy(id = UUID.randomUUID()))
     } else {
       Mono.just(book)
     }
  }
)
 
private fun <T, R> Iterable<T>.jdkMap(transform: (T) -> R): List<R> = this.map(transform)

8. Make variable name distinguishable

Itโ€™s unnecessary to include Mono or Flux for the variable name unless the reader needs attention. When there are Publisher and non-publisher type in one place, or performing an operation between Flux and Mono, we better to include flux or mono in the variable name. Sometimes, the operation between two types would end up in an unexpected result. For example, the result of zipping Flux and Mono will emit only one item ignoring the rest part flux.

// Bad
val numberKor = Mono.just("hana")
val numberEng = Flux.just("one", "two")
numberEng
      .zipWith(numberKor)
      .doOnNext { zipped ->
         val (eng, kr) = zipped
         log.info("English:$eng, Korean:$kr")
       }
 
// Good
val numberKorMono = Mono.just("hana")
val numberEngFlux = Flux.just("one", "two")
 
numberEngFlux
     .zipWith(numberKorMono)
     .doOnNext { zipped ->
        val (eng, kr) = zipped
        log.info("English:$eng, Korean:$kr")
     }

9. Avoid calling subscribe() directly

Avoid calling subscribe() inside of reactive chain except in a clear case, such as spawning Zombie process. There is a danger that the process would be lingered over longer than we had expected because there is no way for us to control over the dangling process. Ideally, we should able to adjust the flux of data by handling Disposable type.

// Bad
userService
         .getUser(req)
         .flatMap(userService::changePassword)
         .doOnNext {
              auditLogger
                      .auditLog(it)
                      .subscribe()
         }

10. Key operator to the performance is flatMap

Speaking of performance improvement in reactive programming, the parallelization approach is usually suggested and is dealt with by flatMap(). Positioning operators that change data flow such as delayElement() around flatMap should be carefully considered as the flux of data might be changed after passing through flatMap. So if you seek to improve the performance in reactive programming, take a gander at the usage of flatMap.

Reference

https://projectreactor.io/docs/core/release/reference/

http://reactivex.io/documentation/operators.html

Clean (Reactive) Code

์—๋“œ์›Œ๋“œ ์Šค๋…ธ๋“  ๊ทธ๋ฆฌ๊ณ  ์ƒŒ๋“œ์›œ

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

์˜๊ตฌ๊ธฐ๋ก(Permanent Record) – ์—๋А์›Œ๋“œ ์Šค๋…ธ๋“ 

์ด ์ฑ…์€ ์Šค๋…ธ๋“ ์˜ ์œ ๋…„์‹œ์ ˆ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์–ด๋–ป๊ฒŒ ๋ฏธ๊ตญ ์ •๋ถ€์˜ ๋ถˆ๋ฒ• ์ ์ธ ๋Œ€์ค‘ ๊ฐ์‹œ๋ฅผ ํญ๋กœํ•˜๊ฒŒ ๋˜์—ˆ๋Š”์ง€ ๊นŒ์ง€ ๊ณผ์ •์„ ์„ค๋ช…ํ•œ ํšŒ๊ณ ๋ก์ด๋‹ค. ์‹ค์ œ ์ŠคํŒŒ์ด ์—…๋ฌด๋‚˜ ์‚ฌ์šฉํ•œ ๊ธฐ์ˆ ์— ๋Œ€ํ•ด ๊นŠ๊ฒŒ ๋‹ค๋ฃจ์ง„ ์•Š์•„์„œ ๋ˆ„๊ตฌ๋‚˜ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

์œ ๋…„์‹œ์ ˆ

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

9/11

๊ทธ๋Ÿฐ ์Šค๋…ธ๋“ ์˜ ์ธ์ƒ์ด ํ™• ๋ฐ”๋€Œ๊ฒŒ ๋œ ์‹œ์ ์€ 9/11์ด๋‹ค. ๊ทธ ์‚ฌ๊ฑด์ดํ›„ ๊ทธ๋ฆฐ๋ฒ ๋ ˆ์— ์ง€์›ํ–ˆ์ง€๋งŒ ํ›ˆ๋ จ ๋„์ค‘ ๋ถ€์ƒ์„ ์ž…๊ณ  ์ „์—ญํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ ๋’ค์— NSA ์‹œ์„ค์˜ ๊ฒฝ๋น„์›์œผ๋กœ 1๋…„์ •๋„ ์ผํ•œ๋‹ค. ์–ด๋–ป๊ฒŒ ๋ณด๋ฉด ์Šค๋…ธ๋“ ์€ ์†Œ์œ„ ๋งํ•˜๋Š” ๊ณ ์ŠคํŽ™์„ ๊ฐ€์ง„ ์ธ๋ฌผ์€ ์•„๋‹ˆ๋‹ค. ๋ถ€๋ชจ๋‹˜์ด ๋ชจ๋‘ ์ •๋ถ€๊ธฐ๊ด€์—์„œ ์ผํ–ˆ๊ณ  ๊ตฐ์— ์ž์›ํ•œ ๊ฒฝํ—˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋†’์€ ๋ณด์•ˆ ๋“ฑ๊ธ‰์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์—ˆ๊ณ  9/11 ์ดํ›„์˜ ์ •๋ณด๊ธฐ๊ด€์˜ ํ™•์žฅ ์‹œ๊ธฐ์™€ ๋งž๋ฌผ๋ ค์„œ CIA์—์„œ ์ผํ•˜๊ฒŒ ๋œ๋‹ค. CIA๋ผ๊ณ  ํ•ด๋„ ๋ง‰์ƒ ์ปดํ“จํ„ฐ์— ์ •ํ†ตํ•œ ์‚ฌ๋žŒ์€ ์–ผ๋งˆ ์—†์—ˆ๊ธฐ์— ์Šค๋…ธ๋“ ์€ ๊ณง ๋‘๊ฐ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

๋‚ด๋ถ€๊ณ ๋ฐœ๊นŒ์ง€

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

๊ทธ๋ ‡๊ฒŒ ์–ธ๋ก ์— ์ œ๋ณดํ•  ๊ฒฐ์‹ฌ์„ ํ•œ๋’ค์—๋Š” ์‹œ์Šคํ…œ ๋‚ด๋ถ€์—์„œ ๋ฌธ์„œ๋ฅผ ํฌ๋กค๋งํ•˜๋Š” Heartbeat๋ฅผ ๊ฐœ๋ฐœํ•œ๋’ค ๊ธฐ์ž์— ์ ‘๊ทผํ•˜๊ณ  ๊ฒฐ๊ตญ ํ™์ฝฉ์—์„œ ๊ธฐ์‚ฌ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๊นŒ์ง€์˜ ์ƒํ™ฉ์„ ์„ค๋ช…ํ•œ๋‹ค. ๋‹น์—ฐํ•˜๊ฒ ์ง€๋งŒ ์ •๋ง ๋งŽ์€ ๊ณ ๋ฏผ์„ ํ•œ ๊ฒƒ ๊ฐ™์œผ๋ฉฐ ๋ฏธ๊ตญ ์ •๋ถ€์™€ ์˜ํšŒ๊ฐ€ ์„œ๋กœ ๊ฒฌ์ œํ•˜๋Š”๋ฐ ์‹คํŒจ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ๋ฐฉ๋ฒ•์„ ํƒํ•˜๊ฒŒ ๋๋‹ค๊ณ  ํ•œ๋‹ค. ์Šค๋…ธ๋“ ์ด ๊ทธ ๊ณ ๋ฏผ ์†์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋น„์œ ๋ฅผ ํ–ˆ๋Š”๋ฐ ๋งˆ์Œ์— ์™€ ๋‹ฟ์•˜๋‹ค.

Ultimately, saying that you donโ€™t care about privacy because you have nothing to hide is no different from saying you donโ€™t care about freedom of speech because you have nothing to say. Or that you donโ€™t care about freedom of the press because you donโ€™t like to read. Or that you donโ€™t care about freedom of religion because you donโ€™t believe in God. Or that you donโ€™t care about the freedom to peaceably assemble because youโ€™re a lazy, antisocial agoraphobe.

๋Œ€์ถฉ ๋ฒˆ์—ญํ•˜๋ฉด ์ž๊ธฐ๋Š” ๋‹น๋‹นํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋ผ์ด๋ฒ„์‹œ์— ๋ณ„๋กœ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ ์ฝ๋Š” ๊ฒƒ์„ ์ข‹์•„ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ถœํŒ์˜ ์ž์œ ๋„ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์œผ๋ฉฐ ์‹ ์„ ๋ฏฟ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ข…๊ต์˜ ์ž์œ ๋„ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งํ•˜๋Š” ๊ฒƒ๊ณผ ๋ณ„๋ฐ˜ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค๋Š” ๋‚ด์šฉ.

์Šค๋…ธ๋“ ์˜ ๋ฏธ๋ž˜๋Š”?

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

์ด์‚ฌ๊ฑด์„ ๊ณ„๊ธฐ๋กœ ๋ฏธ๊ตญ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ „์„ธ๊ณ„์˜ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๊ฐ์ž ์ •๋ถ€๊ธฐ๊ด€์˜ ์ž˜๋ชป๋œ ํ–‰ํƒœ์— ๊ด€์‹ฌ์„ ๊ฐ€์ง€๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ๋‚˜๋„ ์ฑ…์„ ์ฝ์œผ๋ฉด์„œ ์ •๋ถ€์˜ ์ž˜๋ชป๋œ ๊ด€ํ–‰์— ๊ฒฌ์ œ์˜ ์‹œ๊ทธ๋„์„ – ์›๋ž˜๋ผ๋ฉด ๊ตญํšŒ๊ฐ€ ํ•ด์•ผํ•  – ๋ณด๋‚ด์ค€ ์Šค๋…ธ๋“ ์—๊ฒŒ ํฌ๊ฒŒ ๊ฐ์‚ฌํ•จ์„ ๋А๊ผˆ๋‹ค.

๊ด€๋ จ๊ธฐ์‚ฌ

์Šค๋…ธ๋“ ๋„ ์‚ฌ์šฉํ•œ ๋ฉ”์‹ ์ € ‘์‹œ๊ทธ๋„’ ๋ณด์•ˆ์„ฑ ์ตœ๊ฐ•

์—๋“œ์›Œ๋“œ ์Šค๋…ธ๋“  โ€œK๋ฐฉ์—ญ ์ •๋ณด์ˆ˜์ง‘, ํšจ๊ณผ ํ™•์‹ค์น˜ ์•Š๋‹คโ€

์ƒŒ๋“œ์›œ(Sandworm) – ์•ค๋”” ๊ทธ๋ฆฐ๋ฒ„๊ทธ

Sandworm

์ƒŒ๋“œ์›œ์€ ๋Ÿฌ์‹œ์•„ GRU์†Œ์†์˜ ํ•ดํ‚น ์ „๋‹ด ๋ถ€๋Œ€์˜ ์ฝ”๋“œ๋ช…์ด๋‹ค. ์ด ์ฑ…์€ ์ตœ๊ทผ์— ๋ฒŒ์–ด์ง„ ์ผ๋ จ์˜ ํ•ดํ‚น ์‚ฌ๊ฑด๋“ค์˜ ๋ฐฐํ›„๋ฅผ ๋‹ค๋ฃจ๋Š” ๋…ผํ”ฝ์…˜์œผ๋กœ ์ฑ…์—์„œ ์–ธ๊ธ‰๋œ ์‚ฌ๊ฑด์„ ์ •๋ฆฌํ•œ ์•„๋ž˜ ๋ชฉ๋ก์€ ๊ธฐ์Šน์ „๋Ÿฌ ๋Œ€๋ถ€๋ถ„ ๋Ÿฌ์‹œ์•„๊ฐ€ ๊ทธ ๋ฐฐํ›„์— ์žˆ๋‹ค. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์•„๋ž˜ ์‚ฌ๊ฑด๋“ค์ค‘ ํ•œ๋‘๊ฐ€์ง€๋Š” ๋‹จ์‹  ๋‰ด์Šค๋กœ ๋ดค์„ ๊ฒƒ์ด๋‹ค.

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

๋ฏธ๋ฏธ์นด์ธ 

์ฑ…์—์„œ ์–ธ๊ธ‰๋œ ๋Œ€๋ถ€๋ถ„์˜ ์ œ๋กœ๋ฐ์ด ์ทจ์•ฝ์ ์€ ์œˆ๋„์šฐ ๊ธฐ๋ฐ˜์ด๋‹ค. ํ”„๋ž‘์Šค ์ •๋ถ€๊ธฐ๊ด€์˜ IT๋งค๋‹ˆ์ ธ๋กœ ์ผํ•˜๋˜ ๋ธํ”ผ๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์‚ฌ์šฉ์ž์˜ ํฌ๋ ˆ๋ด์…œ์„ ์ €์žฅํ•˜๋Š” ์œˆ๋„์šฐ WDigest ๊ธฐ๋Šฅ์˜ ์ทจ์•ฝ์ ์„ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ด๋ฅผ MS์— ์ฆ‰๊ฐ ๋ณด๊ณ ํ•˜์ง€๋งŒ MS๋Š” ํ•ด๋‹น ์ทจ์•ฝ์ ์€ ์ปดํ“จํ„ฐ๊ฐ€ ์ด๋ฏธ ํƒˆ์ทจ๋‹นํ•œ ์ƒํƒœ์—์„œ๋งŒ ๋ฌธ์ œ๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์žฅ ํฐ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค ๋ผ๋Š” ์‹์œผ๋กœ ๋Œ€์‘์„ํ•œ๋‹ค. ๋ธํ”ผ๋Š” ๊ฒฝ๊ฐ์‹ฌ์„ ์ผ๊นจ์šฐ๊ธฐ์œ„ํ•ด POC๊ฐœ๋…์œผ๋กœ Mimikatz์„ ๊นƒํ—™์— ๊ณต๊ฐœํ•œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ MS์˜ ๋ฌด์ฑ…์ž„ํ•œ ๋Œ€์‘์œผ๋กœ ๋„ค๋œ๋ž€๋“œ์˜ ์ธ์ฆ์„œ ๋ฐœ๊ธ‰ ์—…์ฒด์˜€๋˜ DigiNotar๋Š” ์ธ์ฆ์„œ๋ฅผ ๋ถ€์ • ๋ฐœ๊ธ‰ํ•˜๊ฒŒ ๋˜๊ณ  ํŒŒ์‚ฐ์— ์ด๋ฅด๊ฒŒ ๋œ๋‹ค.

ํ”ผํ•ด์•ก๋งŒ ํ•ด๋„ ์กฐ๋‹จ์œ„๊ฐ€ ๋„˜์–ด๊ฐ€๋Š” ํŽ˜ํŠธ์•ผ๋Š” NSA๊ฐ€ ๋‚˜์ค‘์— ์จ๋จน๊ธฐ ์œ„ํ•ด ๊ฝ๊ฝ ์ˆจ๊ฒจ๋’€๋˜ ์ดํ„ฐ๋„๋ธ”๋ฃจ ์ทจ์•ฝ์ ์„ ์‚ฌ์šฉํ•ด ๊ณต๊ฒฉ์„ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ์˜๋„์น˜์•Š๊ฒŒ ์ •๋ณด๊ธฐ๊ด€๋ผ๋ฆฌ ์ทจ์•ฝ์ ๋“ค์„ ๊ณต์œ ํ•ด์„œ ๋”์šฑ ๋” ๋ฐœ์ „๋œ ๋ฉ€์›จ์–ด๊ฐ€ ๋‚˜์˜ค๊ฒŒ ํ•œ๋‹ค.

๋Ÿฌ์‹œ์•„์˜ ์‚ฌ์ด๋ฒ„ ์›Œ

๋Ÿฌ์‹œ์•„๋Š” ์™œ์ด๋ ‡๊ฒŒ ํ•ดํ‚น์„ ํ•˜๋Š” ๊ฑธ๊นŒ? ๋ƒ‰์ „ ์ดํ›„์— ๋Ÿฌ์‹œ์•„์˜ ์„ธ๊ณ„ ๊ฒฝ์Ÿ๋ ฅ์€ ๋‚ด๋ฆฌ๋ง‰๊ธธ์„ ๊ฑธ์—ˆ๊ณ  ๋‚จ์€๊ฑด ๋ง‰๋Œ€ํ•œ ๊ตฐ์‚ฌ๋ ฅ๋ฟ ๊ทธ๋งˆ์ €๋„ ๋ฏธ๊ตญ ๊ตญ๋ฐฉ๋น„์˜ 1/10์— ๊ทธ์น˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์ „๊ฐ™์€ ์˜ํ–ฅ๋ ฅ์„ ๋˜์ฐพ๊ณ  ์‹ถ์–ดํ•˜๋Š” ์‹œ๋„๋ผ๋Š” ๋ถ„์„์ด๋‹ค. 2014๋…„์— ์ผ์–ด๋‚œ ๋Ÿฌ์‹œ์•„ ์šฐํฌ๋ผ์ด๋‚˜ ์ „์Ÿ์—์„œ ๋Ÿฌ์‹œ์•„๋Š” ์Šน๋ฆฌํ–ˆ์ง€๋งŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŒ๋‹จ ์ฐฉ์˜ค๋กœ ์ธํ•ด ์ฒฉ๋ณด ๊ธฐ๊ด€์ธ GRU๋Š” ์‹ ๋ขฐ๋„์— ํฐ ํƒ€๊ฒฉ์„ ์ž…๋Š”๋‹ค. ์ด๋ฅผ ๋ณต๊ตฌํ•˜๊ธฐ ์œ„ํ•ด ์ ์€ ๋น„์šฉ์œผ๋กœ ํฐ ํ˜ผ๋ž€์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์‚ฌ์ด๋ฒ„ ์ „์Ÿ์— ์ ๊ทน์ ์œผ๋กœ ๋›ฐ์–ด๋“ค๊ณ  ์žˆ๋‹ค๋Š” ๋ถ„์„์ด๋‹ค.

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

๋ฐฐํ›„(attribution)๋ฌธ์ œ

๋Ÿฌ์‹œ์•„ ๋ณด์•ˆ์—…์ฒด์ธ ์นด์Šคํผ์Šคํ‚ค์˜ ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€๋Š” ์ €์ž์™€ ์ธํ„ฐ๋ทฐ์ค‘์— ํ‰์ฐฝ ์˜ฌ๋ฆผํ”ฝ ํ•ดํ‚น์€ ๋ถํ•œ ์†Œํ–‰์ด ์•„๋‹ˆ๋ฉฐ ๋ˆ„๊ตฐ๊ฐ€ ์ผ๋ถ€๋Ÿฌ ๊ทธ๋ ‡๊ฒŒ ๋ณด์ด๊ฒŒ ์„ค๊ณ„ํ–ˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์ €์ž๋Š” ๊ทธ๋ž˜์„œ ๋ถํ•œ์ด์•„๋‹ˆ๋ผ๋ฉด ๋ˆ„๊ตฌ ์†Œํ–‰์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ƒ? ๋ผ๊ณ  ๋‹ต์ •๋‚จ ์งˆ๋ฌธ์„ ํ•˜์ง€๋งŒ ํ•ด๋‹น ์ „๋ฌธ๊ฐ€๋Š” ๊ฐ€๋ฐฉ์—์„œ attribution dice(๋ฐฐํ›„?ย ์ฃผ์‚ฌ์œ„)๋ฅผ ๊บผ๋‚ด์„œ ๋ณด์—ฌ์ฃผ๋Š”๋ฐ ์ด๋ถ€๋ถ„์€ ์กฐ๊ธˆ ์›ƒ๊ฒผ๋‹ค:) ์ด๋Š” ๊ทธ๋งŒํผ ํ•ดํ‚น์˜ ์ฑ…์ž„์ž๋ฅผ ๊ฐ€๋ ค๋‚ด๊ธฐ๊ฐ€ ํž˜๋“ค๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋œปํ•œ๋‹ค.

๋ฐฐํ›„๋ฅผ ๋ฐํ˜€๋‚ด๊ฐ€ ํž˜๋“  ์‚ฌ์ด๋ฒ„์›Œ์˜ ํŠน์„ฑ์ƒ ์•ž์œผ๋กœ์˜ ์„ธ์ƒ์€ ์˜ˆ์ „ ์ „์Ÿ์ฒ˜๋Ÿผ ๊ฐœ์ „๊ณผ ์ข…์ „์œผ๋กœ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„๋˜์ง€ ์•Š๊ณ  ํ•ญ์‹œ ์กด์žฌํ•  ๊ฒƒ์ด๋ผ๋Š” ๋ถ„์„์ด๋‹ค.

๋ณต์›๋ ฅ

๊ทธ๋ฆฌ๊ณ  ์ €์ž๋Š” ์‚ฌ์ด๋ฒ„์›Œ์— ๋Œ€ํ•œ ๋Œ€์‘์œผ๋กœ ๋Œ„ ์ง€์–ด๊ฐ€ ์ฃผ์žฅํ•˜๋Š” ๋ณต์›๋ ฅ(Resilence)๋ฅผ ์–ธ๊ธ‰ํ•˜๋ฉฐ ์ฑ…์„ ๋งˆ๋ฌด๋ฆฌํ•œ๋‹ค. ๋Œ„์ง€์–ด๋Š” ๋ฏธ๊ตญ ์ •๋ณด๊ธฐ๊ด€์˜ ๋ฒค์ฒ˜์บํ”ผํƒˆ ์—ญํ• ์„ ํ•˜๋Š” ๋น„์˜๋ฆฌ๊ธฐ๊ด€์ธ In-Q-Tel ์˜ ์ตœ๊ณ ๋ณด์•ˆ ์ฑ…์ž„์ž์ด๋‹ค. ๋งŽ์€ ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€๋“ค์ด ํ•ดํ‚น ๊ณต๊ฒฉ์— ๋Œ€ํ•ด์„œ ๋” ๋งŽ์€ ๋ณด์•ˆ ํŒจ์น˜, ๋จธ์‹ ๋Ÿฌ๋‹์— ์˜ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ง๋“ฑ ๊ฐ•๋ ฅํ•œ ์กฐ์น˜๋ฅผ ์ด์•ผ๊ธฐ ํ•˜์ง€๋งŒ ๊ทธ๋Š” ๋ฐฉ์ง€๋ณด๋‹ค๋Š” ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•˜๊ณ  ๋ณต๊ตฌ์‹œ๊ฐ„์„ ์ค„์ด๋Š”๋ฐ ๋”์šฑ ์ง‘์ค‘ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ ํ•ต์‹ฌ ๊ธฐ๋…์ด ๋ณต์›๋ ฅ์œผ๋กœ ๋…๋ฆฝ์„ฑ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ฐ ๊ตฌ์„ฑ์š”์†Œ๋“ค์ด ๋„คํŠธ์›Œํฌ์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ์•„๋‚ ๋กœ๊ทธ๋กœ๋„ ๋…์ง‘์ ์œผ๋กœ ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ํ˜„๋Œ€ ์‚ฌํšŒ๋Š” ์•„์ฃผ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ๋“ค์ด ๋‹จ๊ณ„์ ์œผ๋กœ ์—ฎ์—ฌ ์žˆ์–ด์„œ ๊ทธ ๊ธฐ๋ฐ˜์ด ๋ฌด๋„ˆ์ง€๋ฉด ์ „์ฒด๊ฐ€ ๋‹ค ๋ฌด๋„ˆ๊ธฐ ์‰ฝ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฉ”์ผ ์‹œ์Šคํ…œ์ด ๋ฉˆ์ถ”๋ฉด ์šฐํŽธ์‹œ์Šคํ…œ์ด ์ž‘๋™ํ•ด์•ผ ํ•˜๊ณ  ์ด๋™์ „ํ™”๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ์œ ์„ ์ „ํ™”๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌํšŒ๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค. ๊ทธ ์˜ˆ๋กœ ์šฐํฌ๋ผ์ด๋‚˜์—์„œ ํ•ดํ‚น์œผ๋กœ ๋Œ€๊ทœ๋ชจ ์ •์ „ ์ผ์–ด๋‚ฌ์–ด๋„ ์ƒ๊ฐ๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ ๋ณต๊ตฌ๊ฐ€ ๊ฐ€๋Šฅํ–ˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์šฐํฌ๋ผ์ด๋‚˜์˜ ๊ธฐ๋ฐ˜ ์‹œ์„ค๋“ค์€ ์„ ์ง„๊ตญ๋ณด๋‹ค ํ‰์†Œ์—๋„ ๋” ์ž์ฃผ ๋ฉˆ์ถ”๊ณ  ๋งŽ์€ ๋ถ€๋ถ„๋“ค์ด ์‚ฌ๋žŒ์†์œผ๋กœ ์•„์ง ์›€์ง์ด๊ณ  ์žˆ์—ˆ๊ธฐ์— ํ•ดํ‚นํ•˜๊ธฐ๋Š” ์‰ฌ์› ์ง€๋งŒ ํ”ผํ•ด๋Š” ์ ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ ์ง„๊ตญ์˜ ์‹œ์„ค๋“ค์€ ํ•ดํ‚นํ•˜๊ธฐ๋Š” ๋”์šฑ ์–ด๋ ต์ง€๋งŒ ํ•ดํ‚น ๋‹นํ•ด์„œ ๋ฉˆ์ถ”๊ฒŒ ๋˜๋ฉด ๊ทธ ํ”ผํ•ด๋Š” ํ›จ์”ฌ ๋น„๊ทน์ ์ผ ๊ฒƒ์ด๋‹ค.

์—๋“œ์›Œ๋“œ ์Šค๋…ธ๋“  ๊ทธ๋ฆฌ๊ณ  ์ƒŒ๋“œ์›œ

๋” ๋ผ์ŠคํŠธ ์˜ค๋ธŒ ์–ด์Šค ํŒŒํŠธ2 ๋ฆฌ๋ทฐ

์ด ๋ฆฌ๋ทฐ๋Š” ๊ฐ•๋ ฅํ•œ ์Šคํฌ์ผ๋Ÿฌ๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

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

์‚ฌ๋žŒ๋“ค์ด ๋ถ„๋…ธํ•˜๋Š” ์ด์œ , ์ด์•ผ๊ธฐ ๋•Œ๋ฌธ

์ธํ„ฐ๋„ท์—์„œ ๋ณธ ๋ฐ•ํ•œ ํ‰๊ฐ€๋“ค์€ ์ฃผ๋กœ ์ „์ž‘์˜ ํŒฌ๋“ค์„ ๊ณ ๋ คํ•˜์ง€ ์•Š์€ ๊ฐœ์—ฐ์„ฑ ์—†๋Š” ์ „๊ฐœ, ๊ณต๊ฐํ•  ์ˆ˜ ์—†๋Š” ๋ฉ”์‹œ์ง€ ๋“ฑ ์ฃผ๋กœ ์บ๋ฆญํ„ฐ์™€ ์Šคํ† ๋ฆฌ์— ๊ด€ํ•œ ํ‰๊ฐ€๊ฐ€ ์ฃผ๋ฅผ ์ด๋ฃฌ๋‹ค. ๋ผ์˜ค์–ด1 ์—์„œ ํ”Œ๋ ˆ์ด์–ด๋“ค์ด ๊ทธํ† ๋ก ๊ฐ์ •์ด์ž…์„ ํ–ˆ๋˜ ์กฐ์—˜์ด ย PC์„ค์ •์œผ๋กœ ์ถ”๊ฐ€๋œ ์ƒˆ๋กœ์šด ์บ๋ฆญํ„ฐ์—๊ฒŒ ๊ณจํ”„๊ณต ์ทจ๊ธ‰์„ ๋‹นํ•˜๊ณ  ์นœ๋”ธ๋ณด๋‹ค ๋” ์ •์„ฑ์Šค๋Ÿฝ๊ฒŒ ์ง€์ผœ๋‚ธ ์—˜๋ฆฌ๊ฐ€ ๋ ˆ์ฆˆ๋น„์–ธ์ด๋ผ๋Š” ์„ค์ •, ๊ทธ๋ฆฌ๊ณ  ํ”Œ๋ ˆ์ด์–ด์—๊ฒŒ ๊ทธ๋…€๋ฅผ ์ฅ ์žก๋“ฏ์ด ๋‘๋“œ๋ ค ํŒจ๋„๋ก ํ•˜๋Š” ์ง„ํ–‰๊นŒ์ง€..์ •ํ™•ํžˆ ๊ทธ ์‹œ์ ๋ถ€ํ„ฐ ์—”๋”ฉ๊นŒ์ง€ ์ด์–ด์ง€๋Š” ์ด์•ผ๊ธฐ๋Š” ๋‚˜๋„ ๋ชจ๋ฅด๊ฒŒ ์–ผ๊ตด์ด ์ฐŒํ‘ธ๋ฆฌ๋ฉด์„œ “์•„ ๋‚˜๋Š” ์ด๋ ‡๊ฒŒ ํ•˜๊ธฐ ์‹ซ์–ด”๋ผ๊ณ  ์™ธ์น˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ์ด ๊ฒŒ์ž„์€ ํ”Œ๋ ˆ์ด์–ด๋“ค์ด ์ฃผ๋ง ๋“œ๋ผ๋งˆ์˜ ์ƒํ™ฉ์— ์™„์ „ํžˆ ๋ชฐ์ž…ํ•œ ์ฃผ๋ถ€์ฒ˜๋Ÿผ ๋งŒ๋“ค์–ด ๋ฒ„๋ ธ๋‹ค.

์Šคํ† ๋ฆฌ ์œ„์ฃผ์˜ ๊ฒŒ์ž„ ๋งŒ๋“ค๊ธฐ ์‰ฝ์ง€ ์•Š๋‹ค

๋ง๋ถ™์—ฌ์„œ ์š”์ฆ˜ ๊ฐ™์€ ์‹œ๊ธฐ์— ์ด์•ผ๊ธฐ ์œ„์ฃผ์˜ ๊ฒŒ์ž„์„ ๋งŒ๋“œ๋Š” ์ผ์€ ์ ˆ๋Œ€ ์‰ฌ์šด ์ผ์ด ์•„๋‹ˆ๋‹ค. ๊ทผ 10๋…„๊ฐ„ ์ถœ์‹œ๋œ ๊ฒŒ์ž„์„ ๋ณด๋ฉด ์Šคํ† ๋ฆฌ๋Š” ๋Œ€๋ถ€๋ถ„ ๋ถ€์ˆ˜์ ์ธ ์—ญํ• ์„ ํ•œ๋‹ค. 8,ย 90๋…„ ๋Œ€์—๋Š” ์ด์•ผ๊ธฐ ์ค‘์‹ฌ์˜ ๊ฒŒ์ž„์ด ๋‚˜์™”์ง€๋งŒ, ์˜คํžˆ๋ ค ์ปดํ“จํ„ฐ ๊ทธ๋ž˜ํ”ฝ ๊ธฐ์ˆ ์ด ์›”๋“ฑํ•˜๊ฒŒ ๋ฐœ์ „ํ•œ ์ตœ๊ทผ์—๋Š” ์Šคํ† ๋ฆฌ ์œ„์ฃผ์˜ ๊ฒŒ์ž„๋ณด๋‹ค๋Š” ํ™”๋ฉด์˜ ํ™”๋ คํ•จ์ด๋‚˜ ์ฐธ์‹ ํ•œ ํ”Œ๋ ˆ์ดย ๋“ฑ์ด ์ฃผ๋กœ AAํƒ€์ดํ‹€๋กœ ๋“ฑ์žฅํ•œ๋‹ค. ํ™”๋ คํ•œ ๊ทธ๋ž˜ํ”ฝ๊ณผ ์Šคํ† ๋ฆฌ๋ฅผ ๋‹ค ์žก์œผ๋ ค๋ฉด ํ—ค๋น„๋ ˆ์ธ์ด๋‚˜ ๋””ํŠธ๋กœ์ดํŠธ ๋น„์ปด ํœด๋จผ๊ฐ™์€ ์žฅ๋ฅด ๋ง๊ณ ๋Š” ์„ ํƒ์ง€๊ฐ€ ์—†์—ˆ๋‹ค.

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

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

๋ผ์˜ค์–ด2 ๋””ํ…Œ์ผ์— ๊ด€ํ•œ ์œ ํŠœ๋ธŒ ๋น„๋””์˜ค 1
๋ผ์˜ค์–ด2 ๋””ํ…Œ์ผ์— ๊ด€ํ•œ ์œ ํŠœ๋ธŒ ๋น„๋””์˜ค 2

๊ฐœ์ธ์ ์œผ๋กœ ๋งŒ์กฑํ–ˆ๋˜ ์ด์•ผ๊ธฐ

๋งŽ์€ ๋ผ์˜ค์–ด1 ํŒฌ๋“ค์˜ ์น˜๋ฅผ ๋–จ๊ฒŒ ๋งŒ๋“ค์—ˆ๋˜ ์Šคํ† ๋ฆฌ๋„ ์‚ฌ๋žŒ๋ณ„๋กœ ์—ฌ๋Ÿฌย ๊ฐ€์ง€ ํ•ด์„์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ์—์„œ ๋‚˜๋Š” ๋ง˜์— ๋“ค์—ˆ๋‹ค. 1์˜ ์ด์•ผ๊ธฐ๋„ ์ข‹์•˜์ง€๋งŒ ์—ฌํƒœ๊นŒ์ง€ ์ ‘ํ•ด๋ดค๋˜ ์ด์•ผ๊ธฐ ํ˜•ํƒœ๋ผ๋ฉด ๋ผ์˜ค์–ด2์˜ ์ด์•ผ๊ธฐ๊ณผ ์ฃผ์ œ๋Š” ์ฐธ์‹ ํ•˜๊ฒŒ ๋‹ค๊ฐ€์™”๋‹ค.

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

PC์„ค์ •์˜ ์ˆœ๊ธฐ๋Šฅ

์—˜๋ฆฌ๋ฅผ ๋ ˆ์ฆˆ๋น„์–ธ์œผ๋กœ ์„ค์ •ํ•œ ๊ฒƒ๋„ ๋‹จ์ˆœํžˆ PC์„ค์ •์ด๋ผ๊ธฐ ๋ณด๋‹ค๋Š”, ์กฐ์—˜์ด ์†Œ์ค‘ํ•œ ์‚ฌ๋žŒ์˜ ์ƒ๋ช…์ด ์ธ๋ฅ˜์˜ ๋ฏธ๋ž˜๋ณด๋‹ค ์ค‘์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์œ ์‚ฌํ•˜๊ฒŒ ์—˜๋ฆฌ๋„ ์ธ๋ฅ˜์˜ ๋ฏธ๋ž˜ ๋ณด๋‹ค ์ž์‹ ์˜ ์ธ์ƒ์„ ์‚ด์•„๊ฐ€๊ธฐ๋กœ ํ•œ ๊ฒƒ ์•„๋‹๊นŒ? ๊ณต๊ณต์˜ ์ด์ต์„ ๋ณด๋ฉด ์—˜๋ฆฌ๋Š” ์ด์„ฑ๊ณผ ์—ฐ์• ํ•ด์„œ ์ž์‹ ์˜ ๋ฉด์—ญ๋ ฅ์„ ๋„๋ฆฌ ํผํŠธ๋ฆฌ๊ฑฐ๋‚˜ ์ ์–ด๋„ ๋ณด์กดํ•ด์•ผ๋งŒ ํ•œ๋‹ค. ย ์ด๋Ÿฐ ์ž์œ ์ฃผ์˜์  ๋ฉ”์‹œ์ง€๋ฅผ ์œ„ํ•ด์„œ ์—˜๋ฆฌ๋ฅผ ๋ ˆ์ฆˆ๋น„์–ธ์œผ๋กœ ์„ค์ •ํ•œ ๊ฒƒ์€ ์•„๋‹๊นŒ ์ƒ๊ฐํ•ด๋ณธ๋‹ค.

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

๋์œผ๋กœ

์—ฌ๋ คย ๋ฉด์—์„œ ๋งŽ์€ ์ƒ๊ฐ์„ ํ•˜๊ฒŒ ํ•˜๊ณ  ์˜๊ฐ์„ ์ฃผ๋Š”ย ๊ฒŒ์ž„์ด์—ˆ๋‹ค. ๋‹ ๋“œ๋Ÿญ๋งŒ์ด ํŠธ์œ„ํ„ฐ์—์„œ ๊ทธ๋ฅผ ๋น„๋‚œํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ํ–ฅํ•ด ๊ณต๊ฒฉ์ ์ธ ๋Œ€์‘์œผ๋กœ ์š•์„ ๋จน๊ณ  ์žˆ์ง€๋งŒ ๋ผ์–ด์˜ค2๋กœ ์ธํ•ด ์ด์ œ ๊ทธ ๋ˆ„๊ตฌ๋„ ๋‹ ๋“œ๋Ÿญ๋งŒ์ด ๊ฒŒ์ž„ ์—…๊ณ„์—์„œ ์ตœ๊ณ ์˜ Bigshot์ด ๋˜์—ˆ์Œ์„ ๋ถ€์ •ํ•  ์ˆ˜๋Š” ์—†์„ ๊ฒƒ์ด๋‹ค.

๋” ๋ผ์ŠคํŠธ ์˜ค๋ธŒ ์–ด์Šค ํŒŒํŠธ2 ๋ฆฌ๋ทฐ

ํด๋ฆฐ (๋ฆฌ์•กํ‹ฐ๋ธŒ) ์ฝ”๋“œ

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

๋‚˜๋Š” 3~4๋…„์— ๊ฑธ์ณ ์Šคํ”„๋ง WebFlux๋ฅผ ์‚ฌ์šฉํ•ด ์ฆ๊ถŒ์•ฑ์˜ ์„œ๋ฒ„ ๊ทธ๋ฆฌ๊ณ  GraphQL์˜ ๊ฒŒ์ดํŠธ์›จ์ด ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.ย WebFlux๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฐ๊ณผ์— ์ถฉ๋ถ„ํžˆ ๋งŒ์กฑํ–ˆ๊ณ  Graphql ๊ฒŒ์ดํŠธ์›จ์ด๋Š” ์•ˆ์ •ํ™” ๋‹จ๊ณ„์— ์ ‘์–ด๋“ค์–ด์„œ ์ตœ๊ทผ์—๋Š” ์—ฌ๋Ÿฌ๋ฒˆ์˜ ์ฝ”๋“œ๋ฆฌ๋ทฐ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.ย ๋‹ค์–‘ํ•œ ์‚ฌ๋žŒ๋“ค์˜ ์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ทฐํ•˜๋ฉด์„œ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ž์‹ ๋“ค๋งŒ์˜ ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์Œ์„ ๊นจ๋‹ฌ์•˜๋‹ค. ๊ณต์‹ ๋งค๋‰ด์–ผ์„ ์ฝ๊ณ  ์ž‘๋™ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ธด ํ–ˆ์ง€๋งŒ ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„์ง€์— ๋Œ€ํ•œ ๋ฐฐ๋ ค๋Š” ๋ถ€์กฑํ–ˆ๋‹ค.

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

์—ฌ๊ธฐ์„œ๋Š” ๋‚ด๊ฐ€ ๊ฐœ์ธ์ ์œผ๋กœ ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์‹œ์— ์ž์ฃผ ์–ธ๊ธ‰ํ•˜๋Š” ํ•ญ๋ชฉ๋“ค์„ ์ •๋ฆฌํ•ด ๋ณธ๋‹ค. ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ๋‚ด์šฉ๋“ค์ด ๋ฐ˜๋“œ์‹œ ์˜ณ๋‹ค๋Š” ์ด์•ผ๊ธฐ๋Š” ์•„๋‹ˆ๋ฉฐ ํ”„๋กœ์ ํŠธ ์ƒํ™ฉ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒ ์ง€๋งŒ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌ๋ทฐ์–ด์˜ ์ž…์žฅ์—์„œ ์ฝ๊ธฐ ํŽธ์•ˆํ•จ์— ์ค‘์ ์„ ๋‘๊ณ  ๋‚˜์—ดํ•ด๋ณธ๋‹ค.

๋ชจ๋“  ์˜ˆ์ œ๋Š” ์ฝ”ํ‹€๋ฆฐ์ฝ”๋“œ๋กœ ์ž‘์„ฑ๋˜์—ˆ๋‹ค.

1. ์—ฐ์‚ฐ์ž๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ

์—ฐ์‚ฐ์ž(Operator)๋Š” ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์—ฐ์‚ฐ์˜ ๊ธฐ๋ณธ ๋‹จ์œ„์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ์—ฐ์‚ฐ์ž๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์™ผ์ชฝ์—์„œ ์˜ค๋ฅธ์ชฝ, ์œ„์—์„œ ์•„๋ž˜ ๋ฐฉํ–ฅ์œผ๋กœ ๋งˆ์น˜ ์ฑ…์„ ์ฝ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ์ œ์ผ ์ดํ•ดํ•˜๊ธฐ ํŽธํ•˜๋‹ค.

//Bad
userService.getFavorites(userId).map(Favorite:toRequestModel)
           .flatMap(favoriteService::getDetails) 

// Good
userService.getFavorites(userId) 
           .map(Favorite:toRequestModel)
           .flatMap(favoriteService::getDetails) 

2. map, flatMap์˜ ํ•จ์ˆ˜๋Š” ์ตœ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•˜๊ฒŒ

1๋ฒˆ ์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ชจ๋“  ์ฝ”๋“œ๋Š” ์—ฐ์‚ฐ์ž ์ค‘์‹ฌ์œผ๋กœ ์ฝํ˜€์ ธ์•ผ ํ•œ๋‹ค. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ œ์ผ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” map๊ณผ flatMap์˜ ํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์žฅํ™ฉํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ž‘์„ฑํ•˜๊ณค ํ•œ๋‹ค. ์ด๊ฒƒ์€ ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ๋Š๊ธฐ๊ฒŒ ํ•ด ๊ฐ€๋…์„ฑ์— ์ข‹์ง€ ์•Š๋‹ค. ์ค‘์š”ํ•œ ํฐ ํ๋ฆ„์€ ์—ฐ์‚ฐ์ž์˜ ์ฒด์ธ๊ณผ ๊ทธ ์ธ์ž๋งŒ์œผ๋กœ ์ฝ์–ด๋‚ผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์žฅํ™ฉํ•œ ๋กœ์ง์€ ์™ธ๋ถ€ ํ•จ์ˆ˜๋กœ ๋นผ๋‚ด๋„๋ก ํ•˜์ž.

//Bad
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               val userRequest = user.toUserRequest()
               GetFavoriteDetailRequest(
                   favorites = favorites,
                   user = userRequest
               )
            }
           .flatMap(favoriteService::getDetails) 

//Good
userService.getFavorites(userId)
           .map(this:toRequestModel)
           .flatMap(favoriteService::getDetails) 

fun toRequestModel(input: Tuple2<Favorites, Users>) {
     val (favorites, user) = input
     userRequest = user.toUserRequest()
     GetFavoriteDetailRequest(
        favorites = favorites,
        user = userRequest
     )
} 

3. ์—ฐ์‚ฐ์ž๋Š” ๋ชฉ์ ๊ณผ ๊ทธ ์ด๋ฆ„์— ๊ฑธ๋งž๊ฒŒ ์‚ฌ์šฉ

๋ฆฌ์•กํ‹ฐ๋ธŒ ์—ฐ์‚ฐ์ž์˜ ์ด๋ฆ„์€ ์‹ค์ œ ์ž‘๋™ ๋ฐฉ์‹๋งŒํผ์ด๋‚˜ ์ค‘์š”ํ•˜๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด map์€ ๊ฐ’์„ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ๋งคํ•‘ํ• ๋•Œ, flatMap์€ ๊ฐ’์„ Publisher ํƒ€์ž…์œผ๋กœ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ Publihserํƒ€์ž…์€ ๊ฐ’์˜ ๊ณ„์‚ฐ ์ž์ฒด๋ฅผ ์ถ”์ƒํ™”ํ•œ ํƒ€์ž…์ด๋‹ค. doOnNext ์—ฐ์‚ฐ์ž๋Š” ์ „์ฒด ์—ฐ์‚ฐ์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ๋ชปํ•˜๋Š” ๋ถ€์ˆ˜ ํšจ๊ณผ(Side effect)๋ฅผ ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋ฆ„์— ๋งž๋Š” ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๋ฆฌ๋ทฐ์‹œ์— ์ธ์ง€๋ถ€ํ•˜๋ฅผ ์ค„์—ฌ ์ค€๋‹ค.

//Bad
userService.getFavorites(userId)
           .map { 
               log.info("Received favoirtes, $it")
               it.toRequestModel()
            }
           .flatMap(favoriteService::getDetails) 

//Good
userService.getFavorites(userId)
           .doOnNext { log.info("Received favoirtes, $it") }
           .map(this:toRequestModel)

           .flatMap(favoriteService::getDetails) 

4. Mono, Flux ์‚ฌ์šฉ์ด ํ•„์š”ํ•œ์ง€ ํ™•์ธ

3๋ฒˆ์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด Mon, Flux์˜ ์ƒ์œ„ ํƒ€์ž…์ธ Publisher ํƒ€์ž…์€ “๊ณ„์‚ฐ” ์ž์ฒด๋ฅผ ์ถ”์ƒํ™” ํ•œ๋‹ค. ์ด ํƒ€์ž…์€ ๋ฆฌ์•กํ‹ฐ๋ธŒ์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์žฅ์ ์ธ ๋น„๋™๊ธฐ ์—ฐ์‚ฐ์„ ์ œ์–ดํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๊ตฌ๋‚œ๋ฐฉ์œผ๋กœ ์‚ฌ์šฉ๋˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ๋˜๋„๋ก ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์˜ ๋ฉค๋ฒ„๋Š” ๋ชจ๋‘ Non-Publihserํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•œ๋‹ค.

//Bad
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               GetRequestModel(
                   Mono.just(favorites),
                   Mono.just(user)
               )
            }
           .flatMap(favoriteService::getDetails) 

data class GetRequestModel(
     val favorites: Mono<Favorites>,
     val user: Mono<User>
)

//Good
userService.getFavorites(userId)
           .map { 
               val (favorites, user) = it
               GetRequestModel(favorites, user)
           }
           .flatMap(favoriteService::getDetails) 

data class GetRequestModel(
     val favorites: Favorites,
     val user: User
)

5. Publihser์˜ Null ํƒ€์ž…์€ Mono.empty

๋‹ค์‹œ ํ•œ๋ฒˆ ์ด์•ผ๊ธฐ ํ•˜์ง€๋งŒ Publisher ํƒ€์ž…์€ ๋ฏธ๋ž˜์˜ ๊ณ„์‚ฐ์— ๋Œ€ํ•œ ์ถ”์ƒํ™”์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Publihserํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ์žฅ์†Œ์—์„œ Null์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” non-nullible ํƒ€์ž…์„ ์ œ๊ณตํ•ด์„œ ํ•œ๊ฒฐ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ˆ˜์›”ํ•˜์ง€๋งŒ ์ž๋ฐ”๋Š” Mono๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ Null์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ  Mono.empty ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•œ๋‹ค. ๋ฐ˜๋Œ€๋กœ ๊ฐ’์—์„œ ๊ฐ’์œผ๋กœ์˜ ๋งตํ•‘์„ ์ˆ˜ํ–‰ํ•˜๋Š” map์€ null์„ ๋ฐ˜ํ™˜ํ•ด๋„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  Mono.empty ํƒ€์ž…๊ณผ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

// Bad
Mono
   .just("test")
   .flatMap { testFunc(it) }

// Good
Mono
  .just("test")
  .flatMap { 
      testFunc(it) ?: Mono.empty()
   }

private fun testFunc(seed: String): Mono<String>? =
    if (seed  == "test") {
        null
    } else {
        Mono.just("Mono - test")
    }

6. Mono๋‚˜ Flux๊ฐ€ ์ค‘์ฒฉ๋˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฉ”์†Œ๋“œ ์ฐธ์กฐ๋ฅผ ํ™œ์šฉ

1๋ฒˆ์—์„œ ์–ธ๊ธ‰ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์—ฐ์‚ฐ์ž๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋ฉ”์†Œ๋“œ ์ฒด์ธ ํ˜•์‹์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ตœ์„ ์ด๋‚˜ ์–ด์ฉ” ์ˆ˜ ์—†์ด ์—ฐ์‚ฐ์ž๋“ค์„ ์ค‘์ฒฉํ•ด์„œ ์‚ฌ์šฉํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค. ๊ทธ๋Ÿด ๋•Œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. ํŠนํžˆ ๋žŒ๋‹ค์—์„œ ๋ฌต์‹œ์  ํƒ€์ž… “it” ์„ ํ—ˆ์šฉํ•˜๋Š” ์ฝ”ํ‹€๋ฆฐ์—์„œ ๋ชจ๋“  ๋žŒ๋‹ค์˜ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ it์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ€์ˆ˜ ์Šค์ฝ”ํ”„๊ฐ€ ๊ฐ€๋ ค์ ธ์„œ (Variable shadowing) ๊ฐ€๋…์„ฑ์— ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ž๋ฐ”์—์„œ๋„ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๊ฐ€ ์œ ๋ฆฌํ•œ ์ด์œ ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•œ ํด๋ž˜์Šค ๋‚ด์—์„œ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณ€์ˆ˜๋ช…์„ ์ง€์ •ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ ํŠน๋ณ„ํ•œ ์˜๋ฏธ ์—†์ด ๋น„์Šทํ•œ ์ด๋ฆ„์„ ์—ฐ์†ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

// Bad
userService.getUser(userId)
           .map { it.toFavoriteReq() }
           .flatMap { 
               favoriteService
                       .getFavorites(it)
                       .flatMap { 
                          favoriteService.getDetails(it.toDetailRequest) 
                       }

           }

// Good 
userService.getUser(userId)
           .map(User::toFavoriteReq)
           .flatMap { favoriteReq ->
               favoriteService
                       .getFavorites(favoriteReq)
                       .map(Favorite:toDetailRequest)
                       .flatMap(favoriteService::getDetail) 
                          
           }

7. Collection API์™€ ๊ฒน์น˜์ง€ ์•Š๊ฒŒ ์‚ฌ์šฉ

Mono ํƒ€์ž…์˜ ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์—ฐ์‚ฐ์ž์ธ map๊ณผ flatMap, filter ๋“ฑ์€ ์ž๋ฐ”, ์ฝ”ํ‹€๋ฆฐ์˜ ์ปฌ๋ ‰์…˜ API๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค. ๋™์ผํ•œ ๋ฒ”์œ„์—์„œ ์‚ฌ์šฉ๋˜๋ฉด ์†Œ์Šค ํƒ€์ž…์ด Publisher์ธ์ง€ Iterable์ธ์ง€ ๋ฐ”๋กœ ์•Œ๊ธฐ๊ฐ€ ํž˜๋“ค๋‹ค. ๋‘ ๊ฐ€์ง€๊ฐ€ ์„œ๋กœ ๊ฒน์น˜๊ฒŒ ๋˜๋ฉด ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์ž.

// val books: List<Book>
// Bad
Flux.merge(
  books.map { book ->
     if  (book.id == null) {
       Mono.just(card.copy(id = UUID.randomUUID()))
     } else {
       Mono.just(book)
     }
  }
)

//Good
Flux.merge(
  books.collectionMap { book ->
     if  (book.id == null) {
       Mono.just(card.copy(id = UUID.randomUUID()))
     } else {
       Mono.just(book)
     }
  }
)

private fun <T, R> Iterable<T>.collectionMap(transform: (T) -> R): List<R> = this.map(transform)

8. Mon, Flux์˜ ๊ตฌ๋ถ„์ด ํ•„์š”ํ•œ ๊ณณ์€ ๋ณ€์ˆ˜๋ช…์— ๋ช…์‹œํ•˜๊ธฐ

์ผ๋ฐ˜์ ์œผ๋กœ ๋ชจ๋“  Publisherํƒ€์ž…์— mono๋‚˜ flux๋ฅผ ๋ณ€์ˆ˜ ๋ช…์— ๋„ฃ์–ด์ค„ ํ•„์š”๋Š” ์—†๋‹ค. ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋ณ€์ˆ˜๊ฐ€ Publisher์ž„์„ ๋ช…์‹œ์ ์œผ๋กœ ํ•˜๊ณ  ์‹ถ๊ฑฐ๋‚˜ Flux์™€ Mono ์‚ฌ์ด์— ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ๋Š” ๋ณ€์ˆ˜๋ช…์— flux๋‚˜  mono๋ฅผ ํฌํ•จ ์‹œํ‚ค๋„๋ก ํ•œ๋‹ค. ํŠนํžˆ flux ์™€ mono ๊ฐ„์˜ ์—ฐ์‚ฐ์ด ํ•„์š”ํ•  ๋•Œ๋Š” ๋ณ€์ˆ˜๋ช…์„ mono๋‚˜ flux๋ฅผ ๋ถ™์—ฌ์„œ ์„œ๋กœ ๋‹ค๋ฅธ ํƒ€์ž…๊ณผ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” Flux ์™€ Mono๋ฅผ zipํ•˜๋Š”๋ฐ ๊ทธ ๊ฒฐ๊ณผ๋Š” Monoํƒ€์ž…์ด ๋œ๋‹ค.

// Bad
val numberKor = Mono.just("hana")
val numberEng = Flux.just("one", "two")
numberEng
      .zipWith(numberKor)
      .doOnNext { zipped ->
         val (eng, kr) = zipped
         log.info("English:$eng, Korean:$kr")
       }

// Good
val numberKorMono = Mono.just("hana")
val numberEngFlux = Flux.just("one", "two")

numberEngFlux
     .zipWith(numberKorMono)
     .doOnNext { zipped ->
        val (eng, kr) = zipped
        log.info("English:$eng, Korean:$kr")
     }

9. ๋ช…์‹œ์ ์ธ subscribe() ํ˜ธ์ถœ์€ ์‹ ์ค‘ํ•˜๊ฒŒ

๋ฆฌ์•กํ‹ฐ๋ธŒ ์ฒด์ธ ๋‚ด๋ถ€์—์„œ subscibe๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ๋˜๋„๋ก ํ”ผํ•˜์ž. ์ด๋Š” ์—”์ง€๋‹ˆ์–ด๊ฐ€ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋Š” ์‚ฌ์ด ์žฅ๊ธฐ๊ฐ„ ๋Œ์•„๊ฐ€๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค. ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ subscribe๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” Disposable ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

// Bad
userService
         .getUser(req)
         .flatMap(userService::changePassword)
         .doOnNext {
              auditLogger
                      .auditLog(it)
                      .subscribe()
         }

10. ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์—ฐ์‚ฐ์ž๋Š” flatMap

๋ฆฌ์•กํ‹ฐ๋ธŒ์—์„œ ์ด์•ผ๊ธฐ ํ•˜๋Š” ์„ฑ๋Šฅ์˜ ํ–ฅ์ƒ, ์ฆ‰ ๋†’์€ ๋™์‹œ์„ฑ์€ ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„์€ flatMap์„ ์ค‘์‹ฌ์œผ๋กœ ์ด๋ฃจ์–ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์กฐ์ ˆํ•˜๋Š” delayElement๋“ฑ์„ flatMap ์ฃผ๋ณ€์—์„œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์œ ์˜ํ•˜๊ณ  ์ž๋งค ์—ฐ์‚ฐ์ž์ธ flatMapSequential, concatMap ๋“ฑ๊ณผ์˜ ์ฐจ์ด์ ์„ ํ™•์‹คํžˆ ํŒŒ์•…ํ•˜๋„๋ก ํ•˜์ž.

ํด๋ฆฐ (๋ฆฌ์•กํ‹ฐ๋ธŒ) ์ฝ”๋“œ

์ฑ„์‹์„ ์‹œ์ž‘ํ•œ ์ด์œ 

2020๋…„ ์ดˆ๋ถ€ํ„ฐ ์‹๋‹จ์„ ์ฑ„์‹์œผ๋กœ ๋ฐ”๊ฟจ๋Š”๋ฐ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ๋‚˜ ์ด์ œ ์ฑ„์‹ํ•ด ๋ผ๊ณ  ๋งํ–ˆ์„ ๋•Œ ๋ฐ˜๋“œ์‹œ ์™œ ๊ฐ‘์ž๊ธฐ?” ๋ผ๋Š” ์งˆ๋ฌธ์„ ์—ฌ๋Ÿฌ๋ฒˆ ๋ฐ›์•˜๊ธฐ์— ์ดํ›„์— ์กฐ๊ธˆ ๋” ์„ค๋“๋ ฅ์žˆ๊ฒŒ ๋Œ€๋‹ตํ•˜๊ธฐ ์œ„ํ•ด ๊ธ€๋กœ ๋‚จ๊ฒจ๋ณธ๋‹ค.

๊ณ„๊ธฐ๋Š” ๋น„์š˜๋“œ๋ฏธํŠธ

๋น„์š˜๋“œ๋ฏธํŠธ๋Š” 2009๋…„์— Ethan Brown์ด ์ฐฝ์—…ํ•œ ๋Œ€์ฒด์œก ํšŒ์‚ฌ์ด๋‹ค. ์ƒ๋Œ€์ ์œผ๋กœ ํ‰๋‚ด๋‚ด๊ธฐ ์‰ฌ์šด ํ–„๋ฒ„๊ฑฐ ํŒจํ‹ฐ, ์†Œ์‹œ์ง€, ์—ฐ์œก๋“ฑ์„ ๋Œ€ํ˜•๋งˆํŠธ์™€ ์ž์‚ฌ ์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ์„ ํ†ตํ•ด ํŒ๋งคํ•˜๊ณ  ์žˆ๋‹ค. ๋ฌผ๋ก  ํ˜„์žฌ๋Š” ์‹ค์ œ ๊ณ ๊ธฐ๋ณด๋‹ค ๊ฐ€๊ฒฉ๋„ ๋น„์‹ธ๋ฉฐ ๋ง›๋„ ๋–จ์–ด์ง„๋‹ค. ๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  2020๋…„ ์ดˆ์— ์ฒ˜์Œ์œผ๋กœ ๋ง›๋ณธ ๋น„์š˜๋“œ ๋ฏธํŠธ๋Š” ์žŠ๊ณ ์žˆ์—ˆ๋˜ ์ฑ„์‹์— ๋Œ€ํ•œ ์˜์ง€๋ฅผ ๋‹ค์‹œํ•œ๋ฒˆ ์ผ๊นจ์›Œ์คฌ๋‹ค. ํ˜ธ์ฃผ์˜ ์œ ๋ช… ํ–„๋ฒ„๊ฑฐ ์ฒด์ธ์—์„œ ํŒ๋งคํ•˜๋Š” ๋น„์š˜๋“œ ๋ฏธํŠธ๋ฅผ ์‚ฌ์šฉํ•œ ํ–„๋ฒ„๊ฑฐ๋Š” ์‹ค์ œ ํ–„๋ฒ„๊ฑฐ์™€ 80% ์ •๋„ ์œ ์‚ฌํ•˜๊ณ  ๊ฐ€๊ฒฉ๋„ ์ผ๋ฐ˜ ํ–„๋ฒ„๊ฑฐ์™€ ๋™์ผํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ง‘์—์„œ ๋น„์š˜๋“œ ๋ฏธํŠธ๋ฅผ ์กฐ๋ฆฌํ–ˆ์„ ๋•Œ ์‚ฌ๋ฃŒ(?) ๋น„์Šทํ•œ ํ–ฅ๊ธฐ๊ฐ€ ๋‚˜์„œ ์กฐ๊ธˆ ๋ง˜์— ๊ฑธ๋ ธ์ง€๋งŒ ์ผ๋ฐ˜ ์‹๋‹น์—์„œ ์ด๋ ‡๊ฒŒ ๋Œ€์ฒด์œก ์˜ต์…˜์„ ์ œ๊ณตํ•œ๋‹ค๋ฉด ์ฃผ์ €์—†์ด ์„ ํƒํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•˜๋‹ค. ๋ฐ”๋กœ ๊ทธ๋•Œ ์•„ ์ด๋ฒˆ์—๋Š” ์ •๋ง ์ฑ„์‹์ด ๊ฐ€๋Šฅํ•˜๊ฒ ๋‹ค ๋ผ๋Š” ๋А๋‚Œ์ด ์™”๋‹ค.

๊ฐ‘์ž๊ธฐ ์™œ ์ฑ„์‹์„?

๋ฌด์—‡์„ ๋จน๊ณ  ์‚ด ๊ฒƒ์ธ๊ฐ€ ๋ผ๋Š” ์˜๋ฌธ์€ ์กด์žฌ๋ก ์ ์ธ ๊ณ ๋ฏผ๋“ค๊ณผ ๊ต‰์žฅํžˆ ์œ ์‚ฌํ•˜๋‹ค. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ํฌ๊ฒŒ ๊ด˜๋…์น˜ ์•Š๊ณ  ์‚ด์•„๊ฐ€์ง€๋งŒ ํ•œ๋ฒˆ ์˜๋ฌธ์„ ํ’ˆ์œผ๋ฉด ๊ทธ์•ผ๋ง๋กœ ์Šค์Šค๋กœ ์งˆ๋ฌธ๊ณผ ๋‹ต์„ ๋ฐ˜๋ณตํ•˜๊ฒŒ ์ƒํƒœ์— ๋น ์ง€๊ณ ๋งŒ๋‹ค. ์ฒ˜์Œ ์ฑ„์‹์„ ํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๊ฒฐ์ •ํ–ˆ์„ ๋•Œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ด์„ฑ์ ์ด๊ณ  ๊ฐ์„ฑ์ ์ธ ์š”์ธ๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ์—ˆ๋‹ค. ๊ทธ์ค‘์—์„œ๋„ ์‹œ์ž‘์€ ํŠนํžˆ ์Šค์Šค๋กœ ๋™๋ฌผ์„ ์ข‹์•„ํ•œ๋‹ค๊ณ  ๋งํ•˜๋ฉด์„œ ์‚ผ์‹œ์„ธ๋ผ ์†Œ,๋ผ์ง€,๋‹ญ์„ ๋จน๋Š”๊ฒƒ์— ๋Œ€ํ•œ ์ž๊ธฐ๋ฐฐ๋ฐ˜์ ์ธ ๊ฐ์ •์ด์—ˆ๋‹ค.

์˜์–‘ ๋„˜์น˜๋Š” ์ฑ„์‹

์‹œ์ž‘์€ ๊ทธ๋Ÿฌํ–ˆ์ง€๋งŒ ๋‚˜๋„ ์ฒ˜์Œ์—๋Š” ์ฑ„์‹์œผ๋กœ ์„ฑ์ธ ๋‚จ์„ฑ์—๊ฒŒ ์ ์ ˆํ•œ ์˜์–‘์†Œ๋ฅผ ๊ณต๊ธ‰ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ํ•˜๋Š” ์˜๋ฌธ์„ ๊ฐ€์กŒ๋‹ค. ์›น์œผ๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์กฐ์‚ฌ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ๋ช‡๋‹ฌ๊ฐ„ ํ•ด๋ณธ๊ฒฐ๊ณผ ๊ทธ๋Ÿฐ ๊ณ ๋ฏผ์€ ๊ธฐ์šฐ์˜€์Œ์ด ๋ฐํ˜€์กŒ๋‹ค. ์ด์ƒํ•œ ์ถœ์ฒ˜๋ฅผ ์•Œ ์ˆ˜ ์—†๋Š” ์ž๋ฃŒ๋“ค ๋ณด๋‹ค ์˜๊ตญ์˜ ๊ตญ๋ฏผ๊ฑด๊ฐ•๋ณดํ—˜์— ํ•ด๋‹นํ•˜๋Š” NHS์—์„œ ๋‚ด๋†“์€ ์ž๋ฃŒ๋“ค์ด ํ›จ์”ฌ ์‹ ๋ขฐ๊ฐ€ ๊ฐ”๋‹ค. ๋ฌผ๋ก  ์˜๊ตญ๊ณผ ํ•œ๊ตญ๊ฐ„์˜ ์ธ์ข…์ ์ธ ์ฐจ์ด๋Š” ์žˆ๊ฒ ์ง€๋งŒ ํ•œ๊ตญ์—์„œ ์ด๋ค„์ง€๋Š” ๋‹ค์–‘ํ•œ ์—ฐ ์ฑ„์‹์„ ๊ฑด๊ฐ•์‹์œผ๋กœ ๋ฐ›์•„๋“ค์ด๊ธฐ ์‹œ์ž‘ํ•œ ๊ฒƒ ๊ฐ™๋‹ค. ๊ธฐ์‚ฌ โ€œ๊ธˆ์—ฐยท์˜ˆ๋ฐฉ์ ‘์ข…ยท์ฑ„์‹ยท์šด๋™ํ•˜๋ฉด ์•” 70% ์˜ˆ๋ฐฉโ€

๊ณ ๊ธฐ๋ฅผ ๋จน์—ˆ์„ ๋•Œ๋ณด๋‹ค ์„ญ์ทจํ•˜๋Š” ์‹ํ’ˆ์˜ ์˜์–‘์— ์‹ ๊ฒฝ ์“ฐ๊ฒŒ ๋œ ์ ๋„ ์•„์ฃผ ๊ธ์ •์ ์ธ ๋ณ€ํ™”์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด์„œ ํ•œ๊ตญ์˜ ์‹์Šต๊ด€์ด ์ฑ„์‹์— ์œ ๋ฆฌํ•œ์ ๋„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๊ทธ ์˜ˆ๋กœ ๋น„ํƒ€๋ฏผ B12๋Š” ์ฑ„์‹์„ ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฒฐํ•์„ ์ฃผ์˜ํ•ด์•ผํ•˜๋Š” ์˜์–‘์†Œ๋กœ ์ž์ฃผ ์–ธ๊ธ‰๋˜๋Š”๋ฐ ํ•œ๊ตญ์ธ๋“ค์ด ์ž์ฃผ๋จน๋Š” ๊น€์— ๋ฐ”๋กœ ๊ทธ B12๊ฐ€ ํ’๋ถ€ํ•˜๊ฒŒ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์•„์ง 1๋…„์ด ๋˜์ง€ ์•Š์•˜๊ณ  ๊ฑด๊ฐ•๊ฒ€์ง„์„ ๋ฐ›์ง€ ์•Š์€ ์ƒํƒœ๋ผ ์ฑ„์‹์ด ์–ด๋–ป๊ฒŒ ๋‚ด ๋ชธ์— ์˜ํ–ฅ์„ ๋ผ์ณค๋Š”์ง€ ์ˆ˜์น˜ํ™” ํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ ๋А๋ผ๊ธฐ์—” ์•„์ฃผ ๊ฑด๊ฐ•ํ•ด์ง„ ๊ฒƒ ๊ฐ™๋‹ค. ์ฑ„์‹ ์ด์ „์—๋„ ํŠน๋ณ„ํžˆ ์–ด๋””๊ฐ€ ์•„ํ”„๊ฑฐ๋‚˜ ํ•œ๊ฒƒ์€ ์•„๋‹ˆ์—ˆ์ง€๋งŒ ๋ˆˆ์— ๋„๋Š” ๋ณ€ํ™”์ค‘ ํ•˜๋‚˜๋Š” ์ฑ„์‹ํ›„์— ๋ฐฐ๊ฐ€ ์•„ํŒ ๋˜ ์ ์ด ํ•œ๋ฒˆ๋„ ์—†์—ˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ์ „์— ๊ณ ๊ธฐ๋ฅผ ๋จน๊ณ  ํŠนํžˆ ๋ฐค์— ์†Œํ™”๊ฐ€ ์ž˜ ์•ˆ๋˜๊ฑฐ๋‚˜ ์•„๋žซ๋ฐฐ๊ฐ€ ์•„ํŒŒ์„œ ๋ฐค์— ์ž๋‹ค๊ฐ€ ๊นจ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์—ˆ๋Š”๋ฐ ์ •๋ง ํ™”์žฅ์‹ค์—์„œ ๊ธฐ์ ˆ ์ง์ „๊นŒ์ง€ ๊ฐ„ ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค. ์ฑ„์‹์„ ํ•˜๊ณ ๋Š” ๋ชธ์•ˆ์ด ๊ต‰์žฅํžˆ ๊นจ๋—ํ•ด์ง„ ๋А๋‚Œ์ธ๋ฐ ๋ชธ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ƒ‰์žฅ๊ณ ์™€ ์“ฐ๋ ˆ๊ธฐํ†ต๊นŒ์ง€ ๊น”๋”ํ•ด์ง„ ๊ฒƒ์€ ๋ค์ด๋‹ค.

๊ธฐํ›„๋ณ€ํ™” ๋Œ€์‘

๊ฐœ์ธ์ ์ธ ๋ฒ”์œ„์˜ ๋ณ€ํ™”๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ง€๊ตฌ๋ฅผ ์œ„ํ•ด์„œ๋„ ์ฑ„์‹์€ ๊ถŒ์žฅํ•  ๋งŒํ•˜๋‹ค. ๋‚ด๊ฐ€ ์‚ด์•„์˜ค๋ฉด์„œ ๋А๋‚€ ํ™˜๊ฒฝ์šด๋™์ด ๊ฐ€์ง€๋Š” ๋ฌธ์ œ์ ์ค‘ ํ•˜๋‚˜๋Š” ๊ฐœ์ธ์ด ๋ฌด์—‡์„ ํ•ด์•ผํ• ์ง€ ๋šœ๋ ทํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ณ  ์ •์ฑ…์˜ ๋ณ€ํ™”๋‚˜ ์˜์‹์˜ ์ „ํ™˜๋งŒ์„ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ์—์„œ ๊ทธ์นœ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋ฐ˜๋ฉด ์ฑ„์‹์€ ๋ฐ”๋กœ ์‹ค์ฒœ ๊ฐ€๋Šฅํ•˜๋‹ค. ์ „์„ธ๊ณ„ ์ธ๊ตฌ๊ฐ€ ๊ธฐํ›„๋ณ€ํ™”์˜ ์ฃผ์ฒด์ž„์„ ๊นจ๋‹ฌ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ฑ„์‹์œผ๋กœ ์ „ํ™˜ํ•˜๊ฑฐ๋‚˜ ์„ญ์ทจํ•˜๋Š” ์œก๋ฅ˜๋ฅผ ์กฐ๊ธˆ์ด๋ผ๋„ ์ค„์ด๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๊ธฐํ›„๋ณ€ํ™”๋ฅผ ๋ง‰๋Š”๋ฐ ํž˜์„ ๋ณดํƒค ์ˆ˜ ์žˆ๋‹ค. ๊ฐ€๋” ์ถ•์‚ฐ์ด ์˜จ์‹คํšจ๊ณผ์— ๋ผ์น˜๋Š” ์˜ํ–ฅ์ด ํ™•์‹คํžˆ ์•Š๋‹ค๊ณ  ์ฑ„์‹์˜ ๊ธฐํ›„๋ณ€ํ™” ๋Œ€์‘ํšจ๊ณผ๋ฅผ ์ถ•์†Œํ•˜๋ ค๋Š” ์‚ฌ๋žŒ๋“ค์„ ์ž์ฃผ ๋ณด์•˜๋‹ค. ๋†์ถ•์‚ฐ์ด ์˜จ์‹คํšจ๊ณผ์— ๋ผ์น˜๋Š” ์˜ํ–ฅ์€ 15%์—์„œ 50% ๊นŒ์ง€ ๋‹ค์–‘ํ•œ ์—ฐ๊ตฌ ๊ฒฐ๊ณผ๊ฐ€ ๋ณด๊ณ  ๋˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์‚ฐํ™”ํƒ„์†Œ, ๋ฉ”ํƒ„๋“ฑ ๊ฐ์ข… ์š”์ธ๋“ค์ด ์„œ๋กœ ์˜ํ–ฅ์„ ๋ผ์น˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์•ž์œผ๋กœ๋„ ์ •ํ™•ํ•œ ์ˆซ์ž๋ฅผ ์•Œ์•„ ๋‚ด๋Š” ๊ฒƒ์€ ํž˜๋“ค ๊ฒƒ์ด๋‹ค. ์„ค๋ น ๋†์ถ•์‚ฐ์ด ์˜จ์‹คํšจ๊ณผ์— 10%๋งŒ ๊ธฐ์—ฌ ํ•œ๋‹ค๊ณ  ํ•ด์„œ ๋‹น์žฅ ์ „๊ธฐ์ฐจ๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ๊ฒƒ์ด ์ฑ„์‹๋ณด๋‹ค ๋” ๊ธฐํ›„๋ณ€ํ™”์— ์•ž์žฅ์„œ๋Š” ๋Œ€์‘์ผ ์ˆ˜๋Š” ์—†๋‹ค. ๋ชจ๋“  ๋ถ€๋ถ„์—์„œ ๊ฑธ์ณ์„œ ๋…ธ๋ ฅ์„ ํ•ด์•ผํ•˜์ง€๋งŒ ํ™”์„์—ฐ๋ฃŒ ์‚ฌ์šฉ์„ ์ค„์ด๋Š” ๋ถ€๋ถ„์€ ๊ฐ ๋‚˜๋ผ์˜ ์ •์ฑ… ๋ฐฉํ–ฅ์— ํฌ๊ฒŒ ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ์‚ฌ์‹ค์ด๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด ์ „๊ธฐ์ฐจ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์ „๊ธฐ๊ฐ€ ์„ํƒ„์œผ๋กœ ์ƒ์„ฑ๋œ ๊ฒƒ์ด๋ผ๋ฉด ๊ฒฐ๊ตญ ์ œ์ž๋ฆฌ์ด๋‹ค. ๊ฒฐ๊ตญ ์ฑ„์‹์€ ๊ฐœ์ธ์ด ์˜ค๋Š˜๋‹น์žฅ ์‹ค์ฒœํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์†์‰ฌ์šด ๊ธฐ๋ถ€๋ณ€ํ™” ๋Œ€์‘ ํ–‰๋™์ด๋‹ค. ๊ธฐ์‚ฌ – ๊ธฐํ›„์œ„๊ธฐ ์‹œ๋Œ€, ์ฑ„์‹์ด ์ง€๊ตฌ๋ฅผ ์‚ด๋ฆฐ๋‹ค

์ฑ„์‹, ์‰ฝ์ง€๋Š” ์•Š๋‹ค

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

ํšŒ์‹์€ ์‚ผ๊ฒน์‚ด์•„๋‹ˆ๋ฉด ์น˜ํ‚จ..์ฉ์ฉ

ํ•œ๊ตญํšŒ์‚ฌ์—์„œ ์ผ ๋งˆ์น˜๊ณ  ๋จน์—ˆ๋˜ ์‚ฝ๊ฒน์‚ด๊ณผ ์น˜ํ‚จ. ๊ณผ์—ฐ ๋‹ค์Œ์— ๋˜‘๊ฐ™์€ ๊ธฐํšŒ๊ฐ€ ์ƒ๊ฒจ๋„ ์ €ํ•ญํ•  ์ˆ˜ ์žˆ์„๊นŒ?

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

๋ฐ˜๋ฉด ํ•œ๊ตญ์—์„œ๋Š” ํ•™๊ต, ํšŒ์‚ฌ, ๊ตฐ๋Œ€๋“ฑ ๋‹ค์–‘ํ•œ ์žฅ์†Œ์—์„œ ๊ธ‰์‹์„ ํ•˜๋Š”๋ฐ ์ฑ„์‹์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€ ๊ฐœ์ธ์—๊ฒŒ ํฐ ์‹œ๋ จ์ด ๋  ์ˆ˜๋„ ์žˆ๋‹ค. ๋‹คํ–‰ํžˆ๋„ ํ•œ๊ตญ๋„ ์ง‘๋‹จ ๊ธ‰์‹์„ ํ•˜๋Š” ํ•™๊ต๋‚˜ ๊ตฐ๋Œ€์—์„œ ์ฑ„์‹์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ”๋€Œ๊ณ  ์žˆ๋Š”๋ฐ ์ด๋Š” ์ ์  ํ•œ๊ตญ๋„ ๊ฐœ์ธ์˜ ๊ฐœ์„ฑ๊ณผ ๋‹ค์–‘์„ฑ์„ ์กด์ค‘ํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์˜ฎ๊ฒจ๊ฐ€๊ณ  ์žˆ๋‹ค๋Š” ์ฆ๊ฑฐ๋กœ ๋ณด์—ฌ์ง„๋‹ค.

์„œ์šธ ํ•™๊ต๊ธ‰์‹์— ‘์ฑ„์‹์„ ํƒ๊ถŒ’ ๋„์ž…โ€ฆ”์ดˆ์ค‘๊ณ  ์ ์ฐจ ํ™•๋Œ€”

[๋‹จ๋…]๊ตฐ๋Œ€์„œ๋„ ๋น„๊ฑด ๊ธ‰์‹ ๋จน๋Š”๋‹คโ€ฆ ์ฑ„์‹์ฃผ์˜์ž, ์งฌ๋ฐฅ์„ ๋ฐ”๊พธ๋‹ค

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

์ฑ„์‹์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„ 

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

ํ•œ๊ตญ๋„ ๋‚จ์˜์ผ์ด ์•„๋‹ˆ๋‹ค

์ฑ„์‹์— ๋Œ€ํ•œ ๊ด€์‹ฌ์ด ๋†’์€ ๋ฏธ๊ตญ์ด๋‚˜ ํ˜ธ์ฃผ๋Š” ์‹๋Ÿ‰ ์ž๊ธ‰๋ฅ ์ด ๊ต‰์žฅํžˆ ๋†’์€ ํŽธ์ธ๋ฐ ํ•œ๊ตญ์€ ์ž๊ธ‰๋ฅ ์ด 50%๊ฐ€ ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฐธ๊ณ  – [๋ฐ์Šคํฌ์˜๋ˆˆ] ์ฝ”๋กœ๋‚˜็™ผ ์‹๋Ÿ‰ ์œ„๊ธฐ๋ก  ๋‹ค๋ฅธ๋ง๋กœ ํ•œ๊ตญ์€ ์„ธ๊ณ„ ์‹๋Ÿ‰ ์‚ฌ์ •์— ๋”ฐ๋ผ ๋น„์ž๋ฐœ ์ ์œผ๋กœ ์‹์Šต๊ด€์„ ๊ธ‰๊ฒฉํ•˜๊ฒŒ ๋ฐ”๊ฟ”์•ผ ํ• ์ˆ˜๋„ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๊ฒŒ ์ฑ„์‹์ด ๋ ์ง€๋Š” ์•„๋ฌด๋„ ๋ชจ๋ฅด์ง€๋งŒ ์ ์–ด๋„ ์œก์‹๋ณด๋‹ค๋Š” ์ž๊ธ‰๋ฅ ์„ ๋†’์ผ ์ˆ˜ ์žˆ๋Š” ์ฑ„์‹ ๋ฐ”๋žŒ์— ๋Œ€๋น„ํ•ด๋†“์•„์•ผ ํ•˜์ง€ ์•Š์„๊นŒ. ํ•œ๊ตญ์—์„œ๋„ ์ฑ„์‹ํ•˜๋Š” ์ธ๊ตฌ๊ฐ€ ๋งŽ์ด ๋Š˜์–ด์‚ฌ์„œ ๋‹ค์–‘ํ•œ ์Œ์‹๋“ค์ด ์ƒˆ๋กœ ๊ฐœ๋ฐœ ๋˜๊ณ  ์‹๋‹น์—์„œ๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฑ„์‹ ์˜ต์…˜์ด ์ œ๊ณต๋  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ด๋ณธ๋‹ค.

Aside

์•„์น˜์œ ๋‹›(ArchUnit) ํ…Œ์ŠคํŠธ

์•„์น˜์œ ๋‹›(ArchUnit)์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

“๋‘ ํŒจํ‚ค์ง€ ์‚ฌ์ด์— ์ˆœํ™˜ ์ฐธ์กฐ(Circular Dependency)๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•ด์š”.”

“@SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” test ํด๋”๊ฐ€ ์•„๋‹ˆ๋ผ integration-test ํด๋”์— ์œ„์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.”

“Service ๋ ˆ์ด์–ด๋Š” Controller์™€ Model ํŒจํ‚ค์ง€ ์—์„œ๋งŒ ์ ‘๊ทผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค”

ํŒ€๋‚ด ๊ฒฝ๋ ฅ์ด ์˜ค๋ž˜๋œ ์‹œ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž๋Š” ์œ„์™€ ๊ฐ™์€ ์ปค๋ฉ˜ํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด์„œ ์™œ ๊ทธ๋ž˜์•ผ ํ•˜๋Š”์ง€ ์ฝ”๋“œ๋‚˜ ํŒจํ‚ค์ง€ ๋‹จ์œ„๋กœ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ ค์„œ ์–ด๋–ป๊ฒŒ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์„œ๋กœ ์ž‘๋™์„ ํ•ด์•ผํ•˜๋Š”์ง€ ์„ค๋ช…ํ•œ ๊ฒฝํ—˜์ด ์žˆ์ง€ ์•Š์€๊ฐ€?

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

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

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

์•„์น˜์œ ๋‹› ๊ตฌ์„ฑ

์•„์น˜์œ ๋‹›์€ Core ๋ ˆ์ด์–ด, Lang ๋ ˆ์ด์–ด, Library ๋ ˆ์ด์–ด๊ฐ€ ์กด์žฌํ•œ๋‹ค. Core API๋ฅผ ํ†ตํ•ด ๋Œ€์ƒ์„ ํŠน์ •ํ•˜๊ณ  Lang API๋ฅผ ์‚ฌ์šฉํ•ด ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค. Library ๋ ˆ์ด์–ด๋Š” ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ทœ์น™๋“ค, ์˜ˆ๋ฅผ๋“ค์–ด 3 Tier ์•„ํ‚คํ…์ฒ˜, 6๊ฐํ˜• ์•„ํ‚คํ…์ฒ˜(Hexagonal Architecture) ๋“ฑ์„ ์œ„ํ•œ ๊ทœ์น™๋“ค์„ ์ œ๊ณตํ•œ๋‹ค. ์•„์ง์€ ์‹คํ—˜์ ์ธ ์ƒํƒœ๋กœ ๋ณด์ด๋ฉฐ ์ถ”ํ›„ ํ™•์žฅ์˜ ์—ฌ์ง€๊ฐ€ ์žˆ๋Š” ๋ถ€๋ถ„์ด๋‹ค.

Core ๋ ˆ์ด์–ด์˜ ClassImporter

๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” Core๋ ˆ์ด์–ด์—์„œ๋Š” ClassImporter๊ฐ€ ๊ฐ€์žฅ ์ค‘์š”ํ•œ API๋“ค์„ ์ œ๊ณตํ•œ๋‹ค. ClassImporters๋Š” ์ปดํŒŒ์ผ๋œ ํด๋ž˜์Šค๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋Š” com.book ํŒจํ‚ค์ง€๋‚ด์˜ ํด๋ž˜์Šค์ค‘์— Jar๋‚˜ ํ…Œ์ŠคํŠธ ํด๋”๋“ฑ์„ ๋นผ๋†“๊ณ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ๋งŒ ๋Œ€์ƒ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ์„ค์ •์ด๋‹ค.

ClassFileImporter()
        .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_ARCHIVES)
      .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS)
        .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
        .importPackages("com.book")

Lang ๋ ˆ์ด์–ด๋Š” ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™์„ ์ •์˜ํ•˜๋Š” API๋“ค์„ ์ œ๊ณตํ•œ๋‹ค. ๊ทœ์น™์„ ์ •ํ•˜๊ณ  ๋‚˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹ค์ œ ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

JavaClasses importedClasses = new ClassFileImporter().importPackage("com.myapp");
ArchRule rule = // ์•„๋ž˜ ์˜ˆ์™€ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ๋ฃฐ ์ƒ์„ฑ
rule.check(importedClasses);

ํŒจํ‚ค์ง€ ์˜์กด์„ฑ ํ™•์ธ

service ํŒจํ‚ค์ง€๋Š” controller์™€ resource์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.

classes()
   .that()
   .resideInAPackage("..service..")
   .should()
   .onlyHaveDependentClassesThat()
   .resideInAnyPackage("..controller..", "..resource..")

ํด๋ž˜์Šค ์˜์กด์„ฑ ํ™•์ธ

*Service ํด๋ž˜์Šค๋Š” Controller ํด๋ž˜์Šค ์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋‹ค.

classes()
  .that()
  .haveNameMatching(".*Service")
  .should()
  .onlyBeAccessed()
  .byClassesThat()
  .haveSimpleName("Controller")

ํด๋ž˜์Šค์™€ ํŒจํ‚ค์ง€ ๊ด€๊ณ„ ํ™•์ธ

Book์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํด๋ž˜์Šค๋Š” com.book ํŒจํ‚ค์ง€์— ์œ„์น˜ํ•ด์•ผ ํ•œ๋‹ค

classes()
  .that()
  .haveSimpleNameStartingWith("Book")
  .should()
  .resideInAPackage("com.book")

์ƒ์† ๊ด€๊ณ„ ํ™•์ธ

Connection ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๋Š” ์ด๋ฆ„์ด Connectiond์œผ๋กœ ๋๋‚˜์•ผํ•œ๋‹ค.

classes()
  .that()
  .implement(Connection.class)
  .should()
  .haveSimpleNameEndingWith("Connection")

EntityMangerํด๋ž˜์Šค๋กœ ํ• ๋‹น ๊ฐ€๋Šฅํ•œ ํด๋ž˜์Šค๋“ค์€ persistence ํŒจํ‚ค์ง€์— ์œ„์น˜ํ•ด์•ผ ํ•œ๋‹ค.

classes()
  .that()
  .areAssignableTo(EntityManager.class)   
  .should()
  .onlyBeAccessed()
  .byAnyPackage("..persistence..")

์ฃผ์„ ํ…Œ์ŠคํŠธ

com.book ํŒจํ‚ค์ง€ ์ค‘์—์„œ๋„ “build/classes/kotlin/test” ํด๋”์— ์œ„์น˜ํ•œ ํ…Œ์ŠคํŠธ๋“ค์€ SpringBootTest๋ฅผ ์‚ฌ์šฉํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค.

 classes()
    .that()
    .resideInAPackage("com.book")
    .should()
    .notBeAnnotatedWith(SpringBootTest::class.java)
    .check(ClassFileImporter()
         .importPath("build/classes/kotlin/test"))

๋ ˆ์ด์–ด ํ…Œ์ŠคํŠธ

๋…ผ๋ฆฌ์ ์ธ ๋ ˆ์ด์–ด๋ฅผ ๊ตฌ์„ฑํ•ด์„œ ๊ทธ ๊ด€๊ณ„๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. ํŒจํ‚ค์ง€๋กœ ๊ตฌ๋ถ„๋œ controller, service, persistence ๋ ˆ์ด์–ด๋ฅผ ๊ฐ๊ฐ ์ •์˜ํ•˜๊ณ  ๊ฐ ๋ ˆ์ด์–ด ๋ณ„๋กœ ์ ‘๊ทผ๊ฐ€๋Šฅํ•œ ๋ ˆ์ด์–ด๋“ค์„ ์ •์˜ํ•œ๋‹ค.

layeredArchitecture()
    .layer("Controller").definedBy("..controller..")
    .layer("Service").definedBy("..service..")
    .layer("Persistence").definedBy("..persistence..")
    .whereLayer("Controller")
    .mayNotBeAccessedByAnyLayer()
    .whereLayer("Service")
    .mayOnlyBeAccessedByLayers("Controller")
    .whereLayer("Persistence")
    .mayOnlyBeAccessedByLayers("Service")

์ˆœํ™˜ ์ฐธ์กฐ ํ…Œ์ŠคํŠธ

ํŒจํ‚ค์ง€ com.book ์˜ ํ•˜์œ„ ํŒจํ‚ค์ง€๋“ค์„ slice๋กœ ๊ตฌ์„ฑํ•ด์„œ ๊ฐ slice๋“ค์ด ์ˆœํ™˜ ์ฐธ์กฐ ํ•˜์ง€ ์•Š๋Š” ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค.

slices()
 .matching("com.book.(**)")
 .should().beFreeOfCycles()
 .check(javaClasses)

Aside

์œ ์šฉํ•œ ์• ์ž์ผ ์˜์‹๋“ค (Agile Rituals)

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

์• ์ž์ผ ์˜์‹์œผ๋กœ ์–ป๋Š” ๊ฒƒ

์ •ํ™•ํ•œ ์˜์‚ฌ์ „๋‹ฌ

์ด๋ ‡๊ฒŒ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๋„๊ตฌ๋“ค์„ ์‚ฌ์šฉํ•  ๋•Œ ์–ป๋Š” ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ ๊ตฌ์„ฑ์›๊ฐ„ ์˜๋ฏธ์ „๋‹ฌ์ด ์•„์ฃผ ๋ช…ํ™•ํ•ด ์ง„๋‹ค๋Š” ์ ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด “์ด๋ฒˆ์ฃผ ๊ธˆ์š”์ผ์— ํšŒ์˜ ์žˆ์–ด์š””. “์˜ค๋Š˜์€ ์ผํ•˜๋Š” ๋‚ ์ด์—์š”” ๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค “์ด๋ฒˆ์ฃผ ๊ธˆ์š”์ผ์— ์ŠคํŒŒ๋ง ์žˆ์Šต๋‹ˆ๋‹ค”, “์˜ค๋Š˜์€ GSD๋‚ ์ด์—์š””. ๋ผ๊ณ  ๋งํ•˜๋Š”๊ฒŒ ์„ธ์„ธํ•œ ์ฐจ์ด๊นŒ์ง€ ์ „๋ถ€ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์‚ฐ์„ฑ ์ฆ๊ฐ€

์„ ์ง„๊ตญ์„ ์ค‘์‹ฌ์œผ๋กœ ๊ทผ๋กœ์ž์˜ ์›” ํ‰๊ท  ๊ทผ๋ฌด์‹œ๊ฐ„์€ ๊ณ„์† ์ค„์–ด๋“ค๊ณ  ์žˆ๋‹ค. ์–ด๋–ป๊ฒŒ ์ ๊ฒŒ ์ผํ•˜๋ฉด์„œ ๋” ๋†’์€ ์ƒ์‚ฐ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ํšŒ์‚ฌ ์ „์ฒด์˜ ์ƒ์‚ฐ์„ฑ์„ ์œ„ํ•ด์„œ๋Š” ์œ„ํ•ด์„  ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ๋ชจ์ด๋Š” ํšŒ์˜๋‚˜ ์˜์‚ฌ๊ฒฐ์ •์˜ ์ƒ์‚ฐ์„ฑ์ด ๋ฌด์—‡๋ณด๋‹ค ์ค‘์š”ํ•˜๋‹ค. ํŒ€์›์ด 5๋ช…์ธ ๊ฒฝ์šฐ 1์‹œ๊ฐ„ ๊ฑธ๋ฆด ์˜์‚ฌ ๊ฒฐ์ •์„ 3์‹œ๊ฐ„ ๊ฑธ๋ ค์„œ ๋๋‚ผ ๊ฒฝ์šฐ 10์‹œ๊ฐ„์˜ ์ถ”๊ฐ€ ๊ทผ๋ฌด๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๊ฒฐ๊ณผ๋ฌผ ๋ช…ํ™•ํ™”

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

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

์•„ํ‹€๋ผ์‹œ์•ˆ์˜ ํŒ€ํ”Œ๋ ˆ์ด ๋ถ์€ ์•„ํ‹€๋ผ์‹œ์•ˆ ๋‚ด๋ถ€์—์„œ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ์—ฌ๋Ÿฌ ์˜์‹๋“ค์„ ํ”Œ๋ ˆ์ด(Play) ํ˜•ํƒœ๋กœ ์ œ๊ณตํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ”Œ๋ ˆ์ด ๋ถ์˜ ์˜์‹๋“ค์„ ์œ„์ฃผ๋กœ ์–ด๋–ค ์ƒํ™ฉ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„ ๋ณธ๋‹ค. (์œ„ํ‚ค์‚ฝ์ž…)

[insert page=’agile-ritual’ display=’all’]

Aside

Renovate๋กœ ์˜์กด์„ฑ ๊ด€๋ฆฌ

MSA์—์„œ ์˜์กด์„ฑ ๊ด€๋ฆฌ

MSA์—์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—…๋ฐ์ดํŠธ๋Š” ๊ผญ ํ•ด์•ผ ํ•˜์ง€๋งŒ ์žŠ๊ธฐ ์‰ฌ์šด ํŠน์„ฑ์„ ๊ฐ€์ง„๋‹ค. ์ด๋Š” ์†์”ป๊ธฐ๋‚˜ ์–‘์น˜์งˆ๋“ฑ๊ณผ ๋‹ฎ์•„ ์žˆ๋‹ค. ์—ด์‹ฌํžˆ ํ•ด๋„ ํ‹ฐ๊ฐ€ ์•ˆ๋‚œ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์ „๊นŒ์ง„!

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์˜ ๋ฐฑ๋ฏธ๋Š” ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ์ ์€ ๋ฆฌ์†Œ์Šค๋กœ ๋งŽ์€ ์‚ฌ์šฉ์ž๋“ค์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ ์„œ๋น„์Šค์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ ์žˆ๋‹ค. Renovate๋Š” ๋ฒ„์ „์—… ํ”„๋กœ์„ธ์Šค๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๋งŒํผ ์ž๋™ํ™” ์‹œ์ผœ์ค€๋‹ค.

์ฃผ๊ธฐ์ ์ธ ๋ฒ„์ „์—…์€ ์ž ์žฌ์ ์ธ ๊ธฐ์ˆ ๋ถ€์ฑ„๋ฅผ ์ค„์ด๊ณ  ์„œ๋น„์Šค๋ฅผ ๋”์šฑ ์•ˆ์ •์ ์œผ๋กœ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ์ตœ๊ทผ์—๋Š” ๋ณด์•ˆ ๊ด€๋ จ ํŒจ์น˜๊ฐ€ ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ํ†ตํ•ด ์ž์ฃผ ์ผ์–ด๋‚œ๋‹ค.

๋•Œ๋ฌธ์— ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ์ˆ  ๋ถ€์ฑ„๋ฅผ ์กฐ๊ธˆ์”ฉ ์ €์ถ•ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค. ์ง€๊ธˆ ํŒ€์—์„œ๋Š” ๋งค ๋นŒ๋“œ์‹œ๋งˆ๋‹ค SourceClear ๋ฅผ ์‚ฌ์šฉํ•ด ์†Œ์Šค์ฝ”๋“œ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ทจ์•ฝ์ ์„ ๋ถ„์„ํ•œ๋‹ค.

์ž‘์„ฑํ•œ ์ฝ”๋“œ์—์„œ ์ทจ์•ฝ์ ์ด ๋ฐœ๊ฒฌ ๋˜๋Š” ๊ฒฝ์šฐ ์ง์ ‘ ์ˆ˜์ •์„ ํ•˜๋ฉด ๋˜์ง€๋งŒ Spring์ด๋‚˜ Jackson๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ์ค‘์ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ์ด ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๋กœ ํ•„์š”ํ•˜๋‹ค. ๋ฌธ์ œ๋Š” ์ด ์ทจ์•ฝ์ ์ด ๊ฝค๋‚˜ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ๊ฒฌ๋œ๋‹ค๋Š” ์ ์ด๋‹ค. SourceClear์™€ ์—ฐ๋™ํ•˜๊ณ  ๋‚˜์„œ ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ํŒ€๋‚ด์˜ ์žก์ผ์ฒ˜๋Ÿผ ๋˜์–ด๋ฒ„๋ ธ๋‹ค.

๊ทธ์™€์ค‘์— Renovate๋Š” ๊ฐ€๋ญ„์— ๋‹จ๋น„๊ฐ™์€ ์กด์žฌ, ์‚ฌ์šฉ์ž ์„ค์ •ํ•œ ๋‚ด์šฉ๋Œ€๋กœ ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์œ„ํ•œ ํ’€๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. ์ž๋™์œผ๋กœ master๋จธ์ง€๋„ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์‹œ๊ฐ„๋‹น ์ƒ์„ฑํ•˜๋Š” ํ’€๋ฆฌํ€˜์ŠคํŠธ์˜ ์ˆ˜ ๋“ฑ ์•„์ฃผ ๋‹ค์–‘ํ•œ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

ํŒ€์—์„œ๋Š” kotlin + spring + gradle + docker ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์™ธ์—๋„ ์•„์ฃผ ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ง€์›ํ•œ๋‹ค. ์•„๋ž˜ ์Šคํฌ๋ฆฐ์ƒท์—์„œ๋„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ์ด change log๊นŒ์ง€ ์ „๋ถ€ ์ฒจ๋ถ€ํ•ด์ค€๋‹ค. ์ด๊ฑด ๊ฐœ๋ฐœ์ž๊ฐ€ PR์„ ์ƒ์„ฑํ• ๋–„ ์ž˜ ์•ˆํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์ธ๋ฐ, Renovate๋Š” ์„ธ์‹ฌํ•˜๋‹ค.

Github์—์„œ๋Š” App์œผ๋กœ ์ œ๊ณต๋˜๊ณ  ์žˆ์œผ๋ฉฐ Gitlab์ด๋‚˜ ๊ธฐํƒ€ ์„ค์น˜ํ˜• ์ €์žฅ์†Œ์—์„œ๋Š” Renovate Bot์„ ์ง์ ‘ ํ˜ธ์ŠคํŒ… ํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค. ์•„๋ž˜๋Š” ํŒ€์˜ ์ผ๋ถ€ ์ €์žฅ์†Œ์—์„œ ์‚ฌ์šฉ์ค‘์ธ renovate.json ์„ค์ • ํŒŒ์ผ์ด๋‹ค. ์›ํ•˜๋Š” ๋งŒํผ ์„ธ์„ธํ•˜๊ฒŒ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. https://docs.renovatebot.com/self-hosted-configuration/

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "schedule": [
    "after 10am and before 5pm every weekday"
  ],
  "timezone": "Australia/Sydney",
  "prConcurrentLimit": 20,
  "prHourlyLimit": 1,
  "automerge": true,
  "dockerfile": {
    "enabled": true
  },
  "maven": {
    "enabled": true
  },
  "terraform": {
    "enabled": true
  },
  "extends": [
    "config:base"
  ]
}

Bitbucket์—์„œ ํ˜ผ์ž์„œ ์—ด์‹ฌํžˆ ๋ฒ„์ „ ์—…์ค‘์ธ Renovate
Pull Request ํ…œํ”Œ๋ฆฟ

๋ฌธ์ œ์ 

ํ•˜์ง€๋งŒ ์†Œํ”„ํŠธ์›จ์–ด ๋ฌธ์ œ๊ฐ€ ์–ธ์ œ๋‚˜ ๊ทธ๋ ‡๋“ฏ์ด, ํ•ญ์ƒ ์ข‹๊ธฐ๋งŒ ํ•œ ๊ฑด ์—†๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์•„๋„ ๋ช‡์ผ์ „์— ๊ด€๋ จ๋œ ์žฅ์• ๊ฐ€ ํ•œ ๊ฑด ์žˆ์—ˆ๋‹ค. reactor-netty ์—์„œ ์ค‘๋Œ€ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ๊ฒฌ๋˜์„œ ๋ถ€๋žด๋ถ€๋žด ์ „๋ถ€ ๋กค๋ฐฑํ•ด์•ผํ–ˆ๋‹ค.

์‚ฌ์‹ค Renovate์˜ ๋ฌธ์ œ๋ผ๊ธฐ ๋ณด๋‹ค ์‹œ์Šคํ…œ์ ์œผ๋กœ ๋ฒ„์ „์—…์‹œ์˜ ์ถฉ๊ฒฉ์„ ํก์ˆ˜ ํ•  ์ˆ˜ ์žˆ๋Š” ์ฟ ์…˜์ด๋‚˜ ๋ฒ„ํผ๊ฐ€ ์กด์žฌํ–ˆ์–ด์•ผ ํ•œ๋‹ค. ๋ฐฐํฌ๊ฐ€ ์žฆ์ง€ ์•Š๋‹ค๋ฉด ์Šคํ…Œ์ด์ง• ์„œ๋ฒ„๊ฐ€ ์ด๋Ÿฐ ์—ญํ• ์„ ํ•  ์ˆ˜ ๋„ ์žˆ๊ฒ ์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์ผ์–ด๋‚œ ์„œ๋น„์Šค๋Š” ๋ฐฐํฌ๊ฐ€ ์•„์ฃผ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜์„œ ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ๋ฌธ์ œ์ธ์ง€ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋จธ์ง€ํ•œ PR์ด ๋ฌธ์ œ์ธ์ง€ ํŒ๋‹จํ•˜๊ธฐ ํž˜๋“ค์—ˆ๋‹ค. 

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๋ชจ๋“  ์Šคํ”„๋ง๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ์˜ ๋ฒ ์ด์Šค๊ฐ€ ๋˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ  ๋งŒ๋“ค๊ณ  ํ•ด๋‹น ํ”„๋กœ์ ํŠธ๋Š” ์ž๋™์ ์œผ๋กœ ์˜์กด์„ฑ์„ ๊ฐฑ์‹ ํ•˜๊ณ  ๋ฐฐํฌ๋˜๋„๋ก ํ–ˆ๋‹ค. 

์ด๊ธ€์„ ์ฝ๋Š” ๋ถ„๋“ค๋„ ์ €์žฅ์†Œ์— Renovate๋ฅผ ์ ์šฉํ•ด ๋ณด๊ณ  ์–ด๋А์ •๋„ ์ž๋™ํ™”๊ฐ€ ๊ฐ€๋Šฅํ• ์ง€ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ธธ ๊ถŒํ•œ๋‹ค.

Aside

๋ผ์ธ ์ฆ๊ถŒ ํ”„๋กœ์ ํŠธ ํšŒ๊ณ 

ํ•œ๊ตญ์—์„œ๋Š” ํฌ๊ฒŒ ์•Œ๋ ค์ง€์ง€ ์•Š์•˜์ง€๋งŒ 2019๋…„์— ๋ผ์ธ ์ฆ๊ถŒ ์„œ๋น„์Šค๋ฅผ ์ผ๋ณธ์„ ๋Œ€์ƒ์œผ๋กœ ๊ณต๊ฐœํ–ˆ๋‹ค. ๋ผ์ธ์—์„œ ๊ทผ๋ฌดํ•œ ์‹œ๊ฐ„์€ ๊ธธ์ง€ ์•Š์ง€๋งŒ ์šด ์ข‹๊ฒŒ ํŒŒ์ด๋‚ธ์…œ ๊ฐœ๋ฐœ์‹ค์— ์†Œ์†๋˜์–ด ์ •๋ณด ๋ฒค๋”(๋ธ”๋ฃธ๋ฒ„๊ทธ, ๋กœ์ดํ„ฐ๋“ฑ๊ณผ ๊ฐ™์€ ํšŒ์‚ฌ)๋“ค๊ณผ ๋‚ด๋ถ€์‹œ์Šคํ…œ์˜ ์—ฐ๋™, ๊ฐ€๊ฒฉ ๊ฒฐ์ • ์‹œ์Šคํ…œ ๋“ฑ ๋‚˜๋ฆ„ ํ•ต์‹ฌ์ ์ธ ์—…๋ฌด๋“ค์„ 1๋…„๊ฐ„ ์ˆ˜ํ–‰ํ–ˆ๋‹ค. ๋ฐฐ์šด ๊ฒƒ๋“ค์„ ์ •๋ฆฌํ•ด๋‘๋ ค๊ณ  ํ–ˆ์ง€๋งŒ, ๊ตญ์ œ์ด์‚ฌ์— ์ด์ง๊นŒ์ง€ ๊ฒน์ณ์„œ ํ•˜๋ฃจ ์ดํ‹€ ๋ฏธ๋ฃจ๋‹ค๊ฐ€ ๋” ๋Šฆ๊ธฐ ์ „์— ์ด๊ณณ์— ์˜ฎ๊ฒจ ๋ณธ๋‹ค.

๋ผ์ธ ์ฆ๊ถŒ์— ๋Œ€ํ•ด์„œ

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

์†Œ์•ก์œผ๋กœ ํˆฌ์ž๊ฐ€ ๊ฐ€๋Šฅ

์ตœ๊ทผ ์ฃผ์‹์„ ์‹œ์ž‘ํ•œ ์‚ฌ๋žŒ๋“ค์€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ๋ช‡ ๋…„ ์ „ ๊นŒ์ง€๋งŒ ํ•ด๋„ ์ฝ”์Šคํ”ผ๋Š” 10์ฃผ ๋‹จ์œ„๋กœ๋งŒ ๊ฑฐ๋ž˜๊ฐ€ ๊ฐ€๋Šฅํ–ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•ก๋ฉด ๋ถ„ํ•  ์ „์˜ ์‚ผ์„ฑ์ „์ž๋ฅผ ์‚ฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” 1,000,000~ * 10, ์ ์–ด๋„ ์ฒœ๋งŒ ์› ์ด์ƒ์˜ ํ˜„๊ธˆ์ด ํ•„์š”ํ–ˆ๋‹ค. (2015๋…„ ์ฝ”์Šคํ”ผ๋Š” ์ฆ์‹œ ํ™œ์„ฑํ™”๋ฅผ ์œ„ํ•ด 1์ฃผ ๋‹จ์œ„ ๊ฑฐ๋ž˜๋ฅผ ํ—ˆ์šฉํ–ˆ๋‹ค, ๊ด€๋ จ๊ธฐ์‚ฌ) ๋ฐ˜๋ฉด ๋‹›์ผ€์ด์˜ ๊ฑฐ๋ž˜ ๋‹จ์œ„๋Š” ๋ฌด๋ ค 100์ฃผ. ๋‹Œํ…๋„ ์ฃผ์‹์ด ํ•œ ์ฃผ์— 4๋งŒ์—” ์ •๋„ ํ•˜๋‹ˆ ๋‹Œํ…๋„์— ํˆฌ์žํ•˜๊ธฐ ์œ„ํ•ด์„  400๋งŒ ์—”์ด ํ•„์š”ํ•˜๋‹ค. ๊ทธ๋ž˜์„œ์ธ์ง€ ์ผ๋ณธ์—์„œ ์ฃผ์‹์— ํˆฌ์žํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์€ ์—ฌ์œ ๋ˆ์„ ๋ช‡์–ต ์”ฉ ๊ตด๋ฆด ์ˆ˜ ์žˆ๋Š” ๋ถ€์ž์˜ ์ด๋ฏธ์ง€๊ฐ€ ๊ฐ•ํ•˜๋‹ค. ์ผ๋ณธ์˜ 2, 30 ๋Œ€์˜ ์‚ฌํšŒ ์ดˆ๋…„์ƒ๋“ค์ด ์ฃผ์‹์„ ํ•  ์ˆ˜ ์žˆ์„ ํ„ฑ์ด ์—†๋‹ค. ๋ผ์ธ ์ฆ๊ถŒ์€ ์ด๋ฅผ ํŒŒ๊ณ ๋“ค์–ด ์ Š์€ ์‚ฌ๋žŒ๋“ค์ด ์†Œ์•ก ํˆฌ์ž๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฑธ ๋ชฉํ‘œ๋กœ ํ–ˆ๋‹ค.

ํŽธ๋ฆฌํ•œ UI

๋‚ด๊ฐ€ UI/UX์˜ ์ „๋ฌธ๊ฐ€๋Š” ์•„๋‹ˆ์ง€๋งŒ ์ ์–ด๋„ ์ดˆ์‹ฌ์ž๋ฅผ ๋Œ€์ƒ์œผ๋กœ๋Š” ๋ผ์ฟ ํ…(ๆฅฝๅคฉ)์ด๋‚˜ ๋‹ค์ด์™€(ๅคงๅ’Œ)๋ณด๋‹ค๋Š” ๋กœ๋นˆํ›„๋“œ๊ฐ€ ์ œ๊ณตํ•˜๋Š” UI/UX๊ฐ€ ๋ช‡ ๋ฐฐ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์›Œ ๋ณด์ธ๋‹ค. ๋ผ์ธ ์ฆ๊ถŒ์€ ๊ธฐ์กด ์ผ๋ณธ ์ฆ๊ถŒ์‚ฌ๋ณด๋‹ค ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด UI์™€ ์†์‰ฌ์šด ๊ฑฐ๋ž˜ ํ”Œ๋กœ์šฐ๋ฅผ ์ œ๊ณตํ•˜๋ ค๊ณ  ํ–ˆ๋‹ค.

๋กœ๋นˆ ํ›„๋“œ

๊ฒฐ๊ณผ์ ์œผ๋กœ ์ฒซ ๋ฒˆ์งธ ๋‘ ๋ฒˆ์งธ ๋ชจ๋‘ ์žฅ์™ธ๊ฑฐ๋ž˜(Over The Counter)๋ฅผ ๋„์ž…ํ•ด ์‹œ์Šคํ…œ์„ ๋ฐ‘๋ฐ”๋‹ฅ ๋ถ€ํ„ฐ ๊ฐœ๋ฐœํ•จ์œผ๋กœ์„œ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ–ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์ฃผ์‹ ๊ฑฐ๋ž˜๋Š” ์ฆ๊ถŒ ๊ฑฐ๋ž˜์†Œ๋ฅผ ํ†ตํ•ด ์ด๋ค„์ง€๊ณ  ๊ฑฐ๋ž˜ ๋‹น์‚ฌ์ž๋“ค์€ ์ค‘๊ฐœ ์ˆ˜์ˆ˜๋ฃŒ๋งŒ ์ฆ๊ถŒ์‚ฌ์— ์ง€๋ถˆํ•˜๋Š” ๊ตฌ์กฐ๋‹ค. ์žฅ์™ธ ๊ฑฐ๋ž˜๋ฅผ ์‚ฌ์šฉํ•ด ๋ผ์ธ์ด ๋ฏธ๋ฆฌ ์„ ์ •ํ•œ ์ฃผ์‹๋“ค์„ ๋ณด์œ ํ•˜๊ณ  ๊ณ ๊ฐ๋“ค์€ ๊ฑฐ๋ž˜์†Œ๋ฅผ ํ†ตํ•ด ํƒ€์ธ๊ณผ ๊ฑฐ๋ž˜ํ•˜๋Š” ๋Œ€์‹  ๋ผ์ธ๊ณผ ๊ฑฐ๋ž˜๋ฅผ ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ 100์ฃผ ๋‹จ์œ„๊ฐ€ ์•„๋‹ˆ๋ผ 1์ฃผ ๋‹จ์œ„ ๊ฑฐ๋ž˜๋Š” ๋ฌผ๋ก  ์ •๊ทœ ๊ฑฐ๋ž˜ ์‹œ๊ฐ„์„ ์ง€๋‚˜์„œ ์•ผ๊ฐ„์—๋„ ๊ฑฐ๋ž˜๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง€๋ฉฐ ์‚ฌ์ž/ํŒ”์ž ์ฃผ๋ฌธ์˜ ํ๋ฆ„๋„ ๋‹จ์ˆœํ•ด์ง„๋‹ค.  ๋”๋ถˆ์–ด ๋ผ์ธ๋„ ์ฃผ์‹์„ ๋ณด์œ ํ•˜๊ณ  ๊ฑฐ๋ž˜ํ•˜๋Š” ๋ฆฌ์Šคํฌ๋ฅผ ์ง€๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜ ๊ฑฐ๋ž˜์†Œ๋ฅผ ํ†ตํ•œ ๊ฑฐ๋ž˜ ๋ณด๋‹ค๋Š” ์ˆ˜์ˆ˜๋ฃŒ๊ฐ€ ๋” ๋ถ™๋Š”๋‹ค. ์ด ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ๋ถ™์ธ ๊ฐ€๊ฒฉ์„ ์ƒํ™ฉ์— ๋งž๊ฒŒ ๋‹ค์–‘ํ•˜๊ฒŒ ๊ณ„์‚ฐํ•ด ๋ฆฌ์–ผํƒ€์ž„์œผ๋กœ ๊ฐ€๊ฒฉ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์œผ๋กœ  ์ผ๋ณธ ๊ฑฐ๋ž˜์†Œ๊ฐ€ ๊ฐ€์ง€๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ œ์•ฝ์œผ๋กœ๋ถ€ํ„ฐ ํ’€๋ ค๋‚˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์ผ๋ฐ˜์ ์ธ ์ฆ๊ถŒํšŒ์‚ฌ์˜ ์‹œ์Šคํ…œ๋“ค์ด ํ•œ๊ตญ ์ผ๋ณธ ๋ชจ๋‘ ์–ด๋А ์ •๋„ ํŒจํ‚ค์ง€ํ™”๊ฐ€ ๋งŽ์ด ๋˜์–ด ์žˆ์–ด์„œ ๊ณ„์ขŒ ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๋ฒค๋”์˜ ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์žฌ์‚ฌ์šฉํ•ด ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•œ๋ฐ ์ด๋ ‡๊ฒŒ ์ƒˆ๋กœ์šด ๊ฑฐ๋ž˜ ๋ฐฉ๋ฒ•์„ ๋„์ž…ํ•˜๋ฉด ๋˜๋ฉด ์ถ”๊ฐ€์ ์ธ ๊ฐœ๋ฐœ์ด ๊ต‰์žฅํžˆ ๋งŽ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์ ์ด๋‹ค. ๋ผ์ธ์ด ์ด๋Ÿฐ ๋ชจ๋ธ์„ ํƒํ•˜๊ฒŒ ๋˜๋ฉด์„œ ์‚ฌ์‹ค ๊ฐœ๋ฐœ ๋‚œ์ด๋„๋„ ๋†’์•„์ง€๊ณ  ํ”„๋กœ์ ํŠธ๊ฐ€ ์‹คํŒจํ•  ๊ฐ€๋Šฅ์„ฑ๋„ ๋†’์•„์ง€๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค. ๋‹ค์Œ์€ ์ถ”๊ฐ€ ๊ฐœ๋ฐœ์ด ํ•„์š”ํ–ˆ๋˜ ๋‚ด์šฉ์ด๋‹ค.

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

๋ผ์ธ ์ฆ๊ถŒ์˜ ์•„ํ‚คํ…์ณ ๋ฐฉํ–ฅ์„ฑ

๋‚˜๋Š” ์ง€๊ธˆ๋„ ๊ทธ๋ ‡์ง€๋งŒ ์ ˆ๋Œ€์ ์œผ๋กœ ์ด ์ •๋„ ๊ทœ๋ชจ์˜ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋Š” MSA๋กœ ๊ฐ€์•ผ ํ•˜๋ฉฐ 2~3๋ช…์œผ๋กœ ๋‚˜๋ˆ„์–ด์ง„ ๋„๋ฉ”์ธ ํŒ€๋ผ๋ฆฌ๋Š” API๋กœ ๋Œ€ํ™”ํ•˜๋ฉฐ ๋„๋ฉ”์ธ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๊ธธ ์›ํ–ˆ๋‹ค.  ๋‚ด๊ฐ€ ๊ฒฝํ—˜ํ•œ ๋ชจ๋…ธ๋ฆฌ์Šค ์„œ๋น„์Šค๋Š” git ์ปค๋งจ๋“œ ์กฐ์ฐจ๋„ ์‰ฝ๊ฒŒ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ๋กœ์ปฌ์—์„œ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๊ธฐ ์œ„ํ•ด์„  8๊ธฐ๊ฐ€ ์ด์ƒ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ–ˆ์œผ๋ฉฐ ์ œ์ผ ์ค‘์š”ํ•˜๊ฒŒ๋Š” ๋ฐฐํฌ๊ฐ€ ์ œํ•œ์ ์œผ๋กœ ์ผ์–ด๋‚  ์ˆ˜๋ฐ–์— ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋ฆ„๋Œ€๋กœ ๊ณ„์† MSA๋ฅผ ์ฃผ์žฅํ–ˆ๋‹ค.

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

๋ผ์ธ ์ฆ๊ถŒ ๊ฐœ๋ฐœ์–ธ์–ด

๊ฐœ๋ฐœ ์–ธ์–ด๋Š” ์Šคํ”„๋ง ๊ธฐ๋ฐ˜์˜ ์ž๋ฐ”๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ๋‚˜๋Š” WebFlux์˜ ๋„์ž…์„ ์ ๊ทน์ ์œผ๋กœ ์ด๋Œ์—ˆ๋˜ ๋ฐ˜๋ฉด ์ผ๋ณธ์ธ ํŒ€์€ ์‚ฌ์šฉ์ด์œ ์— ๋Œ€ํ•ด์„œ ์ž˜ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ๊ณ  ๋ณธ์ธ๋“ค์ด ๊ธฐ์กด์— ์‚ฌ์šฉํ•ด์„œ ์ต์ˆ™ํ•œ Spring Web์„ ์‚ฌ์šฉํ•˜๊ธธ ์›ํ–ˆ๋‹ค. ๊ธฐ์ˆ  ์„ ํƒ์— ์žˆ์–ด์„œ ์ต์ˆ™ํ•จ์ด ๊ทธ๊ฒƒ์„ ์„ ํƒํ•˜๋Š” ์œ ์ผํ•œ ์ด์œ ๊ฐ€ ๋˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ณ  ๋‹ค๋ฅธ ํŒ€์›๋“ค์ด ๋”ฐ๋ผ์™€ ์ฃผ๊ธธ ๋ฐ”๋žฌ๋Š”๋ฐ ์‚ฌ์‹ค ๊ทธ๋“ค๋„ ์‹œ๋‹ˆ์–ด์˜€๊ธฐ ๋•Œ๋ฌธ์— ๋ช‡๋ฒˆ ์ถฉ๋Œ์ด ์ผ์–ด๋‚ฌ์—ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์ง„ํ–‰์‹œ์— ๊ฐ€๋” ๋ณธ์งˆ์ ์ธ ์—…๋ฌด๊ฐ€ ์•„๋‹Œ ๋ถ€๊ฐ€์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ์‹œ๊ฐ„์„ ๋” ์žก์•„๋จน๊ฑฐ๋‚˜ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•„์„œ ๋‹ต๋‹ตํ•จ์„ ๋А๋ผ๊ฑฐ๋‚˜ ์งœ์ฆ์„ ๋‚ด๊ณคํ•œ๋‹ค. ๊ทธ ๋‹น์‹œ์—๋Š” ํŒ€ ๋‚ด ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์„ ๋ถ€๊ฐ€์ ์ธ ์—…๋ฌด๋ผ๊ณ  ์ƒ๊ฐํ•ด ์„ค๋“์— ์†Œํ™€ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ์ง€๊ธˆ ์™€์„œ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ณผ์—ฐ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋–จ์–ด์ง€๋Š” ๋ฌธ์ œ์˜€์„๊นŒ?  ํŒ€๋‚ด์— ๋‹ค์ˆ˜์˜ ์‚ฌ๋žŒ์ด ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ ์ƒํ™ฉ์—์„œ WebFlux๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒฐ์ •์€ ์˜ณ์•˜๋˜ ๊ฑธ๊นŒ? ์„ฑ๊ณต์ ์œผ๋กœ ์ถœ์‹œ๋Š” ๊ฐ€๋Šฅํ–ˆ์ง€๋งŒ, ๋‹ค์‹œ ๊ฐœ๋ฐœํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด ๊ทธ ๋ถ€๋ถ„์€ ์กฐ๊ธˆ ๊ณ ์ณ๋ณด๊ณ  ์‹ถ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์ฝ”๋“œ๋งŒ ๋ณด๊ฒ ๋‹ค๋Š” ์ž๊ธฐ ์•”์‹œ์— ๋น ์ง€๊ธฐ ์‰ฌ์šด๋ฐ ๊ฒฝ๋ ฅ์ด 10๋…„์ด ๋„˜์–ด๊ฐ€๋ฉด์„œ๋ถ€ํ„ฐ ํŒ€ ๋‚ด์˜ ์†Œํ†ต์ด์•ผ๋ง๋กœ ๋†’์€ ์ˆ˜์ค€์˜ ์ฝ”๋“œ์ž‘์„ฑ ๋งŒํผ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋†’๊ฒŒ ๋‘์–ด์•ผ ํ•  ๋ฌธ์ œ์ž„์„ ๋А๋‚€๋‹ค.

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

๋ผ์ธ ์ฆ๊ถŒ ๊ฐœ๋ฐœ/๋ฐฐํฌ ์ธํ”„๋ผ

์ธํ”„๋ผ์˜ ๊ฒฝ์šฐ ํด๋ผ์šฐ๋“œ๋Š” ์ „ํ˜€ ๊ณ ๋ คํ•˜์ง€ ๋ชปํ–ˆ๊ณ  ๋ ˆ๋””์Šค, ์—˜๋ผ์Šคํ‹ฑ ์„œ์น˜, ์นดํ”„๋ผ, MySQL๋“ฑ ์„ ๋‹ค์–‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ–ˆ๋‹ค. ๊ธˆ์œต ์„œ๋น„์Šค๋ผ์„œ ์™ธ๋ถ€์— ๋ญ”๊ฐ€ ์•ˆ์ •์ ์ธ ๋А๋‚Œ์„ ์ฃผ๊ธฐ์œ„ํ•ด์„œ MySQL์„ ์‚ฌ์šฉํ–ˆ์„ ๋ฟ ๊ณ„์ขŒ ์‹œ์Šคํ…œ์„ ์ œ์™ธํ•œ ์ฆ๊ถŒ ์„œ๋น„์Šค๋Š” ๋ ˆ๋””์Šค, ์—˜๋ผ์Šคํ‹ฑ ์„œ์น˜๋งŒ ๊ฐ€์ง€๊ณ ๋„ ์•ˆ์ •์ ์œผ๋กœ ์šด์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๋ณธ๋‹ค. ๋ผ์ธ์€ ์ž์ฒด์ ์œผ๋กœ ์•„์ฃผ ๋Œ€์šฉ๋Ÿ‰์˜ ๋ ˆ๋””์Šค ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์šด์šฉํ•˜๊ณ  ์žˆ์–ด์„œ ๋…ธํ•˜์šฐ๋„ ํ’๋ถ€ํ•˜๊ณ  Lettuce ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ์•กํ‹ฐ๋ธŒ ์—ฐ๋™๋„ ์•„์ฃผ ๋งˆ์Œ์— ๋“ค์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋งŒํผ ์ž˜ ์šด์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ํ•„์š”ํ–ˆ์œผ๋ฉฐ ์ด๊ณณ์— ์‚ฌ๋ก€๋“ค์„ ์—ด๊ฑฐํ•˜์ง€ ์•Š๊ฒ ์ง€๋งŒ ์ฆ๊ถŒ ์กฐ์ง ์ž์ฒด์—์„œ SPOF๊ฐ€ ๋  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ์ฐพ์•„ ์ด์ค‘ํ™”ํ•˜๊ณ  ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์‰ฝ๊ฒŒ ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•˜์ž๋Š” ๋ถ„์œ„๊ธฐ๊ฐ€ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์ข‹์€ ์‚ฌ๋ก€๋“ค์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

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

๊ธฐํƒ€์™ธ๋ถ€ ์š”์ธ

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

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

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

๋งˆ๋ฌด๋ฆฌ

์ด๊ฒƒ์œผ๋กœ ๋Œ€์ถฉ ์ •๋ฆฌ๋ฅผ ๋งˆ์นœ๋‹ค. ์‚ฌ์‹ค ๋ผ์ธ์— ์ž…์‚ฌํ•˜๊ณ  2๋…„๋„ ์•ˆ ๋ผ์„œ ๋›ฐ์ณ๋‚˜์˜ค๊ธด ํ–ˆ์ง€๋งŒ,  ๋งค๋‹ˆ์ €์˜ ๋ฐฐ๋ ค์™€ ์šด์ด ์ ์ ˆํ•˜๊ฒŒ ๋งž์•„๋–จ์–ด์ ธ์„œ ์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ์„ ํ•ด๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.  (1๋…„๋งŒ์—  ์ฆ๊ถŒ ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์ถœ์‹œํ•˜๋Š” ๊ฒฝํ—˜์€ ํ”์น˜ ์•Š๋‹ค)  ์•„์‰ฌ์šด ์ ์œผ๋กœ๋Š” ๊ตฐ ์ƒํ™œ์„ ์ „์—ญ ํ›„์— ๋Œ์•„๋ณด๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ๊ธฐ์–ต์ด ์ข‹์€ ์ถ”์–ต ์œ„์ฃผ๋กœ ๋‚จ์•„์„œ ๊ฐ€๋” ํŒ€์›๋“ค์—๊ฒŒ ์กฐ๊ธˆ ๋” ํ˜‘์กฐ์ ์œผ๋กœ ๋‘ฅ๊ธ€๋‘ฅ๊ธ€ํ•˜๊ฒŒ ๋Œ€ํ• ๊ฑธ ํ•˜๋Š” ์•„์‰ฌ์›€์ด ๋“ ๋‹ค.  ์ธ์ƒ ์„ ๋ฐฐ๋“ค์ด ์ž์ฃผ ํ•˜๋Š” ์กฐ์–ธ์ค‘์— “Don’t burn your bridges”๊ฐ€ ๋งˆ์Œ์— ์™€๋‹ฟ๋Š”๋ฐ, ํšŒ์‚ฌ๋‚ด์—์„œ๋Š” ํ‹ฐ๊ฒฉํƒœ๊ฒฉ ์ฃผ๋„๊ถŒ์„ ์œ„ํ•ด ๋‹คํˆฌ์ง€๋งŒ ๊ฒฐ๊ตญ ์ด์งํ•˜๊ณ  ๋‚˜๋ฉด ๊ฐ™์€ ์—…๊ณ„ ์‚ฌ๋žŒ์ผ ๋ฟ์ด๋‹ค. ์ฑ„์šฉ ๊ธฐ์ค€์ด ๋ฌด์—‡ ์ด๋ƒ๋Š” ์งˆ๋ฌธ์„ ๋ฐ›์•˜์„ ๋•Œ “์ธ์ƒ”์ด๋ผ๊ณ  ๋Œ€๋‹ตํ–ˆ๋˜ ๋งค๋‹ˆ์ ธ์˜ ๋ง์„ ๋‹น์‹œ์—๋Š” ๋†๋‹ด์œผ๋กœ ๋ฐ›์•„๋“ค์˜€์ง€๋งŒ, ์—…๊ณ„ 20๋…„ ์ด์ƒ์˜ ๊ฒฝ๋ ฅ์—์„œ ๋‚˜์˜ค๋Š” 6๊ฐ, ์ง๊ด€๋ ฅ๋“ฑ ๊ทธ ๋Šฅ๋ ฅ์„ ์ด์ œ๋Š” ์ธ์ •ํ•ด ์ค„๋งŒ ํ•˜๋‹ค. ๋‚ด๊ฐ€ ๋ถ€์กฑํ•œ ์–ธ์–ด ๋Šฅ๋ ฅ์œผ๋กœ ์ธํ•ด ๋…๋ถˆ์žฅ๊ตฐ์ฒ˜๋Ÿผ ๊ณ ์ง‘์„ ๊ตฝํžˆ์ง€ ์•Š์•˜์„ ๋•Œ ์ฒ ์ €ํžˆ ์œ„์ž„ํ•˜๊ณ  ๋งก๊ฒจ์ค€ ๊ทธ ํŒ๋‹จ์ด ๋งž์•˜๋‹ค. ํ•œ๊ตญ,์ผ๋ณธ,๋ฏธ๊ตญ, ํ˜ธ์ฃผ๋“ฑ ๋‹ค์–‘ํ•œ ๊ณณ์—์„œ ๊ฐœ๋ฐœ์„ ํ–ˆ์ง€๋งŒ ๊ฐœ์ธ์ ์œผ๋กœ๋Š” ๋ผ์ธ ์ฆ๊ถŒ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ผ์ ์œผ๋กœ๋Š” ์ œ์ผ ์žฌ๋ฏธ์žˆ์—ˆ๋‹ค. ์ผ๋ณธ์˜ ๊ฐœ๋ฐœ ๋ฌธํ™”์™€ ๋น„๊ตํ•ด ๋ณด๋ฉด ์•„์ด๋Ÿฌ๋‹ˆํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ˆ ์…‹๋“ค์˜ ํ™”๋ คํ•จ์ด ์•„๋‹ˆ๋ผ ๋ณธ์ธ์ด ์Šค์Šค๋กœ ๋А๋ผ๋Š” ํ”„๋กœ์ ํŠธ ๊ธฐ์—ฌ๋„ ๋ชฐ์ž…๋„ ๋“ฑ์ด ๋งŒ์กฑ๋„์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค.  ์‚ฌ์‹ค ๋„ค์ด๋ฒ„, ์นด์นด์˜ค ๊ฐ™์€ ์กฐ์ง์—์„œ๋„ ์ƒˆ๋กœ์šด ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์ถœ์‹œํ•˜๊ณ  ์„ฑ์žฅํ•˜๋Š” ๋ชจ์Šต์„ ์ฒ˜์Œ ๋ถ€ํ„ฐ ๋ณด๋Š” ์‚ฌ๋žŒ๋“ค์€ ๋ช‡ ์•ˆ๋˜๊ณ  ๊ทธ๋Ÿด ์ˆ˜ ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค์€ ๊ต‰์žฅํžˆ ์šด์ด ์ข‹์€ ์ถ•์— ์†ํ•œ๋‹ค. ๋‚˜๋Š” ๋ผ์ธ ์ฆ๊ถŒ์—์„œ ๋А๋‚„ ์ˆ˜ ์žˆ๋Š” ์ต์ˆ™ํ•จ์„ ๋’ค๋กœํ•˜๊ณ  ์•„์˜ˆ ๋„๋ฉ”์ธ์ด ๋ฐ”๋€Œ์–ด ๋ฒ„๋ ธ์ง€๋งŒ ๊ฐ€๋”์€ ๊ทธ๊ณณ์—์„œ ์ง‘์ค‘ํ•˜๋ฉด์„œ ๊ฐœ๋ฐœํ–ˆ๋˜ ์‹œ์ ˆ์ด ๊ทธ๋ฆฝ๋‹ค.

๊ณง ๋ผ์ธ ์ฆ๊ถŒ์˜ ๊ฑฐ๋ž˜์†Œ ์„œ๋น„์Šค๊ฐ€ ์˜คํ”ˆํ•œ๋‹ค๋Š” ์†Œ์‹์„ ๋“ค์—ˆ๋Š”๋ฐ ๋งค๋‹ˆ์ €์™€ ํŒ€์›๋“ค์—๊ฒŒ ๋…ธ๋ ฅ์˜ ์—ด๋งค๊ฐ€ ๋ชจ๋‘ ๊ณจ๊ณ ๋ฃจ ๋Œ์•„๊ฐ€๊ธธ ๋ฐ”๋ž€๋‹ค.

Aside