๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐Ÿญ Chapter 7: ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ

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

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

๐ŸŽƒ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ณด๋‹ค ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ผโ€‹

์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ํŽธ์ด ๋‚ซ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ˜ธ์ถœ์ž ์ฝ”๋“œ๊ฐ€ ๋” ๊น”๋”ํ•ด์ง„๋‹ค. ๋…ผ๋ฆฌ๊ฐ€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์ฝ”๋“œ์™€ ๋’ค์„ž์ด์ง€ ์•Š์œผ๋‹ˆ๊นŒ.

public class DeviceController {
// ...
public void sendShutDown() {
try {
tryToShutDown();
} catch (DeviceShutDownError e) {
logger.log(e);
}
}

private void tryToShutDown() throws DeviceShutDownError {
DeviceHandle handle = getHandle(DEV1);
DeviceRecord record = retrieveDeviceRecord(handle);

pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}

private DeviceHandle getHandle(DeviceID id) {
// ...
throw new DeviceShutDownError("Invalid handle for: " + id.toString());
// ...
}

// ...
}

๋””๋ฐ”์ด์Šค๋ฅผ ์ข…๋ฃŒํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ถ„๋ฆฌํ–ˆ๋‹ค. ๊ฐ ๊ฐœ๋…์„ ๋…๋ฆฝ์ ์œผ๋กœ ์‚ดํŽด๋ณด๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŽƒ Try-Catch-Finally ๋ฌธ๋ถ€ํ„ฐ ์ž‘์„ฑํ•˜๋ผโ€‹

์˜ˆ์™ธ์—์„œ ํ”„๋กœ๊ทธ๋žจ ์•ˆ์—๋‹ค ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์€ ๋งค์šฐ ํฅ๋ฏธ๋กญ๋‹ค. try-catch-finally ๋ฌธ์—์„œ try ๋ธ”๋ก์— ๋“ค์–ด๊ฐ€๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์–ด๋Š ์‹œ์ ์—์„œ๋“  ์‹คํ–‰์ด ์ค‘๋‹จ๋œ ํ›„ catch ๋ธ”๋ก์œผ๋กœ ๋„˜์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

์–ด๋–ค ๋ฉด์—์„œ try ๋ธ”๋ก์€ ํŠธ๋žœ์žญ์…˜๊ณผ ๋น„์Šทํ•˜๋‹ค. try ๋ธ”๋ก์—์„œ ๋ฌด์Šจ ์ผ์ด ์ƒ๊ธฐ๋“ ์ง€ catch ๋ธ”๋ก์€ ํ”„๋กœ๊ทธ๋žจ ์ƒํƒœ๋ฅผ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์งค ๋•Œ๋Š” try-catch-finally ๋ฌธ์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํŽธ์ด ๋‚ซ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด try ๋ธ”๋ก์—์„œ ๋ฌด์Šจ ์ผ์ด ์ƒ๊ธฐ๋“ ์ง€ ํ˜ธ์ถœ์ž๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ์ƒํƒœ๋ฅผ ์ •์˜ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

๋‹ค์Œ์€ ํŒŒ์ผ์ด ์—†์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š”์ง€ ์•Œ์•„๋ณด๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋‹ค.

@Test(expected = StorageException.class)
public void retrieveSectionShouldThrowOnInvalidFileName() {
sectionStore.retrieveSection("invalid - file");
}

๋‹จ์œ„ ํ…Œ์ŠคํŠธ์— ๋งž์ถฐ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค.

public List<RecordedGrip> retrieveSection(String sectionName) {
// ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•  ๋•Œ๊นŒ์ง€ ๋น„์–ด ์žˆ๋Š” ๋”๋ฏธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
return new ArrayList<RecordedGrip>();
}

๊ทธ๋Ÿฐ๋ฐ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจํ•œ๋‹ค. ์ž˜๋ชป๋œ ํŒŒ์ผ ์ ‘๊ทผ์„ ์‹œ๋„ํ•˜๊ฒŒ ๊ตฌํ˜„์„ ๋ณ€๊ฒฝํ•˜์ž. ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.

public List<RecordedGrip> retrieveSection(String sectionName) {
try {
FileInputStream stream = new FileInputStream(sectionName);
} catch (Exception e) {
throw new StorageException("retrieval error", e);
}
return new ArrayList<RecordedGrip>();
}

์ฝ”๋“œ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋ฏ€๋กœ ์ด์ œ๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณตํ•œ๋‹ค. ์ด ์‹œ์ ์—์„œ ๋ฆฌํŒฉํ„ฐ๋ง์ด ๊ฐ€๋Šฅํ•˜๋‹ค. catch ๋ธ”๋ก์—์„œ ์˜ˆ์™ธ ์œ ํ˜•์„ ์ขํ˜€ ์‹ค์ œ๋กœ FileInputStream ์ƒ์„ฑ์ž๊ฐ€ ๋˜์ง€๋Š” FileNotFoundException์„ ์žก์•„๋‚ธ๋‹ค.

public List<RecordedGrip> retrieveSection(String sectionName) {
try {
FileInputStream stream = new FileInputStream(sectionName);
stream.close();
} catch (FileNotFoundException e) {
throw new StorageException("retrieval error", e);
}
return new ArrayList<RecordedGrip>();
}

try-catch ๊ตฌ์กฐ๋กœ ๋ฒ”์œ„๋ฅผ ์ •ํ–ˆ์œผ๋ฏ€๋กœ TDD๋ฅผ ์‚ฌ์šฉํ•ด ํ•„์š”ํ•œ ๋‚˜๋จธ์ง€ ๋…ผ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

๋จผ์ € ๊ฐ•์ œ๋กœ ์˜ˆ์™ธ๋ฅผ ์ผ์œผํ‚ค๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•œ ํ›„ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ถŒ์žฅํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ try ๋ธ”๋ก์˜ ํŠธ๋žœ์ ์…˜ ๋ฒ”์œ„๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ๋ฒ”์œ„ ๋‚ด์—์„œ ํŠธ๋žœ์žญ์…˜ ๋ณธ์งˆ์„ ์œ ์ง€ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

๐ŸŽƒ ๋ฏธํ™•์ธ(unchecked) ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ผโ€‹

ํ™•์ธ๋œ ์˜ˆ์™ธ๋Š” OCP(Open Closed Principle)๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค. ๋ฉ”์„œ๋“œ์—์„œ ํ™•์ธ๋œ ์˜ˆ์™ธ๋ฅผ ๋˜์กŒ๋Š”๋ฐ catch ๋ธ”๋ก์ด ์„ธ ๋‹จ๊ณ„ ์œ„์— ์žˆ๋‹ค๋ฉด ๊ทธ ์‚ฌ์ด ๋ฉ”์„œ๋“œ ๋ชจ๋‘๊ฐ€ ์„ ์–ธ๋ถ€์— ํ•ด๋‹น ์˜ˆ์™ธ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ํ•˜์œ„ ๋‹จ๊ณ„์—์„œ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ƒ์œ„ ๋‹จ๊ณ„ ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€๋ฅผ ์ „๋ถ€ ๊ณ ์ณ์•ผ ํ•œ๋‹ค๋Š” ๋ง์ด๋‹ค. ๋ชจ๋“ˆ๊ณผ ๊ด€๋ จ๋œ ์ฝ”๋“œ๊ฐ€ ์ „ํ˜€ ๋ฐ”๋€Œ์ง€ ์•Š๋”๋ผ๋„ (์„ ์–ธ๋ถ€๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋ฏ€๋กœ) ๋ชจ๋“ˆ์„ ๋‹ค์‹œ ๋นŒ๋“œํ•œ ๋‹ค์Œ ๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ง์ด๋‹ค.

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

๐ŸŽƒ ์˜ˆ์™ธ์— ์˜๋ฏธ๋ฅผ ์ œ๊ณตํ•˜๋ผโ€‹

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

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

๐ŸŽƒ ํ˜ธ์ถœ์ž๋ฅผ ๊ณ ๋ คํ•ด ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋ผโ€‹

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

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

๐ŸŽƒ ์ •์ƒ ํ๋ฆ„์„ ์ •์˜ํ•˜๋ผโ€‹

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

์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด์ž. ๋‹ค์Œ์€ ๋น„์šฉ ์ฒญ๊ตฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ด๊ณ„๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ํ—ˆ์ˆ ํ•œ ์ฝ”๋“œ๋‹ค.

try {
MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
m_total += getMealPerDiem();
}

์˜ˆ์™ธ๊ฐ€ ๋…ผ๋ฆฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค. ํŠน์ˆ˜ ์ƒํ™ฉ์„ ์ฐจ๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋ฉด ๋” ์ข‹์ง€ ์•Š์„๊นŒ? ๊ทธ๋Ÿฌ๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๋” ๊ฐ„๊ฒฐํ•ด์ง€๋ฆฌ๋ผ. ๋‹ค์Œ์„ ์‚ดํŽด๋ณด์ž.

MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

์œ„์ฒ˜๋Ÿผ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋Šฅํ• ๊นŒ? ๊ฐ€๋Šฅํ•˜๋‹ค. ExpenseReportDAO๋ฅผ ๊ณ ์ณ ์–ธ์ œ๋‚˜ MealExpense ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ฒญ๊ตฌํ•œ ์‹๋น„๊ฐ€ ์—†๋‹ค๋ฉด ์ผ์ผ ๊ธฐ๋ณธ ์‹๋น„๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” MealExpense ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

public class PerDiemMealExpenses implements MealExpenses {
public int getTotal() {
// ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ผ์ผ ๊ธฐ๋ณธ ์‹๋น„๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
}
}

์ด๋ฅผ ํŠน์ˆ˜ ์‚ฌ๋ก€ ํŒจํ„ด(SPECIAL CASE PATTERN)์ด๋ผ ๋ถ€๋ฅธ๋‹ค. ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๊ฐ์ฒด๋ฅผ ์กฐ์ž‘ํ•ด ํŠน์ˆ˜ ์‚ฌ๋ก€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค. ํด๋ž˜์Šค๋‚˜ ๊ฐ์ฒด๊ฐ€ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์„ ์บก์Šํ™”ํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ.

๐ŸŽƒ null์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ๋งˆ๋ผโ€‹

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

public void registerItem(Item item) {
if (item != null) {
ItemRegistry registry = peristentStore.getItemRegistry();
if (registry != null) {
Item existing = registry.getItem(item.getID());
if (existing.getBillingPeriod().hasRetailOwner()) {
existing.register(item);
}
}
}
}

null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ผ๊ฑฐ๋ฆฌ๋ฅผ ๋Š˜๋ฆด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ˜ธ์ถœ์ž์—๊ฒŒ ๋ฌธ์ œ๋ฅผ ๋– ๋„˜๊ธด๋‹ค. ๋ˆ„๊ตฌ ํ•˜๋‚˜๋ผ๋„ null ํ™•์ธ์„ ๋นผ๋จน๋Š”๋‹ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํ†ต์ œ ๋ถˆ๋Šฅ์— ๋น ์งˆ์ง€๋„ ๋ชจ๋ฅธ๋‹ค.

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

๋งŽ์€ ๊ฒฝ์šฐ์— ํŠน์ˆ˜ ์‚ฌ๋ก€ ๊ฐ์ฒด๊ฐ€ ์†์‰ฌ์šด ํ•ด๊ฒฐ์ฑ…์ด๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ƒ๊ฐํ•ด๋ณด์ž.

List<Employee> employees = getEmployees();
if (employees != null) {
for (Employee e : employees) {
totalPay += e.getPay();
}
}

์œ„ ์˜ˆ์ œ์—์„œ ๋ฐ˜๋“œ์‹œ null์„ ๋ฐ˜ํ™˜ํ•  ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ? getEmployees๋ฅผ ๋ณ€๊ฒฝํ•ด ๋นˆ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ๊น”๋”ํ•ด์ง„๋‹ค.

List<Employee> employees = getEmployees();
for(Employee e : employees) {
totalPay += e.getPay();
}

๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ ์ž๋ฐ”์—๋Š” Collections.emptyList()๊ฐ€ ์žˆ์–ด ๋ฏธ๋ฆฌ ์ •์˜๋œ ์ฝ๊ธฐ ์ „์šฉ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์šฐ๋ฆฌ ๋ชฉ์ ์— ์ ํ•ฉํ•œ ๋ฆฌ์ŠคํŠธ๋‹ค.

public List<Employee> getEmployees() {
if (/* ์ง์›์ด ์—†๋‹ค๋ฉด */)
return Collections.emptyList();
}

์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ฝ”๋“œ๋„ ๊น”๋”ํ•ด์งˆ๋ฟ๋”๋Ÿฌ NullPointerException์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ๋„ ์ค„์–ด๋“ ๋‹ค.

๐ŸŽƒ null์„ ์ „๋‹ฌํ•˜์ง€ ๋งˆ๋ผโ€‹

๋ฉ”์„œ๋“œ์—์„œ null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹๋„ ๋‚˜์˜์ง€๋งŒ ๋ฉ”์„œ๋“œ๋กœ null์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์€ ๋” ๋‚˜์˜๋‹ค. ์ •์ƒ์ ์ธ ์ธ์ˆ˜๋กœ null์„ ๊ธฐ๋Œ€ํ•˜๋Š” API๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ฉ”์„œ๋“œ๋กœ null์„ ์ „๋‹ฌํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ตœ๋Œ€ํ•œ ํ”ผํ•œ๋‹ค.

๋Œ€๋‹ค์ˆ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋Š” ํ˜ธ์ถœ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ ๋„˜๊ธฐ๋Š” null์„ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์• ์ดˆ์— null์„ ๋„˜๊ธฐ์ง€ ๋ชปํ•˜๋„๋ก ๊ธˆ์ง€ํ•˜๋Š” ์ •์ฑ…์ด ํ•ฉ๋ฆฌ์ ์ด๋‹ค. ์ฆ‰, ์ธ์ˆ˜๋กœ null์ด ๋„˜์–ด์˜ค๋ฉด ์ฝ”๋“œ์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค. ์ด๋Ÿฐ ์ •์ฑ…์„ ๋”ฐ๋ฅด๋ฉด ๊ทธ๋งŒํผ ๋ถ€์ฃผ์˜ํ•œ ์‹ค์ˆ˜๋ฅผ ์ €์ง€๋ฅผ ํ™•๋ฅ ๋„ ์ž‘์•„์ง„๋‹ค.