CleanCode TIL (2022.02.15)

Henry Choยท2022๋…„ 2์›” 15์ผ
0

๋…ธ๊ฐœ๋ถ

๋ชฉ๋ก ๋ณด๊ธฐ
21/31

DAY 23

๐Ÿ”–ย ์˜ค๋Š˜ ์ฝ์€ ๋ฒ”์œ„ : 11. ์‹œ์Šคํ…œ(194~205p)


๐Ÿค“ย ์ฑ…์—์„œ ๊ธฐ์–ตํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ

๋„์‹œ๋ฅผ ์„ธ์šด๋‹ค๋ฉด?

  • ๋„์‹œ์—๋Š” ํฐ ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ๋Š” ์‚ฌ๋žŒ๋“ค๋„ ์žˆ๊ณ  ์ž‘์€ ์‚ฌํ•ญ์— ์ง‘์ค‘ํ•˜๋Š” ์‚ฌ๋žŒ๋“ค๋„ ์žˆ๋‹ค
  • ์ ์ ˆํ•œ ์ถ”์ƒํ™”์™€ ๋ชจ๋“ˆํ™” โ†’ ํฐ ๊ทธ๋ฆผ์€ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ด๋„ ๊ฐœ์ธ์ด ๊ด€๋ฆฌํ•˜๋Š” ๊ตฌ์„ฑ์š”์†Œ๋Š” ํšจ์œจ์ ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค
  • ๊นจ๋—ํ•œ ์ฝ”๋“œ โ†’ ๋‚ฎ์€ ์ถ”์ƒํ™” ์ˆ˜์ค€์—์„œ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ
  • ๋†’์€ ์ถ”์ƒํ™” ์ˆ˜์ค€ โ†’ ์‹œ์Šคํ…œ ์ˆ˜์ค€ ์—์„œ๋„ ๊นจ๋—ํ•จ์„ ์œ ์ง€ํ•˜์ž

์‹œ์Šคํ…œ ์ œ์ž‘๊ณผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ์„ ๋ถ„๋ฆฌํ•˜๋ผ

์ œ์ž‘ ๊ณผ ์‚ฌ์šฉ์€ ์•„์ฃผ ๋‹ค๋ฅด๋‹ค

  • ์ œ์ž‘(์ค€๋น„๊ณผ์ •)
    • APP ๊ฐ์ฒด๋ฅผ ์ œ์ž‘ํ•˜๊ณ  ์˜์กด์„ฑ์„ ์„œ๋กœ ์—ฐ๊ฒฐ
  • ์‚ฌ์šฉ(๋Ÿฐํƒ€์ž„ ๋กœ์ง)
    • ์ค€๋น„ ๊ณผ์ • ์ดํ›„์— ์ด์–ด์ง€๋Š” ๋กœ์ง

์ดˆ๊ธฐํ™” ์ง€์—ฐ(๊ณ„์‚ฐ ์ง€์—ฐ) ๊ธฐ๋ฒ•

public Service getService() {
    if (service == null)
        service = new MyServiceImpl(...); // ๋ชจ๋“  ์ƒํ™ฉ์— ์ ํ•ฉํ•œ ๊ธฐ๋ณธ๊ฐ’์ผ๊นŒ? 
    return service;
}
  • ์žฅ์ 
    • ์‹ค์ œ๋กœ ํ•„์š”ํ•  ๋•Œ๊นŒ์ง€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ถ€ํ•˜ ๊ฐ์†Œ โ†’ APP ๊ธฐ๋™ ์‹œ๊ฐ„ ๋นจ๋ผ์ง
    • ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ null ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ
  • ๋ฌธ์ œ์ 
    • getService ๋ฉ”์„œ๋“œ๊ฐ€ MyServiceImpl ๊ณผ ์ƒ์„ฑ์ž ์ธ์ˆ˜์— ๋ช…์‹œ์ ์œผ๋กœ ์˜์กด
    • ์˜์กด์„ฑ ํ•ด๊ฒฐํ•˜์ง€ ์•Š์œผ๋ฉด ์ปดํŒŒ์ผ์ด ์•ˆ๋Œ
    • MyServiceImpl์ด ๋ฌด์„œ์šด ๊ฐ์ฒด๋ผ๋ฉด ํ˜ธ์ถœ์ „์— ํ…Œ์ŠคํŠธ ์ „์šฉ ๊ฐ์ฒด๋ฅผ service ํ•„๋“œ์— ํ• ๋‹น ํ•„์š”
    • ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์ด ์„ž์—ฌ์žˆ์œผ๋ฏ€๋กœ ๋‹จ์ผ ์ฑ…์ž„ ์›์น™ ์œ„๋ฐ˜
    • MyServiceImpl์ด ๋ชจ๋“  ์ƒํ™ฉ์— ์ ํ•ฉํ•œ ๊ฐ์ฒด์ธ์ง€ ํ™•์ธ ๋ถˆ๊ฐ€
    • ์ž์ฃผ ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋“ˆ์„ฑ ์ €ํ•˜, ์ค‘๋ณต๋„ ์˜ฌ๋ผ๊ฐ

Main ๋ถ„๋ฆฌ ๊ธฐ๋ฒ•

  • ์ƒ์„ฑ๊ณผ ๊ด€๋ จํ•œ ์ฝ”๋“œ๋Š” main ํ˜น์€ main์ด ํ˜ธ์ถœํ•˜๋Š” ๋ชจ๋“ˆ๋กœ
  • ๋‚˜๋จธ์ง€ ์‹œ์Šคํ…œ์€ ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๊ณ  ์˜์กด์„ฑ ์—ฐ๊ฒฐ ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •

ํŒฉํ† ๋ฆฌ

  • ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์‹œ์ ์„ APP์ด ๊ฒฐ์ •ํ•  ํ•„์š” ์ƒ๊น€
  • OrderProcessing APP์€ LineItem ์ด ์ƒ์„ฑ๋˜๋Š” ๊ตฌ์ฒด์ ์ธ ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด์ง€๋งŒ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์‹œ์ ์„ ํ†ต์ œ ๊ฐ€๋Šฅ
  • ์ƒ์„ฑ ๋ฐฉ๋ฒ•์€ main์˜ LineItemFactoryImplementation ์— ๊ธฐ์ˆ 

์˜์กด์„ฑ ์ฃผ์ž…

  • ์ œ์–ด ์—ญ์ „ ๊ธฐ๋ฒ•์„ ์˜์กด์„ฑ ๊ด€๋ฆฌ ์—์„œ ์ ์šฉํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜
  • ์ œ์–ด ์—ญ์ „: ํ•œ ๊ฐ์ฒด๊ฐ€ ๋งก์€ ๋ณด์กฐ ์ฑ…์ž„์„ ์ƒˆ๋กœ์šด ๊ฐ์ฒด์—๊ฒŒ ๋– ๋„˜๊น€ โ†’ SRP ์„ฑ๋ฆฝ
  • ์˜์กด์„ฑ ๊ด€๋ฆฌ: ๊ฐ์ฒด๋Š” ์˜์กด์„ฑ ์ž์ฒด๋ฅผ ์ธ์Šคํ„ด์Šค๋กœ ๋งŒ๋“œ๋Š” ์ฑ…์ž„์€ ์ง€์ง€ ์•Š์ง€๋งŒ ๋‹ค๋ฅธ ์ „๋‹ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋„˜๊ฒจ์•ผํ•จ
  • ์ดˆ๊ธฐ ์„ค์ •์€ ์‹œ์Šคํ…œ ์ „์ฒด์—์„œ ํ•„์š”ํ•˜๋ฏ€๋กœ main ์ด๋‚˜ ํŠน์ˆ˜ ์ปจํ…Œ์ด๋„ˆ์— ๋„˜๊น€
  • eg) JNDI ๊ฒ€์ƒ‰
    MyService myService = (MyService)(jndiContext.lookup("NameOfMyService"));
    • ๊ฐ์ฒด๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ์„œ๋ฒ„์— ์ด๋ฆ„์„ ์ œ๊ณตํ•˜๊ณ  ๊ทธ ์ด๋ฆ„์— ์ผ์น˜ํ•˜๋Š” ์„œ๋น„์Šค๋ฅผ ์š”์ฒญ
    • ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ์ฒด์˜ ์œ ํ˜•์„ ์ œ์–ดํ•˜์ง€ ์•Š๊ณ  ์˜์กด์„ฑ์„ ๋Šฅ๋™์ ์œผ๋กœ ํ•ด๊ฒฐ
  • ํด๋ž˜์Šค๋Š” ์˜์กด์„ฑ์— ๋Œ€ํ•ด ์™„์ „ํžˆ ์ˆ˜๋™์ 
  • ๋Œ€์‹ ์— ์˜์กด์„ฑ ์ฃผ์ž…๋ฒ•์œผ๋กœ ์„ค์ •์ž๋‚˜ ์ƒ์„ฑ์ž ์ธ์ˆ˜๋ฅผ ์ œ๊ณต
  • eg) ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ
    • ๊ฐ์ฒด ์‚ฌ์ด์˜ ์˜์กด์„ฑ์€ XML ํŒŒ์ผ์— ์ •์˜
    • ์ฝ”๋“œ์—์„œ๋Š” ์ด๋ฆ„์œผ๋กœ ํŠน์ •ํ•œ ๊ฐ์ฒด๋ฅผ ์š”์ฒญ
  • ์ดˆ๊ธฐํ™” ๊ธฐ๋ฒ•์—์„œ๋„ ์˜์กด์„ฑ ์ฃผ์ž… ์‚ฌ์šฉ ๊ฐ€๋Šฅ
    • ๋Œ€๋ถ€๋ถ„์€ ๊ณ„์‚ฐ ์ง€์—ฐ์ด๋‚˜ ์ตœ์ ํ™”์— ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํŒฉํ† ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ• ์ œ๊ณต
    • eg) ๊ณ„์‚ฐ ์ง€์—ฐ ๊ธฐ๋ฒ•, ์ตœ์ ํ™” ๊ธฐ๋ฒ•

ํ™•์žฅ

  • ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜๋Š” ์‚ฌ์ „ ๊ณ„ํš์ด ํ•„์š”ํ•˜๋‹ค
  • ์ˆ˜๋ช…์ด ์งง๋‹ค ๋Š” ๋ณธ์งˆ๋กœ ์ธํ•ด ์•„ํ‚คํ…์ฒ˜์˜ ์ ์ง„์ ์ธ ๋ฐœ์ „ ๊ฐ€๋Šฅ
  • ๊ด€์‹ฌ์‚ฌ๋ฅผ ์ ์ ˆํžˆ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌ

ํšก๋‹จ(cross-cutting) ๊ด€์‹ฌ์‚ฌ

  • ์˜์†์„ฑ๊ณผ ๊ฐ™์€ ๊ด€์‹ฌ์‚ฌ๋Š” APP์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ฐ์ฒด ๊ฒฝ๊ณ„๋ฅผ ๋„˜๋‚˜๋“ ๋‹ค
  • ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ์ „๋ฐ˜์ ์œผ๋กœ ๋™์ผํ•œ ๋ฐฉ์‹์„ ์ด์šฉํ•˜๊ฒŒ ํ•ด์•ผํ•œ๋‹ค.
    • ํŠน์ • DBMS, ๋…์ž์ ์ด ํŒŒ์ผ ์‚ฌ์šฉ, ํ…Œ์ด๋ธ”๊ณผ ์—ด์€ ๊ฐ™์€ ๋„ค์ด๋ฐ๋ฃฐ, ํŠธ๋žœ์žญ์…˜ ์˜๋ฏธ๊ฐ€ ์ผ๊ด€์ 
  • AOP(Aspect-Oriented Programming): ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
    • ํŠน์ • ๊ด€์‹ฌ์‚ฌ ์ง€์›ํ•˜๋ ค๋ฉด ์‹œ์Šคํ…œ์—์„œ ํŠน์ • ์ง€์ ๋“ค์ด ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹์„ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ๋ฐ”๊ฟ”์•ผํ•จ
  1. ์˜์†์ ์œผ๋กœ ์ €์žฅํ•  ๊ฐ์ฒด์™€ ์†์„ฑ์„ ์„ ์–ธ
  2. ์˜์†์„ฑ ์ฑ…์ž„์„ ์˜์†์„ฑ ํ”„๋ ˆ์ž„์›Œํฌ์— ์œ„์ž„
  3. AOP ํ”„๋ ˆ์ž„์›Œํฌ ๋Œ€์ƒ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š” ์ƒํƒœ๋กœ ๋™์ž‘ ๋ฐฉ์‹ ๋ณ€๊ฒฝ

์ž๋ฐ” ํ”„๋ก์‹œ

  • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ๊ฐ์‹ธ๋Š” ๋‹จ์ˆœํ•œ ์ƒํ™ฉ์— ์ ํ•ฉ
  • ํด๋ž˜์Šค ํ”„๋ก์‹œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด CGLIB, ASM, Javassist ๊ฐ™์€ ๋ฐ”์ดํŠธ ์ฝ”๋“œ ์ฒ˜๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•„์š”
// Bank.java (ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ๊ฐ์ถ˜๋‹ค)
import java.util.*;
// ์€ํ–‰ ์ถ”์ƒํ™”
public interface Bank {
    Collection < Account > getAccounts();
    void setAccounts(Collection < Account > accounts);
}
// BankImpl.java 
import java.util.*;
// ์ถ”์ƒํ™”๋ฅผ ์œ„ํ•œ POJO("Plain Old Java Object") ๊ตฌํ˜„
public class BankImpl implements Bank {
    private List < Account > accounts;
    public Collection < Account > getAccounts() {
        return accounts;
    }
    public void setAccounts(Collection < Account > accounts) {
        this.accounts = new ArrayList < Account > ();
        for (Account account: accounts) {
            this.accounts.add(account);
        }
    }
}
// BankProxyHandler.java 
import java.lang.reflect.*;
import java.util.*;
// ํ”„๋ก์‹œ API๊ฐ€ ํ•„์š”ํ•œ "InvocationHandler"
public class BankProxyHandler implements InvocationHandler {
    private Bank bank;
    public BankProxyHandler(Bank bank) {
        this.bank = bank;
    }
    // InvocationHandler์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("getAccounts")) {
            bank.setAccounts(getAccountsFromDatabase());
            return bank.getAccounts();
        } else if (methodName.equals("setAccounts")) {
            bank.setAccounts((Collection < Account > ) args[0]);
            setAccountsToDatabase(bank.getAccounts());
            return null;
        } else { ...
        }
    }
    // ์„ธ๋ถ€์‚ฌํ•ญ์€ ์—ฌ๊ธฐ์— ์ด์–ด์ง„๋‹ค.
    protected Collection < Account > getAccountsFromDatabase() { ...
    }
    protected void setAccountsToDatabase(Collection < Account > accounts) { ...
    }
}
// ๋‹ค๋ฅธ ๊ณณ์— ์œ„์น˜ํ•˜๋Š” ์ฝ”๋“œ
Bank bank = (Bank) Proxy.newProxyInstance(Bank.class.getClassLoader(),
    new Class[] {
        Bank.class
    },
    new BankProxyHandler(new BankImpl()));
  • ํ”„๋ก์‹œ๋กœ ๊ฐ์Œ€ ์ธํ„ฐํŽ˜์ด์Šค Bank์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๋…ผ๋ฆฌ ๊ตฌํ˜„ํ•˜๋Š” POJO BankImpl์„ ์ •์˜
  • ํ”„๋ก์‹œ API์—๋Š” InvocationHandler๋ฅผ ๋„˜๊น€ โ†’ ํ”„๋ก์‹œ์— ํ˜ธ์ถœ๋˜๋Š” Bank ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์— ์‚ฌ์šฉ
  • BankProxyHandler๋Š” ์ž๋ฐ” ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์‚ฌ์šฉํ•ด ์ œ๋„ˆ๋ฆญ ๋ฉ”์„œ๋“œ์— ์ƒ์‘ํ•˜๋Š” BankImpl ๋งค์„œ๋“œ๋กœ ๋งคํ•‘
  • ํ”„๋ก์‹œ๋Š” ์ฝ”๋“œ ์–‘์ด ๋งŽ๊ณ  ์‹œ์Šคํ…œ ๋‹จ์œ„๋กœ ์‹คํ–‰ ์ง€์ ์„ ๋ช…์‹œํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์ œ๊ณตํ•˜์ง€ ์•Š์Œ

๐Ÿค”ย ๋– ์˜ค๋ฅด๋Š” ์ƒ๊ฐ

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

๐Ÿ”Žย ์งˆ๋ฌธ

๐Ÿ“ย ์†Œ๊ฐ 3์ค„ ์š”์•ฝ

  • ์‹œ์Šคํ…œ ์ œ์ž‘๊ณผ ์‹œ์Šคํ…œ ์‚ฌ์šฉ์„ ๋ถ„๋ฆฌํ•˜๋ผ(main๋ถ„๋ฆฌ, ํŒฉํ† ๋ฆฌ, ์˜์กด์„ฑ ์ฃผ์ž… ๊ธฐ๋ฒ•)
  • ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(AOP): ํŠน์ • ๊ด€์‹ฌ์‚ฌ์— ๋Œ€ํ•ด์„œ๋Š” ํŠน์ • ์ง€์ ์˜ ๋™์ž‘ ๋ฐฉ์‹์ด ์ผ๊ด€๋˜์–ด์•ผ ํ•จ
  • ์ž๋ฐ” ํ”„๋ก์‹œ: ํŠน์ • ๊ด€์ ์— ๋Œ€ํ•œ ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‚˜ ์ฝ”๋“œ์˜ ์–‘์ด ๋งŽ์•„์ง€๊ณ  ์‹คํ–‰ ์ง€์  ๋ช…์‹œ ๋ชปํ•จ
profile
Full stack tech visionary

0๊ฐœ์˜ ๋Œ“๊ธ€