DevLog ๐Ÿ˜ถ

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์ง€ํ•˜์ฒ  ๋ฏธ์…˜ ํšŒ๊ณ  ๋ณธ๋ฌธ

์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค/๋ ˆ๋ฒจ 2

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์ง€ํ•˜์ฒ  ๋ฏธ์…˜ ํšŒ๊ณ 

dolmeng2 2023. 5. 22. 22:47

๊ฐ€๋น„์™€ ์ง„ํ–‰ํ•œ ๋ ˆ๋ฒจ 2 ์„ธ ๋ฒˆ์งธ ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฏธ์…˜์ธ ์ง€ํ•˜์ฒ  ๋ฏธ์…˜์ด๋‹ค...!

์šฐํ…Œ์ฝ” ํ•˜๋ฉด์„œ ์ง„ํ–‰ํ–ˆ๋˜ ๋ฏธ์…˜ ์ค‘์—์„œ ๊ฐ€์žฅ ์–ด๋ ค์šด ๋ฏธ์…˜์ด ์•„๋‹ˆ์—ˆ๋‚˜ ์‹ถ๋‹ค.

๊ฐ์ฒด์ง€ํ–ฅ์ ์ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹จ์ˆœ ๊ตฌํ˜„ ๊ทธ ์‚ฌ์ด์—์„œ ์—„์ฒญ ํ—ค๋งธ๋˜ ๊ฒƒ ๊ฐ™๋‹ค...

์•ž์œผ๋กœ ๊ฐœ๋ฐœํ•  ๋•Œ๋„ ๋ ˆ๋ฒจ 1์—์„œ ๋ฐฐ์› ๋˜ ๊ฒƒ๋“ค์„ ์ •๋ง ์žŠ์ง€ ์•Š์•„์•ผ๊ฒ ๋‹ค ๐Ÿฅฒ

 


 

โœ”๏ธ ์ž‘์„ฑํ•œ ์ฝ”๋“œ

 

GitHub - Cl8D/jwp-subway-path: ๋ ˆ๋ฒจ 2 ์ง€ํ•˜์ฒ  ๋ฏธ์…˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ

๋ ˆ๋ฒจ 2 ์ง€ํ•˜์ฒ  ๋ฏธ์…˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ. Contribute to Cl8D/jwp-subway-path development by creating an account on GitHub.

github.com

 

โœ”๏ธ 1์ฐจ PR

 

[1๋‹จ๊ณ„ - ์ง€ํ•˜์ฒ  ์ •๋ณด ๊ด€๋ฆฌ ๊ธฐ๋Šฅ] ์ ธ๋‹ˆ(์ด์ง€์›) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. by Cl8D · Pull Request #45 · woowacourse/

์•ˆ๋…•ํ•˜์„ธ์š”, ๋˜๋ง! ์ ธ๋‹ˆ์ž…๋‹ˆ๋‹ค ๐Ÿ™‡‍โ™€๏ธ ๋ ˆ๋ฒจ 1์˜ ์ฒซ ๋ฏธ์…˜ ๋ฆฌ๋ทฐ์–ด๊ฐ€ ๋˜๋ง์ด์—ˆ๋Š”๋ฐ ๋ฒŒ์จ ๋ ˆ๋ฒจ 2๊ฐ€ ๋˜์—ˆ๋„ค์š”...! ๋‹ค์‹œ ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ€์›Œ์š” ๐Ÿ˜Š ์ด๋ฒˆ ๋ฏธ์…˜์€ ์‹œ๊ฐ„์ด ๋งŽ์ด ๋ถ€์กฑํ•ด์„œ ์ œ๊ฐ€ ๋ด๋„ ์ฝ”๋“œ๊ฐ€ ๋„ˆ

github.com

 

โœ”๏ธ 2์ฐจ PR

 

[2๋‹จ๊ณ„ - ๊ฒฝ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ] ์ ธ๋‹ˆ(์ด์ง€์›) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. by Cl8D · Pull Request #111 · woowacourse/jwp-subwa

์•ˆ๋…•ํ•˜์„ธ์š”, ๋˜๋ง! ์ ธ๋‹ˆ์ž…๋‹ˆ๋‹ค! ์ง€๋‚œ 1๋‹จ๊ณ„ ๋•Œ ํ”ผ๋“œ๋ฐฑ์„ ๋น ๋ฅด๊ฒŒ ์ฃผ์‹  ๋•๋ถ„์— 2๋‹จ๊ณ„๋„ ๊ธˆ๋ฐฉ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค~! 2๋‹จ๊ณ„ - ๊ฒฝ๋กœ ์กฐํšŒ ๊ธฐ๋Šฅ (DB ์„ค์ •, ๊ฒฝ๋กœ ์กฐํšŒ API, ์š”๊ธˆ ์กฐํšŒ ๊ธฐ๋Šฅ)์— ํ•ด๋‹นํ•˜๋Š” ๋ถ€

github.com

 


โœ”๏ธ ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ

- ์ง€ํ•˜์ฒ  ๋…ธ์„ ์— ์—ญ์„ ๋“ฑ๋ก / ์ œ๊ฑฐํ•˜๋Š” API ๊ตฌํ˜„

- ํ•˜๋‚˜์˜ ์—ญ์€ ์—ฌ๋Ÿฌ ๋…ธ์„ ์— ๋“ฑ๋ก๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋…ธ์„ ์€ ๊ฐˆ๋ž˜๊ธธ์„ ๊ฐ€์งˆ ์ˆ˜ ์—†๋‹ค.

- ์ตœ์ดˆ ๋“ฑ๋ก ์‹œ ๋‘ ์—ญ์„ ๋™์‹œ์— ๋“ฑ๋กํ•ด์•ผ ํ•œ๋‹ค.

- ์ด๋ฏธ ๋“ฑ๋ก๋œ ์—ญ ์‚ฌ์ด์— ๋…ธ์„ ์„ ๋“ฑ๋กํ•  ๊ฒฝ์šฐ ๊ฑฐ๋ฆฌ ์ •๋ณด๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

- ๋…ธ์„  ์ œ๊ฑฐ ์‹œ ๊ธฐ์กด ์—ญ ๊ฐ„ ๊ฑฐ๋ฆฌ ์ •๋ณด๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

- ๋…ธ์„ ์— 2๊ฐœ๋งŒ ์—ญ์ด ๋‚จ์•˜์„ ๊ฒฝ์šฐ ๋ชจ๋‘ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค.

- ๋…ธ์„  ์กฐํšŒ ์‹œ ๋“ฑ๋ก๋œ ์—ญ๋„ ํ•จ๊ป˜ ์กฐํšŒ๋˜๋„๋ก API ๊ตฌํ˜„

- ์ถœ๋ฐœ์—ญ์—์„œ ๋„์ฐฉ์—ญ๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ์ตœ๋‹จ ๊ฒฝ๋กœ ์ถœ๋ ฅ ๋ฐ ์š”๊ธˆ ์ •๋ณด ์กฐํšŒ API ์ถ”๊ฐ€

- ๋…ธ์„ ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์š”๊ธˆ ์ •์ฑ… ๋ฐ ์—ฐ๋ น๋ณ„ ์š”๊ธˆ ํ• ์ธ ์ •์ฑ… ์ถ”๊ฐ€

 

์ด๋ฒˆ์—๋„ ๊ณ ๋ฏผํ–ˆ๋˜ ๋ถ€๋ถ„๋“ค์— ๋Œ€ํ•ด์„œ ์ ์–ด๋ณด๊ณ ์ž ํ•œ๋‹ค!

 


 

๐Ÿ’ฌ ๋„๋ฉ”์ธ ์„ค๊ณ„ํ•˜๊ธฐ

ํŽ˜์–ด๋ž‘ ์ง„ํ–‰ํ•˜๋ฉด์„œ DB ์œ„์ฃผ๊ฐ€ ์•„๋‹Œ, ๋„๋ฉ”์ธ ์œ„์ฃผ์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ์–ด์„œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋„๋ฉ”์ธ์„ ์œ„์ฃผ๋กœ ์ž‘์„ฑํ–ˆ์—ˆ๋‹ค.

๋…ธ์„ ๊ณผ ์ง€ํ•˜์ฒ  ์—ญ, ๊ทธ๋ฆฌ๊ณ  ๋…ธ์„ ์— ๋“ฑ๋ก๋œ ์—ญ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ๊ตฌ๊ฐ„ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” 3๊ฐœ์˜ ๋„๋ฉ”์ธ์„ ๋ฉ”์ธ์œผ๋กœ ์žก์•˜๋‹ค.

์•„๋งˆ ๋Œ€๋ถ€๋ถ„์˜ ํฌ๋ฃจ๋“ค์ด ์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ•œ ๊ฒƒ ๊ฐ™์•˜๋‹ค.

 

๐ŸŒฑ Station

์‚ฌ์‹ค ๋ง ๊ทธ๋Œ€๋กœ ์ง€ํ•˜์ฒ  ์—ญ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋„๋ฉ”์ธ์ด๋‹ค.

์ง€ํ•˜์ฒ  ์ด๋ฆ„ ๋ง๊ณ  ํŠน๋ณ„ํ•œ ์ •๋ณด๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋”ฑํžˆ ์„ค๋ช…ํ•  ๊ฒƒ์ด ์—†๋‹ค ใ…Žใ…Ž

 

๐ŸŒฑ Section

๋…ธ์„ ์— ๋Œ€ํ•œ ๊ฐ ๊ตฌ๊ฐ„ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ๋„๋ฉ”์ธ์ด๋‹ค.

์ถœ๋ฐœ์—ญ๊ณผ ๋„์ฐฉ์—ญ, ๊ทธ๋ฆฌ๊ณ  ๋‘ ์—ญ ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค. 

์ฒ˜์Œ์—๋Š” ์—ญ์˜ ํ•˜ํ–‰, ์ƒํ–‰ ์ •๋ณด๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด์„œ ์ด์— ๋Œ€ํ•œ boolean ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ• ๊นŒ ๊ณ ๋ฏผํ–ˆ์—ˆ๋Š”๋ฐ, map์„ ์‚ฌ์šฉํ•ด์„œ ์ด๋Š” ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์—ฌ์„œ ์ถ”ํ›„ ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. (ํ—ˆ๋ธŒ ๊ฐ“ ๐Ÿ‘)

 

๐ŸŒฑ Line

๋…ธ์„  ๋„๋ฉ”์ธ์˜ ๊ฒฝ์šฐ ์ด๋ฆ„๊ณผ ์ƒ‰์ƒ, ์ถ”๊ฐ€ ์š”๊ธˆ, ๊ทธ๋ฆฌ๊ณ  ๋…ธ์„ ์— ๋Œ€ํ•œ ์—ญ ์ •๋ณด์ธ SubwayLine์„ ๊ฐ€์ง€๊ณ  ์žˆ๋„๋ก ํ•˜์˜€๋‹ค.

Section์—์„œ Line์„ ์ฐธ์กฐํ•˜๋Š” ์‹์œผ๋กœ ์„ค๊ณ„๋ฅผ ํ•˜์‹  ํฌ๋ฃจ๋“ค๋„ ๋ช‡๋ช‡ ๋ดค์—ˆ๋Š”๋ฐ, ํ…Œ์ด๋ธ”์ƒ์—์„œ๋Š” Section ํ…Œ์ด๋ธ”์ด Line ํ…Œ์ด๋ธ”์„ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ์ง€๋งŒ ์‹ค์„ธ๊ณ„์—์„œ '๋…ธ์„ ์ด ์—ญ์„ ๊ฐ€์ง„๋‹ค'๋ผ๋Š” ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

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

 

๐ŸŒฑ SubwayLine

SubwayLine์ด๋ผ๋Š” ๋„๋ฉ”์ธ์€ ํ•˜๋‚˜์˜ ๋…ธ์„ ์— ๋Œ€ํ•œ ๊ตฌ๊ฐ„ ์ •๋ณด ๋ฆฌ์ŠคํŠธ์™€ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ๋Š” ์—ญ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, Line

์‚ฌ์‹ค DB์™€ ๊ฐ€์žฅ ๊ฐ„๊ทน์ด ํฐ ๋„๋ฉ”์ธ์ด ์•„๋‹๊นŒ ์‹ถ๋‹ค. (์ด ๊ณผ์ •์—์„œ ๊ฐ์ฒด์ง€ํ–ฅ์  ์„ค๊ณ„๋ฅผ ์ •๋ง ๋งŽ์ด ๊ณ ๋ฏผํ–ˆ๋‹ค.)

๋„๋ฉ”์ธ์ด ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์€ ์‚ฌ์‹ค ์„œ๋น„์Šค์—์„œ๋„ ์ถฉ๋ถ„ํžˆ DB ์กฐํšŒ๋ฅผ ํ†ตํ•ด์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ผ๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

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

๋Œ€์‹ , ์ƒˆ๋กœ์šด ๊ตฌ๊ฐ„ ์ •๋ณด๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•œ '๊ฒ€์ฆ ์ž‘์—…'์„ ์œ„์ฃผ๋กœ ๋„๋ฉ”์ธ์—๊ฒŒ ์ฑ…์ž„์„ ์ „๊ฐ€ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋‹ค.

 

์ฝ”๋“œ ๋ถ€๋ถ„์„ ๋Œ€์ถฉ ๊ฐ€์ ธ์™”๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ Line ๋„๋ฉ”์ธ์„ ์ƒ์„ฑํ•œ ๋‹ค์Œ์—, subwayLine์„ ๋ฝ‘์•„๋ƒˆ๋‹ค.

๐Ÿ’ก ์—ฌ๊ธฐ์„œ Line์˜ subwayLine์„ ๋’ค๋Šฆ๊ฒŒ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์ด์œ ๋Š” ๋‹จ์ˆœํžˆ ๋ผ์ธ์— ๋Œ€ํ•œ ์กฐํšŒ ํ›„, ๋ผ์ธ๊ณผ ์—ฐ๊ด€๋œ ์„น์…˜ ์ •๋ณด๋ฅผ ๋”ฐ๋กœ ์กฐํšŒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 
findById๋ฅผ ํ†ตํ•ด์„œ ์š”์ฒญ๋ฐ›์€ ๋…ธ์„ ์˜ ์•„์ด๋””๊ฐ€ ์œ ํšจํ•œ์ง€ ๊ฒ€์ฆํ•˜๊ณ , ์œ ํšจํ•˜๋‹ค๋ฉด ์„น์…˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ์‹์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๋‹ค ๋ณด๋‹ˆ ์ €๋Ÿฐ ๊ตฌ์กฐ๊ฐ€ ๋๋‹ค. ๋˜ํ•œ, findById์—์„œ ๊ตณ์ด ์—ฌ๋Ÿฌ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ์กฐํ•ฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋‹จ์ˆœํžˆ line์— ๋Œ€ํ•œ ์ •๋ณด๋งŒ ์กฐํšŒ๋˜๋„๋ก ๋งŒ๋“ค๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. findById๋Š” ๋น„๊ต์  ๊ฐ€๋ณ๊ฒŒ ์“ฐ์ด๋Š” ๋ฉ”์„œ๋“œ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค... ใ…Žใ…Ž

 

๊ทธ๋ฆฌ๊ณ  validateSection์„ ํ†ตํ•ด์„œ ์š”์ฒญ๋ฐ›์€ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ๊ฒ€์ฆ ์ž‘์—…์„ ์ง„ํ–‰ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋‚ด๋ถ€์ ์œผ๋กœ ์š”์ฒญ๋ฐ›์€ ์ •๋ณด๊ฐ€ ๋น„์–ด์žˆ๋Š”์ง€, ํ˜น์€ ์ด๋ฏธ ๋“ฑ๋ก๋œ ์ •๋ณด์ธ์ง€, ์‹ค์ œ๋กœ ์ €์žฅ๋œ ์—ญ ์ •๋ณด์ธ์ง€ ํ™•์ธํ•˜๊ฒŒ ๋œ๋‹ค.

์ด๋Ÿฐ ์‹์œผ๋กœ ๋„๋ฉ”์ธ์ด ๋‚ด๋ถ€์ ์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด์šฉ์€ ๊ฒ€์ฆํ•˜๋„๋ก ๋งŒ๋“ค๋ฉด์„œ ์ฑ…์ž„์„ ๋„˜๊ธฐ๊ณ , ์‹ค์ œ DB์— ์‚ฝ์ž…, ์‚ญ์ œ, ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ ์„œ๋น„์Šค์—๊ฒŒ ์ฑ…์ž„์„ ๋„˜๊ฒจ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

 

๋˜ํ•œ, SubwayLine์ด ์ •๋ ฌ๋œ ์—ญ ์ •๋ณด์— ๋Œ€ํ•ด์„œ ํ•„๋“œ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” ๊ฒŒ ์•ฝ๊ฐ„ ์ฐ์ฐํ•˜๊ธฐ๋„ ํ–ˆ์—ˆ๋‹ค.

sections ์ •๋ณด๋ฅผ ํ†ตํ•ด์„œ ์—ญ ์ •๋ณด๋Š” ์ถฉ๋ถ„ํžˆ ์œ ์ถ” ๊ฐ€๋Šฅํ•œ ์ •๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์‹ค ํ•„๋“œ๋กœ ๊ด€๋ฆฌํ•˜์ง€ ์•Š๊ณ  ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ •๋ ฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

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

๋ฆฌ๋ทฐ์–ด๋‹˜๋„ ๋‹ค๋ฅธ ๋ง์”€ ์•ˆ ํ•˜์…จ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋ฆ„ ํ•ฉ๋ฆฌ์ ์ธ ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ์—ˆ๋‚˜ ์‹ถ๋‹ค.

private List<Station> sort(final List<Section> sections) {
    final Map<Station, Station> stationRelationShip = getStationRelationShip(sections);
    final Optional<Station> startStation = getStartStation(stationRelationShip);
    return startStation.map(station -> getSortedStations(stationRelationShip, station))
        .orElse(Collections.emptyList());
}

private Map<Station, Station> getStationRelationShip(final List<Section> sections) {
    return sections.stream()
        .collect(Collectors.toMap(Section::source, Section::target));
}

private Optional<Station> getStartStation(final Map<Station, Station> stationRelationShip) {
    return stationRelationShip.keySet().stream()
        .filter(station -> !stationRelationShip.containsValue(station))
        .findFirst();
}

private List<Station> getSortedStations(final Map<Station, Station> stationRelationShip,
                                        final Station startStation) {
    final List<Station> sortedStations = new ArrayList<>();
    Optional<Station> nextStation = Optional.ofNullable(startStation);
    while (nextStation.isPresent()) {
        sortedStations.add(nextStation.get());
        nextStation = Optional.ofNullable(stationRelationShip.get(nextStation.get()));
    }
    return sortedStations;
}

๊ฐœ์ธ์ ์œผ๋กœ ์ •๋ ฌ ๋กœ์ง์— ๋Œ€ํ•ด์„œ ์ŠคํŠธ๋ฆผ์„ ์ ์ ˆํžˆ ํ™œ์šฉํ•œ ๊ฒƒ ๊ฐ™์•„ ๋ฟŒ๋“ฏํ•˜๋‹ค ๐Ÿ˜Š

 


 

๐Ÿ’ฌ ํ…Œ์ด๋ธ” ์„ค๊ณ„ํ•˜๊ธฐ

ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค๊ณ„ํ•˜์˜€๋‹ค.

CREATE TABLE IF NOT EXISTS station
(
    id bigint auto_increment NOT NULL,
    name VARCHAR(255) NOT NULL UNIQUE,
    PRIMARY KEY(id)
);

CREATE TABLE IF NOT EXISTS line
(
    id bigint auto_increment NOT NULL,
    name VARCHAR(255) NOT NULL UNIQUE,
    color VARCHAR(20) NOT NULL,
    extra_fare int NOT NULL,
    PRIMARY KEY(id)
);

CREATE TABLE IF NOT EXISTS section
(
    id bigint auto_increment NOT NULL,
    line_id bigint NOT NULL,
    source_station_id bigint NOT NULL,
    target_station_id bigint NOT NULL,
    distance int NOT NULL,
    PRIMARY KEY(id),
    FOREIGN KEY(line_id) REFERENCES line(id) ON DELETE CASCADE,
    FOREIGN KEY(source_station_id) REFERENCES station(id),
    FOREIGN KEY(target_station_id) REFERENCES station(id)
);

์‚ฌ์‹ค ์™ธ๋ž˜ํ‚ค์— ๋Œ€ํ•ด์„œ๋„ ์ •๋ง ์˜๊ฒฌ์ด ๋ถ„๋ถ„ํ•˜๊ฒŒ ๋‚˜๋‰˜๋Š”๋ฐ, ๊ฐœ์ธ์ ์œผ๋กœ ์ด๋ ‡๊ฒŒ ์ž‘์€ ๊ทœ๋ชจ๋ผ๋ฉด ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ฑธ์–ด๋‘๋Š” ๊ฒŒ ๋” ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

๋ฌผ๋ก  ๊ทธ๋งŒํผ ํ…Œ์ŠคํŠธ๊ฐ€ ๋นก์„ธ์ง€๋งŒ... ํ…Œ์ŠคํŠธ๋Š” ํ”ฝ์Šค์ฒ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์–ด๋Š ์ •๋„ ํ•ด์†Œํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 

๊ทธ๋ฆฌ๊ณ , section ํ…Œ์ด๋ธ”์˜ ํ•„๋“œ์—๋Š” on delete cascade ์˜ต์…˜์„ ์ฃผ์—ˆ๋Š”๋ฐ, ์ด๋Š” ๋ถ€๋ชจ ํ…Œ์ด๋ธ”์˜ row๊ฐ€ ์ œ๊ฑฐ๋˜์—ˆ์„ ๋•Œ ํ•จ๊ป˜ ์ œ๊ฑฐ๋˜์–ด section์˜ ๊ณ ์•„ ํ•„๋“œ(?)๊ฐ€ ๋‚จ์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

์œ„์—์„œ๋„ ๋งํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ section์˜ ๊ฒฝ์šฐ line์˜ ์ •๋ณด์™€ ์—ฐ๊ด€๋˜์–ด ์žˆ๋Š” ์ •๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋…ธ์„ ์ด ์ œ๊ฑฐ๋œ ์ดํ›„์—๋„ ํ•ด๋‹น ๋…ธ์„ ์˜ ๊ตฌ๊ฐ„ ์ •๋ณด๊ฐ€ ์กด์žฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

๊ทธ๋ฆฌ๊ณ , ์›๋ž˜ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” station ์ชฝ์—๋„ on delete cascade๋ฅผ ๊ฑธ์–ด๋‘์—ˆ์—ˆ๋Š”๋ฐ, ์ƒ๊ฐํ•ด๋ณด๋‹ˆ๊นŒ ์—ญ ์ •๋ณด๊ฐ€ ์ œ๊ฑฐ๋œ๋‹ค๊ณ  ํ•ด์„œ ๊ตฌ๊ฐ„ ์ •๋ณด๋ฅผ ๋ฐ”๋กœ ์ œ๊ฑฐํ•ด๋ฒ„๋ฆฌ๋ฉด ๊ธฐ์กด ๊ตฌ๊ฐ„์— ๋Œ€ํ•œ ๊ฑฐ๋ฆฌ ์—…๋ฐ์ดํŠธ๊ฐ€ ์•ˆ ๋  ๊ฒƒ ๊ฐ™์•„์„œ ํšŒ๊ณ ๋ฅผ ์ž‘์„ฑํ•˜๋ฉฐ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฆฌํŒฉํ„ฐ๋ง์„ ํ•ด๋ณด์•˜๋‹ค.

public int deleteById(final Long id) {
    try {
        final String sql = "DELETE FROM station WHERE id = ?";
        return jdbcTemplate.update(sql, id);
    } catch (final DataIntegrityViolationException e) {
        throw new BadRequestException(ErrorCode.STATION_DELETE_IF_EXISTS_SECTION);
    }
}

๊ทธ๋ƒฅ ์ด๋ ‡๊ฒŒ FK์— ๋Œ€ํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฏธ ๊ตฌ๊ฐ„์— ๋Œ€ํ•œ ์ •๋ณด๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ญ์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋‹ค๋Š” ์˜ˆ์™ธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

์‹คํ–‰ํ•˜๋ฉด ์š”๋Ÿฐ ์‹์œผ๋กœ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค! ๐Ÿ˜Š

 


 

๐Ÿ’ฌ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์˜์กด์„ฑ ์—ญ์ „์‹œํ‚ค๊ธฐ (DIP)

์ด๋ฒˆ ๋ฏธ์…˜์—์„œ ๊ฐ€์žฅ ์‹ ๊ฒฝ์“ด ๋ถ€๋ถ„ ์ค‘์— ํ•˜๋‚˜์ด๋‹ค.

์ €๋ฒˆ ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋ฏธ์…˜ ํšŒ๊ณ  ๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋งˆ์ง€๋ง‰์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ปค๋ฉ˜ํŠธ๋ฅผ ๋‚จ๊ฒผ์—ˆ๋‹ค.

์ง€๋‚œ ๋ฏธ์…˜์˜ ์˜์กด์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ์กฐ์˜€๋‹ค.

์„œ๋น„์Šค์—์„œ ๋ฐ”๋กœ Dao๋ฅผ ์ฐธ์กฐํ•˜๋‹ค ๋ณด๋‹ˆ, Dao๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ธ Entity์™€ ์˜์กด ๊ด€๊ณ„๊ฐ€ ์ƒ๊ธฐ๊ณ , ์ด ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋„๋ฉ”์ธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ ์„œ๋น„์Šค๊ฐ€ ๋„๋ฉ”์ธ๊นŒ์ง€ ์•Œ๊ฒŒ ๋˜๋Š” ๊ตฌ์กฐ๊ฐ€ ๋œ ๊ฒƒ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

Repository ๊ณ„์ธต์„ ์ถ”๊ฐ€ํ•ด์„œ ์„œ๋น„์Šค ๋ ˆ์ด์–ด๋Š” ๋„๋ฉ”์ธ๋งŒ ๋ฐ”๋ผ๋ณด๊ณ , ๋„๋ฉ”์ธ๊ณผ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์„ Repository์—๊ฒŒ ์œ„์ž„ํ•œ ๊ฒƒ์ด๋‹ค.

์ด ๋•๋ถ„์— ๊ธฐ์กด์˜ ์ˆœํ™˜ ๊ด€๊ณ„๋ฅผ ์—†์•จ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ, DAO์™€ Repository๋Š” Persistence layer์— ์กด์žฌํ•˜๊ณ  ์žˆ๋‹ค.

application layer๊ฐ€ Repository๋ฅผ import ํ•˜๋ฉด์„œ ์—ฌ์ „ํžˆ persistence layer๋ฅผ ์˜์กดํ•˜๋ฉฐ domain ํŒจํ‚ค์ง€์— ์กด์žฌํ•˜๋Š” ๋„๋ฉ”์ธ ๊ฐ์ฒด์™€ ๋ชจ๋‘ ์ƒํ˜ธ ์ฐธ์กฐํ•˜๊ฒŒ๋œ๋‹ค๋Š” ๋‹จ์ ์ด ์กด์žฌํ–ˆ๋‹ค.

 

โญ๏ธ ์ด๋ฅผ ์œ„ํ•ด Repository์˜ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด์— ๋‘๊ณ , ์ด์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ persistence layer์— ๋‘์–ด ์˜์กด ๊ด€๊ณ„๋ฅผ ์—ญ์ „์‹œํ‚ค๋Š” ์ „๋žต์„ ์ฑ„ํƒํ•˜์˜€๋‹ค! ๋•๋ถ„์— Application Layer์—์„œ๋Š” ๋„๋ฉ”์ธ ๊ณ„์ธต์˜ ํŒจํ‚ค์ง€๋งŒ ์˜์กดํ•˜๊ฒŒ ๋˜์–ด์„œ ๋” ์ด์ƒ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค.

์‹ค์ œ๋กœ ์„œ๋น„์Šค ๋ ˆ์ด์–ด์˜ import๋ฅผ ๋ณด๋ฉด application, domain์œผ๋กœ๋งŒ ์ฐธ์กฐ ๊ด€๊ณ„๊ฐ€ ํ˜•์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์‹ค์ œ๋กœ ๊ทธ๋ฆผ์œผ๋กœ ์„ค๋ช…ํ•˜์ž๋ฉด ์ด๋Ÿฐ ๋Š๋‚Œ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 


 

๐Ÿ’ฌ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋œ ์˜์กด์ ์ธ ํ˜•ํƒœ๋กœ ์„ค๊ณ„ํ•˜๊ธฐ

์ตœ๋‹จ ๊ฒฝ๋กœ๋ฅผ ๊ตฌํ•˜๊ธฐ ์œ„ํ•˜์—ฌ jgrapht ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ๋ถ€๋„๋Ÿฝ์ง€๋งŒ ๋‚ด ์ฒ˜์Œ ์ œ์ถœ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค.

// RouteService.java
private WeightedMultigraph<Station, DefaultWeightedEdge> getPossibleRouteGraph(
    final Map<Long, List<LineWithSectionRes>> sectionsByLineId) {
    final WeightedMultigraph<Station, DefaultWeightedEdge> routeGraph = new WeightedMultigraph<>(
        DefaultWeightedEdge.class);
    sectionsByLineId.values()
        .forEach(sectionRes -> addRouteBySectionRes(routeGraph, sectionRes));
    return routeGraph;
}

์ „์ฒด ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ๋„ˆ๋ฌด ๊ธธ์–ด ์ผ๋ถ€๋งŒ ๊ฐ€์ ธ์™”์ง€๋งŒ, ์ €๋Ÿฐ ์‹์œผ๋กœ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

๋‹น์—ฐํ•˜๊ฒŒ ๋ฆฌ๋ทฐ์–ด๋‹˜์—๊ฒŒ๋„ ๊ฐ™์€ ์ง€์ ์„ ๋ฐ›์•„์„œ ๋ถ€๋„๋Ÿฌ์› ๋‹ค ๐Ÿฅฒ

์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„์ง€ ๊ณ ๋ฏผํ•˜๋‹ค, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€๋‹ค.

public interface GraphProvider {

    List<Station> getShortestPath(final List<SubwayLine> sections, final Station sourceStation,
                                  final Station targetStation);
}

๋„๋ฉ”์ธ ๊ณ„์ธต์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  (์ด๋ฆ„์„ ์—„์ฒญ ๊ณ ๋ฏผํ–ˆ์—ˆ๋Š”๋ฐ ๋‚˜๋ฆ„... ๊ดœ์ฐฎ๋‚˜?), ์ธ์ž๋กœ ์ถœ๋ฐœ์—ญ์—์„œ ๋„์ฐฉ์—ญ๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ตฌ๊ฐ„ ์ •๋ณด์™€ ์ถœ๋ฐœ์—ญ, ๋„์ฐฉ์—ญ ์ •๋ณด๋ฅผ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ , ์™ธ๋ถ€ ์„œ๋น„์Šค์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” 'infra' ํŒจํ‚ค์ง€์— JgraphService๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

@Component
public class JgraphtService implements GraphProvider {

    @Override
    public List<Station> getShortestPath(final List<SubwayLine> sections,
                                         final Station sourceStation,
                                         final Station targetStation) {
        final WeightedMultigraph<Station, DefaultWeightedEdge> routeGraph = createRouteGraph(sections);
        validateRequestRoute(sourceStation, targetStation, routeGraph);
        return getShortestPath(sourceStation, targetStation, routeGraph);
    }
    ...
}

์‚ฌ์‹ค ๋‚ด๋ถ€๋Š” ๊ทธ๋ƒฅ ์ตœ๋‹จ ๊ฒฝ๋กœ ๊ตฌํ•˜๋Š” ์ฝ”๋“œ์—ฌ์„œ ์˜๋ฏธ๋Š” ์—†์„ ๊ฒƒ ๊ฐ™์•„ ์ฒจ๋ถ€ํ•˜์ง€ ์•Š์•˜๋‹ค.

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

 

์ด๋ ‡๊ฒŒ ์„ค๊ณ„ํ•œ ๋•๋ถ„์— application layer์—์„œ๋Š” ๋„๋ฉ”์ธ ๊ณ„์ธต์˜ GraphProvider ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๋ฐ”๋ผ๋ณด๊ณ  ์žˆ์–ด ์ถ”ํ›„ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ต์ฒด๋˜๋”๋ผ๋„ application layer์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ์—†์–ด ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. 

๐Ÿฅน ๊ฐœ์ธ์ ์œผ๋กœ infra ํŒจํ‚ค์ง€๋Š” ์™œ ํ•ญ์ƒ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ผ๊นŒ ๊ถ๊ธˆํ–ˆ๋Š”๋ฐ... ์ด๋ ‡๊ฒŒ ์ง์ ‘ ๊ตฌ์„ฑํ•˜๋ฉด์„œ ์™„์ „ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 


 

๐Ÿ’ฌ ์š”๊ธˆ ์ •์ฑ… ํšจ์œจ์ ์œผ๋กœ ์ ์šฉํ•˜๊ธฐ

๋ถ€๋„๋Ÿฝ๊ฒŒ๋„ ์š”๊ธˆ ์ •์ฑ… ์—ญ์‹œ ์ฒ˜์Œ์— ์ง  ์ฝ”๋“œ๊ฐ€ ๊ต‰์žฅํžˆ ๋ณ„๋กœ๋‹ค.

public Fare calculateFare(final TotalDistance distance) {
    if (distance.lessThan(EXTRA_FARE_SECTION_TEN)) {
        return BASIC_FARE;
    }
    if (distance.lessAndEqualsThan(EXTRA_FARE_SECTION_FIFTY)) {
        final TotalDistance extraDistance = distance.subtract(EXTRA_FARE_SECTION_TEN);
        return BASIC_FARE.add(calculateByDistance(extraDistance, FARE_SECTION_UNIT_FIVE));
    }
    final TotalDistance fareSectionDistance = EXTRA_FARE_SECTION_FIFTY.subtract(EXTRA_FARE_SECTION_TEN);
    final TotalDistance extraDistance = distance.subtract(EXTRA_FARE_SECTION_FIFTY);
    final Fare extraFare = calculateByDistance(fareSectionDistance, FARE_SECTION_UNIT_FIVE)
        .add(calculateByDistance(extraDistance, FARE_SECTION_UNIT_EIGHT));
    return BASIC_FARE.add(extraFare);
}

10km, 10~50km, 50km ์ด์ƒ์— ๋Œ€ํ•ด ๊ฐ๊ฐ ๋ถ„๊ธฐ๋ฌธ์„ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค ๋ณด๋‹ˆ ํ™•์žฅ์— ๋งค์šฐ ์ทจ์•ฝํ•œ ๊ตฌ์กฐ๊ฐ€ ๋œ ๊ฒƒ์ด๋‹ค.

OCP๋ฅผ ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ์–ด์š”. ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•ด๋ณผ์ˆ˜ ์žˆ์„๊นŒ์š”? (Hint. ํ•ด๋‹น ๊ตฌ๊ฐ„์— ํŒ๋‹จํ•˜๋Š”์ง€ ์—ฌ๋ถ€์™€ ์š”๊ธˆ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๊ณตํ†ต์ ์ด๋„ค์š”)

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

 

๊ทธ๋ž˜์„œ ์•„์˜ˆ ์ •์ฑ…๋ณ„๋กœ ํด๋ž˜์Šค๋ฅผ ๋‚˜๋ˆ„๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€๋‹ค. 

์šฐ์„ , ๊ฐ ์ •์ฑ…์— ๋Œ€ํ•ด ๊ณตํ†ต์ ์ธ ํ–‰์œ„๋ฅผ ์ •์˜ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•ด๋‘์—ˆ๋‹ค.

public interface FarePolicy {

    boolean isAvailable(final Distance distance);

    Fare calculateFare(final Distance distance);
}

isAvailable๊ฐ€ ๊ตฌ๊ฐ„์— ๋Œ€ํ•ด ์ •์ฑ… ์ ์šฉ์ด ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋‹จํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๊ณ , calculateFare๊ฐ€ ์‹ค์ œ๋กœ ์š”๊ธˆ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.

 

 

๊ทธ๋ฆฌ๊ณ , ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ 3๊ฐœ์˜ ๊ตฌ์ฒด ์ •์ฑ… ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜์˜€๋‹ค.

10km ์ดํ•˜์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์ •์ฑ…์€ BasicFarePolicy, 10~50km๋Š” UnitFiveFarePolicy, 50km๋Š” UnitEightFarePolicy๋กœ ์„ค์ •ํ–ˆ๋‹ค.

์ด๋ฆ„์ด ์ข€ ์•„์‰ฝ๊ธด ํ•œ๋ฐ... 5km๋‹น, 8km๋‹น 100์›์”ฉ ์ถ”๊ฐ€๋œ๋‹ค๋Š” ๊ฒŒ ๋” ์ค‘์š”ํ•œ ๊ฒƒ ๊ฐ™์•„์„œ ์ €๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด๋‘์—ˆ๋‹ค ใ…Žใ…Ž

๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ•„๋“œ ๊ฐ’์€ enum์„ ํ†ตํ•ด์„œ ์ •์˜ํ•ด๋‘์—ˆ๋‹ค. (ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ)

 

๊ทธ๋ฆฌ๊ณ , ์ค‘์š”ํ•œ ์ ์€ ๊ฐ ์ •์ฑ…๋“ค์„ 'FarePolicyComposite'๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ํ•œ ๋ฒˆ์— ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

public Fare getTotalFare(final Distance shortestDistance) {
    return farePolicies.stream()
        .filter(policy -> policy.isAvailable(shortestDistance))
        .map(policy -> policy.calculateFare(shortestDistance))
        .findFirst()
        .orElseThrow();
}

ํ•ด๋‹น ์ปดํฌ์ง€ํŠธ ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด, ๊ฐ ์ •์ฑ…๋“ค์„ ํ•˜๋‚˜์”ฉ ์ˆœํšŒํ•˜๋ฉด์„œ ์ ์šฉ ๊ฐ€๋Šฅํ•œ ์ •์ฑ…์ธ์ง€ ํŒ๋‹จํ•œ ๋’ค, ์ ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์ง„ํ–‰ํ•œ๋‹ค.

ํ˜„์žฌ ์„ค๊ณ„์ƒ ๊ตฌ๊ฐ„ ์ •๋ณด๋Š” ๊ฐ ์ •์ฑ… ์ค‘ ํ•˜๋‚˜์˜ ๋ฒ”์œ„์— ๋ฌด์กฐ๊ฑด ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— orElseThrow()๋กœ ๋˜์ ธ์งˆ ์œ„ํ—˜์ด ์—†์„ ๊ฒƒ์ด๋ผ ํŒ๋‹จํ•˜์˜€๊ณ , ์ด๋ ‡๊ฒŒ ๋˜๋ฉด 60km ์ด์ƒ์˜ ๊ตฌ๊ฐ„์—์„œ๋Š” ๋‹ค๋ฅธ ์ •์ฑ…์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ๋•Œ farePolices ๋ฆฌ์ŠคํŠธ์— ์ •์ฑ…์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ™•์žฅ์— ์—ด๋ฆฐ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

๐Ÿ’ก ๋‹จ, ์ด ๊ตฌ์กฐ๋ฅผ ์ฑ„ํƒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” isAvailable์˜ ์กฐ๊ฑด ์ •๋ณด๊ฐ€ ์ด˜์ด˜ํ•ด์•ผ ๋œ๋‹ค๋Š” ์ ์ด๋‹ค. ํ˜„์žฌ ๊ตฌ์กฐ๋Š” ์ด˜์ด˜ํ•˜๊ฒŒ ์„ค๊ณ„๋˜์–ด ๊ดœ์ฐฎ์ง€๋งŒ!

 

๊ทธ๋ ‡๋‹ค๋ฉด, ์ •์ฑ… ๋ฆฌ์ŠคํŠธ๋Š” ์–ด๋””์—์„œ ์ถ”๊ฐ€ํ•ด์ค„๊นŒ?

๊ฐ ์ •์ฑ…๋“ค์€ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— @Configuration์„ ํ™œ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์ฃผ์ž…์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

@Configuration
public class FarePolicyConfig {

    @Bean
    public FarePolicyComposite farePolicies(
        final FarePolicy basicFarePolicy,
        final FarePolicy unitFiveFarePolicy,
        final FarePolicy unitEightFarePolicy
    ) {
        return new FarePolicyComposite(
            List.of(basicFarePolicy, unitFiveFarePolicy, unitEightFarePolicy)
        );
    }
}

์ด๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฃผ์ž…๋˜๋Š” ํด๋ž˜์Šค์˜ ์ด๋ฆ„์€ ๊ผญ ๋นˆ์— ๋“ฑ๋ก๋œ ํด๋ž˜์Šค์˜ ์ด๋ฆ„๊ณผ ๋งž์ถฐ์ค˜์•ผ ํ•œ๋‹ค.

๐Ÿ’ก ์Šคํ”„๋ง์—์„œ  ๊ธฐ๋ณธ์ ์œผ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์„ ๊ธฐ์ค€์œผ๋กœ ๋นˆ์„ ์กฐํšŒํ•œ๋‹ค.
๋งŒ์•ฝ, ๊ฐ™์€ ํƒ€์ž…์˜ ๋นˆ์ด ์—ฌ๋Ÿฌ ๊ฐœ ์กฐํšŒ๋˜๋ฉด ์ถ”๊ฐ€์ ์œผ๋กœ ํ•„๋“œ๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋นˆ์„ ์กฐํšŒํ•œ๋‹ค.

์œ„์˜ ์›๋ฆฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ 'FarePolicyComposite' ํƒ€์ž…์˜ 'farePolicies'๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋นˆ์„ ๋“ฑ๋กํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, application layer์—์„œ๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฃผ์ž…๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

@Service
public class RouteService {
    ...
    private final FarePolicyComposite farePolicies;
}

 

์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ถ”๊ฐ€ ์š”๊ธˆ ์ •์ฑ…๋„ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค ใ…Žใ…Ž

๋‚˜ ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๋‚˜์ด ์ •๋ณด๋ฅผ ๋”ฐ๋กœ ๋ฐ›์ง€ ์•Š๊ณ , ๊ทธ๋ƒฅ ํ• ์ธ๋œ ๊ฐ€๊ฒฉ๋„ ํ•จ๊ป˜ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค๋‹ค ๋ณด๋‹ˆ ๊ฐ€๋Šฅํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

public DiscountFare getDiscountFares(final Fare totalFare) {
    final List<Fare> fares = discountFarePolicies.stream()
        .map(policy -> policy.calculateFare(totalFare))
        .collect(Collectors.toUnmodifiableList());
    return new DiscountFare(fares.get(0), fares.get(1));
}

๋‹จ, ์ด๋•Œ๋Š” ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋œ ์ˆœ์„œ์— ๋”ฐ๋ผ์„œ ์˜ํ–ฅ์„ ๋ฐ›๋‹ค๋ณด๋‹ˆ๊นŒ ์กฐ๊ธˆ ์• ๋งคํ•˜๋‹ค.

๋งŒ์•ฝ ๋‚˜์ด๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ map -> collect ๋Œ€์‹ ์— ์š”๊ธˆ ์ •์ฑ…์„ ์ ์šฉํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ filter๋ฅผ ํ†ตํ•ด ๋ฐ›์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.

 


 

์“ธ ๋‚ด์šฉ์ด ๋ณ„๋กœ ์—†์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ ์“ฐ๋‹ค ๋ณด๋‹ˆ๊นŒ ์˜ค๋Š˜ ํ•˜๋ฃจ๊ฐ€ ๋‹ค ๊ฐ€๋ฒ„๋ ธ๋‹ค ๐Ÿ˜…

์ด๋ฒˆ ๋ฏธ์…˜์€ ์ •์‹ ์—†๋˜ ๊ฒƒ๋งŒํผ ๋‚˜๋ฆ„ ๋งŽ์€ ๊ฑธ ์–ป์–ด๊ฐ”๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

๋‚ด์ผ๋ถ€ํ„ฐ ์ƒˆ๋กœ์šด ๋ฏธ์…˜ ์‹œ์ž‘์ธ๋ฐ... ํŒŒ์ดํŒ… ํ•ด์•ผ๊ฒ ๋‹ค! ๐Ÿ’ช

Comments