DevLog ๐Ÿ˜ถ

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜‘์—… ๋ฏธ์…˜ ํšŒ๊ณ  ๋ณธ๋ฌธ

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

[์šฐํ…Œ์ฝ” 5๊ธฐ] ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜‘์—… ๋ฏธ์…˜ ํšŒ๊ณ 

dolmeng2 2023. 6. 16. 01:23

๋ผ์˜จ, ์ฅฌ๋‹ˆ, ์„ธ์ธ, ์ตธํŒŒ๋ž‘ ์ง„ํ–‰ํ–ˆ๋˜ ๋ ˆ๋ฒจ 2 ๋งˆ์ง€๋ง‰ ๋ฏธ์…˜, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜‘์—… ๋ฏธ์…˜์ด๋‹ค!

ํ”„๋ก ํŠธ ํฌ๋ฃจ๋ถ„๋“ค์ด๋ž‘๋„ ํ•จ๊ป˜ ํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์„œ ๋„ˆ๋ฌด ์žฌ๋ฐŒ์—ˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค ๐Ÿ˜Š

์ด๋ฒˆ ๋ฏธ์…˜์€ ์ €๋ฒˆ์— ์ง„ํ–‰ํ–ˆ๋˜ ๋ฏธ์…˜์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•˜๋‹ค ๋ณด๋‹ˆ๊นŒ ์ฝ”๋“œ์ ์ธ ๋ถ€๋ถ„๋ณด๋‹ค, ๋ฐฐํฌ ์ชฝ์„ ๋” ์‹ ๊ฒฝ์ผ๋˜ ๊ฒƒ ๊ฐ™๋‹ค.

๋งˆ์ง€๋ง‰ ๋ฏธ์…˜์ธ ๋งŒํผ ์ •๋ง ๋งŽ์ด ๋ฐฐ์› ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค ใ…Žใ…Ž

 


 

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

 

GitHub - Cl8D/jwp-shopping-order: ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค ๋ ˆ๋ฒจ 2 ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜‘์—… ๋ฏธ์…˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ

์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค ๋ ˆ๋ฒจ 2 ์žฅ๋ฐ”๊ตฌ๋‹ˆ ํ˜‘์—… ๋ฏธ์…˜ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ. Contribute to Cl8D/jwp-shopping-order development by creating an account on GitHub.

github.com

 

โœ”๏ธ PR

 

[2๋‹จ๊ณ„ - ์ฃผ๋ฌธ ๊ธฐ๋Šฅ ๊ตฌํ˜„] ์ ธ๋‹ˆ(์ด์ง€์›) ๋ฏธ์…˜ ์ œ์ถœํ•ฉ๋‹ˆ๋‹ค. by Cl8D · Pull Request #19 · woowacourse/jwp-shoppi

์•ˆ๋…•ํ•˜์„ธ์š”, ์ฐฐ๋ฆฌ! ์ ธ๋‹ˆ์ž…๋‹ˆ๋‹ค ๐Ÿ˜Š ๋งŒ๋‚˜์„œ ๋ฐ˜๊ฐ€์›Œ์š”! โ˜ƒ๏ธ ๋ฒŒ์จ ๋ ˆ๋ฒจ 2 ๋งˆ์ง€๋ง‰ ๋ฏธ์…˜์ด๋„ค์š”... ์‹œ๊ฐ„์ด ๋„ˆ๋ฌด ๋น ๋ฅธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ใ…Žใ…Ž ใ…  ๋‹ค์Œ ์ฃผ ์›”์š”์ผ์ด ๋งˆ์ง€๋ง‰ PR ์š”์ฒญ์ธ ๊ฑฐ ๋ณด๊ณ  ๋ฆฌ๋ทฐ์— ์ข€ ๋” ์ง‘์ค‘

github.com

 

๋งˆ์ง€๋ง‰ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ผ๋Š” ๊ฒŒ ๋„ˆ๋ฌด ์Šฌํ”„๋‹ค... ๐Ÿฅฒ

 


 

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

- AWS๋กœ ๋ฐฐํฌํ•˜๊ธฐ (์„œ๋ฒ„ ๋ฐฐํฌ์šฉ ์ธ์Šคํ„ด์Šค / DB์šฉ ์ธ์Šคํ„ด์Šค)

: ์ด๋•Œ, DB์šฉ ์ธ์Šคํ„ด์Šค๋Š” ์™ธ๋ถ€ IP๋กœ ๊ณต๊ฐœ๋˜์ง€ ์•Š๋Š”๋‹ค.

- ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด์€ ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ•  ์ˆ˜ ์žˆ๋‹ค.
- ์ƒํ’ˆ ์ฃผ๋ฌธ ์‹œ ํ˜„์‹ค ์„ธ๊ณ„์˜ ์‡ผํ•‘ ์„œ๋น„์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์žฌํ™” ๊ด€๋ จ ์š”์†Œ๋ฅผ ์ตœ์†Œ 1๊ฐ€์ง€ ์ด์ƒ ์ถ”๊ฐ€ํ•œ๋‹ค.
: ์žฌํ™” ๊ด€๋ จ ์š”์†Œ: ์ฟ ํฐ, ํฌ์ธํŠธ, ํ• ์ธ ๋“ฑ
ex) 5๋งŒ์› ์ด์ƒ ์ฃผ๋ฌธ ์‹œ ์ „์ฒด ๊ธˆ์•ก์—์„œ 10% ํ• ์ธ์ด ๋œ๋‹ค.
- ์‚ฌ์šฉ์ž ๋ณ„๋กœ ์ฃผ๋ฌธ ๋ชฉ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
- ํŠน์ • ์ฃผ๋ฌธ์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 


 

โœ”๏ธ AWS ๋ฐฐํฌ ๋ฐ ํ˜‘์—… ํ™˜๊ฒฝ ๊ตฌ์ถ•ํ•˜๊ธฐ

ํ”„๋ก ํŠธ์—”๋“œ ํฌ๋ฃจ๋ถ„๋“ค๊ณผ ํ˜‘์—…ํ•œ๋‹ค๋Š” ์ƒ๊ฐ์— ์‹ ๋‚˜์„œ ๋ฐฐํฌ ์ชฝ์„ ์—ด์‹ฌํžˆ ๋‹ค๋ฃจ์—ˆ๋‹ค.

๊ฑฐ์˜ ๋‹ค ๋ธ”๋กœ๊ทธ๋กœ ์˜ฌ๋ฆฐ ๋‚ด์šฉ์ด์–ด์„œ ๋”ฐ๋กœ ์„ค๋ช…ํ•˜์ง€๋Š” ์•Š๊ณ  ๋„˜์–ด๊ฐ€๊ณ ์ž ํ•œ๋‹ค.

 

 

1. HTTP -> HTTPS ์ ์šฉํ•˜๊ธฐ (SSL / Nginx๋กœ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ์ ์šฉํ•˜๊ธฐ)

 

[Infra] AWS ๋ฐฐํฌ ํ›„ ๋„๋ฉ”์ธ ์—ฐ๊ฒฐ ๋ฐ HTTPS ์ ์šฉ, nginx๋กœ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ์ ์šฉํ•˜๊ธฐ

๐ŸŒฑ ๋“ค์–ด๊ฐ€๊ธฐ ์ „ ๋ฌด๊ณผ๊ธˆ์œผ๋กœ HTTPS ์ ์šฉ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์•˜๋‹ค. ์ •์„๋Œ€๋กœ๋ผ๋ฉด ๊ฐ€๋น„์•„ + Route53 + ACM or ๊ฐ€๋น„์•„ + nginx๋กœ๋งŒ ์ง„ํ–‰ํ•˜๋ฉด ์ข‹์•˜๊ฒ ์ง€๋งŒ... ์—ฌ๋Ÿฌ ์ œ์•ฝ์‚ฌํ•ญ์œผ๋กœ ์ธํ•ด์„œ ์ƒ‰๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋„๋ฉ”

cl8d.tistory.com

 

 

2. ์  ํ‚จ์Šค๋กœ CI / CD ๊ตฌ์ถ•ํ•˜๊ธฐ

 

[Jenkins] AWS ์ธ์Šคํ„ด์Šค๋ฅผ ์  ํ‚จ์Šค๋กœ ๋ฐฐํฌํ•ด๋ณด๊ธฐ - 1ํŽธ

๐ŸŒฑ ๋“ค์–ด๊ฐ€๊ธฐ ์ „ ์ด๋ฒˆ ๋ฏธ์…˜์—์„œ ์  ํ‚จ์Šค๋ฅผ ํ†ตํ•ด CI / CD๋ฅผ ๊ตฌ์ถ•ํ•ด๋ณด๊ณ  ์‹ถ์–ด์„œ โญ๏ธ๋ฒ ๋ฒ  ์„ ์ƒ๋‹˜โญ๏ธ์˜ ํž˜์„ ๋นŒ๋ ค์„œ ํ•œ ๋ฒˆ ์ง„ํ–‰ํ•ด๋ณด์•˜๋‹ค. ๋‚˜๋Š” t4g.micro๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋‹ˆ ๋žจ์ด 1๊ธฐ๊ฐ€๋ฐ–์— ์•ˆ ๋˜์–ด์„œ swap

cl8d.tistory.com

 

 

[Jenkins] AWS ์ธ์Šคํ„ด์Šค๋ฅผ ์  ํ‚จ์Šค๋กœ ๋ฐฐํฌํ•ด๋ณด๊ธฐ - 2ํŽธ

๐ŸŒฑ ๋“ค์–ด๊ฐ€๊ธฐ ์ „ ์ง€๋‚œ ํฌ์ŠคํŒ…์€ ์  ํ‚จ์Šค๋ฅผ ์„ค์น˜ํ•˜๊ณ  ํ•„์š”ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋Š” ๊ณผ์ •๊นŒ์ง€ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ๐ŸŒฑ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ WebHook ๋“ฑ๋กํ•˜๊ธฐ ์šฐ๋ฆฌ๋Š” ๊นƒํ—ˆ๋ธŒ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์˜ ํŠน์ • ๋ธŒ๋žœ์น˜์— push ์ด๋ฒคํŠธ๊ฐ€

cl8d.tistory.com

 

 

3. Logback์œผ๋กœ ์—๋Ÿฌ๋ฅผ ํŒŒ์ผ๋กœ ๋กœ๊น…ํ•˜๊ธฐ

 

[Spring] ์—๋Ÿฌ ๋กœ๊น…ํ•˜๊ธฐ - Logback์„ ์‚ฌ์šฉํ•ด์„œ ERROR ๋ ˆ๋ฒจ๋งŒ ํŒŒ์ผ๋กœ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ฒจ๋ณด์ž!

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

cl8d.tistory.com

 

๊ธฐ์ดˆ ์ค‘์— ๊ธฐ์ดˆ์ง€๋งŒ, ๋‚ด ์†์œผ๋กœ ์ง์ ‘ ๋ฐฐํฌ๋ฅผ ํ•ด๋ณธ๋‹ค๋Š” ๊ฒŒ ๋„ˆ๋ฌด ๋ฟŒ๋“ฏํ•˜๊ณ  ์‹ ๊ธฐํ–ˆ๋˜ ๊ฒฝํ—˜์ด์—ˆ๋‹ค ๐Ÿ˜ถ

๋‹ค์Œ ํ”„๋กœ์ ํŠธ ํ•  ๋•Œ๋Š” ์„œ๋ฒ„๋งŒ ์žˆ๋‹ค๋ฉด...! ๋” ๋ฉ‹์žˆ๊ฒŒ ๊ตฌ์ถ•ํ•ด์•ผ๊ฒ ๋‹ค ใ…Ž_ใ…Ž

 


 

๐Ÿ’ฌ ๋„๋ฉ”์ธ ์„ค๊ณ„ํ•˜๊ธฐ - ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ

์ด๋ฒˆ ๋ฏธ์…˜์˜ ํ•ต์‹ฌ ์š”๊ตฌ์‚ฌํ•ญ ์ค‘์— ํ•˜๋‚˜๊ฐ€ ์žฌํ™” ์ •์ฑ…์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

์šฐ๋ฆฌ ํŒ€์€ '์ฟ ํฐ'์ด๋ผ๋Š” ๋„๋ฉ”์ธ์„ ์„ ํƒํ•˜์˜€๊ณ , ์ฒ˜์Œ์œผ๋กœ ๊ฐ€์ž…ํ–ˆ์„ ๋•Œ์™€ ์ฒ˜์Œ์œผ๋กœ ์ƒํ’ˆ์„ ์ฃผ๋ฌธํ–ˆ์„ ๋•Œ ๋ฐœ๊ธ‰๋ฐ›๋„๋ก ์ง„ํ–‰ํ•˜์˜€๋‹ค.

์ฟ ํฐ ๋„๋ฉ”์ธ์„ ๋” ํ™œ์šฉํ•˜๋ฉด ์ข‹์•˜์„ ๊ฒƒ ๊ฐ™์€๋ฐ, ์ตœ๋Œ€ํ•œ ๊ฐ„๊ฒฐํ•œ ๋ฐฉํ–ฅ์œผ๋กœ ์ง„ํ–‰ํ•˜๋‹ค ๋ณด๋‹ˆ ์ด๋ ‡๊ฒŒ ๋๋‹ค ๐Ÿฅฒ

 

๋˜ํ•œ, ์˜ˆ์ œ ์ฝ”๋“œ์™€ ๋น„์Šทํ•œ ๋“ฏ ๋‹ค๋ฅด๊ฒŒ ๋„๋ฉ”์ธ์„ ์„ค๊ณ„ํ•˜์˜€๋‹ค.

 

๊ทธ๋ฆผ์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋‹ˆ๊นŒ ๋ณต์žกํ•ด ๋ณด์ด๋Š”๋ฐ, ์ฟ ํฐ๊ณผ ์ฃผ๋ฌธ, ํ™˜๋ถˆ ์ •์ฑ… ๋„๋ฉ”์ธ์ด ์ถ”๊ฐ€๋œ ๊ฑฐ ๋นผ๊ณ ๋Š” ์ง€๋‚œ ๋ฏธ์…˜๊ณผ ๊ฑฐ์˜ ๋‹ค๋ฅผ ๊ฒŒ ์—†๋‹ค.

ํ™˜๋ถˆ ์ •์ฑ…์˜ ๊ฒฝ์šฐ ์ง€ํ•˜์ฒ  ๋ฏธ์…˜์—์„œ ์ง„ํ–‰ํ–ˆ๋˜ ์ •์ฑ… ๊ด€๋ฆฌ ํด๋ž˜์Šค์˜ ๋ฐฉ๋ฒ•์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ž๋‹ค. ๋งˆ์Œ์— ๋“ ๋‹ค!

 

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

 

 

๐Ÿ’ก ๋„๋ฉ”์ธ๊ณผ ID

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

(๋ช‡๋ช‡ ํฌ๋ฃจ๋ถ„๋“ค๋„ ๋‚ด๊ฐ€ ์„ค๊ณ„ํ•œ ๊ฒƒ์„ ๋ณด๊ณ  ๋ง์”€์„ ํ•ด์ฃผ์…จ์—ˆ๋Š”๋ฐ, ์ตœ์ข…์ ์œผ๋กœ๋Š” ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค์—ˆ์—ˆ๋‹ค ใ…Žใ…Žใ…Ž)

๋‚ด๊ฐ€ ๋„ˆ๋ฌด ์˜์†์„ฑ ๋ ˆ์ด์–ด์˜ ๊ฐ’์„ ๋„๋ฉ”์ธ ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์— ์ง‘์ฐฉํ–ˆ์—ˆ๋‹ค๋Š” ๊ฒŒ ๋Š๊ปด์กŒ๋‹ค... ใ…Žใ…Ž

JPA๋ฅผ ์“ฐ๋‹ค ๋ณด๋ฉด ์–ด์ฐจํ”ผ ์ผ์–ด๋‚  ์ˆ˜๋ฐ–์— ์—†๋Š” ๊ฒƒ์ด๊ณ , ์˜คํžˆ๋ ค ๋ถ„๋ฆฌํ•จ์œผ๋กœ์„œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•œ ๊ฒƒ์ด ์‚ฌ์‹ค์ด๋‹ˆ๊นŒ...

์•ž์œผ๋กœ๋„ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ์ด๋ ‡๊ฒŒ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†์ง€ ์•Š์„๊นŒ ์‹ถ๋‹ค. ๋˜ ๋‹ค์‹œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ ๊ณ ๋ คํ•ด๋ณผ ์ˆ˜ ์žˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค ใ…Žใ…Ž

 

 

๐Ÿ’ก ์ธํ„ฐํŽ˜์ด์Šค ํ™œ์šฉํ•˜๊ธฐ

๋˜ ํ•œ ๊ฐ€์ง€ ํŠน์ดํ•œ ์ ์€ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ’€์–ด๋‚˜๊ฐ„ ์ ์ธ๋ฐ, ์šฐ์„  ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ถ€ํ„ฐ ๋ณด์ž.

public interface MemberPassword {

    String getPassword();
}

์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ์— ๋Œ€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ ์–ธํ•ด๋‘์—ˆ๋‹ค.

 

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

๋˜ํ•œ, ์•”ํ˜ธํ™”๊ฐ€ ๋˜์–ด ์žˆ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์•”ํ˜ธํ™”๊ฐ€ ๋˜์ง€ ์•Š์€ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ์„œ ํ‘œํ˜„๋˜๋Š” ๊ฒŒ 2๊ฐœ์˜ ์ฑ…์ž„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๋Š๊ปด์กŒ๊ธฐ ๋•Œ๋ฌธ์—, ์ƒํƒœ๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ์„œ ํ‘œํ˜„ํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ๊ฐ์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด๋ฅผ ๋ถ„๋ฆฌํ•˜์˜€๋‹ค.

public class NaturalPassword implements MemberPassword {

    private final String password;

    ...

    public static NaturalPassword create(final String password) {
        validatePassword(password);
        return new NaturalPassword(password);
    }

    private static void validatePassword(final String password) {
        if (password.length() < PASSWORD_MIN_LENGTH || password.length() > PASSWORD_MAX_LENGTH) {
            throw new BadRequestException(MEMBER_PASSWORD_LENGTH);
        }
    }
}

์ด๊ฑฐ๋Š” ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด์˜จ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ์ฒด์ด๋‹ค. validatePassword ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ธธ์ด๋ฅผ ๊ฒ€์ฆํ•˜์˜€๋‹ค.

public class EncryptedPassword implements MemberPassword {

    private final String password;

    ...
    
    public static EncryptedPassword create(final String password) {
        return new EncryptedPassword(password);
    }
}

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

์ฐธ๊ณ ๋กœ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™”์˜ ๊ฒฝ์šฐ SHA256์„ ํ†ตํ•ด์„œ ์ง„ํ–‰ํ•˜์˜€๋‹ค. (ํ˜„์žฌ ์šฐ๋ฆฌ ๊ตฌ์กฐ์ƒ ์›๋ณธ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•Œ ์ด์œ ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ณตํ˜ธํ™”๊ฐ€ ์•ˆ ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ฑ„ํƒํ•˜์˜€๋‹ค.)

public static String encrypt(final String target) {
    try {
        final MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(target.getBytes());
        return bytesToHex(md.digest());
    } catch (NoSuchAlgorithmException ignored) {
        throw new RuntimeException("์•”ํ˜ธํ™” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.");
    }
}

 

์ถ”๊ฐ€์ ์œผ๋กœ ์ฃผ๋ฌธ ์—ญ์‹œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„ํ•˜์˜€๋Š”๋ฐ, ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ๊ฐ™์€ ์ด์œ ๋กœ ์ฟ ํฐ์„ ์‚ฌ์šฉํ•œ ์ฃผ๋ฌธ๊ณผ, ์ฟ ํฐ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์˜€๋‹ค.

public class CouponOrder implements Order {

    ...
    private final Coupon coupon;

    // ์ด ์ฃผ๋ฌธ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ
    private BigDecimal calculateTotalOrderPrice() {
        return OrderPriceCalculator.calculateTotalOrderPrice(cartItems);
    }
	
    // ์ฟ ํฐ์— ์˜ํ•ด์„œ ํ• ์ธ๋œ ๊ธˆ์•ก์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ
    private BigDecimal calculateDiscountPrice() {
        final int discountRate = coupon.discountRate();
        final BigDecimal convertedDiscountRate = BigDecimal.valueOf(
            (PERCENTAGE - discountRate) * DECIMAL_CONVERSION);
        return totalPrice.multiply(convertedDiscountRate).setScale(0, RoundingMode.DOWN);
    }
}

์ฟ ํฐ์„ ์‚ฌ์šฉํ•œ ์ฃผ๋ฌธ์€ ์ฟ ํฐ ๋„๋ฉ”์ธ์— ๋Œ€ํ•œ ์˜์กด ๊ด€๊ณ„๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ์ฟ ํฐ์— ์˜ํ•ด ํ• ์ธ๋œ ๊ธˆ์•ก๋„ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•œ๋‹ค.

๋‹ค๋งŒ, ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ํ•„๋“œ๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด์„œ ์ธํ„ฐํŽ˜์ด์Šค์˜ getter๊ฐ€ ์—„์ฒญ ๋งŽ์•„์กŒ๋‹ค๋Š” ๋‹จ์ ์€ ์žˆ๋‹ค.

์ด์— ๋Œ€ํ•ด์„œ ๋‹ค๋ฅด๊ฒŒ ํ’€์–ด๋‚˜๊ฐ€๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ, ๋ช…์‹œ์ ์ธ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํ˜•์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์„๊นŒ ์‹ถ๊ธด ํ•˜๋‹ค.

์•„๋‹ˆ๋ฉด, ๊ณตํ†ต๋˜๋Š” ํ•„๋“œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” OrderDetail ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ–ˆ์œผ๋ฉด ๋” ์ข‹์•˜์„ ๊ฒƒ ๊ฐ™๋‹ค.

 


 

๐Ÿ’ฌ ์˜ˆ์™ธ ํด๋ž˜์Šค ์„ค๊ณ„ํ•˜๊ธฐ

์‚ฌ์‹ค ์ด์ „๋ถ€ํ„ฐ ๋‚˜๋ฅผ ๊ดด๋กญํžˆ๋˜ ์ฃผ์ œ ์ค‘์— ํ•˜๋‚˜์˜€๋‹ค. '์˜ˆ์™ธ์— ๊ด€ํ•œ ํด๋ž˜์Šค์— ์–ด๋–ค ์ •๋ณด๊นŒ์ง€ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•˜๋Š”๊ฐ€?'

๊ฐœ์ธ์ ์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋˜์ง€๋Š” ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์ถœ๋ ฅ๋˜๋Š” ์šฉ๋„๋ณด๋‹ค๋Š”, ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ™•์ธํ•˜๋Š” ๋ฉ”์‹œ์ง€๊ณ , ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž๊ฐ€ ํ•ด๋‹น ๋ฉ”์‹œ์ง€์— ๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฆฌ๋ทฐ์–ด๋‹˜์€ ์˜คํžˆ๋ ค 2๋ฒˆ์„ ๋” ์„ ํ˜ธํ•˜์…จ๋‹ค.

์ƒ๊ฐํ•ด๋ณด๋ฉด ๋‚ด๊ฐ€ ์•„์ง ํฐ ์„œ๋น„์Šค๋ฅผ ๊ฒฝํ—˜ํ•ด๋ณด์ง€ ๋ชปํ•ด์„œ 1๋ฒˆ์„ ๋” ์„ ํ˜ธํ•˜๋Š” ๊ฑด๊ฐ€ ์‹ถ๊ธฐ๋Š” ํ•˜์ง€๋งŒ... ๐Ÿ˜…

์œ„์™€ ๊ฐ™์€ ์ƒํ™ฉ์ด ์ฃผ์–ด์กŒ์„ ๋•Œ ์‚ฌ์šฉ์ž์—๊ฒŒ '์ƒํ’ˆ ID 1๋ฒˆ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค' ๊ฐ™์€ ๋ฉ”์‹œ์ง€๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

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

 

๋˜ํ•œ, ์ด๋ฒˆ์— ์ž‘์„ฑํ•œ ์˜ˆ์™ธ ํด๋ž˜์Šค์—์„œ๋Š” ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

public class ForbiddenException extends RuntimeException {

    private static final HttpStatus httpStatus = HttpStatus.FORBIDDEN;
    private final ErrorCode errorCode;
    ...
}

๋•๋ถ„์— ExceptionHandler์—์„œ์˜ ์ฝ”๋“œ๊ฐ€ ์ค„์–ด์„œ ๋งˆ์Œ์— ๋“ ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€๋Š” ์™œ ์ด๋Ÿฐ ์ƒ๊ฐ์„ ๋ชป ํ–ˆ์„๊นŒ...! ๐Ÿฅฒ

์•ž์œผ๋กœ๋„ ์ด๋Ÿฐ ์‹์œผ๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š์„๊นŒ ์‹ถ๋‹ค. (errorCode๋Š” ํ˜‘์˜ํ•˜๋Š” ๊ฑฐ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ๊ฒƒ ๊ฐ™๋‹ค!)

 


 

๐Ÿ’ฌ ์ฟ ํฐ ๋ฐœํ–‰ - ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ ํ™œ์šฉํ•˜๊ธฐ

์ „์— ๋ง๋ž‘์˜ ํ…Œ์ฝ”ํ†ก ๋ณด๊ณ  ์ด๋ฒคํŠธ ๋ฐฉ์‹์„ ๊ผญ ํ™œ์šฉํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋Š”๋ฐ, ์ฟ ํฐ ๋ฐœํ–‰์— ๋Œ€ํ•ด์„œ ์ด๋ฒคํŠธ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์–ด์„œ ๋ฐ”๋กœ ๋„์ž…ํ•ด๋ณด์•˜๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ ๋งค์šฐ ๋ฟŒ๋“ฏํ–ˆ๋‹ค... ๐Ÿ˜ถ

 

๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•œ ๋ฐฐ๊ฒฝ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

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

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

๊ทธ๋ž˜์„œ, ์•„๋ž˜์™€ ๊ฐ™์ด ํšŒ์› ๊ฐ€์ž… ์‹œ ์ฟ ํฐ ๋ฐœํ–‰์„ ์ง„ํ–‰ํ•˜๋„๋ก ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜์˜€๋‹ค.

@Transactional
public long save(final MemberJoinRequest memberJoinRequest) {
    if (memberRepository.existByName(memberJoinRequest.getName())) {
        throw new BadRequestException(ErrorCode.MEMBER_DUPLICATE_NAME);
    }
    final Member member = convertMember(memberJoinRequest);
    final long savedMemberId = memberRepository.insert(member);
    applicationEventPublisher.publishEvent(new JoinMemberCouponEvent(savedMemberId));
    return savedMemberId;
}

๊ทธ๋ฆฌ๊ณ , ์ฟ ํฐ ์ €์žฅ ๋กœ์ง์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ง„ํ–‰ํ•˜์˜€๋‹ค.

@TransactionalEventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveJoinMemberCoupon(final JoinMemberCouponEvent joinMemberCouponEvent) {
    final Long memberId = joinMemberCouponEvent.getMemberId();
    final Coupon coupon = couponRepository.findByNameAndDiscountRate(JOIN_MEMBER_COUPON.getName(),
        JOIN_MEMBER_COUPON.getDiscountRate());

    final LocalDateTime issuedAt = LocalDateTime.now();
    validateAlreadyIssued(memberId, coupon.couponId());
    final MemberCoupon memberCoupon = convertMemberCoupon(coupon, issuedAt);
    memberCouponRepository.save(memberId, memberCoupon);
}

๋กœ์ง ์ž์ฒด๋Š” ์ฟ ํฐ์„ ์กฐํšŒํ•ด์„œ ์ด๋ฏธ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์ €์žฅํ•œ๋‹ค.

๐Ÿ’ก @TransactionalEventListener๋ž€?
- phase ์˜ต์…˜์„ ํ†ตํ•ด์„œ ํŠธ๋žœ์žญ์…˜์— ๋”ฐ๋ฅธ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ์ง€์›ํ•œ๋‹ค. 
๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” 'AFTER_COMMIT'์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ ์‹œ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

 

ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ @Transactional(propagation = Propagation.REQUIRES_NEW)๋ผ๋Š” ์˜ต์…˜์ด ๋ˆˆ์— ๋Œ ๊ฒƒ์ด๋‹ค.

๊ธฐ์กด์—๋Š” ์ „ํŒŒ ์†์„ฑ์„ ์„ค์ •ํ•˜์ง€ ์•Š์•˜์—ˆ๋Š”๋ฐ, ๋ฆฌ๋ทฐ์–ด๋‹˜๊ป˜ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•˜๋‹ค.

์™œ ๊ทธ๋Ÿด๊นŒ ๊ณ ๋ฏผ์„ ํ–ˆ์—ˆ๋Š”๋ฐ, ๋‚˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ดํ•ดํ–ˆ๋‹ค.

1. JdbcTemplate์˜ ๊ฒฝ์šฐ @transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์–ด๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ jdbcTemplate.update๋‚˜ query() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์—์„œ DB์— ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ„๋‹ค. (JPA์™€ ๋‹ค๋ฅธ ์ )

2. ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๊ธฐ ์œ„ํ•ด์„œ๋Š” @transactional์„ ํ†ตํ•ด์„œ ๋ฌถ์–ด์ค˜์•ผ ํ•œ๋‹ค. ์•„๋‹ˆ๋ฉด JdbcTemplate์—์„œ ์ด๋ฏธ save๋ฅผ ์ง„ํ–‰ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด์— ๋Œ€ํ•ด ๋กค๋ฐฑํ•  ์ˆ˜ ์—†๋‹ค.

3. ๊ธฐ๋ณธ์ ์œผ๋กœ @TransactionalEventListener๋Š” ์‹œ์ž‘๋œ ํŠธ๋žœ์žญ์…˜ commit ํ›„์— ๋™์ž‘ํ•œ๋‹ค.

4. ํ•˜์ง€๋งŒ, ๋‹จ์ˆœํžˆ @transactional์„ ๋ถ™์ด๋ฉด ๊ธฐ์กด ํšŒ์› ๊ฐ€์ž…์—์„œ ๊ฑธ๋ ค์žˆ๋˜ ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ์ปค๋ฐ‹๋œ ํŠธ๋žœ์žญ์…˜์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์‹œ ์ปค๋ฐ‹์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋กค๋ฐฑ์ด๋“ , ๋‹ค๋ฅธ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋”๋ผ๋„ ๋ฌด์˜๋ฏธํ•˜๋‹ค.
-> ๊ทธ๋ž˜์„œ ๋งŒ์•ฝ JPA ๊ฐ™์ด ์‚ฝ์ž…, ์ˆ˜์ •, ์‚ญ์ œ ์—ฐ์‚ฐ์—์„œ @Transcational์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์˜€๋‹ค๋ฉด ์• ์ดˆ์— ์ฟผ๋ฆฌ ์ž์ฒด๊ฐ€ ๋‚ ๋ผ๊ฐ€์ง€ ์•Š์•˜์„ ๊ฒƒ์ด๋‹ค. (๋ฌผ๋ก  ๋‚˜์˜ ๊ฐ€์ •์ด๋‹ค)

5. โญ๏ธ ํ•˜์ง€๋งŒ, ์ง€๊ธˆ์€ jdbcTemplate์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌ๋Š” ๋‚ ๋ผ๊ฐ„๋‹ค. ํ•˜์ง€๋งŒ, ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

6. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ์กด์˜ ํŠธ๋žœ์žญ์…˜๊ณผ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ถ„๊ธฐ์‹œ์ผœ์„œ ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กค๋ฐฑ์ด ๋˜๋„๋ก ๋งŒ๋“ ๋‹ค!

์›๋ž˜๋Š” ์ด๋ฏธ ์ปค๋ฐ‹๋œ ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜์ •์ด๋“  ์‚ญ์ œ๋“  ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ (JPA ๊ฐ™์ด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด์„œ flush๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ), jdbcTemplate์˜ ๊ฒฝ์šฐ ํŠธ๋žœ์žญ์…˜ ์–ด๋…ธํ…Œ์ด์…˜์— ์ƒ๊ด€์—†์ด ์ฟผ๋ฆฌ๋ฅผ execute ํ•˜๋Š” ์ˆœ๊ฐ„ DB์— ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์–ด๋„ ์ €์žฅ ์ž์ฒด๋Š” ์ž˜ ๋˜์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ '์ด๋ฏธ ์ปค๋ฐ‹๋œ ํŠธ๋žœ์žญ์…˜'์— ์ฐธ์—ฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋กค๋ฐฑ ์—ญ์‹œ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋œ ๊ฒƒ์ด๋‹ค.

 

๊ทธ๋ž˜์„œ, ํŠธ๋žœ์žญ์…˜ ์ „ํŒŒ ์†์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ ๋ณ„๋„์˜ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ถ„๊ธฐํ•ด์ฃผ์—ˆ๋‹ค.

ํŠธ๋žœ์žญ์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ „ํŒŒ ์†์„ฑ์ด 'REQUIRED'๋กœ ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ธฐ์กด ํŠธ๋žœ์žญ์…˜์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์— ์ฐธ์—ฌํ•œ๋‹ค. (์—†์œผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ) ๋ฐ˜๋ฉด์—, REQUIRES_NEW๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ํ•ญ์ƒ ์ƒˆ๋กœ์šด ํŠธ๋žœ์žญ์…˜์—์„œ ์‹คํ–‰๋œ๋‹ค.

๋‘ ํŠธ๋žœ์žญ์…˜์ด ๋ถ„๋ฆฌ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— B์˜ ๋กค๋ฐฑ์ด A์—๋Š” ์˜ํ–ฅ์„ ๋ผ์น˜์ง€ ์•Š๊ฒŒ ๋˜์—ˆ์œผ๋ฉฐ, ๋•๋ถ„์— ์ฟ ํฐ ๋ฐœํ–‰ ๋„์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ํšŒ์›๊ฐ€์ž… ๋กœ์ง๊ณผ ๊ด€๊ณ„ ์—†์ด ์ž˜ ๋กค๋ฐฑ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์•„๋ž˜๋Š” ์ด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

ํšŒ์› ๊ฐ€์ž… ์‹œ ์ด๋ฏธ ๋ฐœํ–‰๋œ ์ฟ ํฐ์ด ์žˆ๋‹ค๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ํ•ด๋‹น ์ฟ ํฐ์— ๋Œ€ํ•ด์„œ ๋‹ค์‹œ ๋ฐœ๊ธ‰์ด ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

@Test
@DisplayName("ํšŒ์› ๊ฐ€์ž… ์ถ•ํ•˜ ์ฟ ํฐ ๋ฐœํ–‰ ์‹œ ์ด๋ฏธ ๋ฐœํ–‰์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.")
void saveJoinMemberCoupon_already_issued() {
    // given
    final String ์ ธ๋‹ˆ_์ด๋ฆ„ = "journey2";
    final Long ์ €์žฅ๋œ_์ ธ๋‹ˆ_์•„์ด๋”” = ์‚ฌ์šฉ์ž๋ฅผ_์ €์žฅํ•œ๋‹ค(์ ธ๋‹ˆ_์ด๋ฆ„);
    final JoinMemberCouponEvent ํšŒ์›_๊ฐ€์ž…_์ฟ ํฐ_๋ฐœํ–‰_์ด๋ฒคํŠธ = new JoinMemberCouponEvent(์ €์žฅ๋œ_์ ธ๋‹ˆ_์•„์ด๋””);
    memberCouponService.saveJoinMemberCoupon(ํšŒ์›_๊ฐ€์ž…_์ฟ ํฐ_๋ฐœํ–‰_์ด๋ฒคํŠธ);

    // then
    assertThatThrownBy(() -> memberCouponService.saveJoinMemberCoupon(ํšŒ์›_๊ฐ€์ž…_์ฟ ํฐ_๋ฐœํ–‰_์ด๋ฒคํŠธ))
        .isInstanceOf(BadRequestException.class)
        .extracting("errorCode")
        .isEqualTo(ErrorCode.COUPON_ALREADY_EXIST);

    // then
    final List<MemberCouponDto> ์ ธ๋‹ˆ_์ฟ ํฐ๋“ค = memberCouponDao.findMyCouponsByName(์ ธ๋‹ˆ_์ด๋ฆ„);
    assertThat(์ ธ๋‹ˆ_์ฟ ํฐ๋“ค)
        .hasSize(1);
}

์˜ˆ์™ธ ๋ฐœ์ƒ ํ›„ ์ฟ ํฐ์„ ์กฐํšŒํ–ˆ์„ ๋•Œ ์ด๋ฏธ ๋ฐœํ–‰๋œ 1์žฅ์˜ ์ฟ ํฐ๋งŒ ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 


 

๐Ÿ’ฌ ์ฃผ๋ฌธ ์ทจ์†Œ ์ •์ฑ… ์„ค๊ณ„ํ•˜๊ธฐ

์‚ฌ์‹ค ์ด๊ฑฐ๋Š” ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ์ง„ํ–‰ํ•œ ๊ฑด ์•„๋‹ˆ๊ณ , ๋‚ด๊ฐ€ ๋”ฐ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์–ด์„œ ๋งŒ๋“  api์ด๋‹ค.

์ฃผ๋ฌธํ•œ ์ง€ 3์ผ ์ด๋‚ด๋ผ๋ฉด ์ „์•ก, 7์ผ ์ด๋‚ด๋ผ๋ฉด ๋ฐ˜์•ก์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ๋ฌผ๋ก  ๊ทธ ์ด์ƒ์€ ํ™˜๋ถˆ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

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

 

// Before
public RefundPolicyComposite refundPolicyComposite(
    final RefundPolicy fullRefundPolicy, final RefundPolicy halfRefundPolicy) {
    return new RefundPolicyComposite(
        List.of(fullRefundPolicy, halfRefundPolicy)
    );
}

// After
public RefundPolicyComposite refundPolicyComposite(final List<RefundPolicy> refundPolicies) {
    return new RefundPolicyComposite(refundPolicies);
}

์ •์ฑ… ๊ด€๋ จ ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ์ฃผ์ž…ํ•  ๋•Œ ๊ธฐ์กด์—๋Š” RefundPolicy์— ๋Œ€ํ•ด์„œ ํ•˜๋‚˜์”ฉ ์ง‘์–ด๋„ฃ์—ˆ๋Š”๋ฐ ๋งค๋ฒˆ Config๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์กด์žฌํ•ด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด List๋ฅผ ํ†ตํ•ด์„œ ์ฃผ์ž…๋ฐ›๋„๋ก ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

 

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ์Šค์บ” ์ˆœ์„œ๋Œ€๋กœ ๋“ฑ๋ก์ด ๋˜์–ด์„œ (์‚ฌ์ „์ˆœ์œผ๋กœ ๋ณด์ธ๋‹ค) Full, Half๊ฐ€ ๋นˆ์œผ๋กœ ์ฃผ์ž…๋˜์—ˆ๋‹ค. ์Šค์บ” ์ˆœ์„œ์— ๋”ฐ๋ฅธ ๋นˆ์— ๋Œ€ํ•œ ์ˆœ์„œ ๋ณด์žฅ์ด ํ™•์‹คํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ •์ฑ…์ด ์ ์šฉ ๊ฐ€๋Šฅํ•œ์ง€ ํŒ๋‹จํ•˜๋Š” ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ„๊ฐ’์„ ์ฒ ์ €ํ•˜๊ฒŒ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

// Before - 50% ํ• ์ธ ์‹œ 3์ผ ์ด์ƒ์ธ์ง€๋งŒ ๊ฒ€์ฆ
@Override
public boolean isAvailable(final Order order, final LocalDateTime currentTime) {
    return currentTime.isBefore(order.getOrderedAt().plusDays(HALF_REFUND.getDay()));
}

// After - 3์ผ ~ 7์ผ์ธ์ง€ ๊ฒ€์ฆ
@Override
public boolean isAvailable(final Order order, final LocalDateTime currentTime) {
    final LocalDateTime orderedAt = order.getOrderedAt();
    return currentTime.isAfter(orderedAt.plusDays(FULL_REFUND.getDay())) &&
        currentTime.isBefore(orderedAt.plusDays(HALF_REFUND.getDay()));
}

 

๋˜ํ•œ, ๊ธˆ์•ก์˜ ๊ฒฝ์šฐ ์ •๋ฐ€ํ•œ ๊ณ„์‚ฐ์„ ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— BigDecimal์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค๊ณ  ๋“ค์–ด์„œ ์š”๋ ‡๊ฒŒ ์ ์šฉํ•˜์˜€๋‹ค.

@Override
public BigDecimal calculatePrice(final BigDecimal price) {
    final BigDecimal refundRate = BigDecimal.valueOf((PERCENTAGE - HALF_REFUND_RATE) * DECIMAL_CONVERSION);
    return price.multiply(refundRate).setScale(0, RoundingMode.DOWN);
}

setScale(0, RoundingMode.DOWN)์„ ํ†ตํ•ด์„œ ์†Œ์ˆ˜์  ์ดํ•˜์˜ ๊ฐ’์„ ๋ฒ„๋ฆฌ๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. (์•„๋‹ˆ๋ฉด ๊ฐ’์ด ๋”๋Ÿฌ์›Œ์ง„๋‹ค!)

 


 

๐Ÿ’ฌ ํŽ˜์ด์ง• ์ฟผ๋ฆฌ ์ž‘์„ฑํ•˜๊ธฐ

์ด ๋ถ€๋ถ„๋„ ํ•ฉ์˜๋œ API๋Š” ์•„๋‹ˆ๊ณ , ๊ฐœ์ธ ํ•™์Šต์šฉ์œผ๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

ํ•ญ์ƒ JPA๋กœ ๋งŒ๋“ค์—ˆ๋‹ค๊ฐ€ ์Œฉ ์ฟผ๋ฆฌ๋กœ ์ž‘์„ฑํ•ด๋ณธ ๊ฑด ์ฒ˜์Œ์ด์—ˆ๋Š”๋ฐ, ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ต์ง€ ์•Š์•˜๋‹ค.

@GetMapping("/pages")
public ResponseEntity<ProductPageResponse> getProductsByPage(@RequestParam("page") final int page,
                                                             @RequestParam("size") final int size) {
    return ResponseEntity.ok(productService.getProductsByPage(page, size));
}

์ƒํ’ˆ ๋ชฉ๋ก์— ๋Œ€ํ•œ ํŽ˜์ด์ง• API์ด๋‹ค. ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์กฐํšŒํ•  ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ์™€ ํ•œ ํŽ˜์ด์ง€์— ๋ช‡ ๊ฐœ์”ฉ ๋ฐ›์„ ๊ฒƒ์ธ์ง€ ์ž…๋ ฅ๋ฐ›์•˜๋‹ค.

 

public class ProductPageResponse {

    private final long totalPage;
    private final List<ProductResponse> productResponse;
    ...
}

๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ๋Š” ์ด ํŽ˜์ด์ง€ ์ˆ˜์™€ ํ•ด๋‹น ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ์ƒํ’ˆ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜์˜€๋‹ค.

 

public List<ProductEntity> getProductsByPage(final int page, final int size) {
    final int offset = calculateOffset(page, size);
    final String sql = "SELECT id, name, image_url, price, is_deleted FROM product "
        + "ORDER BY id LIMIT ? OFFSET ?";
    return jdbcTemplate.query(sql, productEntityRowMapper, size, offset);
}

๋‚˜๋ฆ„ ํ•ต์‹ฌ์ด ๋˜๋Š” ํŽ˜์ด์ง€ ์ฒ˜๋ฆฌ ์ฟผ๋ฆฌ๋‹ค.

โญ๏ธ LIMIT์˜ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ ์ค‘์—์„œ ๋ช‡ ๊ฐœ๋งŒ ์กฐํšŒํ•  ๊ฒƒ์ธ์ง€ ์„ค์ •ํ•˜๊ณ , OFFSET์€ ์–ด๋””์„œ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜ฌ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

SELECT * FROM table_name LIMIT 10 OFFSET 20;
= 20๋ฒˆ์งธ (๊ณ„์‚ฐ๋œ ์˜คํ”„์…‹) ํ–‰๋ถ€ํ„ฐ 10๊ฐœ (size) ์กฐํšŒ
= 21๋ฒˆ์งธ ~ 30๋ฒˆ์งธ ํ–‰๊นŒ์ง€ ์ถœ๋ ฅ

 

์—ฌ๊ธฐ์„œ offset์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”๋ฐ, ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

private int calculateOffset(final int page, final int size) {
    if (page == 1) {
        return 0;
    }
    return (page - 1) * size;
}

 

ํด๋ผ์ด์–ธํŠธ ๊ฐœ๋ฐœ์ž์—๊ฒŒ 1๋ถ€ํ„ฐ ๊ฐ’์„ ๋ฐ›๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์˜€๋‹ค.

1์ด๋ผ๋ฉด ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€๋‹ˆ๊นŒ offset์„ 0์„, ๊ทธ ์ด์ƒ์ด๋ผ๋ฉด (page - 1) * size๋งŒํผ์„ ์„ค์ •ํ•ด์ฃผ์—ˆ๋‹ค.

๋งŒ์•ฝ page = 3, size = 10์ด๋ผ๋Š” ๊ฐ’์ด ๋“ค์–ด์™”๋‹ค๋ฉด 2 * 10 = 20๋ฒˆ์งธ ํ–‰๋ถ€ํ„ฐ 10๊ฐœ๋ฅผ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด ํŽ˜์ด์ง€์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ณ„์‚ฐํ•˜์˜€๋‹ค.

private long calculateTotalPage(final int size, final long totalProductCount) {
    long totalPage = totalProductCount / size;

    if (totalProductCount % size > 0) {
        totalPage++;
    }
    return totalPage;
}

์ƒํ’ˆ์˜ ์ด ๊ฐœ์ˆ˜์—์„œ ํ•œ ํŽ˜์ด์ง€์— ์ถœ๋ ฅํ•  ์ƒํ’ˆ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋‚˜๋ˆˆ ๊ฐ’์„ ์ด ํŽ˜์ด์ง€๋กœ ์„ค์ •ํ•˜์˜€๋‹ค.

์ด๋•Œ, ์ด ๊ฐœ์ˆ˜์—์„œ ํ•œ ํŽ˜์ด์ง€์— ์ถœ๋ ฅํ•  ์ƒํ’ˆ์˜ ๊ฐœ์ˆ˜์˜ ๋‚˜๋จธ์ง€ ๊ฐ’์ด 0๋ณด๋‹ค ํฌ๋ฉด 1์„ ์ฆ๊ฐ€์‹œ์ผœ์ฃผ์—ˆ๋‹ค.

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

 

์‚ฌ์‹ค page๋ž‘ size์— ๋Œ€ํ•ด์„œ๋„ ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ฑธ์–ด๋‘ฌ์•ผ ํ•˜๊ณ  (0 ์ด์ƒ์ด๋ผ๋“ ์ง€, ์ •ํ•ด์ง„ ๊ฐ’์œผ๋กœ size๋ฅผ ๋ฐ›๋Š”๋‹ค๋“ ์ง€...)

๋ฐ˜ํ™˜ ๊ฐ’์—๋„ ํ˜„์žฌ ํŽ˜์ด์ง€๋ž‘ ์‚ฌ์ด์ฆˆ ๊ฐ™์ด ์กฐ๊ธˆ ๋” ๋‹ค์–‘ํ•˜๊ฒŒ ๋‚ด๋ ค์คฌ์–ด์•ผ ํ–ˆ๋Š”๋ฐ ๊ฐœ์ธ ํ•™์Šต์šฉ์ด๋ผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค. ใ…Žใ…Ž

๋‚˜๋ฆ„ ์žฌ๋ฐŒ์—ˆ๋˜ ๊ฒฝํ—˜์ด์—ˆ๋‹ค!

 


 

์ด๋ฒˆ ๋ฏธ์…˜์€ ํ™•์‹คํžˆ ๊ธฐ์กด ์ฝ”๋“œ ๋ฆฌํŒฉํ„ฐ๋ง + ์ธํ”„๋ผ ๊ตฌ์ถ•์— ์‹ ๊ฒฝ์„ ๋งŽ์ด ์จ์„œ ๊ทธ๋Ÿฐ๊ฐ€ ํฌ๊ฒŒ ํšŒ๊ณ ํ•  ๋‚ด์šฉ์ด ์—†์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ชฉํ‘œํ–ˆ๋˜ ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋„ ์จ๋ณด๊ณ , ์—ฌ๋Ÿฌ ๊ฐ€์ง€๋กœ ๋ฐฐ์šด ์ ์ด ๋งŽ์•˜์–ด์„œ ๊ทธ๋Ÿฐ๊ฐ€ ๋ฟŒ๋“ฏํ–ˆ๋˜ ์‹œ๊ฐ„์ด์—ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ๋ฏธ์…˜์ธ ๋งŒํผ ์—ด์‹ฌํžˆ ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์•„์„œ ์ข‹๋‹ค!

 

๋ ˆ๋ฒจ 2 ํšŒ๊ณ ๋„ ์–ผ๋ฅธ ์“ฐ๊ณ  ๋ฐฉํ•™ ์‹ ๋‚˜๊ฒŒ ๋†€์•„์•ผ์ง€

๐Ÿ˜Ž

Comments