๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๊ฐœ๋ฐœ/์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค

์‹ค์Šต์„ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(TDD)

๋“ค์–ด๊ฐ€๋ฉฐ

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

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

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

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

 

ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์€ ๋ฌด์—‡์ธ๊ฐ€?

ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(Test-Driven Development, TDD)๋Š” ์ผ„ํŠธ ๋ฒก(Kent Beck)์ด 1999๋…„ ์ต์ŠคํŠธ๋ฆผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์ œ์•ˆํ•œ ๊ฐœ๋ฐœ๋ก ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์†Œํ”„ํŠธ์›จ์–ด ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์ „์— ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ„ํ˜น ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์„ ์ข‹์€ ์„ค๊ณ„ ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ ๋ณด๊ธฐ๋„ ํ•˜์ง€๋งŒ, ์ผ„ํŠธ ๋ฒก์€ '**์„ค๊ณ„ ํ”ผ๋“œ๋ฐฑ**'์ด๋ผ๊ณ  ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์€ ํ˜„์žฌ ์„ค๊ณ„์˜ ํ’ˆ์งˆ์„ ํ‰๊ฐ€ํ•˜๊ณ  ๊ฐœ์„  ๋ฐฉํ–ฅ์„ ์•Œ๋ ค์ฃผ๋Š” ๋„๊ตฌ์ธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

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

  • ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ์—๋งŒ ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ์ค‘๋ณต์„ ์ œ๊ฑฐํ•œ๋‹ค.

์ด ๋‘ ๊ฐ€์ง€์˜ ๋‹จ์ˆœํ•œ ๊ทœ์น™๋งŒ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ ๊ทœ์น™์„ ํ†ตํ•ด ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ํ•ต์‹ฌ์ธ ‘Red-Green-Refactor’ ์‚ฌ์ดํด์ด ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์‚ฌ์ดํด์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.

  1. Red ใ…ก ์‹คํŒจํ•˜๋Š” ์ž‘์€ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  2. Green ใ…ก ์ตœ๋Œ€ํ•œ ๋น ๋ฅด๊ฒŒ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  3. Refactor ใ…ก Red-Green ๊ณผ์ •์—์„œ ์ƒ๊ฒจ๋‚œ ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค.

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

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

 

ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ ์‹ค์Šต - ํšŒ์›๊ฐ€์ž… API

๊ฐ„๋‹จํ•˜๊ฒŒ ์Šคํ”„๋ง์„ ํ†ตํ•œ ํšŒ์›๊ฐ€์ž… API๋ฅผ ๋งŒ๋“œ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ํ• ๋‹น ๋ฐ›์•˜๋‹ค๊ณ  ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์š”๊ตฌ์‚ฌํ•ญ

  • ์‚ฌ์šฉ์ž๋Š” ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ํšŒ์›๊ฐ€์ž… ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋ฉ”์ผ์€ ์ค‘๋ณต๋  ์ˆ˜ ์—†๋‹ค.
  • ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค.

์‚ฌ์ดํด 1: Member ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ

Red ๐Ÿ›‘

๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์ด๋ฉด์„œ ์ˆœ์ˆ˜ ๋„๋ฉ”์ธ ๊ฐ์ฒด์ธ Member ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. Member๋Š” ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋˜๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

public class MemberTest {

    @Test
    void ์ด๋ฉ”์ผ๊ณผ_๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ_ํšŒ์›์„_์ƒ์„ฑํ•œ๋‹ค() {
        // given
        String email = "member@email.com";
        String password = "password123";

        // when
        Member member = new Member(email, password);

        // then
        assertThat(member.getEmail()).isEqualTo(email);
        assertThat(member.getPassword()).isEqualTo(password);
    }
}


ํ˜„์žฌ ์–ด๋– ํ•œ ๊ฐ์ฒด ์ƒ์„ฑ๋„ ์—†์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ–ˆ๊ธฐ์— ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ผ„ํŠธ ๋ฒก์€ ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋˜ํ•œ Red์˜ ๊ณผ์ •์œผ๋กœ ๋ณด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ด์ œ ์ด๋ฅผ ๋น ๋ฅด๊ฒŒ ์„ฑ๊ณตํ•˜๋„๋ก ๊ตฌํ˜„ํ•ด๋ด…์‹œ๋‹ค.

 

Green ๐ŸŸข

๋น ๋ฅด๊ฒŒ Member ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ†ต๊ณผ๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•œ ๋ถ€๋ถ„๋“ค์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

public class Member {
    
    private final String email;
    private final String password;

    public Member(String email, String password) {
        this.email = email;
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public String getPassword() {
        return password;
    }
}


๊ทธ๋Ÿผ ์ด์ œ ๋ฆฌํŒฉํ„ฐ๋ง์„ ํ•ด์•ผ ํ• ๊นŒ์š”? ํ˜„์žฌ๋Š” ๋ฆฌํŒฉํ„ฐ๋ง ํ•ด์•ผํ•  ์ฝ”๋“œ๊ฐ€ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ•ด๋‹น ๊ณผ์ •์„ ๋น ๋ฅด๊ฒŒ ๊ฑด๋„ˆ๋›ฐ๊ณ  ๋‹ค์‹œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋‘ ๊ฐœ์˜ ์š”๊ตฌ์‚ฌํ•ญ ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ํ…Œ์ŠคํŠธ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? ์ด๋ฉ”์ผ์€ ์ค‘๋ณต๋  ์ˆ˜ ์—†๋Š” ์š”๊ตฌ์‚ฌํ•ญ์€ Member ๊ฐ์ฒด๋งŒ์œผ๋กœ๋Š” ํŒ๋‹จํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ์š”๊ตฌ์‚ฌํ•ญ์€ Member ๊ฐ์ฒด๋งŒ์œผ๋กœ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๊ฒ ๋„ค์š”. ์ด๋ฅผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค.

 

์‚ฌ์ดํด 2: ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ

Red ๐Ÿ›‘

ํ…Œ์ŠคํŠธ๋ฅผ 8์ž ์ด์ƒ ๋˜๋Š” ๋ฏธ๋งŒ ๋‘ ๊ฐ€์ง€ ์ผ€์ด์Šค๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Test
void ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€_8์ž_๋ฏธ๋งŒ์ด๋ฉด_์˜ˆ์™ธ๊ฐ€_๋ฐœ์ƒํ•œ๋‹ค() {
    // given
    String email = "member@email.com";
    String shortPassword = "short";  // 5์ž
    
    // when-then
    assertThatThrownBy(() -> new Member(email, shortPassword))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.");
}

@Test
void ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€_8์ž_์ด์ƒ์ด๋ฉด_ํšŒ์›์ด_์ƒ์„ฑ๋œ๋‹ค() {
    // given
    String email = "member@email.com";
    String validPassword = "password";  // ์ •ํ™•ํžˆ 8์ž
    
    // when
    Member member = new Member(email, validPassword);
    
    // then
    assertThat(member).isNotNull();
}


8์ž ์ด์ƒ์ธ ๊ฒฝ์šฐ Member๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค๋Š” ํ…Œ์ŠคํŠธ๋Š” ์ด๋ฏธ 1๋‹จ๊ณ„์—์„œ ์ž‘์„ฑ์ด ๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, 1๋‹จ๊ณ„์˜ ํ…Œ์ŠคํŠธ๋Š” 8์ž ์ดˆ๊ณผ์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์žˆ๊ธฐ์— ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ •ํ™•ํžˆ 8์ž์ธ ๊ฒฝ๊ณ„๊ฐ’ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€๋กœ ์ž‘์„ฑํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

8์ž ๋ฏธ๋งŒ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ…Œ์ŠคํŠธ์˜ ๊ฒฝ์šฐ์—๋Š” Member ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋˜์ ธ์งˆ ์ˆ˜ ์žˆ๊ฒ ์ฃ . ์—ฌ๊ธฐ์„œ ์ ์ ˆํ•œ ์˜ˆ์™ธ๋Š” IllegalArgumentException์ด๋ผ ์ƒ๊ฐํ•ด ์ด๋ฅผ ๊ฒ€์ฆํ•˜๊ณ , ๋ฉ”์‹œ์ง€๊นŒ์ง€ ์ถ”๊ฐ€๋กœ ํ™•์ธํ•ด์ค๋‹ˆ๋‹ค. ์ด์ œ ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด ์‹คํŒจํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ๋‹จ๊ณ„๋Š” ๋น ๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Green ๐ŸŸข

๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋Š” Member ๊ฐ์ฒด์˜ ์ƒ์„ฑ์ž์—์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ธธ์ด๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

public Member(String email, String password) {  
    if (password.length() < 8) {  
        throw new IllegalArgumentException("๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.");  
    }  
    this.email = email;  
    this.password = password;  
}


์ƒ์„ฑ์ž๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜๊ณ  ์‹คํ–‰ํ•ด๋ณด๋ฉด ์ „์ฒด ํ…Œ์ŠคํŠธ๊ฐ€ ์ž˜ ์„ฑ๊ณตํ•œ ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๋ฆฌํŒฉํ„ฐ๋ง ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๋„ ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” Member ์ƒ์„ฑ์ž ๋กœ์ง์—์„œ if๋ฌธ์œผ๋กœ ๋˜์–ด์žˆ๋Š” ๋ถ€๋ถ„์„ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌํ•ด๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Refactor โš™๏ธ

Member ๊ฐ์ฒด์˜ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

public class Member {  
  
    private String email;  
    private String password;  
  
    public Member(String email, String password) {  
        if (password.length() < 8) {  
            throw new IllegalArgumentException("๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” 8์ž ์ด์ƒ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.");  
        }  
        this.email = email;  
        this.password = password;  
    }
  
    public String getEmail() {  
        return email;  
    }
  
    public String getPassword() {  
        return password;  
    }
}


์—ฌ๊ธฐ์—๋Š” ๋ฆฌํŒฉํ„ฐ๋ง ํฌ์ธํŠธ๊ฐ€ 3๊ฐœ ๋ณด์ž…๋‹ˆ๋‹ค.

  1. ๋งค์ง ๋„˜๋ฒ„๋ฅผ ์ƒ์ˆ˜๋กœ ์ถ”์ถœ
  2. ๊ฒ€์ฆ ๋กœ์ง์„ ๋ณ„๋„ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌ
  3. ์ƒ์„ฑ์ž๋Š” ๊ฐ์ฒด ์ดˆ๊ธฐํ™”์— ์ง‘์ค‘

์ด ์„ธ ๊ฐ€์ง€๋ฅผ ๋ฐ˜์˜ํ•˜๋ฉด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

public class Member {

    public static final int PASSWORD_LENGTH = 8;
    
    private final String email;
    private final String password;

    public Member(String email, String password) {
        validatePasswordLength(password);
        this.email = email;
        this.password = password;
    }

    private void validatePasswordLength(String password) {
        if (password.length() < PASSWORD_LENGTH) {
            throw new IllegalArgumentException();
        }
    }
    // ...
}


์ด์ •๋„๋ฉด Member ๊ฐ์ฒด๊ฐ€ ์ž˜ ๋ฆฌํŒฉํ„ฐ๋ง ๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จ๋˜๋Š”๋ฐ์š”. ์ด์ œ ๋‹ค๋ฅธ ์š”๊ตฌ์‚ฌํ•ญ์ธ ‘์ด๋ฉ”์ผ์€ ์ค‘๋ณต๋  ์ˆ˜ ์—†๋‹ค.’๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์‚ฌ์ดํด 3: ์ด๋ฉ”์ผ ์ค‘๋ณต ๊ฒ€์ฆ

Red ๐Ÿ›‘

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

์ด๋ฅผ ์œ„ํ•ด ํšŒ์›๊ฐ€์ž…์„ ์ˆ˜ํ–‰ํ•˜๋Š” MemberService์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class MemberServiceTest {

    @Autowired
    private MemberService memberService;
    
    @Autowired
    private MemberRepository memberRepository;

    @Test
    void ํšŒ์›๊ฐ€์ž…_์‹œ_์ด๋ฉ”์ผ์€_์ค‘๋ณต๋ _์ˆ˜_์—†๋‹ค() {
        // given
        var email = "duplicate@email.com";
        memberRepository.save(new Member(email, "password123"));

        // when - then
        assertThatThrownBy(() -> memberService.register(email, "password456"))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessage("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค: " + email);
    }
    
    @Test 
    void ์œ ํšจํ•œ_์ •๋ณด๋กœ_ํšŒ์›๊ฐ€์ž…์—_์„ฑ๊ณตํ•œ๋‹ค() { 
    // given 
    String email = "new@email.com"; 
    String password = "password123"; 
    
    // when 
    Long memberId = memberService.register(email, password); 
    
    // then
    Member savedMember = memberRepository.findById(memberId)
    .orElseThrow(); 
    assertThat(savedMember.getEmail()).isEqualTo(email); 
    }
}


Member์˜ ์ €์žฅ์„ ์œ„ํ•œ MemberRepository์™€ ํ•จ๊ป˜ ํšŒ์›๊ฐ€์ž…์„ ์„ฑ๊ณตํ•˜๋Š” ํ…Œ์ŠคํŠธ์™€ ์ด๋ฉ”์ผ ์ค‘๋ณต์œผ๋กœ ์ธํ•ด ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ ๋‘ ๊ฐ€์ง€๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์„ฑ๊ณต์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„  ๊ฝค ๋งŽ์€ ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Green ๐ŸŸข

๊ฐ€์žฅ ๋จผ์ € Member๋ฅผ ์—”ํ‹ฐํ‹ฐ๋กœ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@Entity  
public class Member {  
  
    public static final int MIN_PASSWORD_LENGTH = 8;  
  
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
    private String email;  
    private String password;  
  
    protected Member() {  
    }  
  
    public Member(String email, String password) {  
        this(null, email, password);  
    }  
  
    public Member(Long id, String email, String password) {  
        validatePasswordLength(password);  
        this.id = id;  
        this.email = email;  
        this.password = password;  
    }
    // ...
    
    public Long getId() {  
    	return id;  
    }
    // ...
}


์ด์ œ MemberService์™€ MemberRepository๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@Service  
public class MemberService {  
  
    private final MemberRepository memberRepository;  
  
    public MemberService(MemberRepository memberRepository) {  
        this.memberRepository = memberRepository;  
    }  
  
@Transactional
    public Long register(String email, String password) {  
        if (memberRepository.existsByEmail(email)) {  
            throw new IllegalArgumentException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค: " + email);  
        }  
  
        Member member = new Member(email, password);  
        Member savedMember = memberRepository.save(member);  
        return savedMember.getId();  
    }  
}
public interface MemberRepository extends Repository<Member, Long> {  
  
    Member save(Member member);  
  
    Optional<Member> findById(Long id);  
  
    boolean existsByEmail(String email);  
}


๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์„ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ์ธ๋ฉ”๋ชจ๋ฆฌ H2๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ๋ถ€ํ„ด JPA์™€ ์ฝ”๋“œ๋“ค์ด ์—ฐ๋™๋˜๋ฉด์„œ ๊ทธ๋ฆฐ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ๊ณผ์ •์ด ๊ฝค ๊ธธ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Member ๊ฐ์ฒด์—์„œ์˜ ๋ณ€ํ™”๋Š” JPA๋ฅผ ์œ„ํ•œ ์—ฐ๋™ ๊ณผ์ •์ด์—ˆ์„ ๋ฟ์ด๊ณ , ํ…Œ์ŠคํŠธ ํ•˜๋ ค๊ณ  ํ–ˆ๋˜ MemberService์˜ ๋กœ์ง์„ ๋ณด๋ฉด ์—ฌ์ „ํžˆ ์ตœ๋Œ€ํ•œ ๋น ๋ฅด๊ฒŒ ๊ทธ๋ฆฐ์„ ๋งŒ๋“ค์—ˆ๋‹ค๋Š” ์ ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Refactor โš™๏ธ

์ด์ œ ๋‚จ์€ ์ผ์€ MemberService๋ฅผ ๋ฆฌํŒฉํ„ฐ๋ง ํ•˜๋Š” ์‚ฌ์ดํด์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋„ Member์™€ ๋น„์Šทํ•˜๊ฒŒ ๊ฒ€์ฆ ๋กœ์ง์„ ๋ณ„๋„ ๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Service
public class MemberService {

    @Autowired
    private MemberRepository memberRepository;

    public Member register(String email, String password) {
        validateEmailDuplication(email);

        Member member = new Member(email, password);
        return memberRepository.save(member);
    }

    private void validateEmailNotDuplicated(String email) { 
        if (memberRepository.existsByEmail(email)) { 
        	throw new IllegalArgumentException("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค: " + email); 
        } 
    }
}


์ด ๊ธฐ์„ธ๋ฅผ ์ด์–ด์„œ Controller๊นŒ์ง€ ํ™•์žฅํ•ด๊ฐ€๋ฉฐ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ ์‹ค์Šต์„ ์ด์–ด๊ฐ€๋ฉด ์ข‹๊ฒ ์ง€๋งŒ, ๋ถ„๋Ÿ‰์ƒ ์ œ์™ธํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ง€๊ธˆ๊นŒ์ง€์˜ ๊ณผ์ •์„ ์ž˜ ๋”ฐ๋ผ์˜ค์…จ๋‹ค๋ฉด ํ˜ผ์ž์„œ๋„ ์ถฉ๋ถ„ํžˆ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์„๊ฑฐ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์‹ค์Šต์—์„œ ํ™•์ธํ•œ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ์ด์ 

์•„์ฃผ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ์˜€์ง€๋งŒ, ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ์žฌ๋ฏธ๋ฅผ ๋А๋ผ์…จ์„๊ฑฐ๋ผ ๋ฏฟ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ด์ ๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค ์šฐ์„  ์„ค๊ณ„

์•ž์„œ MemberServiceTest์—์„œ ํšŒ์›๊ฐ€์ž… ๊ด€๋ จ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ‘์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ํšŒ์›๊ฐ€์ž… ๋กœ์ง์„ ๋จผ์ € ์ž‘์„ฑํ•˜์ง€?’๋ผ๋Š” ๊ณ ๋ฏผ๋ณด๋‹ค ‘์–ด๋–ป๊ฒŒ ํšŒ์›๊ฐ€์ž…์„ ์‚ฌ์šฉํ•˜์ง€?’๋ผ๋Š” ๊ณ ๋ฏผ์„ ๋จผ์ €ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ๊ด€์ ์—์„œ API๋ฅผ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ ๋งํ•˜๋Š” ‘**๋ฌป์ง€๋ง๊ณ , ์‹œ์ผœ๋ผ(Tell, Don’t Ask)**’๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์‹ค์ฒœํ•  ์ˆ˜ ์žˆ๋Š” ๋Œ€๋ชฉ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

์ ์ง„์ ์ธ ๋ณต์žก๋„ ๊ด€๋ฆฌ

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

ํ•˜์ง€๋งŒ, ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์„ ํ†ตํ•ด ์š”๊ตฌ์‚ฌํ•ญ์„ ํ•˜๋‚˜์”ฉ ์ ์ง„์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ํ๋ฆ„์„ ์ซ“์•„๊ฐ€๊ธฐ ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค.

  1. Member ์ƒ์„ฑ
  2. ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ
  3. ์ด๋ฉ”์ผ ์ค‘๋ณต ๊ฒ€์ฆ

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

๋ฆฌํŒฉํ„ฐ๋ง ๋‚ด์„ฑ

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

๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ

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

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

์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ์ด์ ๋งŒ ๋ณด๋ฉด ๋งˆ์น˜ ๊ฐœ๋ฐœ์„ ์ž˜ํ•˜๊ฒŒ ๋˜๋Š” ๋งˆ๋ฒ• ๊ฐ™์Šต๋‹ˆ๋‹ค๋งŒ ๋‹น์—ฐํžˆ ๋‹จ์ ๋„ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์˜ ๋‹จ์ 

์ž˜๋ชป๋œ ํ…Œ์ŠคํŠธ์˜ ํ•จ์ •

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

์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ

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

๊ทธ๋Ÿผ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ …

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

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

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

์ด๋ฒˆ ๊ธ€์ด ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•๋ก ์˜ ์†Œ๊ฐœ๋ฅผ ๋„˜์–ด, “๋ถˆ์™„์ „ํ•œ ํ˜„์‹ค ์†์—์„œ๋„ ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ํ–ฅํ•ด ๋‚˜์•„๊ฐ€๋ ค๋Š” ๊ฐœ๋ฐœ์ž์˜ ํƒœ๋„”๋กœ์„œ ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์„ ๋‹ค์‹œ ๋ฐ”๋ผ๋ณด๋Š” ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค.


์ฐธ๊ณ ์ž๋ฃŒ

TDD ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?
์‹ค์ „์—์„œ TDDํ•˜๊ธฐ
TDD Isn’t Design
Test-Driven Development: By Example