๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๋„์„œ ๋ฆฌ๋ทฐ/IT

"์‹ค๋ฌด๋กœ ํ†ตํ•˜๋Š” ํด๋ฆฐ ์ฝ”๋“œ" ๋„์„œ ๋ฆฌ๋ทฐ

by ์ฃผ๋ฐœ2 2024. 8. 26.
๋ฐ˜์‘ํ˜•

 

ํ•œ๋น›๋ฏธ๋””์–ด์˜ ๋‚˜๋Š”๋ฆฌ๋ทฐ์–ด๋‹ค 2024๋ฅผ ํ†ตํ•ด 8์›”, ์‹ค๋ฌด๋กœ ํ†ตํ•˜๋Š” ํด๋ฆฐ ์ฝ”๋“œ (Clean Code Cookbook) ๋„์„œ๋ฅผ ์ œ๊ณต ๋ฐ›์•„ ์ฝ์–ด๋ณด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

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

 

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋ง์”€๋“œ๋ฆฌ๋ฉด ํด๋ฆฐ ์ฝ”๋“œ (Clean Code) ๋„์„œ์™€ ๋น„์Šทํ•œ ๋‚ด์šฉ๋“ค์ด ๋งŽ์•˜์–ด์„œ ํด๋ฆฐ ์ฝ”๋“œ๋ฅผ ์ฝ์—ˆ๋‹ค๋ฉด, ๊ตณ์ด ์ด ์ฑ…์„ ๋ณด์ง€๋Š” ์•Š์•„๋„ ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค..(?)

 

 

2์žฅ ๊ณต๋ฆฌ ์„ค์ •

38p, ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์œผ๋ฉฐ MAPPER (๋งคํผ) ๋ผ๋Š” ์•ฝ์–ด๋กœ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค.
MAPPER (Model: Abstract Partial and Programmable Explaining Reality), ๋ชจ๋ธ์€ ๋ถ€๋ถ„์  ์ถ”์ƒํ™”์™€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ†ตํ•ด ํ˜„์‹ค์„ ๋ฌ˜์‚ฌํ•ด์•ผ ํ•จ

 

MAPPER ๋ผ๋Š” ์šฉ์–ด๊ฐ€ ์ฑ… ์—ฌ๊ธฐ ์ €๊ธฐ์— ๋‚˜์˜ค๋Š”๋ฐ์š”, ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด ํ˜„์‹ค์„ ๋ฌ˜์‚ฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฐœ๋…์ž…๋‹ˆ๋‹ค.

 

 

3์žฅ ๋นˆ์•ฝํ•œ ๋ชจ๋ธ

51p, ๊ฐ์ฒด๋ฅผ ๋””์ž์ธํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋งŒ ๋ชจ๋ธ๋งํ•˜๋Š” ๋นˆ์•ฝํ•œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ธฐ๋ณด๋‹ค๋Š”, ๊ฐ์ฒด์˜ ๋™์ž‘์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๋””์ž์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค
// ...

private List<Integer> data;

public List<Integer> getData() {
  return data; // ์บก์Šํ™” ์œ„๋ฐ˜
}
getData() ๋ฉ”์„œ๋“œ๋Š” ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ์ปฌ๋ ‰์…˜์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“œ๋Š” ๋Œ€์‹  ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ ์ปฌ๋ ‰์…˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ, ์™ธ๋ถ€์—์„œ ์ปฌ๋ ‰์…˜์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ์— ์ง์ ‘ ๋ฐ˜์˜๋˜์–ด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘, ๊ฒฐํ•จ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

4์žฅ ๊ธฐ๋ณธํ˜• ์ง‘์ฐฉ

72p, ์ž‘์€ ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ (VO, Value Object)
// As-Is
public Class Person {
  private final String name;
	
	...
}

// To-Be
public class Person {
  private final Name name;
	
	...
}

VO ๊ฐ์ฒด๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋„ ํ•˜๊ณ , ์‚ฌ์šฉํ•˜์ง€ ์•Š์€ ์ผ€์ด์Šค๋„ ์žˆ์ง€๋งŒ, ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ๋“ค์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

6์žฅ ์„ ์–ธ์  ์ฝ”๋“œ

111p, ๋ณ€์ˆ˜, ๋ฉ”์„œ๋“œ, ํด๋ž˜์Šค์˜ ๋„ค์ด๋ฐ์€ ํ•ญ์ƒ ๊ธ์ •์ ์ธ ๋„ค์ด๋ฐ์œผ๋กœ ์„ค์ •ํ•˜์„ธ์š” (๊ฐ€๋…์„ฑ)
https://refactoring.com/catalog/removeDoubleNegative.html
// Bad
if (!work.isNotFinished()) ...

// Good
if (work.isDone()) ...

๊ฐœ์ธ์ ์œผ๋กœ ๊ณต๊ฐํ•˜๋Š” ๊ทœ์น™์ธ๋ฐ์š”, ์ด์ค‘ ๋ถ€์ •์˜ ๋„ค์ด๋ฐ์€ ํ•œ ๋ฒˆ ๋” ์ƒ๊ฐํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ๋งŽ์•˜๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

7์žฅ ๋ช…๋ช…

134p, ์ด๋ฆ„์„ ์–ด๋–ป๊ฒŒ ์ง€์–ด์•ผ ํ•  ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค๋ฉด, ๋งˆ์ง€๋ง‰ ํ•จ์ˆ˜ ํ˜ธ์ถœ๊ณผ ๋™์ผํ•œ ์ด๋ฆ„์œผ๋กœ ๋„ค์ด๋ฐ
// Bad
var result = calculateAverageSalary();

// Good
var averageSalary = calculateAverageSalary();

๋ชจ๋“  ๊ฐœ๋ฐœ์ž์˜ ๊ณ ์ถฉ = ๋„ค์ด๋ฐ

 

151p, data ๋ช…์นญ ํ”ผํ•˜๊ธฐ

data๊ฐ€ ํฌํ•จ๋œ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด๊ฐ€ ๋นˆ์•ฝํ•ด์ง€๋ฏ€๋กœ ๋„๋ฉ”์ธ ๋ฐ ์—ญํ• ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ด๋ฆ„์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋ณ€์ˆ˜์˜ ์ด๋ฆ„์€ ํ•ด๋‹น ๋ณ€์ˆ˜์˜ ๋„๋ฉ”์ธ๊ณผ ์ด์— ์ˆ˜ํ–‰ํ•˜๋Š” ์—ญํ• ์„ ๋ฐ˜์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
// Bad
if (!dataExists()) ...

// Good
if (!peopleFound()) ...

data... xxxdata ๋“ฑ์˜ ๋„ค์ด๋ฐ์€ ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ์—์„œ ๊ต‰์žฅํžˆ ๋งŽ์ด ๋ด์™”์—ˆ๋˜ ๋„ค์ด๋ฐ์ด์–ด์„œ ๋”์šฑ ๊ณต๊ฐ์ด ๊ฐ€๋Š” ๊ฒƒ ๊ฐ™๋„ค์š”.. ๐Ÿ˜‚

 

 

8์žฅ ์ฃผ์„

164p, ์ฃผ์„์„ ํ…Œ์ŠคํŠธ๋กœ ๋Œ€์ฒดํ•˜๊ธฐ

๋ฌธ์ œ: ํ•จ์ˆ˜๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์„ ์„ค๋ช…ํ•˜๋Š” ์ฃผ์„์ด ์žˆ๋Š”๋ฐ, ์ •์ ์ด๊ณ  ์˜ค๋ž˜๋œ ์„ค๋ช…๋Œ€์‹  ๋™์ ์ด๊ณ  ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ์ž˜ ๋˜๋Š” ๋ฌธ์„œํ™”๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค.
-> ์ฃผ์„์„ ๊ฐ€์ ธ์™€์„œ ํ•จ์ถ•์‹œ์ผœ ํ•จ์ˆ˜๋ช…์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•œ ๋’ค ์ฃผ์„์„ ์ œ๊ฑฐํ•˜์„ธ์š”.
def multiply(a, b):
	# ๋‘ ์ˆซ์ž ๊ณฑํ•œ ๊ฒฐ๊ณผ
	# ์ˆซ์ž ์ค‘ ํ•˜๋‚˜๊ฐ€ 0์ด๋ฉด ๊ฒฐ๊ณผ๋Š” 0
	# ๋‘ ์ˆซ์ž๊ฐ€ ๋ชจ๋‘ ์–‘์ˆ˜์ด๋ฉด ๊ฒฐ๊ณผ๋Š” ์–‘์ˆ˜
	# ๋‘ ์ˆซ์ž๊ฐ€ ๋ชจ๋‘ ์Œ์ˆ˜์ด๋ฉด ๊ฒฐ๊ณผ๋Š” ์–‘์ˆ˜
	return a * b
	
	
# Test
class TestMultiply(unittest.TestCase):
	def test_multiply_both_possitive_outcome_is_possitive ...
	def test_multiply_both_negative_outcome_is_positive
	def test_multiply_first_is_zero_outcome_is_zero ...
	def test_multiply_second_is_zero_outcome_is_zero ...

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

 

 

9์žฅ ํ‘œ์ค€

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

 

 

10์žฅ ๋ณต์žก์„ฑ

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

๋ณต์žก์„ฑ์„ ์ž˜ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๊ต‰์žฅํžˆ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”๋ฐ์š”, ์ฑ…์˜ ๋‚ด์šฉ๋“ค์€ ํด๋ฆฐ ์ฝ”๋“œ์™€ ๊ฑฐ์˜ ๋น„์Šทํ•ด์„œ ๋” ์ž‘์„ฑํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค.

(๋ฐ˜๋ณต ์ฝ”๋“œ ์ œ๊ฑฐ, ์†์„ฑ์œผ๋กœ ์ƒํƒœ ๋ณ€๊ฒฝํ•˜๊ธฐ, ์ฝ”๋“œ์—์„œ ๊ต๋ฌ˜ํ•จ ์ œ๊ฑฐํ•˜๊ธฐ, ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ์ฒด๋กœ ์ถ”์ถœํ•˜๊ธฐ ๋“ฑ๋“ฑ)

 

 

11์žฅ ๋ธ”๋กœํ„ฐ

205p, ๊ณผ๋„ํ•œ ๋ฉ”์„œ๋“œ ์ œ๊ฑฐํ•˜๊ธฐ

ํด๋ž˜์Šค๋ฅผ ๋ณด๋‹ค ์‘์ง‘๋ ฅ์žˆ๋Š” ์ž‘์€ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ„์„ธ์š”.
// Bad
public class MyHelperClass {
	public void print() ...
	public void format() ...
	public vod persist() ...
	
	...
}

// Good
public class Printer {
	public void print() ...
}

public class DateToStringFormatter {
	public void format() ...
}

...

Util ์„ฑ๊ฒฉ์˜ ํด๋ž˜์Šค๋“ค์— ๋Œ€ํ•œ ์˜ˆ์ œ์ด์ง€๋งŒ, ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” ์Šคํ”„๋ง์—์„œ๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. (API, ์ปจํŠธ๋กค๋Ÿฌ ๋ถ„๋ฆฌ)

 

 

13์žฅ ๋น ๋ฅธ ์‹คํŒจ

231p, switch ๋ฌธ์—์„œ ๊ธฐ๋ณธ๊ฐ’ ์ œ๊ฑฐํ•˜๊ธฐ

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

 

 

 

14์žฅ if ๋ฌธ

240p, ์šฐ๋ฐœ์  if ๋ฌธ์„ ๋‹คํ˜•์„ฑ์œผ๋กœ ๋Œ€์ฒดํ•˜๊ธฐ

ํ•„์ˆ˜๋กœ ์‹๋ณ„๋˜๋Š” if๋ฌธ, ์šฐ๋ฐœ์ ์ธ if๋ฌธ์— ๋Œ€ํ•œ ๊ตฌ๋ถ„
// Bad
public class Movie {

    private String rate;

    public Movie(String rate) {
        this.rate = rate;
    }

    public String getRate() {
        return rate;
    }
}


public class MovieWatcher {

    private final int age;

    public MovieWatcher(int age) {
        this.age = age;
    }

    public void watchMovie(Movie movie) {
        // ์šฐ๋ฐœ์ ์ธ (๊ฒฐํ•ฉ๋œ) ๋ถ„๊ธฐ์ฒ˜๋ฆฌ -> ๋ฌธ์ž์—ด๋กœ ๋“ฑ๊ธ‰์„ ๋ชจ๋ธ๋งํ•˜๊ธฐ๋กœ ํ•œ ์„ค๊ณ„๊ฐ€ ๋ฌธ์ œ๋ผ๊ณ  ํ•œ๋‹ค.
        // ์ด๋Š” ํ™•์žฅ์„ ์œ„ํ•ด ๊ฐœ๋ฐฉ์ ์ด์ง€๋„ ์•Š๊ณ , ์ˆ˜์ •์„ ์œ„ํ•ด  ํ์‡„์ ์ด์ง€๋„ ์•Š๋‹ค.
        if ((this.age < 18) && movie.getRate().equals("์„ฑ์ธ ์ „์šฉ")) {
            throw new IllegalStateException("์ด ์˜ํ™”๋ฅผ ์‹œ์ฒญํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
        } else if ((this.age < 13) && movie.getRate().equals("13์„ธ ์ „์šฉ")) {
            throw new IllegalStateException("์ด ์˜ํ™”๋ฅผ ์‹œ์ฒญํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
        }

        // else if, else if ...
        // ์ƒˆ๋กœ์šด ๋“ฑ๊ธ‰์ด ์ƒ๊ธธ ๋•Œ๋งˆ๋‹ค ๋˜ ๋‹ค๋ฅธ if ๋ฌธ์ด ํ•„์š”ํ•˜๋‹ค.
        
        playMovie();
    }

    private void playMovie() {
    }

    public static void main(String[] args) {
        final var jane = new MovieWatcher(12);
        final var theExorcist = new Movie("์„ฑ์ธ ์ „์šฉ");

        // ์ œ์ธ์€ 12์‚ด์ด์–ด์„œ ์˜ํ™” <์—‘์†Œ์‹œํŠธ์Šค>๋ฅผ ์‹œ์ฒญํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
        jane.watchMovie(theExorcist);
    }
}

 

// Good
public interface MovieRate {
	// ์„ฑ์ธ ์ „์šฉ, 13์„ธ ์ „์šฉ ๋“ฑ์˜ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ๊ตฌํ˜„์ฒด
    void warnIfNotAllowed(int age);
}

public class AdultsOnlyMovieRate implements MovieRate {

    // 1. ๋ชจ๋“  if ๋ฌธ์„ ์ถ”์ƒํ™”๋กœ ์ด๋™ํ•˜์„ธ์š”.
    @Override
    public void warnIfNotAllowed(int age) {
        if (age < 18) {
            throw new IllegalStateException("์ด ์˜ํ™”๋ฅผ ์‹œ์ฒญํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
        }
    }
}

public class PG13MovieRate implements MovieRate {

    // 2. ๋ชจ๋“  if ๋ฌธ์„ ์ถ”์ƒํ™”๋กœ ์ด๋™ํ•˜์„ธ์š”.
    @Override
    public void warnIfNotAllowed(int age) {
        if (age < 13) {
            throw new IllegalStateException("์ด ์˜ํ™”๋ฅผ ์‹œ์ฒญํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.");
        }
    }
}

public class Movie {

    private final MovieRate rate;

    public Movie(MovieRate rate) {
        this.rate = rate;
    }
    
    public void checkWatchAllowed(int age) {
        rate.warnIfNotAllowed(age);
    }
}



public class MovieWatcher {
    private final int age;

    public MovieWatcher(int age) {
        this.age = age;
    }

    public void watchMovie(Movie movie) {
        movie.checkWatchAllowed(this.age);
    }

    public static void main(String[] args) {
        final var theExorcist = new Movie(new AdultsOnlyMovieRate());
        final var gremlins = new Movie(new PG13MovieRate());

        final var jane = new MovieWatcher(12);

        jane.watchMovie(theExorcist); // 12์‚ด์ด์–ด์„œ ์‹œ์ฒญ ๋ถˆ๊ฐ€
        jane.watchMovie(gremlins); // 12์‚ด์ด์–ด์„œ ์‹œ์ฒญ ๋ถˆ๊ฐ€

        final var joe = new MovieWatcher(16);

        joe.watchMovie(theExorcist); // 16์‚ด์ด์–ด์„œ ์‹œ์ฒญ ๋ถˆ๊ฐ€
        joe.watchMovie(gremlins); // 16์‚ด์ด์–ด์„œ ์‹œ์ฒญ ๊ฐ€๋Šฅ
    }
}

 

์œ„ ์˜ˆ์ œ์—์„œ๋Š” MovieRate์˜ ๋ฌธ์ž์—ด ๋น„๊ต๊ฐ€ ์šฐ๋ฐœ์ ์ธ if๋ฌธ์— ๋Œ€ํ•œ ๋ถ„๊ธฐ์ฒ˜๋ฆฌ๋ผ๊ณ  ํ•˜์—ฌ, ํ•„์ˆ˜๋กœ ์‹๋ณ„๋˜๋Š” if๋ฌธ์˜ ๊ฒฝ์šฐ ๋Œ€์ฒดํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ๋งํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ด๊ฒƒ์— ๋Œ€ํ•œ ์ •ํ™•ํ•œ ํŒ๋‹จ์ด ์ž˜ ์•ˆ์„œ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

์ด์™€๋Š” ๋ณ„๊ฐœ๋กœ ์œ„์™€ ๊ฐ™์€ if, else if ... ๋“ฑ์˜ ์ฝ”๋“œ๋Š” ๊ต‰์žฅํžˆ ๋งŽ์ด ์กด์žฌํ•˜๋Š” ํ˜•์‹์ด๊ธฐ์— ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

 

 

249p, boolean ๋ณ€์ˆ˜ ์žฌ๊ตฌ์„ฑํ•˜๊ธฐ

boolean ๋ณ€์ˆ˜๋Š” if๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ ํ•˜๋ฏ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ๋ง๊ณ , ๋‹คํ˜•์„ฑ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”.
function processBatch(
	bool $useLogin,
	bool $deleteEntries) {
	// ...
}


function processBatch(
	LoginStrategy $login,
	DeletePolicy $deleteionPolicy) {
	// ...
}

boolean ๋ณ€์ˆ˜ ์ž์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์•ˆ๋„˜๊ธฐ๋Š” ๊ฒƒ๋„ ํ•˜๋‚˜์˜ ๋ฐฉ๋ฒ•์ผ ๊ฒƒ ๊ฐ™์€๋ฐ, ์œ„์™€ ๊ฐ™์ด ๊ฐ์ฒด๋กœ ์žฌ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

 

256p, ์•”์‹œ์  else ๋ฌธ ์ถ”๊ฐ€ํ•˜๊ธฐ

else ๋ฌธ์ด ์—†๋Š” if๋ฌธ์ด ์žˆ๋Š”๋ฐ, ๋ช…์‹œ์ ์œผ๋กœ ํ‘œํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋ช…์‹œ์ ์ธ else ๋ฌธ์ด ์žˆ๋Š” ์ฝ”๋“œ๋Š” ๊ฐ€๋…์„ฑ์ด ๋†’๊ณ  ์ธ์ง€ ๋ถ€ํ•˜๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์กฐ๊ฑด์„ ๊ฐ์ง€ํ•˜๊ณ  ๋น ๋ฅด๊ฒŒ ์‹คํŒจํ•˜๊ธฐ ์›์น™์„ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.
function carBrandImplicit(model) {
	if (model === 'A4') return 'Audi';
	
	return 'Mercedes-Benz';	
}


function carBrandImplicit(model) {
	if (model === 'A4') return 'Audi';
	if (model === 'AMG') return 'Mercedes-Benz';
	
	throw new Exception(model + ' ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
}

์ƒ๊ฐํ•ด๋ณด์ง€ ๋ชปํ–ˆ๋˜ ํฌ์ธํŠธ์˜€๋Š”๋ฐ, ์ข‹์€ ๋ฐฉ๋ฒ•์ธ ๊ฒƒ ๊ฐ™๋„ค์š”.

 

 

15์žฅ null

276p, null ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ

null ๊ฐ์ฒด ํŒจํ„ด์€ ์ผ๋ฐ˜ ๊ฐ์ฒด์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์ง€๋งŒ ๊ธฐ๋Šฅ์ด ๊ฑฐ์˜ ์—†๋Š” ํŠน์ˆ˜ ๊ฐ์ฒด์ธ 'null ๊ฐ์ฒด' ์ƒ์„ฑ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ if๋กœ null ์ฐธ์กฐ๋ฅผ ํ™•์ธํ•˜์ง€ ์•Š๊ณ ๋„ null ๊ฐ์ฒด์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
public class CartItem {

    private int price;

    public CartItem(int price) {
        this.price = price;
    }

    public int getPrice() {
        return price;
    }
}


public interface DiscountCoupon {

    double discount(double subtotal);
}


public class RateDiscountCoupon implements DiscountCoupon {
    private double rate;

    public RateDiscountCoupon(double rate) {
        this.rate = rate;
    }

    @Override
    public double discount(double subtotal) {
        return subtotal * (1 - rate);
    }
}


// Null Object ํŒจํ„ด
public class NullDiscountCoupon implements DiscountCoupon {

    @Override
    public double discount(double subtotal) {
        return subtotal;
    }
}


import java.util.List;

public class Cart {
    private List<CartItem> items;
    private DiscountCoupon discountCoupon;

    public Cart(List<CartItem> items, DiscountCoupon discountCoupon) {
        this.items = items;
        this.discountCoupon = discountCoupon;
    }

    public double subtotal() {
        return items.stream()
                .mapToDouble(CartItem::getPrice)
                .sum();
    }

    public double total() {
        return discountCoupon.discount(subtotal());
    }
}


import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Cart cartWithDiscount = new Cart(
                Arrays.asList(
                        new CartItem(1),
                        new CartItem(2),
                        new CartItem(7)
                ),
                new RateDiscountCoupon(0.15)
        );
        System.out.println("Cart total with discount: " + cartWithDiscount.total()); // 8.5

        Cart cartWithoutDiscount = new Cart(
                Arrays.asList(
                        new CartItem(1),
                        new CartItem(2),
                        new CartItem(7)
                ),
                new NullDiscountCoupon()
        );
        System.out.println("Cart total without discount: " + cartWithoutDiscount.total()); // 10.0
    }
}

 

null ๊ฐ์ฒด ํŒจํ„ด์€ ์ด์ „์—๋„ ์ ์šฉํ•ด๋ณธ ์ผ€์ด์Šค๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ, ์ž˜ ํ™œ์šฉํ•œ๋‹ค๋ฉด ์‹ค์ œ๋กœ๋„ ๊ต‰์žฅํžˆ ์œ ์šฉํ•œ ํŒจํ„ด์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

 

 

17์žฅ ๊ฒฐํ•ฉ๋„

327p, ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์š•์‹ฌ ๋ฐฉ์ง€ํ•˜๊ธฐ

๋ฌธ์ œ: ํ•œ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋„ˆ๋ฌด ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ: ์ข…์†์„ฑ์„ ์—†์• ๊ณ  ๋™์ž‘์„ ๋ฆฌํŒฉํ„ฐ๋งํ•˜์„ธ์š”.
๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์š•์‹ฌ์€ ํ•œ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ž์‹ ์˜ ๋™์ž‘๋ณด๋‹ค ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ๋™์ž‘์— ๋” ๊ด€์‹ฌ์„ ๊ฐ€์งˆ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
// Bad
public class Candidate {
    void printJobAddress(Job job) {
    	print("...");
        print(job.address().street());
        print(job.address().city());
        ...
        }
}


// Good
public class Job {
    void printAddress() {
    	print("...");
        print(address().street());
        print(address().city());
        ...
    }
}

 

 

330p, ๊ธฐ๋ณธ ์ธ์ˆ˜๋ฅผ ๋งจ ๋์œผ๋กœ ์ด๋™ํ•˜๊ธฐ

๋ฌธ์ œ: ์ธ์ˆ˜ ๋ชฉ๋ก ์ค‘๊ฐ„์— ๊ธฐ๋ณธ ์ธ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ: ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ณ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋˜, ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์„ ํƒ์  ์ธ์ˆ˜๋ฅผ ํ•„์ˆ˜ ์ธ์ˆ˜ ์•ž์— ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
// Bad
function buildCar($color = "red", $model) {
	...
}

// Good
function buildCar($model, $color = "red") {
	...
}

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

 

 

20์žฅ ํ…Œ์ŠคํŠธ

์•„๋ฌด๋ฆฌ ๋งŽ์€ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋„ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์ •ํ™•ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ฆ๋ช…ํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ, ํ•œ ๋ฒˆ์˜ ํ…Œ์ŠคํŠธ๋กœ ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Œ์„ ์ฆ๋ช…ํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.  - ์•„๋ฏธ๋ฅด ๊ฐ€๋ผ์ด -

 

 

384p, private ๋ฉ”์„œ๋“œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

๋ฌธ์ œ: private ๋ฉ”์„œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ: private ๋ฉ”์„œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜์ง€ ๋ง๊ณ , ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”์ถœํ•˜์„ธ์š”

๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์ƒ์œ„ ์ˆ˜์ค€์˜ ๊ธฐ๋Šฅ์„ ์ค‘์š”ํ•˜๊ฒŒ ์ง€์›ํ•˜๋Š” ๋‚ด๋ถ€ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š” ์–ด๋ ค์›€์— ์ง๋ฉดํ•œ ์ ์ด ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฉ”์„œ๋“œ์˜ ์บก์Šํ™”๋ฅผ ๊นจ๋œจ๋ฆด ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๊ณ , ๋ฉ”์„œ๋“œ๋ฅผ ๋ณต์‚ฌํ•˜๊ฑฐ๋‚˜ ๊ณต๊ฐœํ•˜๊ณ  ์‹ถ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.
// Bad
public final class Star {

    private double distanceInParsecs;

    public double timeToReachLightToUs() {
        return convertDistanceInParsecsToLightYears(distanceInParsecs);
    }

    private double convertDistanceInParsecsToLightYears(double distanceInParsecs) {
        return 3.26 * distanceInParsecs;
        // ํ•จ์ˆ˜๋Š” ์ด๋ฏธ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
        // ์ด ํ•จ์ˆ˜๋Š” distanceInParsecs์— private ์ ‘๊ทผ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—
        // ์ด๋Š” ๋˜ ๋‹ค๋ฅธ ์ฝ”๋“œ ์Šค๋ฉœ์˜ ์ง€ํ‘œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

        // ์ด ํ•จ์ˆ˜๋Š” private์ด๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    }
}


// Good
public final class Star {

    private double distanceInParsecs;

    public double timeToReachLightToUs() {
        return new ParsecsToLightYearsConverter().convert(distanceInParsecs);
    }
}

public final class ParsecsToLightYearsConverter {
    public double convert(double distanceInParsecs) {
        return 3.26 * distanceInParsecs;
    }
}


public class ParsecsToLightYearsConverterTest {

    @Test
    public void testConvert0ParsecsReturns0LightYears() {
        assertEquals(0, new ParsecsToLightYearsConverter().convert(0));
    }

    // ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ฐ์ฒด์— ์˜์กดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    // ๋”ฐ๋ผ์„œ Star์˜ ๋ณ€ํ™˜์„ ํ…Œ์ŠคํŠธํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
    // ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ Star์˜ timeToReachLightToUs() ๋ฉ”์†Œ๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    // ์ด๊ฑด ๊ฐ„์†Œํ™”๋œ ์‹œ๋‚˜๋ฆฌ์˜ค์ž…๋‹ˆ๋‹ค.
}

 

 

399p, ์บก์Šํ™”๋ฅผ ์œ„๋ฐ˜ํ•˜๋Š” ํ…Œ์ŠคํŠธ ๋ณดํ˜ธํ•˜๊ธฐ

๋ฌธ์ œ: ์บก์Šํ™”๋ฅผ ์œ„๋ฐ˜ํ•˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ: ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ๋งˆ์„ธ์š”.

๊ฐ€๋”์€ ํ…Œ์ŠคํŠธ๋ฅผ ์šฐ์„ ์‹œํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋ฉด ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ์บก์Šํ™”๋ฅผ ์œ„๋ฐ˜ํ•˜๊ณ  ๋‚˜์œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์œ ๋ฐœํ•ด ๋ถˆํ•„์š”ํ•œ ๊ฒฐํ•ฉ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ์ „์ฒด ํ™˜๊ฒฝ์„ ํ†ต์ œํ•ด์•ผ ํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ๋ถ„์ด ๊ฐ์ฒด๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ์›์น˜ ์•Š๋Š” ๊ฒฐํ•ฉ์„ ๋ฐœ๊ฒฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋“ค์„ ๋ถ„๋ฆฌํ•˜์„ธ์š”.
// Bad
import java.util.Random;

public class Hangman {

    private String wordToGuess;

    public Hangman() {
        this.wordToGuess = getRandomWord();
        // ํ…Œ์ŠคํŠธ๋Š” ์ด๊ฒƒ์„ ์ œ์–ดํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    }

    public String getWordToGuess() {
        return wordToGuess;
        // ์•ˆํƒ€๊น๊ฒŒ๋„ ์ด๊ฒƒ์„ ๊ณต๊ฐœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
    }

    private String getRandomWord() {
        // ๋ฌด์ž‘์œ„ ๋‹จ์–ด ์ƒ์„ฑ ๋กœ์ง (๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ)
        String[] words = {"apple", "banana", "cherry"};
        return words[new Random().nextInt(words.length)];
    }
}

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

public class HangmanTest {

    @Test
    public void testWordIsGuessed() {
        Hangman hangmanGame = new Hangman();
        assertEquals("tests", hangmanGame.getWordToGuess());
        // ์–ด๋–ป๊ฒŒ ๋‹จ์–ด๊ฐ€ ๋งž์ถ”์–ด์กŒ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
    }
}


// Good
public interface WordRandomizer {
    String newRandomWord();
}

public class Hangman {

    private String wordToGuess;
    private StringBuilder guessedWord;

    public Hangman(WordRandomizer wordRandomizer) {
        this.wordToGuess = wordRandomizer.newRandomWord();
        this.guessedWord = new StringBuilder("_".repeat(wordToGuess.length()));
    }

    public boolean wordWasGuessed() {
        return wordToGuess.equals(guessedWord.toString());
    }

    public void play(char letter) {
        for (int i = 0; i < wordToGuess.length(); i++) {
            if (wordToGuess.charAt(i) == letter) {
                guessedWord.setCharAt(i, letter);
            }
        }
    }
}

public class MockRandomizer implements WordRandomizer {

    @Override
    public String newRandomWord() {
        return "tests";
    }
}

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;

public class HangmanTest {

    @Test
    public void testWordIsGuessed() {
        Hangman hangmanGame = new Hangman(new MockRandomizer());
        // ์ด์ œ ์™„์ „ํ•œ ์ œ์–ด๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค!
        assertFalse(hangmanGame.wordWasGuessed());
        hangmanGame.play('t');
        assertFalse(hangmanGame.wordWasGuessed());
        hangmanGame.play('e');
        assertFalse(hangmanGame.wordWasGuessed());
        hangmanGame.play('s');
        assertTrue(hangmanGame.wordWasGuessed());
        // ์ด์ œ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
    }
}

 

 

22์žฅ ์˜ˆ์™ธ

420p, ์ค‘์ฒฉ๋œ try/catch ์žฌ์ž‘์„ฑํ•˜๊ธฐ

๋ฌธ์ œ: ์ค‘์ฒฉ๋œ try/catch๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ: ์˜ˆ์™ธ๋ฅผ ์ค‘์ฒฉ์‹œํ‚ค์ง€ ๋งˆ์„ธ์š”. ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ๋‚ด๋ถ€ ๋ธ”๋ก์—์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์„ ๋”ฐ๋ผ๊ฐ€๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ฒ˜๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋‚˜ ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•˜์„ธ์š”.
// Bad
try {
    transaction.commit();
} catch (e) {
    logerror(e);
    if (e instanceOf DBError) {
      try {
          transaction.rollback();
      } catch (e) {
          doMoreLoggingRollbackFailed(e);
      }
    }
}


// Good
try {
    transaction.commit();
} catch (transactionError) {
    this.withTransactionErrorDo(
        transationError, transaction);
}

 

 

25์žฅ ๋ณด์•ˆ

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

 

450p, ์ž…๋ ฅ๊ฐ’ ๊ฒ€์—ดํ•˜๊ธฐ

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

 

์ž…๋ ฅ ๊ฒ€์—ด (Input Sanitization)
์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „์— ์ž…๋ ฅ์ด ์•ˆ์ „ํ•˜๊ณ  ํ˜•์‹์„ ์ค€์ˆ˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์•…์˜์ ์ธ ์‚ฌ์šฉ์ž๊ฐ€ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” SQL ์‚ฝ์ž…, ์‚ฌ์ดํŠธ ๊ฐ„ ์Šคํฌ๋ฆฝํŒ…(XSS), ๊ธฐํƒ€ ๊ณต๊ฒฉ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

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

public class Main {
    public static String sanitize(String input) {
        // ๋ฌธ์ž์™€ ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ๋ชจ๋‘ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
        return input.replaceAll("[^a-zA-Z0-9]", "");
    }

	...
}

 

 

ํ›„๊ธฐ

๋ชจ๋“  ์ฑ•ํ„ฐ๊ฐ€ ๋ฌธ์ œ - ํ•ด๊ฒฐ - ์„ค๋ช… - ์˜ˆ์ œ ์ฝ”๋“œ ๋“ฑ์˜ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์–ด์„œ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ต‰์žฅํžˆ ๋‹ค์–‘ํ•œ ์–ธ์–ด๋“ค๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด์„œ ์˜คํžˆ๋ ค ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ๋” ํž˜๋“  ๋ถ€๋ถ„์ด ๋งŽ์•˜๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. (Java, C#, Python, Ruby, PHP, JS ...)

์˜ˆ์ œ ์ฝ”๋“œ๊ฐ€ ๋Œ€๋ถ€๋ถ„ ๊ธธ์ง€๋Š” ์•Š์ง€๋งŒ, ๊ฐ ์–ธ์–ด๋งˆ๋‹ค ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ผ€์ด์Šค๋“ค๋„ ์กด์žฌํ•ด์„œ ์กฐ๊ธˆ ์•„์‰ฌ์› ๋„ค์š”.

 

๋˜ํ•œ ์„œ๋ก ์—์„œ ๋งํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ, ํด๋ฆฐ ์ฝ”๋“œ์™€ ์ค‘๋ณต ๋‚ด์šฉ๋“ค์ด ๊ต‰์žฅํžˆ ๋งŽ์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์€ ๋Š๋‚Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

"ํ•œ๋น›๋ฏธ๋””์–ด <๋‚˜๋Š” ๋ฆฌ๋ทฐ์–ด๋‹ค> ํ™œ๋™์„ ์œ„ํ•ด์„œ ์ฑ…์„ ์ œ๊ณต๋ฐ›์•„ ์ž‘์„ฑ๋œ ์„œํ‰์ž…๋‹ˆ๋‹ค."

 

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€